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

Unified Diff: net/cert/internal/path_builder_unittest.cc

Issue 1923433002: Certificate path builder for new certificate verification library (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: changes for review comment #20 Created 4 years, 6 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/internal/path_builder_unittest.cc
diff --git a/net/cert/internal/path_builder_unittest.cc b/net/cert/internal/path_builder_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..dfdf25b56fc38b3dd819344aecaf08a7016e351f
--- /dev/null
+++ b/net/cert/internal/path_builder_unittest.cc
@@ -0,0 +1,1108 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/internal/path_builder.h"
+
+#include "base/base_paths.h"
+#include "base/cancelable_callback.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/path_service.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/cert/internal/cert_issuer_source_static.h"
+#include "net/cert/internal/parsed_certificate.h"
+#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/test_helpers.h"
+#include "net/cert/internal/trust_store.h"
+#include "net/cert/internal/verify_certificate_chain.h"
+#include "net/cert/pem_tokenizer.h"
+#include "net/der/input.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_certificate_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::SaveArg;
+using ::testing::StrictMock;
+using ::testing::SetArgPointee;
+using ::testing::Return;
+
+// AsyncCertIssuerSourceStatic always returns its certs asynchronously.
+class AsyncCertIssuerSourceStatic : public CertIssuerSource {
+ public:
+ class StaticAsyncRequest : public Request {
+ public:
+ StaticAsyncRequest(const IssuerCallback& issuers_callback,
+ ParsedCertificateList&& issuers)
+ : cancelable_closure_(base::Bind(&StaticAsyncRequest::RunCallback,
+ base::Unretained(this))),
+ issuers_callback_(issuers_callback) {
+ issuers_.swap(issuers);
+ issuers_iter_ = issuers_.begin();
+ }
+ ~StaticAsyncRequest() override {}
+
+ CompletionStatus GetNext(
+ scoped_refptr<ParsedCertificate>* out_cert) override {
+ if (issuers_iter_ == issuers_.end())
+ *out_cert = nullptr;
+ else
+ *out_cert = std::move(*issuers_iter_++);
+ return CompletionStatus::SYNC;
+ }
+
+ base::Closure callback() { return cancelable_closure_.callback(); }
+
+ private:
+ void RunCallback() { issuers_callback_.Run(this); }
+
+ base::CancelableClosure cancelable_closure_;
+ IssuerCallback issuers_callback_;
+ ParsedCertificateList issuers_;
+ ParsedCertificateList::iterator issuers_iter_;
+
+ DISALLOW_COPY_AND_ASSIGN(StaticAsyncRequest);
+ };
+
+ ~AsyncCertIssuerSourceStatic() override {}
+
+ void AddCert(scoped_refptr<ParsedCertificate> cert) {
+ static_cert_issuer_source_.AddCert(std::move(cert));
+ }
+
+ void SyncGetIssuersOf(const ParsedCertificate* cert,
+ ParsedCertificateList* issuers) override {}
+ void AsyncGetIssuersOf(const ParsedCertificate* cert,
+ const IssuerCallback& issuers_callback,
+ std::unique_ptr<Request>* out_req) override {
+ num_async_gets_++;
+ ParsedCertificateList issuers;
+ static_cert_issuer_source_.SyncGetIssuersOf(cert, &issuers);
+ std::unique_ptr<StaticAsyncRequest> req(
+ new StaticAsyncRequest(issuers_callback, std::move(issuers)));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, req->callback());
+ *out_req = std::move(req);
+ }
+ int num_async_gets() const { return num_async_gets_; }
+
+ private:
+ CertIssuerSourceStatic static_cert_issuer_source_;
+
+ int num_async_gets_ = 0;
+};
+
+// Reads a data file from the unit-test data.
+std::string ReadTestFileToString(const std::string& file_name) {
+ // Compute the full path, relative to the src/ directory.
+ base::FilePath src_root;
+ PathService::Get(base::DIR_SOURCE_ROOT, &src_root);
+ base::FilePath filepath = src_root.AppendASCII(file_name);
+
+ // Read the full contents of the file.
+ std::string file_data;
+ if (!base::ReadFileToString(filepath, &file_data)) {
+ ADD_FAILURE() << "Couldn't read file: " << filepath.value();
+ return std::string();
+ }
+
+ return file_data;
+}
+
+// Reads a verify_certificate_chain_unittest-style test case from |file_name|.
+// Test cases are comprised of a certificate chain, trust store, a timestamp to
+// validate at, and the expected result of verification (though the expected
+// result is ignored here).
+void ReadVerifyCertChainTestFromFile(const std::string& file_name,
+ std::vector<std::string>* chain,
+ scoped_refptr<ParsedCertificate>* root,
+ der::GeneralizedTime* time) {
+ chain->clear();
+
+ std::string file_data = ReadTestFileToString(file_name);
+
+ std::vector<std::string> pem_headers;
+
+ const char kCertificateHeader[] = "CERTIFICATE";
+ const char kTrustedCertificateHeader[] = "TRUSTED_CERTIFICATE";
+ const char kTimeHeader[] = "TIME";
+
+ pem_headers.push_back(kCertificateHeader);
+ pem_headers.push_back(kTrustedCertificateHeader);
+ pem_headers.push_back(kTimeHeader);
+
+ bool has_time = false;
+
+ PEMTokenizer pem_tokenizer(file_data, pem_headers);
+ while (pem_tokenizer.GetNext()) {
+ const std::string& block_type = pem_tokenizer.block_type();
+ const std::string& block_data = pem_tokenizer.data();
+
+ if (block_type == kCertificateHeader) {
+ chain->push_back(block_data);
+ } else if (block_type == kTrustedCertificateHeader) {
+ *root = ParsedCertificate::CreateFromCertificateCopy(block_data, {});
+ ASSERT_TRUE(*root);
+ } else if (block_type == kTimeHeader) {
+ ASSERT_FALSE(has_time) << "Duplicate " << kTimeHeader;
+ has_time = true;
+ ASSERT_TRUE(der::ParseUTCTime(der::Input(&block_data), time));
+ }
+ }
+
+ ASSERT_TRUE(has_time);
+}
+
+::testing::AssertionResult ReadTestPem(const std::string& file_name,
+ const std::string& block_name,
+ std::string* result) {
+ const PemBlockMapping mappings[] = {
+ {block_name.c_str(), result},
+ };
+
+ return ReadTestDataFromPemFile(file_name, mappings);
+}
+
+::testing::AssertionResult ReadTestCert(
+ const std::string& file_name,
+ scoped_refptr<ParsedCertificate>* result) {
+ std::string der;
+ ::testing::AssertionResult r = ReadTestPem(
+ "net/data/ssl/certificates/" + file_name, "CERTIFICATE", &der);
+ if (!r)
+ return r;
+ *result = ParsedCertificate::CreateFromCertificateCopy(der, {});
+ if (!*result)
+ return ::testing::AssertionFailure() << "CreateFromCertificateCopy failed";
+ return ::testing::AssertionSuccess();
+}
+
+// Run the path builder, and wait for async completion if necessary. The return
+// value signifies whether the path builder completed synchronously or
+// asynchronously, not that RunPathBuilder itself is asynchronous.
+CompletionStatus RunPathBuilder(CertPathBuilder* path_builder) {
+ TestClosure callback;
+ CompletionStatus rv = path_builder->Run(callback.closure());
+
+ if (rv == CompletionStatus::ASYNC) {
+ DVLOG(1) << "waiting for async completion...";
+ callback.WaitForResult();
+ DVLOG(1) << "async completed.";
+ }
+ return rv;
+}
+
+class PathBuilderMultiRootTest : public ::testing::Test {
+ public:
+ PathBuilderMultiRootTest() : signature_policy_(1024) {}
+
+ void SetUp() override {
+ ASSERT_TRUE(ReadTestCert("multi-root-A-by-B.pem", &a_by_b_));
+ ASSERT_TRUE(ReadTestCert("multi-root-B-by-C.pem", &b_by_c_));
+ ASSERT_TRUE(ReadTestCert("multi-root-B-by-F.pem", &b_by_f_));
+ ASSERT_TRUE(ReadTestCert("multi-root-C-by-D.pem", &c_by_d_));
+ ASSERT_TRUE(ReadTestCert("multi-root-C-by-E.pem", &c_by_e_));
+ ASSERT_TRUE(ReadTestCert("multi-root-D-by-D.pem", &d_by_d_));
+ ASSERT_TRUE(ReadTestCert("multi-root-E-by-E.pem", &e_by_e_));
+ ASSERT_TRUE(ReadTestCert("multi-root-F-by-E.pem", &f_by_e_));
+ }
+
+ protected:
+ scoped_refptr<ParsedCertificate> a_by_b_, b_by_c_, b_by_f_, c_by_d_, c_by_e_,
+ d_by_d_, e_by_e_, f_by_e_;
+
+ SimpleSignaturePolicy signature_policy_;
+ der::GeneralizedTime time_ = {2016, 4, 11, 0, 0, 0};
+};
+
+// If the target cert is a trust anchor, it should verify and should not include
+// anything else in the path.
+TEST_F(PathBuilderMultiRootTest, TargetIsTrustAnchor) {
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(a_by_b_);
+ trust_store.AddTrustedCertificate(b_by_f_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
+ &result);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+ EXPECT_EQ(1U, result.paths[result.best_result_index]->path.size());
+ EXPECT_EQ(a_by_b_, result.paths[result.best_result_index]->path[0]);
+}
+
+// If the target cert is directly issued by a trust anchor, it should verify
+// without any intermediate certs being provided.
+TEST_F(PathBuilderMultiRootTest, TargetDirectlySignedByTrustAnchor) {
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(b_by_f_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
+ &result);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+ EXPECT_EQ(2U, result.paths[result.best_result_index]->path.size());
+ EXPECT_EQ(a_by_b_, result.paths[result.best_result_index]->path[0]);
+ EXPECT_EQ(b_by_f_, result.paths[result.best_result_index]->path[1]);
+}
+
+// Test that async cert queries are not made if the path can be successfully
+// built with synchronously available certs.
+TEST_F(PathBuilderMultiRootTest, TriesSyncFirst) {
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(e_by_e_);
+
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_f_);
+ sync_certs.AddCert(f_by_e_);
+
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(b_by_c_);
+ async_certs.AddCert(c_by_e_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&async_certs);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+ EXPECT_EQ(0, async_certs.num_async_gets());
+}
+
+// Test that async cert queries are not made if no callback is provided.
+TEST_F(PathBuilderMultiRootTest, SychronousOnlyMode) {
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(e_by_e_);
+
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(f_by_e_);
+
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(b_by_f_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&async_certs);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::SYNC, path_builder.Run(base::Closure()));
+
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.error());
+ EXPECT_EQ(0, async_certs.num_async_gets());
+}
+
+// If async queries are needed, all async sources will be queried
+// simultaneously.
+TEST_F(PathBuilderMultiRootTest, TestAsyncSimultaneous) {
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(e_by_e_);
+
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_c_);
+ sync_certs.AddCert(b_by_f_);
+
+ AsyncCertIssuerSourceStatic async_certs1;
+ async_certs1.AddCert(c_by_e_);
+
+ AsyncCertIssuerSourceStatic async_certs2;
+ async_certs2.AddCert(f_by_e_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&async_certs1);
+ path_builder.AddCertIssuerSource(&async_certs2);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::ASYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+ EXPECT_EQ(1, async_certs1.num_async_gets());
+ EXPECT_EQ(1, async_certs2.num_async_gets());
+}
+
+// Test that PathBuilder does not generate longer paths than necessary if one of
+// the supplied certs is itself a trust anchor.
+TEST_F(PathBuilderMultiRootTest, TestLongChain) {
+ // Both D(D) and C(D) are trusted roots.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(d_by_d_);
+ trust_store.AddTrustedCertificate(c_by_d_);
+
+ // Certs B(C), and C(D) are all supplied.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_c_);
+ sync_certs.AddCert(c_by_d_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+
+ // The result path should be A(B) <- B(C) <- C(D)
+ // not the longer but also valid A(B) <- B(C) <- C(D) <- D(D)
+ EXPECT_EQ(3U, result.paths[result.best_result_index]->path.size());
+}
+
+// Test that PathBuilder will backtrack and try a different path if the first
+// one doesn't work out.
+TEST_F(PathBuilderMultiRootTest, TestBacktracking) {
+ // Only D(D) is a trusted root.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(d_by_d_);
+
+ // Certs B(F) and F(E) are supplied synchronously, thus the path
+ // A(B) <- B(F) <- F(E) should be built first, though it won't verify.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(b_by_f_);
+ sync_certs.AddCert(f_by_e_);
+
+ // Certs B(C), and C(D) are supplied asynchronously, so the path
+ // A(B) <- B(C) <- C(D) <- D(D) should be tried second.
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(b_by_c_);
+ async_certs.AddCert(c_by_d_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&sync_certs);
+ path_builder.AddCertIssuerSource(&async_certs);
+
+ EXPECT_EQ(CompletionStatus::ASYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+
+ // The result path should be A(B) <- B(C) <- C(D) <- D(D)
+ ASSERT_EQ(4U, result.paths[result.best_result_index]->path.size());
+ EXPECT_EQ(a_by_b_, result.paths[result.best_result_index]->path[0]);
+ EXPECT_EQ(b_by_c_, result.paths[result.best_result_index]->path[1]);
+ EXPECT_EQ(c_by_d_, result.paths[result.best_result_index]->path[2]);
+ EXPECT_EQ(d_by_d_, result.paths[result.best_result_index]->path[3]);
+}
+
+// Test that whichever order CertIssuerSource returns the issuers, the path
+// building still succeeds.
+TEST_F(PathBuilderMultiRootTest, TestCertIssuerOrdering) {
+ // Only D(D) is a trusted root.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(d_by_d_);
+
+ for (bool reverse_order : {false, true}) {
+ SCOPED_TRACE(reverse_order);
+ std::vector<scoped_refptr<ParsedCertificate>> certs = {
+ b_by_c_, b_by_f_, f_by_e_, c_by_d_, c_by_e_};
+ CertIssuerSourceStatic sync_certs;
+ if (reverse_order) {
+ for (auto it = certs.rbegin(); it != certs.rend(); ++it)
+ sync_certs.AddCert(*it);
+ } else {
+ for (const auto& cert : certs)
+ sync_certs.AddCert(cert);
+ }
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_,
+ time_, &result);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+
+ // The result path should be A(B) <- B(C) <- C(D) <- D(D)
+ ASSERT_EQ(4U, result.paths[result.best_result_index]->path.size());
+ EXPECT_EQ(a_by_b_, result.paths[result.best_result_index]->path[0]);
+ EXPECT_EQ(b_by_c_, result.paths[result.best_result_index]->path[1]);
+ EXPECT_EQ(c_by_d_, result.paths[result.best_result_index]->path[2]);
+ EXPECT_EQ(d_by_d_, result.paths[result.best_result_index]->path[3]);
+ }
+}
+
+class PathBuilderKeyRolloverTest : public ::testing::Test {
+ public:
+ PathBuilderKeyRolloverTest() : signature_policy_(1024) {}
+
+ void SetUp() override {
+ std::vector<std::string> path;
+
+ ReadVerifyCertChainTestFromFile(
+ "net/data/verify_certificate_chain_unittest/key-rollover-oldchain.pem",
+ &path, &oldroot_, &time_);
+ ASSERT_EQ(2U, path.size());
+ target_ = ParsedCertificate::CreateFromCertificateCopy(path[0], {});
+ oldintermediate_ =
+ ParsedCertificate::CreateFromCertificateCopy(path[1], {});
+ ASSERT_TRUE(target_);
+ ASSERT_TRUE(oldintermediate_);
+
+ ReadVerifyCertChainTestFromFile(
+ "net/data/verify_certificate_chain_unittest/"
+ "key-rollover-longrolloverchain.pem",
+ &path, &oldroot_, &time_);
+ ASSERT_EQ(4U, path.size());
+ newintermediate_ =
+ ParsedCertificate::CreateFromCertificateCopy(path[1], {});
+ newroot_ = ParsedCertificate::CreateFromCertificateCopy(path[2], {});
+ newrootrollover_ =
+ ParsedCertificate::CreateFromCertificateCopy(path[3], {});
+ ASSERT_TRUE(newintermediate_);
+ ASSERT_TRUE(newroot_);
+ ASSERT_TRUE(newrootrollover_);
+ }
+
+ protected:
+ // oldroot-------->newrootrollover newroot
+ // | | |
+ // v v v
+ // oldintermediate newintermediate
+ // | |
+ // +------------+-------------+
+ // |
+ // v
+ // target
+ scoped_refptr<ParsedCertificate> target_;
+ scoped_refptr<ParsedCertificate> oldintermediate_;
+ scoped_refptr<ParsedCertificate> newintermediate_;
+ scoped_refptr<ParsedCertificate> oldroot_;
+ scoped_refptr<ParsedCertificate> newroot_;
+ scoped_refptr<ParsedCertificate> newrootrollover_;
+
+ SimpleSignaturePolicy signature_policy_;
+ der::GeneralizedTime time_;
+};
+
+// Tests that if only the old root cert is trusted, the path builder can build a
+// path through the new intermediate and rollover cert to the old root.
+TEST_F(PathBuilderKeyRolloverTest, TestRolloverOnlyOldRootTrusted) {
+ // Only oldroot is trusted.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(oldroot_);
+
+ // Old intermediate cert is not provided, so the pathbuilder will need to go
+ // through the rollover cert.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(newintermediate_);
+ sync_certs.AddCert(newrootrollover_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+
+ // Path builder will first attempt: target <- newintermediate <- oldroot
+ // but it will fail since newintermediate is signed by newroot.
+ ASSERT_EQ(2U, result.paths.size());
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(target_, result.paths[0]->path[0]);
+ EXPECT_EQ(newintermediate_, result.paths[0]->path[1]);
+ EXPECT_EQ(oldroot_, result.paths[0]->path[2]);
+
+ // Path builder will next attempt:
+ // target <- newintermediate <- newrootrollover <- oldroot
+ // which will succeed.
+ EXPECT_EQ(1U, result.best_result_index);
+ EXPECT_EQ(OK, result.paths[1]->error);
+ ASSERT_EQ(4U, result.paths[1]->path.size());
+ EXPECT_EQ(target_, result.paths[1]->path[0]);
+ EXPECT_EQ(newintermediate_, result.paths[1]->path[1]);
+ EXPECT_EQ(newrootrollover_, result.paths[1]->path[2]);
+ EXPECT_EQ(oldroot_, result.paths[1]->path[3]);
+}
+
+// Tests that if both old and new roots are trusted it can build a path through
+// either.
+// TODO(mattm): Once prioritization is implemented, it should test that it
+// always builds the path through the new intermediate and new root.
+TEST_F(PathBuilderKeyRolloverTest, TestRolloverBothRootsTrusted) {
+ // Both oldroot and newroot are trusted.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(oldroot_);
+ trust_store.AddTrustedCertificate(newroot_);
+
+ // Both old and new intermediates + rollover cert are provided.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(oldintermediate_);
+ sync_certs.AddCert(newintermediate_);
+ sync_certs.AddCert(newrootrollover_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+
+ // Path builder willattempt one of:
+ // target <- oldintermediate <- oldroot
+ // target <- newintermediate <- newroot
+ // either will succeed.
+ ASSERT_EQ(1U, result.paths.size());
+ EXPECT_EQ(OK, result.paths[0]->error);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(target_, result.paths[0]->path[0]);
+ if (result.paths[0]->path[1] != newintermediate_) {
+ DVLOG(1) << "USED OLD";
+ EXPECT_EQ(oldintermediate_, result.paths[0]->path[1]);
+ EXPECT_EQ(oldroot_, result.paths[0]->path[2]);
+ } else {
+ DVLOG(1) << "USED NEW";
+ EXPECT_EQ(newintermediate_, result.paths[0]->path[1]);
+ EXPECT_EQ(newroot_, result.paths[0]->path[2]);
+ }
+}
+
+// Tests that multiple trust root matches on a single path will be considered.
+// Both roots have the same subject but different keys. Only one of them will
+// verify.
+TEST_F(PathBuilderKeyRolloverTest, TestMultipleRootMatchesOnlyOneWorks) {
+ // Both newroot and oldroot are trusted.
+ TrustStore trust_store;
+ // Note: The test assumes newroot will be tried before oldroot.
+ // Currently this depends on the order the roots are added.
+ trust_store.AddTrustedCertificate(newroot_);
+ trust_store.AddTrustedCertificate(oldroot_);
+
+ // Only oldintermediate is supplied, so the path with newroot should fail,
+ // oldroot should succeed.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(oldintermediate_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+ ASSERT_EQ(2U, result.paths.size());
+
+ // Path builder will first attempt: target <- oldintermediate <- newroot
+ // but it will fail since oldintermediate is signed by oldroot.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(target_, result.paths[0]->path[0]);
+ EXPECT_EQ(oldintermediate_, result.paths[0]->path[1]);
+ EXPECT_EQ(newroot_, result.paths[0]->path[2]);
+
+ // Path builder will next attempt:
+ // target <- old intermediate <- oldroot
+ // which should succeed.
+ EXPECT_EQ(OK, result.paths[1]->error);
+ ASSERT_EQ(3U, result.paths[1]->path.size());
+ EXPECT_EQ(target_, result.paths[1]->path[0]);
+ EXPECT_EQ(oldintermediate_, result.paths[1]->path[1]);
+ EXPECT_EQ(oldroot_, result.paths[1]->path[2]);
+}
+
+// Tests that the path builder doesn't build longer than necessary paths.
+TEST_F(PathBuilderKeyRolloverTest, TestRolloverLongChain) {
+ // Only oldroot is trusted.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(oldroot_);
+
+ // New intermediate and new root are provided synchronously.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(newintermediate_);
+ sync_certs.AddCert(newroot_);
+
+ // Rollover cert is only provided asynchronously. This will force the
+ // pathbuilder to first try building a longer than necessary path.
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(newrootrollover_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&sync_certs);
+ path_builder.AddCertIssuerSource(&async_certs);
+
+ EXPECT_EQ(CompletionStatus::ASYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+ ASSERT_EQ(3U, result.paths.size());
+
+ // Path builder will first attempt: target <- newintermediate <- oldroot
+ // but it will fail since newintermediate is signed by newroot.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(target_, result.paths[0]->path[0]);
+ EXPECT_EQ(newintermediate_, result.paths[0]->path[1]);
+ EXPECT_EQ(oldroot_, result.paths[0]->path[2]);
+
+ // Path builder will next attempt:
+ // target <- newintermediate <- newroot <- oldroot
+ // but it will fail since newroot is self-signed.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[1]->error);
+ ASSERT_EQ(4U, result.paths[1]->path.size());
+ EXPECT_EQ(target_, result.paths[1]->path[0]);
+ EXPECT_EQ(newintermediate_, result.paths[1]->path[1]);
+ EXPECT_EQ(newroot_, result.paths[1]->path[2]);
+ EXPECT_EQ(oldroot_, result.paths[1]->path[3]);
+
+ // Path builder will skip:
+ // target <- newintermediate <- newroot <- newrootrollover <- ...
+ // Since newroot and newrootrollover have the same Name+SAN+SPKI.
+
+ // Finally path builder will use:
+ // target <- newintermediate <- newrootrollover <- oldroot
+ EXPECT_EQ(2U, result.best_result_index);
+ EXPECT_EQ(OK, result.paths[2]->error);
+ ASSERT_EQ(4U, result.paths[2]->path.size());
+ EXPECT_EQ(target_, result.paths[2]->path[0]);
+ EXPECT_EQ(newintermediate_, result.paths[2]->path[1]);
+ EXPECT_EQ(newrootrollover_, result.paths[2]->path[2]);
+ EXPECT_EQ(oldroot_, result.paths[2]->path[3]);
+}
+
+// If the target cert is a trust root, that alone is a valid path.
+TEST_F(PathBuilderKeyRolloverTest, TestEndEntityIsTrustRoot) {
+ // Trust newintermediate.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(newintermediate_);
+
+ CertPathBuilder::Result result;
+ // Newintermediate is also the target cert.
+ CertPathBuilder path_builder(newintermediate_, &trust_store,
+ &signature_policy_, time_, &result);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+
+ ASSERT_EQ(1U, result.paths.size());
+ EXPECT_EQ(OK, result.paths[0]->error);
+ ASSERT_EQ(1U, result.paths[0]->path.size());
+ EXPECT_EQ(newintermediate_, result.paths[0]->path[0]);
+}
+
+// If target has same Name+SAN+SPKI as a necessary intermediate, test if a path
+// can still be built.
+// Since LoopChecker will prevent the intermediate from being included, this
+// currently does NOT verify. This case shouldn't occur in the web PKI.
+TEST_F(PathBuilderKeyRolloverTest,
+ TestEndEntityHasSameNameAndSpkiAsIntermediate) {
+ // Trust oldroot.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(oldroot_);
+
+ // New root rollover is provided synchronously.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(newrootrollover_);
+
+ CertPathBuilder::Result result;
+ // Newroot is the target cert.
+ CertPathBuilder path_builder(newroot_, &trust_store, &signature_policy_,
+ time_, &result);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ // This could actually be OK, but CertPathBuilder does not build the
+ // newroot <- newrootrollover <- oldroot path.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.error());
+}
+
+// If target has same Name+SAN+SPKI as the trust root, test that a (trivial)
+// path can still be built.
+TEST_F(PathBuilderKeyRolloverTest,
+ TestEndEntityHasSameNameAndSpkiAsTrustAnchor) {
+ // Trust newrootrollover.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(newrootrollover_);
+
+ CertPathBuilder::Result result;
+ // Newroot is the target cert.
+ CertPathBuilder path_builder(newroot_, &trust_store, &signature_policy_,
+ time_, &result);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+
+ ASSERT_FALSE(result.paths.empty());
+ const CertPathBuilder::ResultPath* best_result =
+ result.paths[result.best_result_index].get();
+
+ // Newroot has same name+SPKI as newrootrollover, thus the path is valid and
+ // only contains newroot.
+ EXPECT_EQ(OK, best_result->error);
+ ASSERT_EQ(1U, best_result->path.size());
+ EXPECT_EQ(newroot_, best_result->path[0]);
+}
+
+// Test that PathBuilder will not try the same path twice if multiple
+// CertIssuerSources provide the same certificate.
+TEST_F(PathBuilderKeyRolloverTest, TestDuplicateIntermediates) {
+ // Create a separate copy of oldintermediate.
+ scoped_refptr<ParsedCertificate> oldintermediate_dupe(
+ ParsedCertificate::CreateFromCertificateCopy(
+ oldintermediate_->der_cert().AsStringPiece(), {}));
+
+ // Only newroot is a trusted root.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(newroot_);
+
+ // The oldintermediate is supplied synchronously by |sync_certs1| and
+ // another copy of oldintermediate is supplied synchronously by |sync_certs2|.
+ // The path target <- oldintermediate <- newroot should be built first,
+ // though it won't verify. It should not be attempted again even though
+ // oldintermediate was supplied twice.
+ CertIssuerSourceStatic sync_certs1;
+ sync_certs1.AddCert(oldintermediate_);
+ CertIssuerSourceStatic sync_certs2;
+ sync_certs2.AddCert(oldintermediate_dupe);
+
+ // The newintermediate is supplied asynchronously, so the path
+ // target <- newintermediate <- newroot should be tried second.
+ AsyncCertIssuerSourceStatic async_certs;
+ async_certs.AddCert(newintermediate_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&sync_certs1);
+ path_builder.AddCertIssuerSource(&sync_certs2);
+ path_builder.AddCertIssuerSource(&async_certs);
+
+ EXPECT_EQ(CompletionStatus::ASYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(OK, result.error());
+ ASSERT_EQ(2U, result.paths.size());
+
+ // Path builder will first attempt: target <- oldintermediate <- newroot
+ // but it will fail since oldintermediate is signed by oldroot.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(target_, result.paths[0]->path[0]);
+ // Compare the DER instead of ParsedCertificate pointer, don't care which copy
+ // of oldintermediate was used in the path.
+ EXPECT_EQ(oldintermediate_->der_cert(), result.paths[0]->path[1]->der_cert());
+ EXPECT_EQ(newroot_, result.paths[0]->path[2]);
+
+ // Path builder will next attempt: target <- newintermediate <- newroot
+ // which will succeed.
+ EXPECT_EQ(1U, result.best_result_index);
+ EXPECT_EQ(OK, result.paths[1]->error);
+ ASSERT_EQ(3U, result.paths[1]->path.size());
+ EXPECT_EQ(target_, result.paths[1]->path[0]);
+ EXPECT_EQ(newintermediate_, result.paths[1]->path[1]);
+ EXPECT_EQ(newroot_, result.paths[1]->path[2]);
+}
+
+// Test that PathBuilder will not try the same path twice if the same cert is
+// presented via a CertIssuerSources and a TrustAnchor.
+TEST_F(PathBuilderKeyRolloverTest, TestDuplicateIntermediateAndRoot) {
+ // Create a separate copy of newroot.
+ scoped_refptr<ParsedCertificate> newroot_dupe(
+ ParsedCertificate::CreateFromCertificateCopy(
+ newroot_->der_cert().AsStringPiece(), {}));
+
+ // Only newroot is a trusted root.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(newroot_);
+
+ // The oldintermediate and newroot are supplied synchronously by |sync_certs|.
+ CertIssuerSourceStatic sync_certs;
+ sync_certs.AddCert(oldintermediate_);
+ sync_certs.AddCert(newroot_dupe);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&sync_certs);
+
+ EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder));
+
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.error());
+ ASSERT_EQ(1U, result.paths.size());
+
+ // Path builder attempt: target <- oldintermediate <- newroot
+ // but it will fail since oldintermediate is signed by oldroot.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(target_, result.paths[0]->path[0]);
+ EXPECT_EQ(oldintermediate_, result.paths[0]->path[1]);
+ // Compare the DER instead of ParsedCertificate pointer, don't care which copy
+ // of newroot was used in the path.
+ EXPECT_EQ(newroot_->der_cert(), result.paths[0]->path[2]->der_cert());
+}
+
+class MockCertIssuerSourceRequest : public CertIssuerSource::Request {
+ public:
+ MOCK_METHOD1(GetNext, CompletionStatus(scoped_refptr<ParsedCertificate>*));
+};
+
+class MockCertIssuerSource : public CertIssuerSource {
+ public:
+ MOCK_METHOD2(SyncGetIssuersOf,
+ void(const ParsedCertificate*, ParsedCertificateList*));
+ MOCK_METHOD3(AsyncGetIssuersOf,
+ void(const ParsedCertificate*,
+ const IssuerCallback&,
+ std::unique_ptr<Request>*));
+};
+
+// Helper class to pass the Request to the PathBuilder when it calls
+// AsyncGetIssuersOf. (GoogleMock has a ByMove helper, but it apparently can
+// only be used with Return, not SetArgPointee.)
+class CertIssuerSourceRequestMover {
+ public:
+ CertIssuerSourceRequestMover(std::unique_ptr<CertIssuerSource::Request> req)
+ : request_(std::move(req)) {}
+ void MoveIt(const ParsedCertificate* cert,
+ const CertIssuerSource::IssuerCallback& issuers_callback,
+ std::unique_ptr<CertIssuerSource::Request>* out_req) {
+ *out_req = std::move(request_);
+ }
+
+ private:
+ std::unique_ptr<CertIssuerSource::Request> request_;
+};
+
+// Test that a single CertIssuerSource returning multiple async batches of
+// issuers is handled correctly. Due to the StrictMocks, it also tests that path
+// builder does not request issuers of certs that it shouldn't.
+TEST_F(PathBuilderKeyRolloverTest, TestMultipleAsyncCallbacksFromSingleSource) {
+ StrictMock<MockCertIssuerSource> cert_issuer_source;
+
+ // Only newroot is a trusted root.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(newroot_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&cert_issuer_source);
+
+ CertIssuerSource::IssuerCallback target_issuers_callback;
+ // Create the mock CertIssuerSource::Request...
+ std::unique_ptr<StrictMock<MockCertIssuerSourceRequest>>
+ target_issuers_req_owner(new StrictMock<MockCertIssuerSourceRequest>());
+ // Keep a raw pointer to the Request...
+ StrictMock<MockCertIssuerSourceRequest>* target_issuers_req =
+ target_issuers_req_owner.get();
+ // Setup helper class to pass ownership of the Request to the PathBuilder when
+ // it calls AsyncGetIssuersOf.
+ CertIssuerSourceRequestMover req_mover(std::move(target_issuers_req_owner));
+ {
+ ::testing::InSequence s;
+ EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(target_.get(), _));
+ EXPECT_CALL(cert_issuer_source, AsyncGetIssuersOf(target_.get(), _, _))
+ .WillOnce(
+ DoAll(SaveArg<1>(&target_issuers_callback),
+ Invoke(&req_mover, &CertIssuerSourceRequestMover::MoveIt)));
+ }
+
+ TestClosure callback;
+ CompletionStatus rv = path_builder.Run(callback.closure());
+ ASSERT_EQ(CompletionStatus::ASYNC, rv);
+
+ ASSERT_FALSE(target_issuers_callback.is_null());
+
+ ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source);
+
+ // First async batch: return oldintermediate_.
+ EXPECT_CALL(*target_issuers_req, GetNext(_))
+ .WillOnce(DoAll(SetArgPointee<0>(oldintermediate_),
+ Return(CompletionStatus::SYNC)))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC)));
+ {
+ ::testing::InSequence s;
+ // oldintermediate_ does not create a valid path, so both sync and async
+ // lookups are expected.
+ EXPECT_CALL(cert_issuer_source,
+ SyncGetIssuersOf(oldintermediate_.get(), _));
+ EXPECT_CALL(cert_issuer_source,
+ AsyncGetIssuersOf(oldintermediate_.get(), _, _));
+ }
+ target_issuers_callback.Run(target_issuers_req);
+ ::testing::Mock::VerifyAndClearExpectations(target_issuers_req);
+ ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source);
+
+ // Second async batch: return newintermediate_.
+ EXPECT_CALL(*target_issuers_req, GetNext(_))
+ .WillOnce(DoAll(SetArgPointee<0>(newintermediate_),
+ Return(CompletionStatus::SYNC)))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC)));
+ // newroot_ is in the trust store, so this path will be completed
+ // synchronously. AsyncGetIssuersOf will not be called on newintermediate_.
+ EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(newintermediate_.get(), _));
+ target_issuers_callback.Run(target_issuers_req);
+ // Note that VerifyAndClearExpectations(target_issuers_req) is not called
+ // here. PathBuilder could have destroyed it already, so just let the
+ // expectations get checked by the destructor.
+ ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source);
+
+ // Ensure pathbuilder finished and filled result.
+ callback.WaitForResult();
+
+ EXPECT_EQ(OK, result.error());
+ ASSERT_EQ(2U, result.paths.size());
+
+ // Path builder first attempts: target <- oldintermediate <- newroot
+ // but it will fail since oldintermediate is signed by oldroot.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(target_, result.paths[0]->path[0]);
+ EXPECT_EQ(oldintermediate_, result.paths[0]->path[1]);
+ EXPECT_EQ(newroot_, result.paths[0]->path[2]);
+
+ // After the second batch of async results, path builder will attempt:
+ // target <- newintermediate <- newroot which will succeed.
+ EXPECT_EQ(OK, result.paths[1]->error);
+ ASSERT_EQ(3U, result.paths[1]->path.size());
+ EXPECT_EQ(target_, result.paths[1]->path[0]);
+ EXPECT_EQ(newintermediate_, result.paths[1]->path[1]);
+ EXPECT_EQ(newroot_, result.paths[1]->path[2]);
+}
+
+// Test that PathBuilder will not try the same path twice if CertIssuerSources
+// asynchronously provide the same certificate multiple times.
+TEST_F(PathBuilderKeyRolloverTest, TestDuplicateAsyncIntermediates) {
+ StrictMock<MockCertIssuerSource> cert_issuer_source;
+
+ // Only newroot is a trusted root.
+ TrustStore trust_store;
+ trust_store.AddTrustedCertificate(newroot_);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_,
+ &result);
+ path_builder.AddCertIssuerSource(&cert_issuer_source);
+
+ CertIssuerSource::IssuerCallback target_issuers_callback;
+ // Create the mock CertIssuerSource::Request...
+ std::unique_ptr<StrictMock<MockCertIssuerSourceRequest>>
+ target_issuers_req_owner(new StrictMock<MockCertIssuerSourceRequest>());
+ // Keep a raw pointer to the Request...
+ StrictMock<MockCertIssuerSourceRequest>* target_issuers_req =
+ target_issuers_req_owner.get();
+ // Setup helper class to pass ownership of the Request to the PathBuilder when
+ // it calls AsyncGetIssuersOf.
+ CertIssuerSourceRequestMover req_mover(std::move(target_issuers_req_owner));
+ {
+ ::testing::InSequence s;
+ EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(target_.get(), _));
+ EXPECT_CALL(cert_issuer_source, AsyncGetIssuersOf(target_.get(), _, _))
+ .WillOnce(
+ DoAll(SaveArg<1>(&target_issuers_callback),
+ Invoke(&req_mover, &CertIssuerSourceRequestMover::MoveIt)));
+ }
+
+ TestClosure callback;
+ CompletionStatus rv = path_builder.Run(callback.closure());
+ ASSERT_EQ(CompletionStatus::ASYNC, rv);
+
+ ASSERT_FALSE(target_issuers_callback.is_null());
+
+ ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source);
+
+ // First async batch: return oldintermediate_.
+ EXPECT_CALL(*target_issuers_req, GetNext(_))
+ .WillOnce(DoAll(SetArgPointee<0>(oldintermediate_),
+ Return(CompletionStatus::SYNC)))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC)));
+ {
+ ::testing::InSequence s;
+ // oldintermediate_ does not create a valid path, so both sync and async
+ // lookups are expected.
+ EXPECT_CALL(cert_issuer_source,
+ SyncGetIssuersOf(oldintermediate_.get(), _));
+ EXPECT_CALL(cert_issuer_source,
+ AsyncGetIssuersOf(oldintermediate_.get(), _, _));
+ }
+ target_issuers_callback.Run(target_issuers_req);
+ ::testing::Mock::VerifyAndClearExpectations(target_issuers_req);
+ ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source);
+
+ // Second async batch: return a different copy of oldintermediate_ again.
+ scoped_refptr<ParsedCertificate> oldintermediate_dupe(
+ ParsedCertificate::CreateFromCertificateCopy(
+ oldintermediate_->der_cert().AsStringPiece(), {}));
+ EXPECT_CALL(*target_issuers_req, GetNext(_))
+ .WillOnce(DoAll(SetArgPointee<0>(oldintermediate_dupe),
+ Return(CompletionStatus::SYNC)))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC)));
+ target_issuers_callback.Run(target_issuers_req);
+ // oldintermediate was already processed above, it should not generate any
+ // more requests.
+ ::testing::Mock::VerifyAndClearExpectations(target_issuers_req);
+ ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source);
+
+ // Third async batch: return newintermediate_.
+ EXPECT_CALL(*target_issuers_req, GetNext(_))
+ .WillOnce(DoAll(SetArgPointee<0>(newintermediate_),
+ Return(CompletionStatus::SYNC)))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC)));
+ // newroot_ is in the trust store, so this path will be completed
+ // synchronously. AsyncGetIssuersOf will not be called on newintermediate_.
+ EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(newintermediate_.get(), _));
+ target_issuers_callback.Run(target_issuers_req);
+ // Note that VerifyAndClearExpectations(target_issuers_req) is not called
+ // here. PathBuilder could have destroyed it already, so just let the
+ // expectations get checked by the destructor.
+ ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source);
+
+ // Ensure pathbuilder finished and filled result.
+ callback.WaitForResult();
+
+ EXPECT_EQ(OK, result.error());
+ ASSERT_EQ(2U, result.paths.size());
+
+ // Path builder first attempts: target <- oldintermediate <- newroot
+ // but it will fail since oldintermediate is signed by oldroot.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(target_, result.paths[0]->path[0]);
+ EXPECT_EQ(oldintermediate_, result.paths[0]->path[1]);
+ EXPECT_EQ(newroot_, result.paths[0]->path[2]);
+
+ // The second async result does not generate any path.
+
+ // After the third batch of async results, path builder will attempt:
+ // target <- newintermediate <- newroot which will succeed.
+ EXPECT_EQ(OK, result.paths[1]->error);
+ ASSERT_EQ(3U, result.paths[1]->path.size());
+ EXPECT_EQ(target_, result.paths[1]->path[0]);
+ EXPECT_EQ(newintermediate_, result.paths[1]->path[1]);
+ EXPECT_EQ(newroot_, result.paths[1]->path[2]);
+}
+
+} // namespace
+
+} // namespace net
« no previous file with comments | « net/cert/internal/path_builder_pkits_unittest.cc ('k') | net/cert/internal/path_builder_verify_certificate_chain_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698