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

Unified Diff: net/cert/cert_verify_proc_unittest.cc

Issue 1557133002: Perform CRLSet evaluation during Path Building on Windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Review feedback Created 4 years, 11 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
Index: net/cert/cert_verify_proc_unittest.cc
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index bdeec339d02cf1d75a7037d6f21392027bab3aad..38c27459ae4286563fadad8812bd8e9be4f5e54e 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -107,6 +107,22 @@ bool SupportsDetectingKnownRoots() {
return true;
}
+// Template helper to load a series of certificate files into a CertificateList.
+// Like CertTestUtil's CreateCertificateListFromFile, except it can load a
+// series of individual certificates (to make the tests clearer).
+template <size_t N>
+void LoadCertificateFiles(const char* const(&cert_files)[N],
+ CertificateList* certs) {
+ certs->clear();
+ for (size_t i = 0; i < N; ++i) {
davidben 2016/01/28 20:52:32 Optional: This could also be a for each loop if yo
Ryan Sleevi 2016/02/05 21:26:03 ? Were you thinking Go? std::for_each is awful I
davidben 2016/02/05 21:32:45 for each loops are hardly a Go innovation...
+ SCOPED_TRACE(cert_files[i]);
+ scoped_refptr<X509Certificate> cert = CreateCertificateChainFromFile(
+ GetTestCertsDirectory(), cert_files[i], X509Certificate::FORMAT_AUTO);
+ ASSERT_TRUE(cert);
+ certs->push_back(cert);
+ }
+}
+
} // namespace
class CertVerifyProcTest : public testing::Test {
@@ -1360,6 +1376,203 @@ TEST_F(CertVerifyProcTest, CRLSetLeafSerial) {
&verify_result);
EXPECT_EQ(ERR_CERT_REVOKED, error);
}
+
+// Tests that revocation by CRLSet functions properly with the certificate
+// immediately before the trust anchor is revoked by that trust anchor, but
+// another version to a different trust anchor exists.
+//
+// The two possible paths are:
+// 1. A(B) -> B(C) -> C(D) -> D(D)
+// 2. A(B) -> B(C) -> C(E) -> E(E)
+//
+// In this test, C(E) is revoked by CRLSet. It is configured to be the more
+// preferable version compared to C(D), once revoked, it should be ignored.
+TEST_F(CertVerifyProcTest, CRLSetRevokedIntermediateSameName) {
+ const char* const kCertificatesToLoad[] = {
+ "multi-root-A-by-B.pem", "multi-root-B-by-C.pem", "multi-root-C-by-D.pem",
+ "multi-root-D-by-D.pem", "multi-root-C-by-E.pem", "multi-root-E-by-E.pem",
+ };
+ CertificateList certs;
+ ASSERT_NO_FATAL_FAILURE(LoadCertificateFiles(kCertificatesToLoad, &certs));
+
+ // Add D and E as trust anchors
+ ScopedTestRoot test_root_D(certs[3].get()); // D-by-D
+ ScopedTestRoot test_root_F(certs[5].get()); // E-by-E
+
+ // Create a chain that sends A(B), B(C), C(E), C(D). The reason that
+ // both C(E) and C(D) are sent are to ensure both certificates are available
+ // for path building. The test
+ // CertVerifyProcTest.VerifyReturnChainFiltersUnrelatedCerts ensures this is
+ // safe to do.
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle()); // B-by-C
+ intermediates.push_back(certs[4]->os_cert_handle()); // C-by-E
+ intermediates.push_back(certs[2]->os_cert_handle()); // C-by-D
+ scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
+ certs[0]->os_cert_handle(), intermediates);
+ ASSERT_TRUE(cert);
+
+ // Sanity check: Ensure that, without any revocation status, the to-be-revoked
+ // path is preferred.
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = Verify(cert.get(), "127.0.0.1", flags, NULL, empty_cert_list_,
davidben 2016/01/28 20:52:32 Nit: nullptr
+ &verify_result);
+ ASSERT_EQ(OK, error);
+ ASSERT_EQ(0U, verify_result.cert_status);
+ ASSERT_TRUE(verify_result.verified_cert.get());
+
+ // The expected path is A(B) -> B(C) -> C(E) -> E(E).
+ const X509Certificate::OSCertHandles& verified_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(3U, verified_intermediates.size());
+ scoped_refptr<X509Certificate> verified_root =
+ X509Certificate::CreateFromHandle(verified_intermediates[2],
+ X509Certificate::OSCertHandles());
+ ASSERT_TRUE(verified_root.get());
+ EXPECT_EQ("E Root CA", verified_root->subject().common_name);
+
+ // Load a CRLSet that blocks C-by-E.
+ scoped_refptr<CRLSet> crl_set;
+ std::string crl_set_bytes;
+ EXPECT_TRUE(base::ReadFileToString(
+ GetTestCertsDirectory().AppendASCII("multi-root-crlset-C-by-E.raw"),
+ &crl_set_bytes));
+ ASSERT_TRUE(CRLSetStorage::Parse(crl_set_bytes, &crl_set));
+
+ // Verify with the CRLSet. Because C-by-E is revoked, the expected path is
+ // A(B) -> B(C) -> C(D) -> D(D).
+ error = Verify(cert.get(), "127.0.0.1", flags, crl_set.get(),
+ empty_cert_list_, &verify_result);
+ ASSERT_EQ(OK, error);
+ ASSERT_EQ(0U, verify_result.cert_status);
+ ASSERT_TRUE(verify_result.verified_cert.get());
+
+ const X509Certificate::OSCertHandles& new_verified_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(3U, new_verified_intermediates.size());
+ verified_root = X509Certificate::CreateFromHandle(
+ new_verified_intermediates[2], X509Certificate::OSCertHandles());
+ ASSERT_TRUE(verified_root.get());
+ EXPECT_EQ("D Root CA", verified_root->subject().common_name);
+
+ // Reverify without the CRLSet, to ensure that CRLSets do not persist between
+ // separate calls. As in the first verification, the expected path is
+ // A(B) -> B(C) -> C(E) -> E(E).
+ error = Verify(cert.get(), "127.0.0.1", flags, NULL, empty_cert_list_,
+ &verify_result);
+ ASSERT_EQ(OK, error);
+ ASSERT_EQ(0U, verify_result.cert_status);
+ ASSERT_TRUE(verify_result.verified_cert.get());
+
+ const X509Certificate::OSCertHandles& final_verified_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(3U, final_verified_intermediates.size());
+ verified_root = X509Certificate::CreateFromHandle(
+ final_verified_intermediates[2], X509Certificate::OSCertHandles());
+ ASSERT_TRUE(verified_root.get());
+ EXPECT_EQ("E Root CA", verified_root->subject().common_name);
+}
+
+// Tests that revocation by CRLSet functions properly when an intermediate is
+// revoked by SPKI. In this case, path building should ignore all certificates
+// with that SPKI, and search for alternatively keyed versions.
+//
+// The two possible paths are:
+// 1. A(B) -> B(C) -> C(D) -> D(D)
+// 2. A(B) -> B(F) -> F(E) -> E(E)
+//
+// The path building algorithm needs to explore B(C) once it discovers that
+// F(E) is revoked, and that there are no valid paths with B(F).
+TEST_F(CertVerifyProcTest, CRLSetRevokedIntermediateCrossIntermediates) {
+ const char* const kCertificatesToLoad[] = {
+ "multi-root-A-by-B.pem", "multi-root-B-by-C.pem", "multi-root-C-by-D.pem",
+ "multi-root-D-by-D.pem", "multi-root-B-by-F.pem", "multi-root-F-by-E.pem",
+ "multi-root-E-by-E.pem",
+ };
+ CertificateList certs;
+ ASSERT_NO_FATAL_FAILURE(LoadCertificateFiles(kCertificatesToLoad, &certs));
+
+ // Add D and E as trust anchors
+ ScopedTestRoot test_root_D(certs[3].get()); // D-by-D
+ ScopedTestRoot test_root_F(certs[6].get()); // E-by-E
+
+ // Create a chain that sends A(B), B(F), F(E), B(C), C(D). The reason that
+ // both B(C) and C(D) are sent are to ensure both certificates are available
+ // for path building. The test
+ // CertVerifyProcTest.VerifyReturnChainFiltersUnrelatedCerts ensures this is
+ // safe to do.
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[4]->os_cert_handle()); // B-by-F
+ intermediates.push_back(certs[5]->os_cert_handle()); // F-by-E
+ intermediates.push_back(certs[1]->os_cert_handle()); // B-by-C
+ intermediates.push_back(certs[2]->os_cert_handle()); // C-by-D
+ scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
+ certs[0]->os_cert_handle(), intermediates);
+ ASSERT_TRUE(cert);
+
+ // Sanity check: Ensure that, without any revocation status, the to-be-revoked
+ // path is preferred.
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = Verify(cert.get(), "127.0.0.1", flags, NULL, empty_cert_list_,
+ &verify_result);
+ ASSERT_EQ(OK, error);
+ ASSERT_EQ(0U, verify_result.cert_status);
+ ASSERT_TRUE(verify_result.verified_cert.get());
+
+ // The expected path is A(B) -> B(F) -> F(E) -> E(E).
+ const X509Certificate::OSCertHandles& verified_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(3U, verified_intermediates.size());
+ scoped_refptr<X509Certificate> verified_root =
+ X509Certificate::CreateFromHandle(verified_intermediates[2],
+ X509Certificate::OSCertHandles());
+ ASSERT_TRUE(verified_root.get());
+ EXPECT_EQ("E Root CA", verified_root->subject().common_name);
+
+ // Load a CRLSet that blocks F.
+ scoped_refptr<CRLSet> crl_set;
+ std::string crl_set_bytes;
+ EXPECT_TRUE(base::ReadFileToString(
+ GetTestCertsDirectory().AppendASCII("multi-root-crlset-F.raw"),
+ &crl_set_bytes));
+ ASSERT_TRUE(CRLSetStorage::Parse(crl_set_bytes, &crl_set));
+
+ // Verify with the CRLSet. Because F is revoked, the expected path is
+ // A(B) -> B(C) -> C(D) -> D(D).
+ error = Verify(cert.get(), "127.0.0.1", flags, crl_set.get(),
+ empty_cert_list_, &verify_result);
+ ASSERT_EQ(OK, error);
+ ASSERT_EQ(0U, verify_result.cert_status);
+ ASSERT_TRUE(verify_result.verified_cert.get());
+
+ const X509Certificate::OSCertHandles& new_verified_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(3U, new_verified_intermediates.size());
+ verified_root = X509Certificate::CreateFromHandle(
+ new_verified_intermediates[2], X509Certificate::OSCertHandles());
+ ASSERT_TRUE(verified_root.get());
+ EXPECT_EQ("D Root CA", verified_root->subject().common_name);
davidben 2016/01/28 20:52:32 Could the existence of C(E) being used in a previo
Ryan Sleevi 2016/02/05 21:26:03 I'm not sure I understand your question. Line 1532
davidben 2016/02/05 21:32:45 *C*(E), not F(E). This tests revokes F(E) and does
Ryan Sleevi 2016/02/05 21:39:26 That's hard to answer, I think, in that you're ask
davidben 2016/02/05 21:46:48 I'm just asking if they do that right now. I agree
Ryan Sleevi 2016/02/05 21:52:11 Right, but I'm not sure that ambiguity is a proble
+
+ // Reverify without the CRLSet, to ensure that CRLSets do not persist between
+ // separate calls. As in the first verification, the expected path is
+ // A(B) -> B(F) -> F(E) -> E(E).
+ error = Verify(cert.get(), "127.0.0.1", flags, NULL, empty_cert_list_,
+ &verify_result);
+ ASSERT_EQ(OK, error);
+ ASSERT_EQ(0U, verify_result.cert_status);
+ ASSERT_TRUE(verify_result.verified_cert.get());
+
+ const X509Certificate::OSCertHandles& final_verified_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(3U, final_verified_intermediates.size());
+ verified_root = X509Certificate::CreateFromHandle(
+ final_verified_intermediates[2], X509Certificate::OSCertHandles());
+ ASSERT_TRUE(verified_root.get());
+ EXPECT_EQ("E Root CA", verified_root->subject().common_name);
+}
+
#endif
enum ExpectedAlgorithms {
« no previous file with comments | « no previous file | net/cert/cert_verify_proc_win.cc » ('j') | net/data/ssl/scripts/generate-multi-root-test-chains.sh » ('J')

Powered by Google App Engine
This is Rietveld 408576698