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

Unified Diff: net/http/transport_security_state_unittest.cc

Issue 2144693004: Add the ability to send Expect-Staple reports. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@ocsp-verify-result
Patch Set: Make switch statements Android/ChromeOS friendly Created 4 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') | no next file » | 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 ea97a369bce366c0b119a40346ac64873cf9e38f..ac97e5d5bcf239dcc110d0ccccfa77f65ec8a8e2 100644
--- a/net/http/transport_security_state_unittest.cc
+++ b/net/http/transport_security_state_unittest.cc
@@ -240,6 +240,110 @@ void CheckHPKPReport(
validated_certificate_chain, report_validated_certificate_chain));
}
+// Checks the following hold for |report| such that it is a valid Expect-Staple
+// report:
+// 1. |report| is a JSON dictionary.
+// 2. The "hostname" and "port" fields match |host_port_pair|.
+// 3. The "response-status" field matches |response_status|
+// 4. The "ocsp-response" field is a base64-encoded verson of |ocsp_response|,
+// and is not present when |ocsp_response| is empty.
+// 5. The "cert-status" field matches |cert_status|, and is not present when
+// |cert_status| is empty.
+// 6. The "validated-chain" and "serverd-chain" fields match those in
+// |ssl_info|, and are only present when |ssl_info.is_issued_by_known_root|
+// is true.
+void CheckSerializedExpectStapleReport(const std::string& report,
+ const HostPortPair& host_port_pair,
+ const SSLInfo& ssl_info,
+ const std::string& ocsp_response,
+ const std::string& response_status,
+ const std::string& cert_status) {
+ std::unique_ptr<base::Value> value(base::JSONReader::Read(report));
+ ASSERT_TRUE(value);
+ ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+
+ base::DictionaryValue* report_dict;
+ ASSERT_TRUE(value->GetAsDictionary(&report_dict));
+
+ std::string report_hostname;
+ EXPECT_TRUE(report_dict->GetString("hostname", &report_hostname));
+ EXPECT_EQ(host_port_pair.host(), report_hostname);
+
+ int report_port;
+ EXPECT_TRUE(report_dict->GetInteger("port", &report_port));
+ EXPECT_EQ(host_port_pair.port(), report_port);
+
+ std::string report_response_status;
+ EXPECT_TRUE(
+ report_dict->GetString("response-status", &report_response_status));
+ EXPECT_EQ(response_status, report_response_status);
+
+ std::string report_ocsp_response;
+ bool has_ocsp_response =
+ report_dict->GetString("ocsp-response", &report_ocsp_response);
+
+ if (!ocsp_response.empty()) {
+ EXPECT_TRUE(has_ocsp_response);
+ std::string decoded_ocsp_response;
+ EXPECT_TRUE(
+ base::Base64Decode(report_ocsp_response, &decoded_ocsp_response));
+ EXPECT_EQ(ocsp_response, decoded_ocsp_response);
+ } else {
+ EXPECT_FALSE(has_ocsp_response);
+ }
+
+ std::string report_cert_status;
+ bool has_cert_status =
+ report_dict->GetString("cert-status", &report_cert_status);
+ if (!cert_status.empty()) {
+ EXPECT_TRUE(has_cert_status);
+ EXPECT_EQ(cert_status, report_cert_status);
+ } else {
+ EXPECT_FALSE(has_cert_status);
+ }
+
+ base::ListValue* report_served_certificate_chain;
+ bool has_served_chain = report_dict->GetList(
+ "served-certificate-chain", &report_served_certificate_chain);
+
+ base::ListValue* report_validated_certificate_chain;
+ bool has_validated_chain = report_dict->GetList(
+ "validated-certificate-chain", &report_validated_certificate_chain);
+
+ if (ssl_info.is_issued_by_known_root) {
+ EXPECT_TRUE(has_served_chain);
+ EXPECT_NO_FATAL_FAILURE(CompareCertificateChainWithList(
+ ssl_info.unverified_cert, report_served_certificate_chain));
+
+ EXPECT_TRUE(has_validated_chain);
+ EXPECT_NO_FATAL_FAILURE(CompareCertificateChainWithList(
+ ssl_info.cert, report_validated_certificate_chain));
+ } else {
+ EXPECT_FALSE(has_served_chain);
+ EXPECT_FALSE(has_validated_chain);
+ }
+}
+
+// Set up |state| for ExpectStaple, call CheckExpectStaple(), and verify the
+// serialized report caught by |reporter|.
+void CheckExpectStapleReport(TransportSecurityState* state,
+ MockCertificateReportSender* reporter,
+ const SSLInfo& ssl_info,
+ const std::string& ocsp_response,
+ const std::string& response_status,
+ const std::string& cert_status) {
+ // Expect-Staple is preload list based, so we use the baked-in test hostname
+ // from the list ("preloaded-expect-staple.badssl.com").
+ HostPortPair host_port(kExpectStapleStaticHostname, 443);
+ state->SetReportSender(reporter);
+ state->CheckExpectStaple(host_port, ssl_info, ocsp_response);
+ EXPECT_EQ(GURL(kExpectStapleStaticReportURI), reporter->latest_report_uri());
+ std::string serialized_report = reporter->latest_report();
+ EXPECT_NO_FATAL_FAILURE(CheckSerializedExpectStapleReport(
+ serialized_report, host_port, ssl_info, ocsp_response, response_status,
+ cert_status));
+}
+
} // namespace
class TransportSecurityStateTest : public testing::Test {
@@ -1902,6 +2006,190 @@ TEST_F(TransportSecurityStateTest, ExpectCTReporter) {
EXPECT_EQ(GURL(kExpectCTStaticReportURI), reporter.report_uri());
}
+static const struct ExpectStapleErrorResponseData {
+ OCSPVerifyResult::ResponseStatus response_status;
+ std::string response_status_string;
+} kExpectStapleReportData[] = {
+ {OCSPVerifyResult::MISSING, "MISSING"},
+ {OCSPVerifyResult::ERROR_RESPONSE, "ERROR_RESPONSE"},
+ {OCSPVerifyResult::BAD_PRODUCED_AT, "BAD_PRODUCED_AT"},
+ {OCSPVerifyResult::NO_MATCHING_RESPONSE, "NO_MATCHING_RESPONSE"},
+ {OCSPVerifyResult::INVALID_DATE, "INVALID_DATE"},
+ {OCSPVerifyResult::PARSE_RESPONSE_ERROR, "PARSE_RESPONSE_ERROR"},
+ {OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR, "PARSE_RESPONSE_DATA_ERROR"},
+};
+
+class ExpectStapleErrorResponseTest
+ : public TransportSecurityStateTest,
+ public testing::WithParamInterface<ExpectStapleErrorResponseData> {};
+
+// For every |response_status| indicating an OCSP response was provided, but had
+// some sort of parsing/validation error, test that the ExpectStaple report is
+// serialized correctly.
+TEST_P(ExpectStapleErrorResponseTest, CheckResponseStatusSerialization) {
+ TransportSecurityState state;
+ TransportSecurityStateTest::EnableStaticExpectStaple(&state);
+ MockCertificateReportSender reporter;
+ ExpectStapleErrorResponseData test = GetParam();
+
+ std::string ocsp_response;
+ if (test.response_status != OCSPVerifyResult::MISSING)
+ ocsp_response = "dummy_response";
+
+ // 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");
+
+ SSLInfo ssl_info;
+ ssl_info.cert = cert1;
+ ssl_info.unverified_cert = cert2;
+ ssl_info.ocsp_result.response_status = test.response_status;
+
+ // Certificate chains should only be included when |is_issued_by_known_root|
+ // is true.
+ ssl_info.is_issued_by_known_root = true;
+ ASSERT_NO_FATAL_FAILURE(
+ CheckExpectStapleReport(&state, &reporter, ssl_info, ocsp_response,
+ test.response_status_string, std::string()));
+
+ // No certificate chains should be included in the report.
+ ssl_info.is_issued_by_known_root = false;
+ ASSERT_NO_FATAL_FAILURE(
+ CheckExpectStapleReport(&state, &reporter, ssl_info, ocsp_response,
+ test.response_status_string, std::string()));
+}
+
+INSTANTIATE_TEST_CASE_P(ExpectStaple,
+ ExpectStapleErrorResponseTest,
+ testing::ValuesIn(kExpectStapleReportData));
+
+static const struct ExpectStapleErrorCertStatusData {
+ OCSPRevocationStatus revocation_status;
+ std::string cert_status_string;
+} kExpectStapleErrorCertStatusData[] = {
+ {OCSPRevocationStatus::REVOKED, "REVOKED"},
+ {OCSPRevocationStatus::UNKNOWN, "UNKNOWN"},
+};
+
+class ExpectStapleErrorCertStatusTest
+ : public TransportSecurityStateTest,
+ public testing::WithParamInterface<ExpectStapleErrorCertStatusData> {};
+
+// Test that |revocation_status| is serialized into the |cert-status| field of
+// the Expect-Staple report whenever |response_status| is PROVIDED and
+// |revocation_status| != GOOD.
+TEST_P(ExpectStapleErrorCertStatusTest, CheckCertStatusSerialization) {
+ TransportSecurityState state;
+ TransportSecurityStateTest::EnableStaticExpectStaple(&state);
+ MockCertificateReportSender reporter;
+ ExpectStapleErrorCertStatusData test = GetParam();
+ std::string ocsp_response = "dummy_response";
+
+ // 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");
+
+ SSLInfo ssl_info;
+ ssl_info.cert = cert1;
+ ssl_info.unverified_cert = cert2;
+ // |response_status| must be set to PROVIDED for |revocation_status| to have
+ // meaning.
+ ssl_info.ocsp_result.response_status = OCSPVerifyResult::PROVIDED;
+ ssl_info.ocsp_result.revocation_status = test.revocation_status;
+
+ // Certificate chains should only be included when |is_issued_by_known_root|
+ // is true.
+ ssl_info.is_issued_by_known_root = true;
+ ASSERT_NO_FATAL_FAILURE(CheckExpectStapleReport(&state, &reporter, ssl_info,
+ ocsp_response, "PROVIDED",
+ test.cert_status_string));
+
+ // No certificate chains should be included in the report.
+ ssl_info.is_issued_by_known_root = false;
+ ASSERT_NO_FATAL_FAILURE(CheckExpectStapleReport(&state, &reporter, ssl_info,
+ ocsp_response, "PROVIDED",
+ test.cert_status_string));
+};
+
+INSTANTIATE_TEST_CASE_P(ExpectStaple,
+ ExpectStapleErrorCertStatusTest,
+ testing::ValuesIn(kExpectStapleErrorCertStatusData));
+
+TEST_F(TransportSecurityStateTest, ExpectStapleDoesNotReportValidStaple) {
+ TransportSecurityState state;
+ TransportSecurityStateTest::EnableStaticExpectStaple(&state);
+ MockCertificateReportSender reporter;
+ state.SetReportSender(&reporter);
+
+ // Baked-in preloaded Expect-Staple test hosts.
+ HostPortPair host_port(kExpectStapleStaticHostname, 443);
+
+ // 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");
+
+ SSLInfo ssl_info;
+ ssl_info.cert = cert1;
+ ssl_info.unverified_cert = cert2;
+ ssl_info.ocsp_result.response_status = OCSPVerifyResult::PROVIDED;
+ ssl_info.ocsp_result.revocation_status = OCSPRevocationStatus::GOOD;
+
+ std::string ocsp_response = "dummy response";
+
+ ssl_info.is_issued_by_known_root = true;
+ state.CheckExpectStaple(host_port, ssl_info, ocsp_response);
+ EXPECT_EQ(GURL(), reporter.latest_report_uri());
+ EXPECT_TRUE(reporter.latest_report().empty());
+
+ ssl_info.is_issued_by_known_root = false;
+ state.CheckExpectStaple(host_port, ssl_info, ocsp_response);
+ EXPECT_EQ(GURL(), reporter.latest_report_uri());
+ EXPECT_TRUE(reporter.latest_report().empty());
+}
+
+TEST_F(TransportSecurityStateTest, ExpectStapleRequiresPreload) {
+ TransportSecurityState state;
+ TransportSecurityStateTest::EnableStaticExpectStaple(&state);
+ MockCertificateReportSender reporter;
+ state.SetReportSender(&reporter);
+
+ HostPortPair host_port("not-preloaded.host.example", 443);
+
+ // 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");
+
+ SSLInfo ssl_info;
+ ssl_info.cert = cert1;
+ ssl_info.unverified_cert = cert2;
+ ssl_info.ocsp_result.response_status = OCSPVerifyResult::MISSING;
+
+ // Empty response
+ std::string ocsp_response;
+
+ ssl_info.is_issued_by_known_root = true;
+ state.CheckExpectStaple(host_port, ssl_info, ocsp_response);
+ EXPECT_EQ(GURL(), reporter.latest_report_uri());
+ EXPECT_TRUE(reporter.latest_report().empty());
+
+ ssl_info.is_issued_by_known_root = false;
+ state.CheckExpectStaple(host_port, ssl_info, ocsp_response);
+ EXPECT_EQ(GURL(), reporter.latest_report_uri());
+ EXPECT_TRUE(reporter.latest_report().empty());
+}
+
// Tests that TransportSecurityState always consults the RequireCTDelegate,
// if supplied.
TEST_F(TransportSecurityStateTest, RequireCTConsultsDelegate) {
« no previous file with comments | « net/http/transport_security_state.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698