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

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: . Created 4 years, 8 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..c4e44fa67f5ffd9d838349afd187fae985672e9b
--- /dev/null
+++ b/net/cert/internal/path_builder_unittest.cc
@@ -0,0 +1,719 @@
+// 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/thread_task_runner_handle.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/internal/parse_name.h"
+#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/test_helpers.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/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// AsyncStaticCertsSource always returns its certs asynchronously.
+class AsyncStaticCertsSource : public CertSource {
+ public:
+ class StaticAsyncRequest : public Request {
+ public:
+ StaticAsyncRequest(const IssuerCallback& issuers_callback,
+ CertVector&& issuers)
+ : cancelable_closure_(base::Bind(&StaticAsyncRequest::RunCallback,
+ base::Unretained(this))),
+ issuers_callback_(issuers_callback) {
+ issuers_.swap(issuers);
+ }
+ ~StaticAsyncRequest() override {}
+
+ void RunCallback() { issuers_callback_.Run(std::move(issuers_)); }
+ base::Closure callback() { return cancelable_closure_.callback(); }
+
+ private:
+ base::CancelableClosure cancelable_closure_;
+ IssuerCallback issuers_callback_;
+ CertVector issuers_;
+ AsyncStaticCertsSource* source_;
+
+ DISALLOW_COPY_AND_ASSIGN(StaticAsyncRequest);
+ };
+
+ AsyncStaticCertsSource() {}
+ ~AsyncStaticCertsSource() override {}
+
+ bool Init(std::vector<der::Input> certs) {
+ return static_cert_source_.Init(certs);
+ }
+ bool SyncGetIssuersOf(const CertThing& cert, CertVector* issuers) override {
+ return true;
+ }
+ int AsyncGetIssuersOf(const CertThing& cert,
+ const IssuerCallback& issuers_callback,
+ std::unique_ptr<Request>* out_req) override {
+ num_async_gets_++;
+ CertVector issuers;
+ if (!static_cert_source_.SyncGetIssuersOf(cert, &issuers))
+ return ERR_FAILED;
+ 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);
+ return ERR_IO_PENDING;
+ }
+ int num_async_gets() const { return num_async_gets_; }
+
+ private:
+ StaticCertsSource static_cert_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,
+ std::string* 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 = block_data;
+ } 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,
+ std::string* result) {
+ return ReadTestPem("net/data/ssl/certificates/" + file_name, "CERTIFICATE",
+ result);
+}
+
+std::string DumpCertChain(
+ const std::vector<std::unique_ptr<CertThing>>& chain) {
+ std::string s;
+ for (const auto& i : chain) {
+ ParsedCertificate cert;
+ ParsedTbsCertificate tbs;
+ if (!ParseCertificate(i->der_cert(), &cert)) // XXX don't reparse
+ return std::string();
+ if (!ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs))
+ return std::string();
+ RDNSequence subject, issuer;
+ if (!ParseName(tbs.subject_tlv, &subject))
+ return std::string();
+ if (!ParseName(tbs.issuer_tlv, &issuer))
+ return std::string();
+
+ std::string subject_str, issuer_str;
+
+ if (!ConvertToRFC2253(subject, &subject_str))
+ return std::string();
+ if (!ConvertToRFC2253(issuer, &issuer_str))
+ return std::string();
+ if (!s.empty())
+ s += " -> ";
+ s += subject_str + "(" + issuer_str + ")";
+ }
+ return s;
+}
+
+void DumpPathBuilderResults(const CertPathBuilder::Result& result) {
+ DVLOG(1) << "CertPathBuilder results (n=" << result.paths.size() << "):";
+ for (size_t i = 0; i < result.paths.size(); ++i) {
+ DVLOG(1) << " " << i << ((result.best_result_index == i) ? "*" : " ") << " "
+ << result.paths[i]->rv << " "
+ << DumpCertChain(result.paths[i]->path);
+ }
+ // DVLOG(1) << "result path: " << DumpCertChain(result.path);
+}
+
+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:
+ std::string 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};
+};
+
+// Test that async cert queries are not made if the path can be successfully
+// built with synchronously available certs.
+TEST_F(PathBuilderMultiRootTest, TriesSyncFirst) {
+ StaticCertsSource sync_cert_source;
+ AsyncStaticCertsSource async_cert_source;
+
+ TrustStore trust_store;
+ ASSERT_TRUE(trust_store.AddTrustedCertificate(e_by_e_));
+
+ std::vector<der::Input> sync_certs;
+ sync_certs.push_back(der::Input(&a_by_b_));
+ // XXX if B(C) is in certs, it may check that path first, which would try to
+ // do async query for issuers of C.
+ // sync_certs.push_back(der::Input(&b_by_c));
+ sync_certs.push_back(der::Input(&b_by_f_));
+ sync_certs.push_back(der::Input(&f_by_e_));
+ ASSERT_TRUE(sync_cert_source.Init(sync_certs));
+
+ std::vector<der::Input> async_certs;
+ async_certs.push_back(der::Input(&b_by_c_));
+ async_certs.push_back(der::Input(&c_by_e_));
+ ASSERT_TRUE(async_cert_source.Init(async_certs));
+
+ CertPathBuilder::CertSources cert_sources;
+ cert_sources.push_back(&async_cert_source);
+ cert_sources.push_back(&sync_cert_source);
+
+ std::unique_ptr<CertThing> target_cert(
+ CertThing::CreateFromCertificateCopy(a_by_b_));
+ ASSERT_TRUE(target_cert);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(std::move(target_cert), cert_sources,
+ trust_store, &signature_policy_, time_, &result);
+
+ TestCompletionCallback callback;
+ int rv = path_builder.Run(callback.callback());
+
+ DVLOG(1) << "path_builder.Run rv=" << ErrorToString(rv);
+
+ EXPECT_EQ(OK, rv);
+
+ if (rv == ERR_IO_PENDING) {
+ DVLOG(1) << "waiting for async completion...";
+ rv = callback.WaitForResult();
+ DVLOG(1) << "async rv=" << ErrorToString(rv);
+ EXPECT_EQ(OK, rv);
+ }
+
+ // DVLOG(1) << "result path: " << DumpCertChain(result.path);
+ DumpPathBuilderResults(result);
+
+ EXPECT_EQ(0, async_cert_source.num_async_gets());
+}
+
+// If async queries are needed, all async sources will be queried
+// simultaneously.
+TEST_F(PathBuilderMultiRootTest, TestAsyncSimultaneous) {
+ StaticCertsSource sync_cert_source;
+ AsyncStaticCertsSource async_cert_source1, async_cert_source2;
+
+ TrustStore trust_store;
+ ASSERT_TRUE(trust_store.AddTrustedCertificate(e_by_e_));
+
+ std::vector<der::Input> sync_certs;
+ sync_certs.push_back(der::Input(&a_by_b_));
+ sync_certs.push_back(der::Input(&b_by_c_));
+ sync_certs.push_back(der::Input(&b_by_f_));
+ ASSERT_TRUE(sync_cert_source.Init(sync_certs));
+
+ std::vector<der::Input> async_certs1;
+ async_certs1.push_back(der::Input(&c_by_e_));
+ ASSERT_TRUE(async_cert_source1.Init(async_certs1));
+
+ std::vector<der::Input> async_certs2;
+ async_certs2.push_back(der::Input(&f_by_e_));
+ ASSERT_TRUE(async_cert_source2.Init(async_certs2));
+
+ CertPathBuilder::CertSources cert_sources;
+ cert_sources.push_back(&async_cert_source1);
+ cert_sources.push_back(&async_cert_source2);
+ cert_sources.push_back(&sync_cert_source);
+
+ std::unique_ptr<CertThing> target_cert(
+ CertThing::CreateFromCertificateCopy(a_by_b_));
+ ASSERT_TRUE(target_cert);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(std::move(target_cert), cert_sources,
+ trust_store, &signature_policy_, time_, &result);
+
+ TestCompletionCallback callback;
+ int rv = path_builder.Run(callback.callback());
+
+ DVLOG(1) << "path_builder.Run rv=" << ErrorToString(rv);
+
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ if (rv == ERR_IO_PENDING) {
+ DVLOG(1) << "waiting for async completion...";
+ rv = callback.WaitForResult();
+ DVLOG(1) << "async rv=" << ErrorToString(rv);
+ EXPECT_EQ(OK, rv);
+ }
+
+ // DVLOG(1) << "result path: " << DumpCertChain(result.path);
+ DumpPathBuilderResults(result);
+
+ EXPECT_EQ(1, async_cert_source1.num_async_gets());
+ EXPECT_EQ(1, async_cert_source2.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) {
+ StaticCertsSource sync_cert_source;
+
+ // Both D(D) and C(D) are trusted roots.
+ TrustStore trust_store;
+ ASSERT_TRUE(trust_store.AddTrustedCertificate(d_by_d_));
+ ASSERT_TRUE(trust_store.AddTrustedCertificate(c_by_d_));
+
+ // Certs A(B), B(C), and C(D) are all supplied.
+ std::vector<der::Input> sync_certs;
+ sync_certs.push_back(der::Input(&a_by_b_));
+ sync_certs.push_back(der::Input(&b_by_c_));
+ sync_certs.push_back(der::Input(&c_by_d_));
+ ASSERT_TRUE(sync_cert_source.Init(sync_certs));
+
+ CertPathBuilder::CertSources cert_sources;
+ cert_sources.push_back(&sync_cert_source);
+
+ std::unique_ptr<CertThing> target_cert(
+ CertThing::CreateFromCertificateCopy(a_by_b_));
+ ASSERT_TRUE(target_cert);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(std::move(target_cert), cert_sources,
+ trust_store, &signature_policy_, time_, &result);
+
+ TestCompletionCallback callback;
+ int rv = path_builder.Run(callback.callback());
+
+ DVLOG(1) << "path_builder.Run rv=" << ErrorToString(rv);
+
+ EXPECT_EQ(OK, rv);
+
+ if (rv == ERR_IO_PENDING) {
+ DVLOG(1) << "waiting for async completion...";
+ rv = callback.WaitForResult();
+ DVLOG(1) << "async rv=" << ErrorToString(rv);
+ EXPECT_EQ(OK, rv);
+ }
+
+ // DVLOG(1) << "result path: " << DumpCertChain(result.path);
+ DumpPathBuilderResults(result);
+
+ // 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) {
+ StaticCertsSource sync_cert_source;
+ AsyncStaticCertsSource async_cert_source;
+
+ // Only D(D) is a trusted root.
+ TrustStore trust_store;
+ ASSERT_TRUE(trust_store.AddTrustedCertificate(d_by_d_));
+
+ // Certs A(B), 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.
+ std::vector<der::Input> sync_certs;
+ sync_certs.push_back(der::Input(&a_by_b_));
+ sync_certs.push_back(der::Input(&b_by_f_));
+ sync_certs.push_back(der::Input(&f_by_e_));
+ ASSERT_TRUE(sync_cert_source.Init(sync_certs));
+
+ // Certs B(C), and C(D) are supplied asynchronously, so the path
+ // A(B) -> B(C) -> C(D) -> D(D) should be tried second.
+ std::vector<der::Input> async_certs;
+ async_certs.push_back(der::Input(&b_by_c_));
+ async_certs.push_back(der::Input(&c_by_d_));
+ ASSERT_TRUE(async_cert_source.Init(async_certs));
+
+ CertPathBuilder::CertSources cert_sources;
+ cert_sources.push_back(&sync_cert_source);
+ cert_sources.push_back(&async_cert_source);
+
+ std::unique_ptr<CertThing> target_cert(
+ CertThing::CreateFromCertificateCopy(a_by_b_));
+ ASSERT_TRUE(target_cert);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(std::move(target_cert), cert_sources,
+ trust_store, &signature_policy_, time_, &result);
+
+ TestCompletionCallback callback;
+ int rv = path_builder.Run(callback.callback());
+
+ DVLOG(1) << "path_builder.Run rv=" << ErrorToString(rv);
+
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ if (rv == ERR_IO_PENDING) {
+ DVLOG(1) << "waiting for async completion...";
+ rv = callback.WaitForResult();
+ DVLOG(1) << "async rv=" << ErrorToString(rv);
+ EXPECT_EQ(OK, rv);
+ }
+
+ // DVLOG(1) << "result path: " << DumpCertChain(result.path);
+ DumpPathBuilderResults(result);
+
+ // 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(der::Input(&a_by_b_),
+ result.paths[result.best_result_index]->path[0]->der_cert());
+ EXPECT_EQ(der::Input(&b_by_c_),
+ result.paths[result.best_result_index]->path[1]->der_cert());
+ EXPECT_EQ(der::Input(&c_by_d_),
+ result.paths[result.best_result_index]->path[2]->der_cert());
+ EXPECT_EQ(der::Input(&d_by_d_),
+ result.paths[result.best_result_index]->path[3]->der_cert());
+}
+
+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_ = path[0];
+ oldintermediary_ = path[1];
+
+ ReadVerifyCertChainTestFromFile(
+ "net/data/verify_certificate_chain_unittest/"
+ "key-rollover-longrolloverchain.pem",
+ &path, &oldroot_, &time_);
+ ASSERT_EQ(4U, path.size());
+ newintermediary_ = path[1];
+ newroot_ = path[2];
+ newrootrollover_ = path[3];
+ }
+
+ protected:
+ std::string target_, oldintermediary_, newintermediary_, oldroot_, newroot_,
+ 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) {
+ StaticCertsSource sync_cert_source;
+
+ // Only oldroot is trusted.
+ TrustStore trust_store;
+ ASSERT_TRUE(trust_store.AddTrustedCertificate(oldroot_));
+
+ // Old intermediary cert is not provided, so the pathbuilder will need to go
+ // through the rollover cert.
+ std::vector<der::Input> sync_certs;
+ sync_certs.push_back(der::Input(&newintermediary_));
+ sync_certs.push_back(der::Input(&newrootrollover_));
+ ASSERT_TRUE(sync_cert_source.Init(sync_certs));
+
+ CertPathBuilder::CertSources cert_sources;
+ cert_sources.push_back(&sync_cert_source);
+
+ std::unique_ptr<CertThing> target_cert(
+ CertThing::CreateFromCertificateCopy(target_));
+ ASSERT_TRUE(target_cert);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(std::move(target_cert), cert_sources,
+ trust_store, &signature_policy_, time_, &result);
+
+ TestCompletionCallback callback;
+ int rv = path_builder.Run(callback.callback());
+
+ DVLOG(1) << "path_builder.Run rv=" << ErrorToString(rv);
+
+ EXPECT_EQ(OK, rv);
+
+ if (rv == ERR_IO_PENDING) {
+ DVLOG(1) << "waiting for async completion...";
+ rv = callback.WaitForResult();
+ DVLOG(1) << "async rv=" << ErrorToString(rv);
+ EXPECT_EQ(OK, rv);
+ }
+
+ DumpPathBuilderResults(result);
+
+ // Path builder will first attempt: target -> newintermediary -> oldroot
+ // but it will fail since newintermediary is signed by newroot.
+ ASSERT_EQ(2U, result.paths.size());
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->rv);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(der::Input(&target_), result.paths[0]->path[0]->der_cert());
+ EXPECT_EQ(der::Input(&newintermediary_),
+ result.paths[0]->path[1]->der_cert());
+ EXPECT_EQ(der::Input(&oldroot_), result.paths[0]->path[2]->der_cert());
+
+ // Path builder will next attempt:
+ // target -> newintermediary -> newrootrollover -> oldroot
+ // which will succeed.
+ EXPECT_EQ(1U, result.best_result_index);
+ EXPECT_EQ(OK, result.paths[1]->rv);
+ ASSERT_EQ(4U, result.paths[1]->path.size());
+ EXPECT_EQ(der::Input(&target_), result.paths[1]->path[0]->der_cert());
+ EXPECT_EQ(der::Input(&newintermediary_),
+ result.paths[1]->path[1]->der_cert());
+ EXPECT_EQ(der::Input(&newrootrollover_),
+ result.paths[1]->path[2]->der_cert());
+ EXPECT_EQ(der::Input(&oldroot_), result.paths[1]->path[3]->der_cert());
+}
+
+// Tests that if both old and new roots are trusted it can build a path through
+// either.
+// XXX Once prioritzation is implemented, it should test that it always builds
+// the path through the new root.
+TEST_F(PathBuilderKeyRolloverTest, TestRolloverBothRootsTrusted) {
+ StaticCertsSource sync_cert_source;
+
+ // Both oldroot and newroot are trusted.
+ TrustStore trust_store;
+ ASSERT_TRUE(trust_store.AddTrustedCertificate(oldroot_));
+ ASSERT_TRUE(trust_store.AddTrustedCertificate(newroot_));
+
+ // Both old and new intermediates + rollover cert are provided.
+ std::vector<der::Input> sync_certs;
+ sync_certs.push_back(der::Input(&oldintermediary_));
+ sync_certs.push_back(der::Input(&newintermediary_));
+ sync_certs.push_back(der::Input(&newrootrollover_));
+ ASSERT_TRUE(sync_cert_source.Init(sync_certs));
+
+ CertPathBuilder::CertSources cert_sources;
+ cert_sources.push_back(&sync_cert_source);
+
+ std::unique_ptr<CertThing> target_cert(
+ CertThing::CreateFromCertificateCopy(target_));
+ ASSERT_TRUE(target_cert);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(std::move(target_cert), cert_sources,
+ trust_store, &signature_policy_, time_, &result);
+
+ TestCompletionCallback callback;
+ int rv = path_builder.Run(callback.callback());
+
+ DVLOG(1) << "path_builder.Run rv=" << ErrorToString(rv);
+
+ EXPECT_EQ(OK, rv);
+
+ if (rv == ERR_IO_PENDING) {
+ DVLOG(1) << "waiting for async completion...";
+ rv = callback.WaitForResult();
+ DVLOG(1) << "async rv=" << ErrorToString(rv);
+ EXPECT_EQ(OK, rv);
+ }
+
+ DumpPathBuilderResults(result);
+
+ // Path builder willattempt one of:
+ // target -> oldintermediary -> oldroot
+ // target -> newintermediary -> newroot
+ // either will succeed.
+ ASSERT_EQ(1U, result.paths.size());
+ EXPECT_EQ(OK, result.paths[0]->rv);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(der::Input(&target_), result.paths[0]->path[0]->der_cert());
+ // TODO: once we sort input certs on notbefore date, verify that it prefers
+ // newintermediary.
+ if (result.paths[0]->path[1]->der_cert() != der::Input(&newintermediary_)) {
+ DVLOG(1) << "USED OLD";
+ EXPECT_EQ(der::Input(&oldintermediary_),
+ result.paths[0]->path[1]->der_cert());
+ EXPECT_EQ(der::Input(&oldroot_), result.paths[0]->path[2]->der_cert());
+ } else {
+ DVLOG(1) << "USED NEW";
+ EXPECT_EQ(der::Input(&newintermediary_),
+ result.paths[0]->path[1]->der_cert());
+ EXPECT_EQ(der::Input(&newroot_), result.paths[0]->path[2]->der_cert());
+ }
+}
+
+// Tests that the path builder doesn't build longer than necessary paths.
+// TODO: currently it does. fix that.
+TEST_F(PathBuilderKeyRolloverTest, TestRolloverLongChain) {
+ StaticCertsSource sync_cert_source;
+ AsyncStaticCertsSource async_cert_source;
+
+ // Only oldroot is trusted.
+ TrustStore trust_store;
+ ASSERT_TRUE(trust_store.AddTrustedCertificate(oldroot_));
+
+ // New intermediate and new root are provided synchronously.
+ std::vector<der::Input> sync_certs;
+ sync_certs.push_back(der::Input(&newintermediary_));
+ sync_certs.push_back(der::Input(&newroot_));
+ ASSERT_TRUE(sync_cert_source.Init(sync_certs));
+
+ // Rollover cert is only provided asynchronously. This will force the
+ // pathbuilder to first try building a longer than necessary path.
+ std::vector<der::Input> async_certs;
+ async_certs.push_back(der::Input(&newrootrollover_));
+ ASSERT_TRUE(async_cert_source.Init(async_certs));
+
+ CertPathBuilder::CertSources cert_sources;
+ cert_sources.push_back(&sync_cert_source);
+ cert_sources.push_back(&async_cert_source);
+
+ std::unique_ptr<CertThing> target_cert(
+ CertThing::CreateFromCertificateCopy(target_));
+ ASSERT_TRUE(target_cert);
+
+ CertPathBuilder::Result result;
+ CertPathBuilder path_builder(std::move(target_cert), cert_sources,
+ trust_store, &signature_policy_, time_, &result);
+
+ TestCompletionCallback callback;
+ int rv = path_builder.Run(callback.callback());
+
+ DVLOG(1) << "path_builder.Run rv=" << ErrorToString(rv);
+
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ if (rv == ERR_IO_PENDING) {
+ DVLOG(1) << "waiting for async completion...";
+ rv = callback.WaitForResult();
+ DVLOG(1) << "async rv=" << ErrorToString(rv);
+ EXPECT_EQ(OK, rv);
+ }
+
+ DumpPathBuilderResults(result);
+
+ ASSERT_EQ(3U, result.paths.size());
+
+ // Path builder will first attempt: target -> newintermediary -> oldroot
+ // but it will fail since newintermediary is signed by newroot.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->rv);
+ ASSERT_EQ(3U, result.paths[0]->path.size());
+ EXPECT_EQ(der::Input(&target_), result.paths[0]->path[0]->der_cert());
+ EXPECT_EQ(der::Input(&newintermediary_),
+ result.paths[0]->path[1]->der_cert());
+ EXPECT_EQ(der::Input(&oldroot_), result.paths[0]->path[2]->der_cert());
+
+ // Path builder will next attempt: target -> newintermediary -> newroot ->
+ // oldroot
+ // but it will fail since newroot is self-signed.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[1]->rv);
+ ASSERT_EQ(4U, result.paths[1]->path.size());
+ EXPECT_EQ(der::Input(&target_), result.paths[1]->path[0]->der_cert());
+ EXPECT_EQ(der::Input(&newintermediary_),
+ result.paths[1]->path[1]->der_cert());
+ EXPECT_EQ(der::Input(&newroot_), result.paths[1]->path[2]->der_cert());
+ EXPECT_EQ(der::Input(&oldroot_), result.paths[1]->path[3]->der_cert());
+
+ // Finally path builder will use:
+ // target -> newintermediary -> newroot -> newrootrollover -> oldroot
+ // This path is valid, but longer than necessary (no need for newroot to
+ // be in there).
+ //
+ // TODO: once LoopChecker is keyed on Name + SAN + SPKI, it should skip
+ // that path and use:
+ // target -> newintermediary -> newrootrollover -> oldroot
+ EXPECT_EQ(2U, result.best_result_index);
+ EXPECT_EQ(OK, result.paths[2]->rv);
+ ASSERT_EQ(5U, result.paths[2]->path.size());
+ EXPECT_EQ(der::Input(&target_), result.paths[2]->path[0]->der_cert());
+ EXPECT_EQ(der::Input(&newintermediary_),
+ result.paths[2]->path[1]->der_cert());
+ EXPECT_EQ(der::Input(&newroot_), result.paths[2]->path[2]->der_cert());
+ EXPECT_EQ(der::Input(&newrootrollover_),
+ result.paths[2]->path[3]->der_cert());
+ EXPECT_EQ(der::Input(&oldroot_), result.paths[2]->path[4]->der_cert());
+}
+
+// TODO:
+// * cross signing ?
+// * test of multiple trust anchor matches
+
+} // namespace
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698