Certificate path builder for new certificate verification library

Builds paths from a target certificate to a trust anchor and attempts to verify them according to RFC 5280. Supports asynchronous intermediate lookups (eg, AIA fetching) and backtracking.

This implementation uses a depth-first strategy which is simple and uses minimal resources, but may not be optimal.

BUG=410574

Review-Url: https://codereview.chromium.org/1923433002
Cr-Commit-Position: refs/heads/master@{#404239}
diff --git a/components/cast_certificate/cast_cert_validator.cc b/components/cast_certificate/cast_cert_validator.cc
index 6a81c84d..ec1fae2 100644
--- a/components/cast_certificate/cast_cert_validator.cc
+++ b/components/cast_certificate/cast_cert_validator.cc
@@ -13,15 +13,16 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
+#include "net/cert/internal/cert_issuer_source_static.h"
 #include "net/cert/internal/certificate_policies.h"
 #include "net/cert/internal/extended_key_usage.h"
 #include "net/cert/internal/parse_certificate.h"
 #include "net/cert/internal/parse_name.h"
 #include "net/cert/internal/parsed_certificate.h"
+#include "net/cert/internal/path_builder.h"
 #include "net/cert/internal/signature_algorithm.h"
 #include "net/cert/internal/signature_policy.h"
 #include "net/cert/internal/trust_store.h"
-#include "net/cert/internal/verify_certificate_chain.h"
 #include "net/cert/internal/verify_signed_data.h"
 #include "net/der/input.h"
 
@@ -245,20 +246,6 @@
   return result;
 }
 
-class ScopedCheckUnreferencedCerts {
- public:
-  explicit ScopedCheckUnreferencedCerts(
-      std::vector<scoped_refptr<net::ParsedCertificate>>* certs)
-      : certs_(certs) {}
-  ~ScopedCheckUnreferencedCerts() {
-    for (const auto& cert : *certs_)
-      DCHECK(cert->HasOneRef());
-  }
-
- private:
-  std::vector<scoped_refptr<net::ParsedCertificate>>* certs_;
-};
-
 // Returns the parsing options used for Cast certificates.
 net::ParseCertificateOptions GetCertParsingOptions() {
   net::ParseCertificateOptions options;
@@ -281,38 +268,46 @@
                       const base::Time::Exploded& time,
                       std::unique_ptr<CertVerificationContext>* context,
                       CastDeviceCertPolicy* policy) {
-  // The underlying verification function expects a sequence of
-  // ParsedCertificate.
-  std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
-  // Verify that nothing saves a reference to the input certs, since the backing
-  // data will go out of scope when the function finishes.
-  ScopedCheckUnreferencedCerts ref_checker(&input_chain);
+  if (certs.empty())
+    return false;
 
-  for (const auto& cert_der : certs) {
-    // No reference to the ParsedCertificate is kept past the end of this
-    // function, so using EXTERNAL_REFERENCE here is safe.
-    if (!net::ParsedCertificate::CreateAndAddToVector(
-            reinterpret_cast<const uint8_t*>(cert_der.data()), cert_der.size(),
+  // No reference to these ParsedCertificates is kept past the end of this
+  // function, so using EXTERNAL_REFERENCE here is safe.
+  scoped_refptr<net::ParsedCertificate> target_cert;
+  net::CertIssuerSourceStatic intermediate_cert_issuer_source;
+  for (size_t i = 0; i < certs.size(); ++i) {
+    scoped_refptr<net::ParsedCertificate> cert(
+        net::ParsedCertificate::CreateFromCertificateData(
+            reinterpret_cast<const uint8_t*>(certs[i].data()), certs[i].size(),
             net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE,
-            GetCertParsingOptions(), &input_chain)) {
+            GetCertParsingOptions()));
+    if (!cert)
       return false;
-    }
+
+    if (i == 0)
+      target_cert = std::move(cert);
+    else
+      intermediate_cert_issuer_source.AddCert(std::move(cert));
   }
 
   // Use a signature policy compatible with Cast's PKI.
   auto signature_policy = CreateCastSignaturePolicy();
 
-  // Do RFC 5280 compatible certificate verification using the two Cast
-  // trust anchors and Cast signature policy.
-  if (!net::VerifyCertificateChain(input_chain, CastTrustStore::Get(),
-                                   signature_policy.get(),
-                                   ConvertExplodedTime(time), nullptr)) {
+  // Do path building and RFC 5280 compatible certificate verification using the
+  // two Cast trust anchors and Cast signature policy.
+  net::CertPathBuilder::Result result;
+  net::CertPathBuilder path_builder(target_cert.get(), &CastTrustStore::Get(),
+                                    signature_policy.get(),
+                                    ConvertExplodedTime(time), &result);
+  path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
+  net::CompletionStatus rv = path_builder.Run(base::Closure());
+  DCHECK_EQ(rv, net::CompletionStatus::SYNC);
+  if (!result.is_success())
     return false;
-  }
 
   // Check properties of the leaf certificate (key usage, policy), and construct
   // a CertVerificationContext that uses its public key.
-  return CheckTargetCertificate(input_chain[0].get(), context, policy);
+  return CheckTargetCertificate(target_cert.get(), context, policy);
 }
 
 std::unique_ptr<CertVerificationContext> CertVerificationContextImplForTest(
diff --git a/components/cast_certificate/cast_cert_validator.h b/components/cast_certificate/cast_cert_validator.h
index 1c335ae..23378cb 100644
--- a/components/cast_certificate/cast_cert_validator.h
+++ b/components/cast_certificate/cast_cert_validator.h
@@ -54,9 +54,9 @@
 // Inputs:
 //
 // * |certs| is a chain of DER-encoded certificates:
-//   * |certs[0]| is the target certificate (i.e. the device certificate)
-//   * |certs[i]| is the certificate that issued certs[i-1]
-//   * |certs.back()| must be signed by a trust anchor
+//   * |certs[0]| is the target certificate (i.e. the device certificate).
+//   * |certs[1..n-1]| are intermediates certificates to use in path building.
+//     Their ordering does not matter.
 //
 // * |time| is the UTC time to use for determining if the certificate
 //   is expired.
diff --git a/net/cert/internal/cert_issuer_source.h b/net/cert/internal/cert_issuer_source.h
index 61580a8..1ffc3b3a 100644
--- a/net/cert/internal/cert_issuer_source.h
+++ b/net/cert/internal/cert_issuer_source.h
@@ -11,11 +11,10 @@
 #include "base/callback.h"
 #include "net/base/net_export.h"
 #include "net/cert/internal/completion_status.h"
+#include "net/cert/internal/parsed_certificate.h"
 
 namespace net {
 
-class ParsedCertificate;
-
 // Interface for looking up issuers of a certificate during path building.
 // Provides a synchronous and asynchronous method for retrieving issuers, so the
 // path builder can try to complete synchronously first. The caller is expected
@@ -58,9 +57,8 @@
   // Matches are appended to |issuers|. Any existing contents of |issuers| will
   // not be modified. If the implementation does not support synchronous
   // lookups, or if there are no matches, |issuers| is not modified.
-  virtual void SyncGetIssuersOf(
-      const ParsedCertificate* cert,
-      std::vector<scoped_refptr<ParsedCertificate>>* issuers) = 0;
+  virtual void SyncGetIssuersOf(const ParsedCertificate* cert,
+                                ParsedCertificateList* issuers) = 0;
 
   // Finds certificates whose Subject matches |cert|'s Issuer.
   // If an async callback will be made |*out_req| is filled with a Request
diff --git a/net/cert/internal/cert_issuer_source_aia.cc b/net/cert/internal/cert_issuer_source_aia.cc
index 4dacc34..5ee96a0 100644
--- a/net/cert/internal/cert_issuer_source_aia.cc
+++ b/net/cert/internal/cert_issuer_source_aia.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "net/cert/cert_net_fetcher.h"
-#include "net/cert/internal/parsed_certificate.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -37,7 +36,7 @@
   CertIssuerSource::IssuerCallback issuers_callback_;
   std::vector<std::unique_ptr<CertNetFetcher::Request>> cert_fetcher_requests_;
   size_t pending_requests_ = 0;
-  std::vector<scoped_refptr<ParsedCertificate>> results_;
+  ParsedCertificateList results_;
   size_t current_result_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(AiaRequest);
@@ -109,9 +108,8 @@
 
 CertIssuerSourceAia::~CertIssuerSourceAia() = default;
 
-void CertIssuerSourceAia::SyncGetIssuersOf(
-    const ParsedCertificate* cert,
-    std::vector<scoped_refptr<ParsedCertificate>>* issuers) {
+void CertIssuerSourceAia::SyncGetIssuersOf(const ParsedCertificate* cert,
+                                           ParsedCertificateList* issuers) {
   // CertIssuerSourceAia never returns synchronous results.
 }
 
diff --git a/net/cert/internal/cert_issuer_source_aia.h b/net/cert/internal/cert_issuer_source_aia.h
index e0c7fe7..afeeda5 100644
--- a/net/cert/internal/cert_issuer_source_aia.h
+++ b/net/cert/internal/cert_issuer_source_aia.h
@@ -22,9 +22,8 @@
   ~CertIssuerSourceAia() override;
 
   // CertIssuerSource implementation:
-  void SyncGetIssuersOf(
-      const ParsedCertificate* cert,
-      std::vector<scoped_refptr<ParsedCertificate>>* issuers) override;
+  void SyncGetIssuersOf(const ParsedCertificate* cert,
+                        ParsedCertificateList* issuers) override;
   void AsyncGetIssuersOf(const ParsedCertificate* cert,
                          const IssuerCallback& issuers_callback,
                          std::unique_ptr<Request>* out_req) override;
diff --git a/net/cert/internal/cert_issuer_source_aia_unittest.cc b/net/cert/internal/cert_issuer_source_aia_unittest.cc
index 455cf66..2132080 100644
--- a/net/cert/internal/cert_issuer_source_aia_unittest.cc
+++ b/net/cert/internal/cert_issuer_source_aia_unittest.cc
@@ -166,7 +166,7 @@
 
   StrictMock<MockCertNetFetcherImpl> mock_fetcher;
   CertIssuerSourceAia aia_source(&mock_fetcher);
-  std::vector<scoped_refptr<ParsedCertificate>> issuers;
+  ParsedCertificateList issuers;
   aia_source.SyncGetIssuersOf(cert.get(), &issuers);
   EXPECT_EQ(0U, issuers.size());
 }
diff --git a/net/cert/internal/cert_issuer_source_static.cc b/net/cert/internal/cert_issuer_source_static.cc
index c7685f436..d8a42a3 100644
--- a/net/cert/internal/cert_issuer_source_static.cc
+++ b/net/cert/internal/cert_issuer_source_static.cc
@@ -4,8 +4,6 @@
 
 #include "net/cert/internal/cert_issuer_source_static.h"
 
-#include "net/cert/internal/parsed_certificate.h"
-
 namespace net {
 
 CertIssuerSourceStatic::CertIssuerSourceStatic() = default;
@@ -16,9 +14,8 @@
       cert->normalized_subject().AsStringPiece(), std::move(cert)));
 }
 
-void CertIssuerSourceStatic::SyncGetIssuersOf(
-    const ParsedCertificate* cert,
-    std::vector<scoped_refptr<ParsedCertificate>>* issuers) {
+void CertIssuerSourceStatic::SyncGetIssuersOf(const ParsedCertificate* cert,
+                                              ParsedCertificateList* issuers) {
   auto range =
       intermediates_.equal_range(cert->normalized_issuer().AsStringPiece());
   for (auto it = range.first; it != range.second; ++it)
diff --git a/net/cert/internal/cert_issuer_source_static.h b/net/cert/internal/cert_issuer_source_static.h
index af67268c2..f7151e80 100644
--- a/net/cert/internal/cert_issuer_source_static.h
+++ b/net/cert/internal/cert_issuer_source_static.h
@@ -23,9 +23,8 @@
   void AddCert(scoped_refptr<ParsedCertificate> cert);
 
   // CertIssuerSource implementation:
-  void SyncGetIssuersOf(
-      const ParsedCertificate* cert,
-      std::vector<scoped_refptr<ParsedCertificate>>* issuers) override;
+  void SyncGetIssuersOf(const ParsedCertificate* cert,
+                        ParsedCertificateList* issuers) override;
   void AsyncGetIssuersOf(const ParsedCertificate* cert,
                          const IssuerCallback& issuers_callback,
                          std::unique_ptr<Request>* out_req) override;
diff --git a/net/cert/internal/cert_issuer_source_static_unittest.cc b/net/cert/internal/cert_issuer_source_static_unittest.cc
index 148f362..d59744c 100644
--- a/net/cert/internal/cert_issuer_source_static_unittest.cc
+++ b/net/cert/internal/cert_issuer_source_static_unittest.cc
@@ -78,7 +78,7 @@
   CertIssuerSourceStatic source;
   source.AddCert(root_);
 
-  std::vector<scoped_refptr<ParsedCertificate>> issuers;
+  ParsedCertificateList issuers;
   source.SyncGetIssuersOf(c1_.get(), &issuers);
   ASSERT_EQ(0U, issuers.size());
 }
@@ -87,7 +87,7 @@
   CertIssuerSourceStatic source;
   AddAllCerts(&source);
 
-  std::vector<scoped_refptr<ParsedCertificate>> issuers;
+  ParsedCertificateList issuers;
   source.SyncGetIssuersOf(i1_1_.get(), &issuers);
   ASSERT_EQ(1U, issuers.size());
   EXPECT_TRUE(issuers[0] == root_);
@@ -102,7 +102,7 @@
   CertIssuerSourceStatic source;
   AddAllCerts(&source);
 
-  std::vector<scoped_refptr<ParsedCertificate>> issuers;
+  ParsedCertificateList issuers;
   source.SyncGetIssuersOf(c1_.get(), &issuers);
 
   ASSERT_EQ(2U, issuers.size());
@@ -120,7 +120,7 @@
   CertIssuerSourceStatic source;
   AddAllCerts(&source);
 
-  std::vector<scoped_refptr<ParsedCertificate>> issuers;
+  ParsedCertificateList issuers;
   source.SyncGetIssuersOf(root_.get(), &issuers);
 
   ASSERT_EQ(1U, issuers.size());
diff --git a/net/cert/internal/parsed_certificate.cc b/net/cert/internal/parsed_certificate.cc
index e8e08c4..a3e5cc3 100644
--- a/net/cert/internal/parsed_certificate.cc
+++ b/net/cert/internal/parsed_certificate.cc
@@ -162,7 +162,7 @@
     size_t length,
     DataSource source,
     const ParseCertificateOptions& options,
-    std::vector<scoped_refptr<ParsedCertificate>>* chain) {
+    ParsedCertificateList* chain) {
   scoped_refptr<ParsedCertificate> cert(
       CreateFromCertificateData(data, length, source, options));
   if (!cert)
diff --git a/net/cert/internal/parsed_certificate.h b/net/cert/internal/parsed_certificate.h
index 8237752..a4c6c3b 100644
--- a/net/cert/internal/parsed_certificate.h
+++ b/net/cert/internal/parsed_certificate.h
@@ -18,8 +18,11 @@
 
 struct GeneralNames;
 class NameConstraints;
+class ParsedCertificate;
 class SignatureAlgorithm;
 
+using ParsedCertificateList = std::vector<scoped_refptr<ParsedCertificate>>;
+
 // Represents an X.509 certificate, including Certificate, TBSCertificate, and
 // standard extensions.
 // Creating a ParsedCertificate does not completely parse and validate the
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
new file mode 100644
index 0000000..863d850
--- /dev/null
+++ b/net/cert/internal/path_builder.cc
@@ -0,0 +1,628 @@
+// 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 <set>
+#include <unordered_set>
+
+#include "base/callback_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "net/base/net_errors.h"
+#include "net/cert/internal/cert_issuer_source.h"
+#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/internal/parse_name.h"  // For CertDebugString.
+#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/trust_store.h"
+#include "net/cert/internal/verify_certificate_chain.h"
+#include "net/cert/internal/verify_name_match.h"
+#include "net/der/parser.h"
+#include "net/der/tag.h"
+
+namespace net {
+
+namespace {
+
+using CertIssuerSources = std::vector<CertIssuerSource*>;
+
+// TODO(mattm): decide how much debug logging to keep.
+std::string CertDebugString(const ParsedCertificate* cert) {
+  RDNSequence subject, issuer;
+  std::string subject_str, issuer_str;
+  if (!ParseName(cert->tbs().subject_tlv, &subject) ||
+      !ConvertToRFC2253(subject, &subject_str))
+    subject_str = "???";
+  if (!ParseName(cert->tbs().issuer_tlv, &issuer) ||
+      !ConvertToRFC2253(issuer, &issuer_str))
+    issuer_str = "???";
+
+  return subject_str + "(" + issuer_str + ")";
+}
+
+// CertIssuersIter iterates through the intermediates from |cert_issuer_sources|
+// which may be issuers of |cert|.
+class CertIssuersIter {
+ public:
+  // Constructs the CertIssuersIter. |*cert_issuer_sources| must be valid for
+  // the lifetime of the CertIssuersIter.
+  CertIssuersIter(scoped_refptr<ParsedCertificate> cert,
+                  CertIssuerSources* cert_issuer_sources,
+                  const TrustStore& trust_store);
+
+  // Gets the next candidate issuer. If an issuer is ready synchronously, SYNC
+  // is returned and the cert is stored in |*out_cert|.  If an issuer is not
+  // ready, ASYNC is returned and |callback| will be called once |*out_cert| has
+  // been set. If |callback| is null, always completes synchronously.
+  //
+  // In either case, if all issuers have been exhausted, |*out_cert| is cleared.
+  CompletionStatus GetNextIssuer(scoped_refptr<ParsedCertificate>* out_cert,
+                                 const base::Closure& callback);
+
+  // Returns the |cert| for which issuers are being retrieved.
+  const ParsedCertificate* cert() const { return cert_.get(); }
+  scoped_refptr<ParsedCertificate> reference_cert() const { return cert_; }
+
+ private:
+  void GotAsyncCerts(CertIssuerSource::Request* request);
+
+  scoped_refptr<ParsedCertificate> cert_;
+  CertIssuerSources* cert_issuer_sources_;
+
+  // The list of issuers for |cert_|. This is added to incrementally (first
+  // synchronous results, then possibly multiple times as asynchronous results
+  // arrive.) The issuers may be re-sorted each time new issuers are added, but
+  // only the results from |cur_| onwards should be sorted, since the earlier
+  // results were already returned.
+  // Elements should not be removed from |issuers_| once added, since
+  // |present_issuers_| will point to data owned by the certs.
+  ParsedCertificateList issuers_;
+  // The index of the next cert in |issuers_| to return.
+  size_t cur_ = 0;
+  // Set of DER-encoded values for the certs in |issuers_|. Used to prevent
+  // duplicates. This is based on the full DER of the cert to allow different
+  // versions of the same certificate to be tried in different candidate paths.
+  // This points to data owned by |issuers_|.
+  std::unordered_set<base::StringPiece, base::StringPieceHash> present_issuers_;
+
+  // Tracks whether asynchronous requests have been made yet.
+  bool did_async_query_ = false;
+  // If asynchronous requests were made, how many of them are still outstanding?
+  size_t pending_async_results_;
+  // Owns the Request objects for any asynchronous requests so that they will be
+  // cancelled if CertIssuersIter is destroyed.
+  std::vector<std::unique_ptr<CertIssuerSource::Request>>
+      pending_async_requests_;
+
+  // When GetNextIssuer was called and returned asynchronously, |*out_cert_| is
+  // where the result will be stored, and |callback_| will be run when the
+  // result is ready.
+  scoped_refptr<ParsedCertificate>* out_cert_;
+  base::Closure callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(CertIssuersIter);
+};
+
+CertIssuersIter::CertIssuersIter(scoped_refptr<ParsedCertificate> in_cert,
+                                 CertIssuerSources* cert_issuer_sources,
+                                 const TrustStore& trust_store)
+    : cert_(in_cert), cert_issuer_sources_(cert_issuer_sources) {
+  DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) << ") created";
+  trust_store.FindTrustAnchorsByNormalizedName(in_cert->normalized_issuer(),
+                                               &issuers_);
+  // Insert matching roots into |present_issuers_| in case they also are
+  // returned by a CertIssuerSource. It is assumed
+  // FindTrustAnchorsByNormalizedName does not itself return dupes.
+  for (const auto& root : issuers_)
+    present_issuers_.insert(root->der_cert().AsStringPiece());
+
+  for (auto* cert_issuer_source : *cert_issuer_sources_) {
+    ParsedCertificateList new_issuers;
+    cert_issuer_source->SyncGetIssuersOf(cert(), &new_issuers);
+    for (scoped_refptr<ParsedCertificate>& issuer : new_issuers) {
+      if (present_issuers_.find(issuer->der_cert().AsStringPiece()) !=
+          present_issuers_.end())
+        continue;
+      present_issuers_.insert(issuer->der_cert().AsStringPiece());
+      issuers_.push_back(std::move(issuer));
+    }
+  }
+  // TODO(mattm): sort by notbefore, etc (eg if cert issuer matches a trust
+  // anchor subject (or is a trust anchor), that should be sorted higher too.
+  // See big list of possible sorting hints in RFC 4158.)
+  // (Update PathBuilderKeyRolloverTest.TestRolloverBothRootsTrusted once that
+  // is done)
+}
+
+CompletionStatus CertIssuersIter::GetNextIssuer(
+    scoped_refptr<ParsedCertificate>* out_cert,
+    const base::Closure& callback) {
+  // Should not be called again while already waiting for an async result.
+  DCHECK(callback_.is_null());
+
+  if (cur_ < issuers_.size()) {
+    DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
+             << "): returning item " << cur_ << " of " << issuers_.size();
+    // Still have issuers that haven't been returned yet, return one of them.
+    // A reference to the returned issuer is retained, since |present_issuers_|
+    // points to data owned by it.
+    *out_cert = issuers_[cur_++];
+    return CompletionStatus::SYNC;
+  }
+  if (did_async_query_) {
+    if (pending_async_results_ == 0) {
+      DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
+               << ") Reached the end of all available issuers.";
+      // Reached the end of all available issuers.
+      *out_cert = nullptr;
+      return CompletionStatus::SYNC;
+    }
+
+    DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
+             << ") Still waiting for async results from other "
+                "CertIssuerSources.";
+    // Still waiting for async results from other CertIssuerSources.
+    out_cert_ = out_cert;
+    callback_ = callback;
+    return CompletionStatus::ASYNC;
+  }
+  // Reached the end of synchronously gathered issuers.
+
+  if (callback.is_null()) {
+    // Synchronous-only mode, don't try to query async sources.
+    *out_cert = nullptr;
+    return CompletionStatus::SYNC;
+  }
+
+  // Now issue request(s) for async ones (AIA, etc).
+  did_async_query_ = true;
+  pending_async_results_ = 0;
+  for (auto* cert_issuer_source : *cert_issuer_sources_) {
+    std::unique_ptr<CertIssuerSource::Request> request;
+    cert_issuer_source->AsyncGetIssuersOf(
+        cert(),
+        base::Bind(&CertIssuersIter::GotAsyncCerts, base::Unretained(this)),
+        &request);
+    if (request) {
+      DVLOG(1) << "AsyncGetIssuersOf(" << CertDebugString(cert())
+               << ") pending...";
+      pending_async_results_++;
+      pending_async_requests_.push_back(std::move(request));
+    }
+  }
+
+  if (pending_async_results_ == 0) {
+    DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
+             << ") No cert sources have async results.";
+    // No cert sources have async results.
+    *out_cert = nullptr;
+    return CompletionStatus::SYNC;
+  }
+
+  DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
+           << ") issued AsyncGetIssuersOf call(s) (n=" << pending_async_results_
+           << ")";
+  out_cert_ = out_cert;
+  callback_ = callback;
+  return CompletionStatus::ASYNC;
+}
+
+void CertIssuersIter::GotAsyncCerts(CertIssuerSource::Request* request) {
+  DVLOG(1) << "CertIssuersIter::GotAsyncCerts(" << CertDebugString(cert())
+           << ")";
+  while (true) {
+    scoped_refptr<ParsedCertificate> cert;
+    CompletionStatus status = request->GetNext(&cert);
+    if (!cert) {
+      if (status == CompletionStatus::SYNC) {
+        // Request is exhausted, no more results pending from that
+        // CertIssuerSource.
+        DCHECK_GT(pending_async_results_, 0U);
+        pending_async_results_--;
+      }
+      break;
+    }
+    DCHECK_EQ(status, CompletionStatus::SYNC);
+    if (present_issuers_.find(cert->der_cert().AsStringPiece()) !=
+        present_issuers_.end())
+      continue;
+    present_issuers_.insert(cert->der_cert().AsStringPiece());
+    issuers_.push_back(std::move(cert));
+  }
+
+  // TODO(mattm): re-sort remaining elements of issuers_ (remaining elements may
+  // be more than the ones just inserted, depending on |cur_| value).
+
+  // Notify that more results are available, if necessary.
+  if (!callback_.is_null()) {
+    if (cur_ < issuers_.size()) {
+      DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
+               << "): async returning item " << cur_ << " of "
+               << issuers_.size();
+      *out_cert_ = std::move(issuers_[cur_++]);
+      base::ResetAndReturn(&callback_).Run();
+    } else if (pending_async_results_ == 0) {
+      DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
+               << "): async returning empty result";
+      *out_cert_ = nullptr;
+      base::ResetAndReturn(&callback_).Run();
+    } else {
+      DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
+               << "): empty result, but other async results "
+                  "pending, waiting..";
+    }
+  }
+}
+
+// CertIssuerIterPath tracks which certs are present in the path and prevents
+// paths from being built which repeat any certs (including different versions
+// of the same cert, based on Subject+SubjectAltName+SPKI).
+class CertIssuerIterPath {
+ public:
+  // Returns true if |cert| is already present in the path.
+  bool IsPresent(const ParsedCertificate* cert) const {
+    return present_certs_.find(GetKey(cert)) != present_certs_.end();
+  }
+
+  // Appends |cert_issuers_iter| to the path. The cert referred to by
+  // |cert_issuers_iter| must not be present in the path already.
+  void Append(std::unique_ptr<CertIssuersIter> cert_issuers_iter) {
+    bool added =
+        present_certs_.insert(GetKey(cert_issuers_iter->cert())).second;
+    DCHECK(added);
+    cur_path_.push_back(std::move(cert_issuers_iter));
+  }
+
+  // Pops the last CertIssuersIter off the path.
+  void Pop() {
+    size_t num_erased = present_certs_.erase(GetKey(cur_path_.back()->cert()));
+    DCHECK_EQ(num_erased, 1U);
+    cur_path_.pop_back();
+  }
+
+  // Copies the ParsedCertificate elements of the current path to |*out_path|.
+  void CopyPath(ParsedCertificateList* out_path) {
+    out_path->clear();
+    for (const auto& node : cur_path_)
+      out_path->push_back(node->reference_cert());
+  }
+
+  // Returns true if the path is empty.
+  bool Empty() const { return cur_path_.empty(); }
+
+  // Returns the last CertIssuersIter in the path.
+  CertIssuersIter* back() { return cur_path_.back().get(); }
+
+  std::string PathDebugString() {
+    std::string s;
+    for (const auto& node : cur_path_) {
+      if (!s.empty())
+        s += " <- ";
+      s += CertDebugString(node->cert());
+    }
+    return s;
+  }
+
+ private:
+  using Key =
+      std::tuple<base::StringPiece, base::StringPiece, base::StringPiece>;
+
+  static Key GetKey(const ParsedCertificate* cert) {
+    // TODO(mattm): ideally this would use a normalized version of
+    // SubjectAltName, but it's not that important just for LoopChecker.
+    //
+    // Note that subject_alt_names_extension().value will be empty if the cert
+    // had no SubjectAltName extension, so there is no need for a condition on
+    // has_subject_alt_names().
+    return Key(cert->normalized_subject().AsStringPiece(),
+               cert->subject_alt_names_extension().value.AsStringPiece(),
+               cert->tbs().spki_tlv.AsStringPiece());
+  }
+
+  std::vector<std::unique_ptr<CertIssuersIter>> cur_path_;
+
+  // This refers to data owned by |cur_path_|.
+  // TODO(mattm): use unordered_set. Requires making a hash function for Key.
+  std::set<Key> present_certs_;
+};
+
+}  // namespace
+
+// CertPathIter generates possible paths from |cert| to a trust anchor in
+// |trust_store|, using intermediates from the |cert_issuer_source| objects if
+// necessary.
+class CertPathIter {
+ public:
+  CertPathIter(scoped_refptr<ParsedCertificate> cert,
+               const TrustStore* trust_store);
+
+  // Adds a CertIssuerSource to provide intermediates for use in path building.
+  // The |*cert_issuer_source| must remain valid for the lifetime of the
+  // CertPathIter.
+  void AddCertIssuerSource(CertIssuerSource* cert_issuer_source);
+
+  // Gets the next candidate path. If a path is ready synchronously, SYNC is
+  // returned and the path is stored in |*path|.  If a path is not ready,
+  // ASYNC is returned and |callback| will be called once |*path| has been set.
+  // In either case, if all paths have been exhausted, |*path| is cleared.
+  CompletionStatus GetNextPath(ParsedCertificateList* path,
+                               const base::Closure& callback);
+
+ private:
+  enum State {
+    STATE_NONE,
+    STATE_GET_NEXT_ISSUER,
+    STATE_GET_NEXT_ISSUER_COMPLETE,
+    STATE_RETURN_A_PATH,
+    STATE_BACKTRACK,
+  };
+
+  CompletionStatus DoLoop(bool allow_async);
+
+  CompletionStatus DoGetNextIssuer(bool allow_async);
+  CompletionStatus DoGetNextIssuerComplete();
+  CompletionStatus DoBackTrack();
+
+  void HandleGotNextIssuer(void);
+
+  // Stores the next candidate issuer certificate, until it is used during the
+  // STATE_GET_NEXT_ISSUER_COMPLETE step.
+  scoped_refptr<ParsedCertificate> next_cert_;
+  // The current path being explored, made up of CertIssuerIters. Each node
+  // keeps track of the state of searching for issuers of that cert, so that
+  // when backtracking it can resume the search where it left off.
+  CertIssuerIterPath cur_path_;
+  // The CertIssuerSources for retrieving candidate issuers.
+  CertIssuerSources cert_issuer_sources_;
+  // The TrustStore for checking if a path ends in a trust anchor.
+  const TrustStore* trust_store_;
+  // The output variable for storing the next candidate path, which the client
+  // passes in to GetNextPath. Only used for a single path output.
+  ParsedCertificateList* out_path_;
+  // The callback to be called if an async lookup generated a candidate path.
+  base::Closure callback_;
+  // Current state of the state machine.
+  State next_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(CertPathIter);
+};
+
+CertPathIter::CertPathIter(scoped_refptr<ParsedCertificate> cert,
+                           const TrustStore* trust_store)
+    : next_cert_(std::move(cert)),
+      trust_store_(trust_store),
+      next_state_(STATE_GET_NEXT_ISSUER_COMPLETE) {}
+
+void CertPathIter::AddCertIssuerSource(CertIssuerSource* cert_issuer_source) {
+  cert_issuer_sources_.push_back(cert_issuer_source);
+}
+
+CompletionStatus CertPathIter::GetNextPath(ParsedCertificateList* path,
+                                           const base::Closure& callback) {
+  out_path_ = path;
+  out_path_->clear();
+  CompletionStatus rv = DoLoop(!callback.is_null());
+  if (rv == CompletionStatus::ASYNC) {
+    callback_ = callback;
+  } else {
+    // Clear the reference to the output parameter as a precaution.
+    out_path_ = nullptr;
+  }
+  return rv;
+}
+
+CompletionStatus CertPathIter::DoLoop(bool allow_async) {
+  CompletionStatus result = CompletionStatus::SYNC;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_NONE:
+        NOTREACHED();
+        break;
+      case STATE_GET_NEXT_ISSUER:
+        result = DoGetNextIssuer(allow_async);
+        break;
+      case STATE_GET_NEXT_ISSUER_COMPLETE:
+        result = DoGetNextIssuerComplete();
+        break;
+      case STATE_RETURN_A_PATH:
+        // If the returned path did not verify, keep looking for other paths
+        // (the trust root is not part of cur_path_, so don't need to
+        // backtrack).
+        next_state_ = STATE_GET_NEXT_ISSUER;
+        result = CompletionStatus::SYNC;
+        break;
+      case STATE_BACKTRACK:
+        result = DoBackTrack();
+        break;
+    }
+  } while (result == CompletionStatus::SYNC && next_state_ != STATE_NONE &&
+           next_state_ != STATE_RETURN_A_PATH);
+
+  return result;
+}
+
+CompletionStatus CertPathIter::DoGetNextIssuer(bool allow_async) {
+  next_state_ = STATE_GET_NEXT_ISSUER_COMPLETE;
+  CompletionStatus rv = cur_path_.back()->GetNextIssuer(
+      &next_cert_, allow_async ? base::Bind(&CertPathIter::HandleGotNextIssuer,
+                                            base::Unretained(this))
+                               : base::Closure());
+  return rv;
+}
+
+CompletionStatus CertPathIter::DoGetNextIssuerComplete() {
+  if (next_cert_) {
+    // Skip this cert if it is already in the chain.
+    if (cur_path_.IsPresent(next_cert_.get())) {
+      next_state_ = STATE_GET_NEXT_ISSUER;
+      return CompletionStatus::SYNC;
+    }
+    // If the cert matches a trust root, this is a (possibly) complete path.
+    // Signal readiness. Don't add it to cur_path_, since that would cause an
+    // unnecessary lookup of issuers of the trust root.
+    if (trust_store_->IsTrustedCertificate(next_cert_.get())) {
+      DVLOG(1) << "CertPathIter IsTrustedCertificate("
+               << CertDebugString(next_cert_.get()) << ") = true";
+      next_state_ = STATE_RETURN_A_PATH;
+      cur_path_.CopyPath(out_path_);
+      out_path_->push_back(std::move(next_cert_));
+      next_cert_ = nullptr;
+      return CompletionStatus::SYNC;
+    }
+
+    cur_path_.Append(base::WrapUnique(new CertIssuersIter(
+        std::move(next_cert_), &cert_issuer_sources_, *trust_store_)));
+    next_cert_ = nullptr;
+    DVLOG(1) << "CertPathIter cur_path_ = " << cur_path_.PathDebugString();
+    // Continue descending the tree.
+    next_state_ = STATE_GET_NEXT_ISSUER;
+  } else {
+    // TODO(mattm): should also include such paths in CertPathBuilder::Result,
+    // maybe with a flag to enable it. Or use a visitor pattern so the caller
+    // can decide what to do with any failed paths.
+    // No more issuers for current chain, go back up and see if there are any
+    // more for the previous cert.
+    next_state_ = STATE_BACKTRACK;
+  }
+  return CompletionStatus::SYNC;
+}
+
+CompletionStatus CertPathIter::DoBackTrack() {
+  DVLOG(1) << "CertPathIter backtracking...";
+  cur_path_.Pop();
+  if (cur_path_.Empty()) {
+    // Exhausted all paths.
+    next_state_ = STATE_NONE;
+  } else {
+    // Continue exploring issuers of the previous path.
+    next_state_ = STATE_GET_NEXT_ISSUER;
+  }
+  return CompletionStatus::SYNC;
+}
+
+void CertPathIter::HandleGotNextIssuer(void) {
+  DCHECK(!callback_.is_null());
+  CompletionStatus rv = DoLoop(true /* allow_async */);
+  if (rv == CompletionStatus::SYNC) {
+    // Clear the reference to the output parameter as a precaution.
+    out_path_ = nullptr;
+    base::ResetAndReturn(&callback_).Run();
+  }
+}
+
+CertPathBuilder::ResultPath::ResultPath() = default;
+CertPathBuilder::ResultPath::~ResultPath() = default;
+CertPathBuilder::Result::Result() = default;
+CertPathBuilder::Result::~Result() = default;
+
+CertPathBuilder::CertPathBuilder(scoped_refptr<ParsedCertificate> cert,
+                                 const TrustStore* trust_store,
+                                 const SignaturePolicy* signature_policy,
+                                 const der::GeneralizedTime& time,
+                                 Result* result)
+    : cert_path_iter_(new CertPathIter(std::move(cert), trust_store)),
+      trust_store_(trust_store),
+      signature_policy_(signature_policy),
+      time_(time),
+      next_state_(STATE_NONE),
+      out_result_(result) {}
+
+CertPathBuilder::~CertPathBuilder() {}
+
+void CertPathBuilder::AddCertIssuerSource(
+    CertIssuerSource* cert_issuer_source) {
+  cert_path_iter_->AddCertIssuerSource(cert_issuer_source);
+}
+
+CompletionStatus CertPathBuilder::Run(const base::Closure& callback) {
+  DCHECK_EQ(STATE_NONE, next_state_);
+  next_state_ = STATE_GET_NEXT_PATH;
+  CompletionStatus rv = DoLoop(!callback.is_null());
+
+  if (rv == CompletionStatus::ASYNC)
+    callback_ = callback;
+
+  return rv;
+}
+
+CompletionStatus CertPathBuilder::DoLoop(bool allow_async) {
+  CompletionStatus result = CompletionStatus::SYNC;
+
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_NONE:
+        NOTREACHED();
+        break;
+      case STATE_GET_NEXT_PATH:
+        result = DoGetNextPath(allow_async);
+        break;
+      case STATE_GET_NEXT_PATH_COMPLETE:
+        result = DoGetNextPathComplete();
+        break;
+    }
+  } while (result == CompletionStatus::SYNC && next_state_ != STATE_NONE);
+
+  return result;
+}
+
+CompletionStatus CertPathBuilder::DoGetNextPath(bool allow_async) {
+  next_state_ = STATE_GET_NEXT_PATH_COMPLETE;
+  CompletionStatus rv = cert_path_iter_->GetNextPath(
+      &next_path_, allow_async ? base::Bind(&CertPathBuilder::HandleGotNextPath,
+                                            base::Unretained(this))
+                               : base::Closure());
+  return rv;
+}
+
+void CertPathBuilder::HandleGotNextPath() {
+  DCHECK(!callback_.is_null());
+  CompletionStatus rv = DoLoop(true /* allow_async */);
+  if (rv == CompletionStatus::SYNC)
+    base::ResetAndReturn(&callback_).Run();
+}
+
+CompletionStatus CertPathBuilder::DoGetNextPathComplete() {
+  if (next_path_.empty()) {
+    // No more paths to check, signal completion.
+    next_state_ = STATE_NONE;
+    return CompletionStatus::SYNC;
+  }
+
+  bool verify_result = VerifyCertificateChainAssumingTrustedRoot(
+      next_path_, *trust_store_, signature_policy_, time_);
+  DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = "
+           << verify_result;
+  AddResultPath(next_path_, verify_result);
+
+  if (verify_result) {
+    // Found a valid path, return immediately.
+    // TODO(mattm): add debug/test mode that tries all possible paths.
+    next_state_ = STATE_NONE;
+    return CompletionStatus::SYNC;
+  }
+
+  // Path did not verify. Try more paths. If there are no more paths, the result
+  // will be returned next time DoGetNextPathComplete is called with next_path_
+  // empty.
+  next_state_ = STATE_GET_NEXT_PATH;
+  return CompletionStatus::SYNC;
+}
+
+void CertPathBuilder::AddResultPath(const ParsedCertificateList& path,
+                                    bool is_success) {
+  std::unique_ptr<ResultPath> result_path(new ResultPath());
+  // TODO(mattm): better error reporting.
+  result_path->error = is_success ? OK : ERR_CERT_AUTHORITY_INVALID;
+  // TODO(mattm): set best_result_index based on number or severity of errors.
+  if (result_path->error == OK)
+    out_result_->best_result_index = out_result_->paths.size();
+  // TODO(mattm): add flag to only return a single path or all attempted paths?
+  result_path->path = path;
+  out_result_->paths.push_back(std::move(result_path));
+}
+
+}  // namespace net
diff --git a/net/cert/internal/path_builder.h b/net/cert/internal/path_builder.h
new file mode 100644
index 0000000..f08ad127
--- /dev/null
+++ b/net/cert/internal/path_builder.h
@@ -0,0 +1,172 @@
+// 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.
+
+#ifndef NET_CERT_INTERNAL_PATH_BUILDER_H_
+#define NET_CERT_INTERNAL_PATH_BUILDER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_export.h"
+#include "net/cert/internal/completion_status.h"
+#include "net/cert/internal/parsed_certificate.h"
+#include "net/der/input.h"
+#include "net/der/parse_values.h"
+
+namespace net {
+
+namespace der {
+struct GeneralizedTime;
+}
+
+class CertPathIter;
+class CertIssuerSource;
+class TrustStore;
+class SignaturePolicy;
+
+// Checks whether a certificate is trusted by building candidate paths to trust
+// anchors and verifying those paths according to RFC 5280. Each instance of
+// CertPathBuilder is used for a single verification.
+//
+// WARNING: This implementation is currently experimental.  Consult an OWNER
+// before using it.
+class NET_EXPORT CertPathBuilder {
+ public:
+  // Represents a single candidate path that was built.
+  struct NET_EXPORT ResultPath {
+    ResultPath();
+    ~ResultPath();
+
+    // Returns true if this path was successfully verified.
+    bool is_success() const { return error == OK; }
+
+    // The candidate path, in forward direction.
+    //   * path[0] is the target certificate.
+    //   * path[i+1] is a candidate issuer of path[i]. The subject matches
+    //   path[i]'s issuer, but nothing else is guaranteed unless is_success() is
+    //   true.
+    //   * path[N-1] will be a trust anchor if is_success() is true, otherwise
+    //   it may or may not be a trust anchor.
+    ParsedCertificateList path;
+
+    // A net error code result of attempting to verify this path.
+    // TODO(mattm): may want to have an independent result enum, which caller
+    // can map to a net error if they want.
+    int error = ERR_UNEXPECTED;
+  };
+
+  // Provides the overall result of path building. This includes the paths that
+  // were attempted.
+  struct NET_EXPORT Result {
+    Result();
+    ~Result();
+
+    // Returns true if there was a valid path.
+    bool is_success() const { return error() == OK; }
+
+    // Returns the net error code of the overall best result.
+    int error() const {
+      if (paths.empty())
+        return ERR_CERT_AUTHORITY_INVALID;
+      return paths[best_result_index]->error;
+    }
+
+    // List of paths that were attempted and the result for each.
+    std::vector<std::unique_ptr<ResultPath>> paths;
+
+    // Index into |paths|. Before use, |paths.empty()| must be checked.
+    // NOTE: currently the definition of "best" is fairly limited. Successful is
+    // better than unsuccessful, but otherwise nothing is guaranteed.
+    size_t best_result_index = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Result);
+  };
+
+  // TODO(mattm): allow caller specified hook/callback to extend path
+  // verification.
+  //
+  // Creates a CertPathBuilder that attempts to find a path from |cert| to a
+  // trust anchor in |trust_store|, which satisfies |signature_policy| and is
+  // valid at |time|.  Details of attempted path(s) are stored in |*result|.
+  //
+  // The caller must keep |trust_store|, |signature_policy|, and |*result| valid
+  // for the lifetime of the CertPathBuilder.
+  CertPathBuilder(scoped_refptr<ParsedCertificate> cert,
+                  const TrustStore* trust_store,
+                  const SignaturePolicy* signature_policy,
+                  const der::GeneralizedTime& time,
+                  Result* result);
+  ~CertPathBuilder();
+
+  // Adds a CertIssuerSource to provide intermediates for use in path building.
+  // Multiple sources may be added. Must not be called after Run is called.
+  // The |*cert_issuer_source| must remain valid for the lifetime of the
+  // CertPathBuilder.
+  //
+  // (If no issuer sources are added, the target certificate will only verify if
+  // it is a trust anchor or is directly signed by a trust anchor.)
+  void AddCertIssuerSource(CertIssuerSource* cert_issuer_source);
+
+  // Begins verification of the target certificate.
+  //
+  // If the return value is SYNC then the verification is complete and the
+  // |result| value can be inspected for the status, and |callback| will not be
+  // called.
+  // If the return value is ASYNC, the |callback| will be called asynchronously
+  // once the verification is complete. |result| should not be examined or
+  // modified until the |callback| is run.
+  //
+  // If |callback| is null, verification always completes synchronously, even if
+  // it fails to find a valid path and one could have been found asynchronously.
+  //
+  // The CertPathBuilder may be deleted while an ASYNC verification is pending,
+  // in which case the verification is cancelled, |callback| will not be called,
+  // and the output Result will be in an undefined state.
+  // It is safe to delete the CertPathBuilder during the |callback|.
+  // Run must not be called more than once on each CertPathBuilder instance.
+  CompletionStatus Run(const base::Closure& callback);
+
+ private:
+  enum State {
+    STATE_NONE,
+    STATE_GET_NEXT_PATH,
+    STATE_GET_NEXT_PATH_COMPLETE,
+  };
+
+  CompletionStatus DoLoop(bool allow_async);
+
+  CompletionStatus DoGetNextPath(bool allow_async);
+  void HandleGotNextPath();
+  CompletionStatus DoGetNextPathComplete();
+
+  void AddResultPath(const ParsedCertificateList& path, bool is_success);
+
+  base::Closure callback_;
+
+  std::unique_ptr<CertPathIter> cert_path_iter_;
+  const TrustStore* trust_store_;
+  const SignaturePolicy* signature_policy_;
+  const der::GeneralizedTime time_;
+
+  // Stores the next complete path to attempt verification on. This is filled in
+  // by |cert_path_iter_| during the STATE_GET_NEXT_PATH step, and thus should
+  // only be accessed during the STATE_GET_NEXT_PATH_COMPLETE step.
+  // (Will be empty if all paths have been tried, otherwise will be a candidate
+  // path starting with the target cert and ending with a trust anchor.)
+  ParsedCertificateList next_path_;
+  State next_state_;
+
+  Result* out_result_;
+
+  DISALLOW_COPY_AND_ASSIGN(CertPathBuilder);
+};
+
+}  // namespace net
+
+#endif  // NET_CERT_INTERNAL_PATH_BUILDER_H_
diff --git a/net/cert/internal/path_builder_pkits_unittest.cc b/net/cert/internal/path_builder_pkits_unittest.cc
new file mode 100644
index 0000000..a046d1cf
--- /dev/null
+++ b/net/cert/internal/path_builder_pkits_unittest.cc
@@ -0,0 +1,227 @@
+// 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 "net/base/net_errors.h"
+#include "net/cert/internal/cert_issuer_source_static.h"
+#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/internal/parsed_certificate.h"
+#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/trust_store.h"
+#include "net/cert/internal/verify_certificate_chain.h"
+#include "net/der/input.h"
+
+// Disable tests that require DSA signatures (DSA signatures are intentionally
+// unsupported). Custom versions of the DSA tests are defined below which expect
+// verification to fail.
+#define Section1ValidDSASignaturesTest4 DISABLED_Section1ValidDSASignaturesTest4
+#define Section1ValidDSAParameterInheritanceTest5 \
+  DISABLED_Section1ValidDSAParameterInheritanceTest5
+
+// Disable tests that require name constraints with name types that are
+// intentionally unsupported. Custom versions of the tests are defined below
+// which expect verification to fail.
+#define Section13ValidRFC822nameConstraintsTest21 \
+  DISABLED_Section13ValidRFC822nameConstraintsTest21
+#define Section13ValidRFC822nameConstraintsTest23 \
+  DISABLED_Section13ValidRFC822nameConstraintsTest23
+#define Section13ValidRFC822nameConstraintsTest25 \
+  DISABLED_Section13ValidRFC822nameConstraintsTest25
+#define Section13ValidDNandRFC822nameConstraintsTest27 \
+  DISABLED_Section13ValidDNandRFC822nameConstraintsTest27
+#define Section13ValidURInameConstraintsTest34 \
+  DISABLED_Section13ValidURInameConstraintsTest34
+#define Section13ValidURInameConstraintsTest36 \
+  DISABLED_Section13ValidURInameConstraintsTest36
+
+// TODO(mattm): these require CRL support:
+#define Section7InvalidkeyUsageCriticalcRLSignFalseTest4 \
+  DISABLED_Section7InvalidkeyUsageCriticalcRLSignFalseTest4
+#define Section7InvalidkeyUsageNotCriticalcRLSignFalseTest5 \
+  DISABLED_Section7InvalidkeyUsageNotCriticalcRLSignFalseTest5
+
+#include "net/cert/internal/nist_pkits_unittest.h"
+
+namespace net {
+
+namespace {
+
+class PathBuilderPkitsTestDelegate {
+ public:
+  static bool Verify(std::vector<std::string> cert_ders,
+                     std::vector<std::string> crl_ders) {
+    if (cert_ders.empty()) {
+      ADD_FAILURE() << "cert_ders is empty";
+      return false;
+    }
+    ParsedCertificateList certs;
+    for (const std::string& der : cert_ders) {
+      certs.push_back(ParsedCertificate::CreateFromCertificateCopy(der, {}));
+      if (!certs.back()) {
+        ADD_FAILURE() << "ParsedCertificate::CreateFromCertificateCopy failed";
+        return false;
+      }
+    }
+    // First entry in the PKITS chain is the trust anchor.
+    // TODO(mattm): test with all possible trust anchors in the trust store?
+    TrustStore trust_store;
+    trust_store.AddTrustedCertificate(certs[0]);
+
+    // TODO(mattm): test with other irrelevant certs in cert_issuer_sources?
+    CertIssuerSourceStatic cert_issuer_source;
+    for (size_t i = 1; i < cert_ders.size() - 1; ++i)
+      cert_issuer_source.AddCert(certs[i]);
+
+    scoped_refptr<ParsedCertificate> target_cert(certs.back());
+
+    SimpleSignaturePolicy signature_policy(1024);
+
+    // Run all tests at the time the PKITS was published.
+    der::GeneralizedTime time = {2011, 4, 15, 0, 0, 0};
+
+    CertPathBuilder::Result result;
+    CertPathBuilder path_builder(std::move(target_cert), &trust_store,
+                                 &signature_policy, time, &result);
+    path_builder.AddCertIssuerSource(&cert_issuer_source);
+
+    CompletionStatus rv = path_builder.Run(base::Closure());
+    EXPECT_EQ(CompletionStatus::SYNC, rv);
+
+    return result.is_success();
+  }
+};
+
+}  // namespace
+
+class PkitsTest01SignatureVerificationCustomPathBuilderFoo
+    : public PkitsTest<PathBuilderPkitsTestDelegate> {};
+
+// Modified version of 4.1.4 Valid DSA Signatures Test4
+TEST_F(PkitsTest01SignatureVerificationCustomPathBuilderFoo,
+       Section1ValidDSASignaturesTest4Custom) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "DSACACert",
+                               "ValidDSASignaturesTest4EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "DSACACRL"};
+  // DSA signatures are intentionally unsupported.
+  ASSERT_FALSE(this->Verify(certs, crls));
+}
+
+// Modified version of 4.1.5 Valid DSA Parameter Inheritance Test5
+TEST_F(PkitsTest01SignatureVerificationCustomPathBuilderFoo,
+       Section1ValidDSAParameterInheritanceTest5Custom) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "DSACACert",
+                               "DSAParametersInheritedCACert",
+                               "ValidDSAParameterInheritanceTest5EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "DSACACRL",
+                              "DSAParametersInheritedCACRL"};
+  // DSA signatures are intentionally unsupported.
+  ASSERT_FALSE(this->Verify(certs, crls));
+}
+
+class PkitsTest13SignatureVerificationCustomPathBuilderFoo
+    : public PkitsTest<PathBuilderPkitsTestDelegate> {};
+
+// Modified version of 4.13.21 Valid RFC822 nameConstraints Test21
+TEST_F(PkitsTest13SignatureVerificationCustomPathBuilderFoo,
+       Section13ValidRFC822nameConstraintsTest21Custom) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "nameConstraintsRFC822CA1Cert",
+                               "ValidRFC822nameConstraintsTest21EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "nameConstraintsRFC822CA1CRL"};
+  // Name constraints on rfc822Names are not supported.
+  ASSERT_FALSE(this->Verify(certs, crls));
+}
+
+// Modified version of 4.13.23 Valid RFC822 nameConstraints Test23
+TEST_F(PkitsTest13SignatureVerificationCustomPathBuilderFoo,
+       Section13ValidRFC822nameConstraintsTest23Custom) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "nameConstraintsRFC822CA2Cert",
+                               "ValidRFC822nameConstraintsTest23EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "nameConstraintsRFC822CA2CRL"};
+  // Name constraints on rfc822Names are not supported.
+  ASSERT_FALSE(this->Verify(certs, crls));
+}
+
+// Modified version of 4.13.25 Valid RFC822 nameConstraints Test25
+TEST_F(PkitsTest13SignatureVerificationCustomPathBuilderFoo,
+       Section13ValidRFC822nameConstraintsTest25Custom) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "nameConstraintsRFC822CA3Cert",
+                               "ValidRFC822nameConstraintsTest25EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "nameConstraintsRFC822CA3CRL"};
+  // Name constraints on rfc822Names are not supported.
+  ASSERT_FALSE(this->Verify(certs, crls));
+}
+
+// Modified version of 4.13.27 Valid DN and RFC822 nameConstraints Test27
+TEST_F(PkitsTest13SignatureVerificationCustomPathBuilderFoo,
+       Section13ValidDNandRFC822nameConstraintsTest27Custom) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "nameConstraintsDN1CACert",
+                               "nameConstraintsDN1subCA3Cert",
+                               "ValidDNandRFC822nameConstraintsTest27EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL",
+                              "nameConstraintsDN1subCA3CRL"};
+  // Name constraints on rfc822Names are not supported.
+  ASSERT_FALSE(this->Verify(certs, crls));
+}
+
+// Modified version of 4.13.34 Valid URI nameConstraints Test34
+TEST_F(PkitsTest13SignatureVerificationCustomPathBuilderFoo,
+       Section13ValidURInameConstraintsTest34Custom) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "nameConstraintsURI1CACert",
+                               "ValidURInameConstraintsTest34EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI1CACRL"};
+  // Name constraints on uniformResourceIdentifiers are not supported.
+  ASSERT_FALSE(this->Verify(certs, crls));
+}
+
+// Modified version of 4.13.36 Valid URI nameConstraints Test36
+TEST_F(PkitsTest13SignatureVerificationCustomPathBuilderFoo,
+       Section13ValidURInameConstraintsTest36Custom) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "nameConstraintsURI2CACert",
+                               "ValidURInameConstraintsTest36EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI2CACRL"};
+  // Name constraints on uniformResourceIdentifiers are not supported.
+  ASSERT_FALSE(this->Verify(certs, crls));
+}
+
+INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
+                              PkitsTest01SignatureVerification,
+                              PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
+                              PkitsTest02ValidityPeriods,
+                              PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
+                              PkitsTest03VerifyingNameChaining,
+                              PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
+                              PkitsTest06VerifyingBasicConstraints,
+                              PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
+                              PkitsTest07KeyUsage,
+                              PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
+                              PkitsTest13NameConstraints,
+                              PathBuilderPkitsTestDelegate);
+INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
+                              PkitsTest16PrivateCertificateExtensions,
+                              PathBuilderPkitsTestDelegate);
+
+// TODO(mattm): CRL support: PkitsTest04BasicCertificateRevocationTests,
+// PkitsTest05VerifyingPathswithSelfIssuedCertificates,
+// PkitsTest14DistributionPoints, PkitsTest15DeltaCRLs
+
+// TODO(mattm): Certificate Policies support: PkitsTest08CertificatePolicies,
+// PkitsTest09RequireExplicitPolicy PkitsTest10PolicyMappings,
+// PkitsTest11InhibitPolicyMapping, PkitsTest12InhibitAnyPolicy
+
+}  // namespace net
diff --git a/net/cert/internal/path_builder_unittest.cc b/net/cert/internal/path_builder_unittest.cc
new file mode 100644
index 0000000..dfdf25b
--- /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
diff --git a/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
new file mode 100644
index 0000000..e08ea30
--- /dev/null
+++ b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
@@ -0,0 +1,56 @@
+// 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 "net/cert/internal/cert_issuer_source_static.h"
+#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/trust_store.h"
+#include "net/cert/internal/verify_certificate_chain_typed_unittest.h"
+
+namespace net {
+
+namespace {
+
+class PathBuilderDelegate {
+ public:
+  static void Verify(const ParsedCertificateList& chain,
+                     const ParsedCertificateList& roots,
+                     const der::GeneralizedTime& time,
+                     bool expected_result) {
+    SimpleSignaturePolicy signature_policy(1024);
+    ASSERT_FALSE(chain.empty());
+
+    TrustStore trust_store;
+    for (const auto& root : roots)
+      trust_store.AddTrustedCertificate(root);
+
+    CertIssuerSourceStatic intermediate_cert_issuer_source;
+    for (size_t i = 1; i < chain.size(); ++i)
+      intermediate_cert_issuer_source.AddCert(chain[i]);
+
+    CertPathBuilder::Result result;
+    // First cert in the |chain| is the target.
+    CertPathBuilder path_builder(chain.front(), &trust_store, &signature_policy,
+                                 time, &result);
+    path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
+
+    CompletionStatus rv = path_builder.Run(base::Closure());
+    EXPECT_EQ(CompletionStatus::SYNC, rv);
+
+    EXPECT_EQ(expected_result, result.is_success());
+  }
+};
+
+}  // namespace
+
+INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
+                              VerifyCertificateChainSingleRootTest,
+                              PathBuilderDelegate);
+
+INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
+                              VerifyCertificateChainNonSingleRootTest,
+                              PathBuilderDelegate);
+
+}  // namespace net
diff --git a/net/cert/internal/trust_store.cc b/net/cert/internal/trust_store.cc
index 892698b..d46933b 100644
--- a/net/cert/internal/trust_store.cc
+++ b/net/cert/internal/trust_store.cc
@@ -4,8 +4,6 @@
 
 #include "net/cert/internal/trust_store.h"
 
-#include "net/cert/internal/parsed_certificate.h"
-
 namespace net {
 
 TrustStore::TrustStore() {}
@@ -24,7 +22,7 @@
 
 void TrustStore::FindTrustAnchorsByNormalizedName(
     const der::Input& normalized_name,
-    std::vector<scoped_refptr<ParsedCertificate>>* matches) const {
+    ParsedCertificateList* matches) const {
   auto range = anchors_.equal_range(normalized_name.AsStringPiece());
   for (auto it = range.first; it != range.second; ++it)
     matches->push_back(it->second);
@@ -33,9 +31,13 @@
 bool TrustStore::IsTrustedCertificate(const ParsedCertificate* cert) const {
   auto range = anchors_.equal_range(cert->normalized_subject().AsStringPiece());
   for (auto it = range.first; it != range.second; ++it) {
-    // First compare the ParsedCertificate pointers as an optimization, fall
-    // back to comparing full DER encoding.
-    if (it->second == cert || it->second->der_cert() == cert->der_cert())
+    // First compare the ParsedCertificate pointers as an optimization.
+    if (it->second == cert ||
+        // Trust check is based on Name+SPKI match. This could match the same
+        // certificate stored in a different ParsedCertificate object, or a
+        // different cert that has the same Name+SPKI.
+        (it->second->normalized_subject() == cert->normalized_subject() &&
+         it->second->tbs().spki_tlv == cert->tbs().spki_tlv))
       return true;
   }
   return false;
diff --git a/net/cert/internal/trust_store.h b/net/cert/internal/trust_store.h
index 611af1c..0afbf9a 100644
--- a/net/cert/internal/trust_store.h
+++ b/net/cert/internal/trust_store.h
@@ -11,6 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_piece.h"
 #include "net/base/net_export.h"
+#include "net/cert/internal/parsed_certificate.h"
 
 namespace net {
 
@@ -18,8 +19,6 @@
 class Input;
 }
 
-class ParsedCertificate;
-
 // A very simple implementation of a TrustStore, which contains a set of
 // trusted certificates.
 // TODO(mattm): convert this into an interface, provide implementations that
@@ -36,9 +35,8 @@
   void AddTrustedCertificate(scoped_refptr<ParsedCertificate> anchor);
 
   // Returns the trust anchors that match |name| in |*matches|, if any.
-  void FindTrustAnchorsByNormalizedName(
-      const der::Input& normalized_name,
-      std::vector<scoped_refptr<ParsedCertificate>>* matches) const;
+  void FindTrustAnchorsByNormalizedName(const der::Input& normalized_name,
+                                        ParsedCertificateList* matches) const;
 
   // Returns true if |cert| matches a certificate in the TrustStore.
   bool IsTrustedCertificate(const ParsedCertificate* cert) const
diff --git a/net/cert/internal/verify_certificate_chain.cc b/net/cert/internal/verify_certificate_chain.cc
index 73bd6f13..baef57a 100644
--- a/net/cert/internal/verify_certificate_chain.cc
+++ b/net/cert/internal/verify_certificate_chain.cc
@@ -9,7 +9,6 @@
 #include "base/logging.h"
 #include "net/cert/internal/name_constraints.h"
 #include "net/cert/internal/parse_certificate.h"
-#include "net/cert/internal/parsed_certificate.h"
 #include "net/cert/internal/signature_algorithm.h"
 #include "net/cert/internal/signature_policy.h"
 #include "net/cert/internal/trust_store.h"
@@ -336,9 +335,6 @@
 
 }  // namespace
 
-// TODO(eroman): Move this into existing anonymous namespace.
-namespace {
-
 // This implementation is structured to mimic the description of certificate
 // path verification given by RFC 5280 section 6.1.
 //
@@ -346,7 +342,7 @@
 // the chain. This root certificate is assumed to be trusted, and neither its
 // signature nor issuer name are verified. (It needn't be self-signed).
 bool VerifyCertificateChainAssumingTrustedRoot(
-    const std::vector<scoped_refptr<ParsedCertificate>>& certs,
+    const ParsedCertificateList& certs,
     // The trust store is only used for assertions.
     const TrustStore& trust_store,
     const SignaturePolicy* signature_policy,
@@ -450,56 +446,4 @@
   return true;
 }
 
-// TODO(eroman): This function is a temporary hack in the absence of full
-// path building. It may insert 1 certificate at the root of the
-// chain to ensure that the path's root certificate is a trust anchor.
-//
-// Beyond this no other verification is done on the chain. The caller is
-// responsible for verifying the subsequent chain's correctness.
-WARN_UNUSED_RESULT bool BuildSimplePathToTrustAnchor(
-    const TrustStore& trust_store,
-    std::vector<scoped_refptr<ParsedCertificate>>* certs) {
-  if (certs->empty())
-    return false;
-
-  // Check if the current root certificate is trusted. If it is then no
-  // extra work is needed.
-  if (trust_store.IsTrustedCertificate(certs->back().get()))
-    return true;
-
-  std::vector<scoped_refptr<ParsedCertificate>> trust_anchors;
-  trust_store.FindTrustAnchorsByNormalizedName(
-      certs->back()->normalized_issuer(), &trust_anchors);
-  if (trust_anchors.empty())
-    return false;
-  // TODO(mattm): this only tries the first match, even if there are multiple.
-  certs->push_back(std::move(trust_anchors[0]));
-  return true;
-}
-
-}  // namespace
-
-bool VerifyCertificateChain(
-    const std::vector<scoped_refptr<ParsedCertificate>>& cert_chain,
-    const TrustStore& trust_store,
-    const SignaturePolicy* signature_policy,
-    const der::GeneralizedTime& time,
-    std::vector<scoped_refptr<ParsedCertificate>>* trusted_chain_out) {
-  if (cert_chain.empty())
-    return false;
-
-  std::vector<scoped_refptr<ParsedCertificate>> full_chain = cert_chain;
-
-  // Modify the certificate chain so that its root is a trusted certificate.
-  if (!BuildSimplePathToTrustAnchor(trust_store, &full_chain))
-    return false;
-
-  // Verify the chain.
-  bool success = VerifyCertificateChainAssumingTrustedRoot(
-      full_chain, trust_store, signature_policy, time);
-  if (success && trusted_chain_out != nullptr)
-    *trusted_chain_out = std::move(full_chain);
-  return success;
-}
-
 }  // namespace net
diff --git a/net/cert/internal/verify_certificate_chain.h b/net/cert/internal/verify_certificate_chain.h
index 291c843..5dc6275 100644
--- a/net/cert/internal/verify_certificate_chain.h
+++ b/net/cert/internal/verify_certificate_chain.h
@@ -10,6 +10,7 @@
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "net/base/net_export.h"
+#include "net/cert/internal/parsed_certificate.h"
 #include "net/der/input.h"
 
 namespace net {
@@ -18,12 +19,12 @@
 struct GeneralizedTime;
 }
 
-class ParsedCertificate;
 class SignaturePolicy;
 class TrustStore;
 
-// VerifyCertificateChain() verifies a certificate path (chain) based on the
-// rules in RFC 5280.
+// VerifyCertificateChainAssumingTrustedRoot() verifies a certificate path
+// (chain) based on the rules in RFC 5280. The caller is responsible for
+// building the path and ensuring the chain ends in a trusted root certificate.
 //
 // WARNING: This implementation is in progress, and is currently incomplete.
 // Consult an OWNER before using it.
@@ -38,11 +39,11 @@
 //
 //      * cert_chain[0] is the target certificate to verify.
 //      * cert_chain[i+1] holds the certificate that issued cert_chain[i].
-//      * cert_chain[N-1] must be the trust anchor, or have been directly
-//        issued by a trust anchor.
+//      * cert_chain[N-1] must be the trust anchor.
 //
 //   trust_store:
-//     Contains the set of trusted public keys (and their names).
+//     Contains the set of trusted public keys (and their names). This is only
+//     used to DCHECK that the final cert is a trust anchor.
 //
 //   signature_policy:
 //     The policy to use when verifying signatures (what hash algorithms are
@@ -51,28 +52,17 @@
 //   time:
 //     The UTC time to use for expiration checks.
 //
-//   trusted_chain_out:
-//     The vector to populate with the verified trusted certificate chain.
-//      * trusted_chain_out[0] is the target certificate verified.
-//      * trusted_chain_out[i+1] holds the certificate that issued
-//        trusted_chain_out[i].
-//      * trusted_chain_out[N-1] is the trust anchor.
-//     If a nullptr is passed, this parameter is ignored.
-//     If the target certificate can not be verified, this parameter is
-//     ignored.
-//
 // ---------
 // Outputs
 // ---------
 //
 //   Returns true if the target certificate can be verified.
-NET_EXPORT bool VerifyCertificateChain(
-    const std::vector<scoped_refptr<ParsedCertificate>>& cert_chain,
+NET_EXPORT bool VerifyCertificateChainAssumingTrustedRoot(
+    const ParsedCertificateList& certs,
+    // The trust store is only used for assertions.
     const TrustStore& trust_store,
     const SignaturePolicy* signature_policy,
-    const der::GeneralizedTime& time,
-    std::vector<scoped_refptr<ParsedCertificate>>* trusted_chain_out)
-    WARN_UNUSED_RESULT;
+    const der::GeneralizedTime& time) WARN_UNUSED_RESULT;
 
 }  // namespace net
 
diff --git a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
index 4c543f4..33a1156 100644
--- a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
@@ -52,35 +52,30 @@
       ADD_FAILURE() << "cert_ders is empty";
       return false;
     }
-    // First entry in the PKITS chain is the trust anchor.
-    TrustStore trust_store;
-    scoped_refptr<ParsedCertificate> anchor(
-        ParsedCertificate::CreateFromCertificateCopy(cert_ders[0], {}));
-    EXPECT_TRUE(anchor);
-    if (anchor)
-      trust_store.AddTrustedCertificate(std::move(anchor));
 
     // PKITS lists chains from trust anchor to target, VerifyCertificateChain
     // takes them starting with the target and not including the trust anchor.
     std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
-    for (size_t i = cert_ders.size() - 1; i > 0; --i) {
+    for (auto i = cert_ders.rbegin(); i != cert_ders.rend(); ++i) {
       if (!net::ParsedCertificate::CreateAndAddToVector(
-              reinterpret_cast<const uint8_t*>(cert_ders[i].data()),
-              cert_ders[i].size(),
+              reinterpret_cast<const uint8_t*>(i->data()), i->size(),
               net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {},
               &input_chain)) {
-        ADD_FAILURE() << "cert " << i << " failed to parse";
+        ADD_FAILURE() << "cert failed to parse";
         return false;
       }
     }
 
+    TrustStore trust_store;
+    trust_store.AddTrustedCertificate(input_chain.back());
+
     SimpleSignaturePolicy signature_policy(1024);
 
     // Run all tests at the time the PKITS was published.
     der::GeneralizedTime time = {2011, 4, 15, 0, 0, 0};
 
-    return VerifyCertificateChain(input_chain, trust_store, &signature_policy,
-                                  time, nullptr);
+    return VerifyCertificateChainAssumingTrustedRoot(input_chain, trust_store,
+                                                     &signature_policy, time);
   }
 };
 
diff --git a/net/cert/internal/verify_certificate_chain_typed_unittest.h b/net/cert/internal/verify_certificate_chain_typed_unittest.h
new file mode 100644
index 0000000..1ddd464
--- /dev/null
+++ b/net/cert/internal/verify_certificate_chain_typed_unittest.h
@@ -0,0 +1,305 @@
+// 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.
+
+#ifndef NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_TYPED_UNITTEST_H_
+#define NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_TYPED_UNITTEST_H_
+
+#include "base/base_paths.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "net/cert/internal/parsed_certificate.h"
+#include "net/cert/internal/test_helpers.h"
+#include "net/cert/pem_tokenizer.h"
+#include "net/der/input.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+template <typename TestDelegate>
+class VerifyCertificateChainTest : public ::testing::Test {
+ public:
+  void RunTest(const char* file_name) {
+    ParsedCertificateList chain;
+    ParsedCertificateList roots;
+    der::GeneralizedTime time;
+    bool expected_result;
+
+    ReadTestFromFile(file_name, &chain, &roots, &time, &expected_result);
+
+    TestDelegate::Verify(chain, roots, time, expected_result);
+  }
+
+ private:
+  // 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(
+        std::string("net/data/verify_certificate_chain_unittest/") + 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 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.
+  void ReadTestFromFile(const std::string& file_name,
+                        ParsedCertificateList* chain,
+                        ParsedCertificateList* roots,
+                        der::GeneralizedTime* time,
+                        bool* verify_result) {
+    chain->clear();
+    roots->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";
+    const char kResultHeader[] = "VERIFY_RESULT";
+
+    pem_headers.push_back(kCertificateHeader);
+    pem_headers.push_back(kTrustedCertificateHeader);
+    pem_headers.push_back(kTimeHeader);
+    pem_headers.push_back(kResultHeader);
+
+    bool has_time = false;
+    bool has_result = 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) {
+        ASSERT_TRUE(net::ParsedCertificate::CreateAndAddToVector(
+            reinterpret_cast<const uint8_t*>(block_data.data()),
+            block_data.size(),
+            net::ParsedCertificate::DataSource::INTERNAL_COPY, {}, chain));
+      } else if (block_type == kTrustedCertificateHeader) {
+        ASSERT_TRUE(net::ParsedCertificate::CreateAndAddToVector(
+            reinterpret_cast<const uint8_t*>(block_data.data()),
+            block_data.size(),
+            net::ParsedCertificate::DataSource::INTERNAL_COPY, {}, roots));
+      } else if (block_type == kTimeHeader) {
+        ASSERT_FALSE(has_time) << "Duplicate " << kTimeHeader;
+        has_time = true;
+        ASSERT_TRUE(der::ParseUTCTime(der::Input(&block_data), time));
+      } else if (block_type == kResultHeader) {
+        ASSERT_FALSE(has_result) << "Duplicate " << kResultHeader;
+        ASSERT_TRUE(block_data == "SUCCESS" || block_data == "FAIL")
+            << "Unrecognized result: " << block_data;
+        has_result = true;
+        *verify_result = block_data == "SUCCESS";
+      }
+    }
+
+    ASSERT_TRUE(has_time);
+    ASSERT_TRUE(has_result);
+  }
+};
+
+// Tests that have only one root. These can be tested without requiring any
+// path-building ability.
+template <typename TestDelegate>
+class VerifyCertificateChainSingleRootTest
+    : public VerifyCertificateChainTest<TestDelegate> {};
+
+TYPED_TEST_CASE_P(VerifyCertificateChainSingleRootTest);
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetAndIntermediary) {
+  this->RunTest("target-and-intermediary.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             IntermediaryLacksBasicConstraints) {
+  this->RunTest("intermediary-lacks-basic-constraints.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             IntermediaryBasicConstraintsCaFalse) {
+  this->RunTest("intermediary-basic-constraints-ca-false.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             IntermediaryBasicConstraintsNotCritical) {
+  this->RunTest("intermediary-basic-constraints-not-critical.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             IntermediaryLacksSigningKeyUsage) {
+  this->RunTest("intermediary-lacks-signing-key-usage.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             IntermediaryUnknownCriticalExtension) {
+  this->RunTest("intermediary-unknown-critical-extension.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             IntermediaryUnknownNonCriticalExtension) {
+  this->RunTest("intermediary-unknown-non-critical-extension.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             ViolatesBasicConstraintsPathlen0) {
+  this->RunTest("violates-basic-constraints-pathlen-0.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             BasicConstraintsPathlen0SelfIssued) {
+  this->RunTest("basic-constraints-pathlen-0-self-issued.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedWithMd5) {
+  this->RunTest("target-signed-with-md5.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, IntermediarySignedWithMd5) {
+  this->RunTest("intermediary-signed-with-md5.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetWrongSignature) {
+  this->RunTest("target-wrong-signature.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedBy512bitRsa) {
+  this->RunTest("target-signed-by-512bit-rsa.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedUsingEcdsa) {
+  this->RunTest("target-signed-using-ecdsa.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredIntermediary) {
+  this->RunTest("expired-intermediary.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredTarget) {
+  this->RunTest("expired-target.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredTargetNotBefore) {
+  this->RunTest("expired-target-notBefore.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredRoot) {
+  this->RunTest("expired-root.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetNotEndEntity) {
+  this->RunTest("target-not-end-entity.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             TargetHasKeyCertSignButNotCa) {
+  this->RunTest("target-has-keycertsign-but-not-ca.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetHasPathlenButNotCa) {
+  this->RunTest("target-has-pathlen-but-not-ca.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             TargetUnknownCriticalExtension) {
+  this->RunTest("target-unknown-critical-extension.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             IssuerAndSubjectNotByteForByteEqual) {
+  this->RunTest("issuer-and-subject-not-byte-for-byte-equal.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             IssuerAndSubjectNotByteForByteEqualAnchor) {
+  this->RunTest("issuer-and-subject-not-byte-for-byte-equal-anchor.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ViolatesPathlen1Root) {
+  this->RunTest("violates-pathlen-1-root.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, NonSelfSignedRoot) {
+  this->RunTest("non-self-signed-root.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRolloverOldChain) {
+  this->RunTest("key-rollover-oldchain.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRolloverRolloverChain) {
+  this->RunTest("key-rollover-rolloverchain.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
+             KeyRolloverLongRolloverChain) {
+  this->RunTest("key-rollover-longrolloverchain.pem");
+}
+
+TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRolloverNewChain) {
+  this->RunTest("key-rollover-newchain.pem");
+}
+
+// TODO(eroman): Add test that invalidate validity dates where the day or month
+// ordinal not in range, like "March 39, 2016" are rejected.
+
+REGISTER_TYPED_TEST_CASE_P(VerifyCertificateChainSingleRootTest,
+                           TargetAndIntermediary,
+                           IntermediaryLacksBasicConstraints,
+                           IntermediaryBasicConstraintsCaFalse,
+                           IntermediaryBasicConstraintsNotCritical,
+                           IntermediaryLacksSigningKeyUsage,
+                           IntermediaryUnknownCriticalExtension,
+                           IntermediaryUnknownNonCriticalExtension,
+                           ViolatesBasicConstraintsPathlen0,
+                           BasicConstraintsPathlen0SelfIssued,
+                           TargetSignedWithMd5,
+                           IntermediarySignedWithMd5,
+                           TargetWrongSignature,
+                           TargetSignedBy512bitRsa,
+                           TargetSignedUsingEcdsa,
+                           ExpiredIntermediary,
+                           ExpiredTarget,
+                           ExpiredTargetNotBefore,
+                           ExpiredRoot,
+                           TargetNotEndEntity,
+                           TargetHasKeyCertSignButNotCa,
+                           TargetHasPathlenButNotCa,
+                           TargetUnknownCriticalExtension,
+                           IssuerAndSubjectNotByteForByteEqual,
+                           IssuerAndSubjectNotByteForByteEqualAnchor,
+                           ViolatesPathlen1Root,
+                           NonSelfSignedRoot,
+                           KeyRolloverOldChain,
+                           KeyRolloverRolloverChain,
+                           KeyRolloverLongRolloverChain,
+                           KeyRolloverNewChain);
+
+// Tests that have zero roots or more than one root.
+template <typename TestDelegate>
+class VerifyCertificateChainNonSingleRootTest
+    : public VerifyCertificateChainTest<TestDelegate> {};
+
+TYPED_TEST_CASE_P(VerifyCertificateChainNonSingleRootTest);
+
+TYPED_TEST_P(VerifyCertificateChainNonSingleRootTest, UnknownRoot) {
+  this->RunTest("unknown-root.pem");
+}
+
+REGISTER_TYPED_TEST_CASE_P(VerifyCertificateChainNonSingleRootTest,
+                           UnknownRoot);
+
+}  // namespace net
+
+#endif  // NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_TYPED_UNITTEST_H_
diff --git a/net/cert/internal/verify_certificate_chain_unittest.cc b/net/cert/internal/verify_certificate_chain_unittest.cc
index 9103f12f..b857296 100644
--- a/net/cert/internal/verify_certificate_chain_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_unittest.cc
@@ -4,254 +4,41 @@
 
 #include "net/cert/internal/verify_certificate_chain.h"
 
-#include "base/base_paths.h"
-#include "base/files/file_util.h"
-#include "base/path_service.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.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/pem_tokenizer.h"
-#include "net/der/input.h"
-#include "testing/gtest/include/gtest/gtest.h"
+#include "net/cert/internal/verify_certificate_chain_typed_unittest.h"
 
 namespace net {
 
 namespace {
 
-// 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(
-      std::string("net/data/verify_certificate_chain_unittest/") + file_name);
+class VerifyCertificateChainAssumingTrustedRootDelegate {
+ public:
+  static void Verify(const ParsedCertificateList& chain,
+                     const ParsedCertificateList& roots,
+                     const der::GeneralizedTime& time,
+                     bool expected_result) {
+    TrustStore trust_store;
+    ASSERT_EQ(1U, roots.size());
+    trust_store.AddTrustedCertificate(roots[0]);
 
-  // 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();
+    ParsedCertificateList full_chain(chain);
+    full_chain.push_back(roots[0]);
+
+    SimpleSignaturePolicy signature_policy(1024);
+
+    bool result = VerifyCertificateChainAssumingTrustedRoot(
+        full_chain, trust_store, &signature_policy, time);
+
+    ASSERT_EQ(expected_result, result);
   }
-
-  return file_data;
-}
-
-// Reads a 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.
-void ReadTestFromFile(const std::string& file_name,
-                      std::vector<std::string>* chain,
-                      TrustStore* trust_store,
-                      der::GeneralizedTime* time,
-                      bool* verify_result) {
-  chain->clear();
-  trust_store->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";
-  const char kResultHeader[] = "VERIFY_RESULT";
-
-  pem_headers.push_back(kCertificateHeader);
-  pem_headers.push_back(kTrustedCertificateHeader);
-  pem_headers.push_back(kTimeHeader);
-  pem_headers.push_back(kResultHeader);
-
-  bool has_time = false;
-  bool has_result = 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) {
-      scoped_refptr<ParsedCertificate> cert(
-          ParsedCertificate::CreateFromCertificateCopy(block_data, {}));
-      ASSERT_TRUE(cert);
-      trust_store->AddTrustedCertificate(std::move(cert));
-    } else if (block_type == kTimeHeader) {
-      ASSERT_FALSE(has_time) << "Duplicate " << kTimeHeader;
-      has_time = true;
-      ASSERT_TRUE(der::ParseUTCTime(der::Input(&block_data), time));
-    } else if (block_type == kResultHeader) {
-      ASSERT_FALSE(has_result) << "Duplicate " << kResultHeader;
-      ASSERT_TRUE(block_data == "SUCCESS" || block_data == "FAIL")
-          << "Unrecognized result: " << block_data;
-      has_result = true;
-      *verify_result = block_data == "SUCCESS";
-    }
-  }
-
-  ASSERT_TRUE(has_time);
-  ASSERT_TRUE(has_result);
-}
-
-void RunTest(const char* file_name) {
-  std::vector<std::string> chain;
-  TrustStore trust_store;
-  der::GeneralizedTime time;
-  bool expected_result;
-
-  ReadTestFromFile(file_name, &chain, &trust_store, &time, &expected_result);
-
-  std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
-  for (const auto& cert_der : chain) {
-    ASSERT_TRUE(net::ParsedCertificate::CreateAndAddToVector(
-        reinterpret_cast<const uint8_t*>(cert_der.data()), cert_der.size(),
-        net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {},
-        &input_chain));
-  }
-
-  SimpleSignaturePolicy signature_policy(1024);
-
-  std::vector<scoped_refptr<ParsedCertificate>> trusted_chain;
-  bool result = VerifyCertificateChain(input_chain, trust_store,
-                                       &signature_policy, time, &trusted_chain);
-  if (result) {
-    ASSERT_EQ(trusted_chain.size(), input_chain.size() + 1);
-    ASSERT_TRUE(std::equal(input_chain.begin(), input_chain.end(),
-                           trusted_chain.begin()));
-    ASSERT_TRUE(trust_store.IsTrustedCertificate(trusted_chain.back().get()));
-  } else {
-    ASSERT_EQ(trusted_chain.size(), 0u);
-  }
-
-  ASSERT_EQ(expected_result, result);
-}
-
-TEST(VerifyCertificateChainTest, TargetAndIntermediary) {
-  RunTest("target-and-intermediary.pem");
-}
-
-TEST(VerifyCertificateChainTest, UnknownRoot) {
-  RunTest("unknown-root.pem");
-}
-
-TEST(VerifyCertificateChainTest, IntermediaryLacksBasicConstraints) {
-  RunTest("intermediary-lacks-basic-constraints.pem");
-}
-
-TEST(VerifyCertificateChainTest, IntermediaryBasicConstraintsCaFalse) {
-  RunTest("intermediary-basic-constraints-ca-false.pem");
-}
-
-TEST(VerifyCertificateChainTest, IntermediaryBasicConstraintsNotCritical) {
-  RunTest("intermediary-basic-constraints-not-critical.pem");
-}
-
-TEST(VerifyCertificateChainTest, IntermediaryLacksSigningKeyUsage) {
-  RunTest("intermediary-lacks-signing-key-usage.pem");
-}
-
-TEST(VerifyCertificateChainTest, IntermediaryUnknownCriticalExtension) {
-  RunTest("intermediary-unknown-critical-extension.pem");
-}
-
-TEST(VerifyCertificateChainTest, IntermediaryUnknownNonCriticalExtension) {
-  RunTest("intermediary-unknown-non-critical-extension.pem");
-}
-
-TEST(VerifyCertificateChainTest, ViolatesBasicConstraintsPathlen0) {
-  RunTest("violates-basic-constraints-pathlen-0.pem");
-}
-
-TEST(VerifyCertificateChainTest, BasicConstraintsPathlen0SelfIssued) {
-  RunTest("basic-constraints-pathlen-0-self-issued.pem");
-}
-
-TEST(VerifyCertificateChainTest, TargetSignedWithMd5) {
-  RunTest("target-signed-with-md5.pem");
-}
-
-TEST(VerifyCertificateChainTest, IntermediarySignedWithMd5) {
-  RunTest("intermediary-signed-with-md5.pem");
-}
-
-TEST(VerifyCertificateChainTest, TargetWrongSignature) {
-  RunTest("target-wrong-signature.pem");
-}
-
-TEST(VerifyCertificateChainTest, TargetSignedBy512bitRsa) {
-  RunTest("target-signed-by-512bit-rsa.pem");
-}
-
-TEST(VerifyCertificateChainTest, TargetSignedUsingEcdsa) {
-  RunTest("target-signed-using-ecdsa.pem");
-}
-
-TEST(VerifyCertificateChainTest, ExpiredIntermediary) {
-  RunTest("expired-intermediary.pem");
-}
-
-TEST(VerifyCertificateChainTest, ExpiredTarget) {
-  RunTest("expired-target.pem");
-}
-
-TEST(VerifyCertificateChainTest, ExpiredTargetNotBefore) {
-  RunTest("expired-target-notBefore.pem");
-}
-
-TEST(VerifyCertificateChainTest, ExpiredRoot) {
-  RunTest("expired-root.pem");
-}
-
-TEST(VerifyCertificateChainTest, TargetNotEndEntity) {
-  RunTest("target-not-end-entity.pem");
-}
-
-TEST(VerifyCertificateChainTest, TargetHasKeyCertSignButNotCa) {
-  RunTest("target-has-keycertsign-but-not-ca.pem");
-}
-
-TEST(VerifyCertificateChainTest, TargetHasPathlenButNotCa) {
-  RunTest("target-has-pathlen-but-not-ca.pem");
-}
-
-TEST(VerifyCertificateChainTest, TargetUnknownCriticalExtension) {
-  RunTest("target-unknown-critical-extension.pem");
-}
-
-TEST(VerifyCertificateChainTest, IssuerAndSubjectNotByteForByteEqual) {
-  RunTest("issuer-and-subject-not-byte-for-byte-equal.pem");
-}
-
-TEST(VerifyCertificateChainTest, IssuerAndSubjectNotByteForByteEqualAnchor) {
-  RunTest("issuer-and-subject-not-byte-for-byte-equal-anchor.pem");
-}
-
-TEST(VerifyCertificateChainTest, ViolatesPathlen1Root) {
-  RunTest("violates-pathlen-1-root.pem");
-}
-
-TEST(VerifyCertificateChainTest, NonSelfSignedRoot) {
-  RunTest("non-self-signed-root.pem");
-}
-
-// Tests that verifying a chain with no certificates fails.
-TEST(VerifyCertificateChainTest, EmptyChainIsInvalid) {
-  TrustStore trust_store;
-  der::GeneralizedTime time;
-  std::vector<scoped_refptr<ParsedCertificate>> chain;
-  SimpleSignaturePolicy signature_policy(2048);
-
-  ASSERT_FALSE(VerifyCertificateChain(chain, trust_store, &signature_policy,
-                                      time, nullptr));
-}
-
-// TODO(eroman): Add test that invalidate validity dates where the day or month
-// ordinal not in range, like "March 39, 2016" are rejected.
+};
 
 }  // namespace
 
+INSTANTIATE_TYPED_TEST_CASE_P(
+    VerifyCertificateChainAssumingTrustedRoot,
+    VerifyCertificateChainSingleRootTest,
+    VerifyCertificateChainAssumingTrustedRootDelegate);
+
 }  // namespace net
diff --git a/net/data/verify_certificate_chain_unittest/generate-key-rollover.py b/net/data/verify_certificate_chain_unittest/generate-key-rollover.py
new file mode 100755
index 0000000..e06b452
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/generate-key-rollover.py
@@ -0,0 +1,92 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+"""A certificate tree with two self-signed root certificates(oldroot, newroot),
+and a third root certificate (newrootrollover) which has the same key as newroot
+but is signed by oldroot, all with the same subject and issuer.
+There are two intermediates with the same key, subject and issuer
+(oldintermediate signed by oldroot, and newintermediate signed by newroot).
+The target certificate is signed by the intermediate key.
+
+
+In graphical form:
+
+   oldroot-------->newrootrollover  newroot
+      |                      |        |
+      v                      v        v
+oldintermediate           newintermediate
+      |                          |
+      +------------+-------------+
+                   |
+                   v
+                 target
+
+
+Several chains are output:
+  key-rollover-oldchain.pem:
+    target<-oldintermediate<-oldroot
+  key-rollover-rolloverchain.pem:
+    target<-newintermediate<-newrootrollover<-oldroot
+  key-rollover-longrolloverchain.pem:
+    target<-newintermediate<-newroot<-newrootrollover<-oldroot
+  key-rollover-newchain.pem:
+    target<-newintermediate<-newroot
+
+All of these chains should verify successfully.
+"""
+
+import common
+
+# The new certs should have a newer notbefore date than "old" certs. This should
+# affect path builder sorting, but otherwise won't matter.
+JANUARY_2_2015_UTC = '150102120000Z'
+
+# Self-signed root certificates. Same name, different keys.
+oldroot = common.create_self_signed_root_certificate('Root')
+oldroot.set_validity_range(common.JANUARY_1_2015_UTC, common.JANUARY_1_2016_UTC)
+newroot = common.create_self_signed_root_certificate('Root')
+newroot.set_validity_range(JANUARY_2_2015_UTC, common.JANUARY_1_2016_UTC)
+# Root with the new key signed by the old key.
+newrootrollover = common.create_intermediary_certificate('Root', oldroot)
+newrootrollover.set_key_path(newroot.get_key_path())
+newrootrollover.set_validity_range(JANUARY_2_2015_UTC,
+                                   common.JANUARY_1_2016_UTC)
+
+# Intermediate signed by oldroot.
+oldintermediate = common.create_intermediary_certificate('Intermediate',
+                                                         oldroot)
+oldintermediate.set_validity_range(common.JANUARY_1_2015_UTC,
+                                   common.JANUARY_1_2016_UTC)
+# Intermediate signed by newroot. Same key as oldintermediate.
+newintermediate = common.create_intermediary_certificate('Intermediate',
+                                                         newroot)
+newintermediate.set_key_path(oldintermediate.get_key_path())
+newintermediate.set_validity_range(JANUARY_2_2015_UTC,
+                                   common.JANUARY_1_2016_UTC)
+
+# Target certificate.
+target = common.create_end_entity_certificate('Target', oldintermediate)
+
+oldchain = [target, oldintermediate]
+rolloverchain = [target, newintermediate, newrootrollover]
+longrolloverchain = [target, newintermediate, newroot, newrootrollover]
+oldtrusted = [oldroot]
+
+newchain = [target, newintermediate]
+newtrusted = [newroot]
+
+time = common.DEFAULT_TIME
+verify_result = True
+
+common.write_test_file(__doc__, oldchain, oldtrusted, time, verify_result,
+                       out_pem="key-rollover-oldchain.pem")
+common.write_test_file(__doc__, rolloverchain, oldtrusted, time, verify_result,
+                       out_pem="key-rollover-rolloverchain.pem")
+common.write_test_file(__doc__, longrolloverchain, oldtrusted, time,
+                       verify_result,
+                       out_pem="key-rollover-longrolloverchain.pem")
+common.write_test_file(__doc__, newchain, newtrusted, time, verify_result,
+                       out_pem="key-rollover-newchain.pem")
+
diff --git a/net/data/verify_certificate_chain_unittest/key-rollover-longrolloverchain.pem b/net/data/verify_certificate_chain_unittest/key-rollover-longrolloverchain.pem
new file mode 100644
index 0000000..fcd63fa
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/key-rollover-longrolloverchain.pem
@@ -0,0 +1,489 @@
+[Created by: ./generate-key-rollover.py]
+
+A certificate tree with two self-signed root certificates(oldroot, newroot),
+and a third root certificate (newrootrollover) which has the same key as newroot
+but is signed by oldroot, all with the same subject and issuer.
+There are two intermediates with the same key, subject and issuer
+(oldintermediate signed by oldroot, and newintermediate signed by newroot).
+The target certificate is signed by the intermediate key.
+
+
+In graphical form:
+
+   oldroot-------->newrootrollover  newroot
+      |                      |        |
+      v                      v        v
+oldintermediate           newintermediate
+      |                          |
+      +------------+-------------+
+                   |
+                   v
+                 target
+
+
+Several chains are output:
+  key-rollover-oldchain.pem:
+    target<-oldintermediate<-oldroot
+  key-rollover-rolloverchain.pem:
+    target<-newintermediate<-newrootrollover<-oldroot
+  key-rollover-longrolloverchain.pem:
+    target<-newintermediate<-newroot<-newrootrollover<-oldroot
+  key-rollover-newchain.pem:
+    target<-newintermediate<-newroot
+
+All of these chains should verify successfully.
+
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:be:12:1c:48:e4:73:1f:5c:d2:54:a9:7b:58:1c:
+                    37:73:c2:49:26:3e:ed:b5:6b:55:17:c9:4c:52:34:
+                    ce:d9:76:86:32:74:74:ae:11:b2:99:1b:51:a0:33:
+                    48:34:2f:b9:d3:2b:06:c2:5c:29:53:35:ce:7c:a6:
+                    67:b2:6a:d4:33:c3:13:62:30:a1:53:5f:45:78:5b:
+                    bb:47:ad:07:a4:98:9a:e9:e3:b1:3b:e6:33:c2:c1:
+                    5c:95:d7:c8:b9:a6:72:27:7a:79:da:c4:c8:5a:1a:
+                    3e:5e:5e:a6:62:64:c6:72:86:b1:78:98:5b:63:27:
+                    70:15:04:6b:b1:0f:11:9c:4d:3b:5c:e7:8d:c0:be:
+                    d5:84:46:6c:bd:11:1e:21:c1:82:9c:d0:aa:2d:2f:
+                    f8:2a:e9:3b:e4:35:15:6d:c7:4a:dd:a8:65:69:b8:
+                    16:a1:8a:04:a2:44:68:40:b6:99:ae:61:df:9f:6c:
+                    40:ef:79:c9:a3:6d:e4:2d:07:01:68:f1:21:4e:0e:
+                    28:a7:fd:2f:ad:ee:7d:65:cf:36:fd:4f:1b:ba:10:
+                    8e:86:fd:ec:37:67:0c:20:71:66:48:64:f3:82:af:
+                    f5:e1:73:c9:09:36:03:3f:c2:47:7a:f2:33:b9:f9:
+                    9f:53:9b:24:5e:c3:cc:05:d9:a9:ed:d7:b2:2a:c5:
+                    b7:39
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                C9:45:0B:2A:F2:D8:8D:2A:D7:CE:AF:56:BF:82:B0:84:0C:C8:2E:F4
+            X509v3 Authority Key Identifier: 
+                keyid:B1:39:79:13:35:D0:03:6B:E9:C4:63:2B:CC:D6:61:C3:82:EC:14:C1
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         63:66:9e:6c:34:8c:5d:74:ae:90:25:55:ae:86:49:b9:3d:fd:
+         27:bc:4f:69:7b:70:cb:25:0e:a3:8c:7a:7d:9c:4f:0b:7c:f2:
+         85:a5:ea:82:d2:37:c2:74:a2:ae:a8:bf:62:f4:5f:d4:c6:41:
+         45:0c:cc:27:53:aa:8f:66:58:e9:b0:de:ae:98:14:bd:92:df:
+         9b:0f:f2:c5:3b:d2:bc:1c:3e:80:b4:09:0f:c1:9f:d6:3a:29:
+         52:71:b6:1a:92:95:5a:18:dc:b4:30:dc:61:61:93:54:d1:55:
+         83:92:5d:c0:c7:dc:ab:d7:08:dd:8a:44:cf:92:f9:4d:86:25:
+         aa:ac:52:f6:0e:17:99:0b:31:d2:75:5e:33:f9:f5:b6:77:42:
+         07:62:a9:53:cc:f3:79:84:57:d9:14:3f:ab:4c:8b:ae:c7:9f:
+         cf:7a:1f:bf:7e:1d:44:bd:76:b4:cd:8d:c8:1d:75:f7:3b:b5:
+         bc:35:8b:3f:29:b1:cb:67:a4:17:af:a4:ca:9f:2b:e7:15:66:
+         e4:c8:c1:7c:08:78:9e:5d:4b:c3:c6:58:66:96:42:e8:e6:40:
+         fd:dc:24:ce:3b:58:11:38:40:0e:fc:a9:c0:2c:0f:e5:cc:bb:
+         02:32:31:b9:bc:6f:2d:1d:f6:2b:7c:d3:f8:24:f6:60:38:8a:
+         1f:dd:e1:50
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+EhxI
+5HMfXNJUqXtYHDdzwkkmPu21a1UXyUxSNM7ZdoYydHSuEbKZG1GgM0g0L7nTKwbC
+XClTNc58pmeyatQzwxNiMKFTX0V4W7tHrQekmJrp47E75jPCwVyV18i5pnInenna
+xMhaGj5eXqZiZMZyhrF4mFtjJ3AVBGuxDxGcTTtc543AvtWERmy9ER4hwYKc0Kot
+L/gq6TvkNRVtx0rdqGVpuBahigSiRGhAtpmuYd+fbEDvecmjbeQtBwFo8SFODiin
+/S+t7n1lzzb9Txu6EI6G/ew3ZwwgcWZIZPOCr/Xhc8kJNgM/wkd68jO5+Z9TmyRe
+w8wF2ant17Iqxbc5AgMBAAGjgekwgeYwHQYDVR0OBBYEFMlFCyry2I0q186vVr+C
+sIQMyC70MB8GA1UdIwQYMBaAFLE5eRM10ANr6cRjK8zWYcOC7BTBMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAY2aebDSMXXSukCVVroZJ
+uT39J7xPaXtwyyUOo4x6fZxPC3zyhaXqgtI3wnSirqi/YvRf1MZBRQzMJ1Oqj2ZY
+6bDerpgUvZLfmw/yxTvSvBw+gLQJD8Gf1jopUnG2GpKVWhjctDDcYWGTVNFVg5Jd
+wMfcq9cI3YpEz5L5TYYlqqxS9g4XmQsx0nVeM/n1tndCB2KpU8zzeYRX2RQ/q0yL
+rsefz3ofv34dRL12tM2NyB119zu1vDWLPymxy2ekF6+kyp8r5xVm5MjBfAh4nl1L
+w8ZYZpZC6OZA/dwkzjtYEThADvypwCwP5cy7AjIxubxvLR32K3zT+CT2YDiKH93h
+UA==
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  2 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:9c:d0:2e:3b:06:d6:ea:65:bd:dd:b2:d3:e8:88:
+                    ea:08:73:01:42:ac:ca:38:28:17:32:93:5e:16:a8:
+                    c1:79:44:9a:db:24:08:ba:81:52:63:9c:b4:ed:57:
+                    d4:b2:ac:54:64:3b:70:39:7e:37:da:11:e1:8c:ba:
+                    09:bc:1a:9b:e7:fe:6d:75:f8:71:31:f0:ca:52:89:
+                    2a:9e:d5:53:db:b8:c0:76:cf:bf:58:58:e1:bb:81:
+                    de:62:bb:06:58:1f:9b:64:03:75:7d:ee:76:6f:39:
+                    47:cb:8e:34:32:07:83:89:b0:83:2a:78:d0:ac:e2:
+                    86:0a:a8:ab:3b:97:81:de:9d:36:b4:03:b7:d5:06:
+                    05:53:d7:80:03:44:86:53:72:db:7a:5f:c5:20:dd:
+                    c7:44:58:3b:40:7f:0e:39:bc:be:0d:ca:6a:f6:82:
+                    a2:97:a2:17:79:51:6f:42:5d:0d:6a:b7:a0:de:5f:
+                    6a:00:be:e7:5a:b7:91:e9:fc:77:fd:75:88:8d:52:
+                    76:3d:0e:91:4b:c7:db:96:a4:5f:39:59:55:62:65:
+                    3b:15:7a:bc:7b:09:9f:3e:75:d9:9e:c5:00:b3:19:
+                    d4:26:7e:eb:db:62:07:c2:f5:b6:4e:87:2d:eb:56:
+                    8b:5a:68:6c:85:2f:b4:3e:1d:dd:5d:31:49:98:8b:
+                    06:55
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                B1:39:79:13:35:D0:03:6B:E9:C4:63:2B:CC:D6:61:C3:82:EC:14:C1
+            X509v3 Authority Key Identifier: 
+                keyid:15:9E:A6:AD:F5:9F:8A:A1:C1:08:99:BF:66:6F:CF:CA:72:CD:0C:34
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         a8:ad:48:d8:6e:1d:24:09:d2:b3:29:3c:48:60:27:7f:37:64:
+         d5:f1:3b:b3:5c:43:de:7c:b4:5f:ee:3a:f2:1a:25:24:0a:8a:
+         25:6d:19:5b:dc:0c:4e:48:61:2f:60:d3:6b:f3:9c:03:2c:d3:
+         fa:c8:9b:99:e7:2e:c5:43:c0:5f:14:cd:8b:92:62:4f:e5:3a:
+         cd:b5:0a:d8:b2:01:c7:44:b4:3a:86:66:bf:fa:11:a5:f8:24:
+         3f:d1:1a:e8:eb:1e:ad:f0:70:31:6f:bc:21:cf:db:ce:63:4e:
+         84:e9:52:9e:bb:1b:c4:72:ae:e0:6c:88:9e:99:dc:79:d9:fd:
+         83:26:8e:f6:19:70:d9:5e:fc:f4:0c:d1:17:6f:af:10:f6:64:
+         16:08:d8:72:ba:3a:2d:66:28:5a:41:0d:f3:47:87:a7:9c:78:
+         c6:cd:5e:25:71:0b:f2:93:b8:26:17:b2:19:17:cc:03:ed:c0:
+         6e:06:e2:4b:4a:57:5f:23:02:2a:69:06:7a:c4:b7:3e:2f:e8:
+         f0:03:ae:b8:2d:df:63:22:20:73:23:75:d9:3c:d7:22:e4:b4:
+         65:65:ed:b9:e6:02:1d:b5:51:11:9c:db:92:e4:fe:8c:1d:bb:
+         c5:95:87:5b:38:ee:ff:e4:01:d1:5d:84:b9:73:d3:da:23:ca:
+         5e:05:d3:7d
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMjEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNAuOwbW
+6mW93bLT6IjqCHMBQqzKOCgXMpNeFqjBeUSa2yQIuoFSY5y07VfUsqxUZDtwOX43
+2hHhjLoJvBqb5/5tdfhxMfDKUokqntVT27jAds+/WFjhu4HeYrsGWB+bZAN1fe52
+bzlHy440MgeDibCDKnjQrOKGCqirO5eB3p02tAO31QYFU9eAA0SGU3Lbel/FIN3H
+RFg7QH8OOby+Dcpq9oKil6IXeVFvQl0Nareg3l9qAL7nWreR6fx3/XWIjVJ2PQ6R
+S8fblqRfOVlVYmU7FXq8ewmfPnXZnsUAsxnUJn7r22IHwvW2Toct61aLWmhshS+0
+Ph3dXTFJmIsGVQIDAQABo4HLMIHIMB0GA1UdDgQWBBSxOXkTNdADa+nEYyvM1mHD
+guwUwTAfBgNVHSMEGDAWgBQVnqat9Z+KocEImb9mb8/Kcs0MNDA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AKitSNhuHSQJ0rMpPEhgJ383ZNXxO7NcQ958tF/uOvIaJSQKiiVtGVvcDE5IYS9g
+02vznAMs0/rIm5nnLsVDwF8UzYuSYk/lOs21CtiyAcdEtDqGZr/6EaX4JD/RGujr
+Hq3wcDFvvCHP285jToTpUp67G8RyruBsiJ6Z3HnZ/YMmjvYZcNle/PQM0RdvrxD2
+ZBYI2HK6Oi1mKFpBDfNHh6eceMbNXiVxC/KTuCYXshkXzAPtwG4G4ktKV18jAipp
+BnrEtz4v6PADrrgt32MiIHMjddk81yLktGVl7bnmAh21URGc25Lk/owdu8WVh1s4
+7v/kAdFdhLlz09ojyl4F030=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  2 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:ae:ea:3b:3f:b6:e9:3d:ea:eb:3d:dd:e8:4d:45:
+                    83:63:78:ea:07:90:3a:3c:4f:92:54:2b:2d:02:1b:
+                    eb:9e:81:72:68:2e:73:f8:4a:a1:de:0c:d6:f0:c2:
+                    61:26:90:0b:48:59:ab:23:25:8f:e4:4a:6b:c9:2d:
+                    ba:a7:35:c4:22:df:76:99:d8:7b:f7:6d:ca:9b:da:
+                    d2:ed:7e:c8:93:b2:a7:f6:f0:05:6a:5d:c6:e1:79:
+                    d0:25:59:a9:50:1e:65:eb:1c:c9:cd:4e:6a:3a:2a:
+                    a4:1a:fa:81:a3:e7:ae:d7:de:43:d9:e8:0b:5c:b0:
+                    6b:46:39:c5:9c:4a:6d:59:bf:da:70:2e:80:ac:c8:
+                    80:e3:83:d1:71:7b:a7:0b:92:bf:a8:81:ad:5c:b2:
+                    d5:e9:b9:5f:b5:4f:93:43:67:72:36:b3:f7:17:b9:
+                    1b:da:2a:13:83:70:36:ae:59:03:3d:f0:71:de:a2:
+                    7a:41:ad:b5:e9:a2:51:e4:18:ec:88:ad:48:f1:df:
+                    17:04:43:54:2a:af:3c:c0:f5:84:39:43:d1:a7:d2:
+                    52:0f:3c:dd:ef:13:58:8c:1d:d4:dd:2e:6d:1a:e7:
+                    73:9b:8b:f3:41:7b:9a:53:4e:0d:92:d3:5d:3f:fc:
+                    c3:61:dc:5f:a0:93:3c:08:cc:b4:9b:ce:9d:78:e3:
+                    77:c9
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                15:9E:A6:AD:F5:9F:8A:A1:C1:08:99:BF:66:6F:CF:CA:72:CD:0C:34
+            X509v3 Authority Key Identifier: 
+                keyid:15:9E:A6:AD:F5:9F:8A:A1:C1:08:99:BF:66:6F:CF:CA:72:CD:0C:34
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         58:4b:60:7e:0b:c2:3d:20:f3:49:34:91:55:d3:5f:5b:ce:81:
+         31:da:71:9f:72:d7:f4:cc:be:cf:58:b8:97:aa:99:87:3d:34:
+         12:ac:40:1c:8e:02:46:83:ee:04:e1:7e:5e:57:ca:a4:ae:e1:
+         d0:b4:12:e9:65:33:f5:4e:c4:f6:49:00:7f:83:6d:75:67:84:
+         b2:db:52:5a:a2:3e:5d:2d:5c:f3:45:fe:6a:d3:c4:0a:76:52:
+         c6:9b:b0:89:01:b9:b6:be:30:60:d9:b4:2e:1d:1e:bf:ef:d8:
+         12:90:9f:cb:67:29:20:61:9f:1a:67:64:88:4c:43:ec:10:7d:
+         87:11:00:44:6a:ce:37:af:73:f4:fa:d2:22:2f:24:3a:6f:79:
+         09:6c:8d:de:b5:71:0e:6e:b7:64:a9:be:73:a8:c1:c8:50:74:
+         d2:c4:2b:ef:4f:25:20:8f:41:f0:1f:6e:52:77:eb:a0:1a:94:
+         87:7c:35:11:37:5c:33:f5:83:47:e0:f2:0e:97:af:23:61:23:
+         25:0b:92:6c:3b:30:a1:aa:c6:dc:4a:05:6e:43:76:58:82:66:
+         cd:f6:d6:ef:9b:80:36:d6:95:b7:d1:ec:5c:53:f7:78:84:ef:
+         48:6a:2c:f7:93:97:f2:7a:ce:ec:f3:eb:63:e1:5a:e0:69:02:
+         5d:34:36:93
+-----BEGIN CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMjEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7qOz+26T3q6z3d6E1F
+g2N46geQOjxPklQrLQIb656Bcmguc/hKod4M1vDCYSaQC0hZqyMlj+RKa8ktuqc1
+xCLfdpnYe/dtypva0u1+yJOyp/bwBWpdxuF50CVZqVAeZescyc1OajoqpBr6gaPn
+rtfeQ9noC1ywa0Y5xZxKbVm/2nAugKzIgOOD0XF7pwuSv6iBrVyy1em5X7VPk0Nn
+cjaz9xe5G9oqE4NwNq5ZAz3wcd6iekGttemiUeQY7IitSPHfFwRDVCqvPMD1hDlD
+0afSUg883e8TWIwd1N0ubRrnc5uL80F7mlNODZLTXT/8w2HcX6CTPAjMtJvOnXjj
+d8kCAwEAAaOByzCByDAdBgNVHQ4EFgQUFZ6mrfWfiqHBCJm/Zm/PynLNDDQwHwYD
+VR0jBBgwFoAUFZ6mrfWfiqHBCJm/Zm/PynLNDDQwNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBYS2B+C8I9
+IPNJNJFV019bzoEx2nGfctf0zL7PWLiXqpmHPTQSrEAcjgJGg+4E4X5eV8qkruHQ
+tBLpZTP1TsT2SQB/g211Z4Sy21Jaoj5dLVzzRf5q08QKdlLGm7CJAbm2vjBg2bQu
+HR6/79gSkJ/LZykgYZ8aZ2SITEPsEH2HEQBEas43r3P0+tIiLyQ6b3kJbI3etXEO
+brdkqb5zqMHIUHTSxCvvTyUgj0HwH25Sd+ugGpSHfDURN1wz9YNH4PIOl68jYSMl
+C5JsOzChqsbcSgVuQ3ZYgmbN9tbvm4A21pW30excU/d4hO9Iaiz3k5fyes7s8+tj
+4VrgaQJdNDaT
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 3 (0x3)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  2 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:ae:ea:3b:3f:b6:e9:3d:ea:eb:3d:dd:e8:4d:45:
+                    83:63:78:ea:07:90:3a:3c:4f:92:54:2b:2d:02:1b:
+                    eb:9e:81:72:68:2e:73:f8:4a:a1:de:0c:d6:f0:c2:
+                    61:26:90:0b:48:59:ab:23:25:8f:e4:4a:6b:c9:2d:
+                    ba:a7:35:c4:22:df:76:99:d8:7b:f7:6d:ca:9b:da:
+                    d2:ed:7e:c8:93:b2:a7:f6:f0:05:6a:5d:c6:e1:79:
+                    d0:25:59:a9:50:1e:65:eb:1c:c9:cd:4e:6a:3a:2a:
+                    a4:1a:fa:81:a3:e7:ae:d7:de:43:d9:e8:0b:5c:b0:
+                    6b:46:39:c5:9c:4a:6d:59:bf:da:70:2e:80:ac:c8:
+                    80:e3:83:d1:71:7b:a7:0b:92:bf:a8:81:ad:5c:b2:
+                    d5:e9:b9:5f:b5:4f:93:43:67:72:36:b3:f7:17:b9:
+                    1b:da:2a:13:83:70:36:ae:59:03:3d:f0:71:de:a2:
+                    7a:41:ad:b5:e9:a2:51:e4:18:ec:88:ad:48:f1:df:
+                    17:04:43:54:2a:af:3c:c0:f5:84:39:43:d1:a7:d2:
+                    52:0f:3c:dd:ef:13:58:8c:1d:d4:dd:2e:6d:1a:e7:
+                    73:9b:8b:f3:41:7b:9a:53:4e:0d:92:d3:5d:3f:fc:
+                    c3:61:dc:5f:a0:93:3c:08:cc:b4:9b:ce:9d:78:e3:
+                    77:c9
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                15:9E:A6:AD:F5:9F:8A:A1:C1:08:99:BF:66:6F:CF:CA:72:CD:0C:34
+            X509v3 Authority Key Identifier: 
+                keyid:02:CE:F6:AC:1A:39:1E:85:E8:72:D1:8A:C6:1D:E8:7A:8F:9D:15:6B
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         51:9b:dd:56:f2:b1:2b:e5:36:c8:2f:1d:a9:53:1f:89:e1:24:
+         33:bd:ac:56:c1:c3:1a:38:a6:7e:fc:61:9a:ae:7c:1f:13:3e:
+         37:e4:e6:a9:11:9e:2b:6e:ce:dd:12:0c:c1:b2:b7:eb:48:0e:
+         c7:a5:65:f0:86:49:8a:dc:cf:1b:6d:33:af:af:96:51:49:01:
+         e4:82:d6:e6:5a:d0:41:c7:05:9f:16:eb:06:bd:bc:ab:fe:a0:
+         d7:ac:de:62:d1:71:7e:69:82:31:03:e3:60:28:e6:18:3b:e5:
+         93:2b:58:ee:d5:0b:7b:b6:af:f2:4f:22:eb:4d:b7:a6:74:68:
+         b7:82:68:7f:a9:b6:ee:a0:20:d7:c6:16:0e:9c:1c:39:ea:24:
+         5e:60:12:fc:39:60:0d:54:3e:aa:b3:43:e1:0f:ef:d7:8f:3e:
+         09:a9:55:95:e9:3d:0c:4f:ad:cb:c2:f3:2c:10:43:67:54:f9:
+         66:54:81:ff:62:61:94:05:b0:42:af:f0:c5:ac:00:91:28:5c:
+         aa:a3:61:44:ba:c2:a6:ab:f8:1d:7e:02:69:33:48:fe:ac:93:
+         7f:4c:99:91:d9:18:37:f9:70:3f:56:2a:ee:4a:e0:4d:f3:60:
+         12:5d:30:d8:37:bf:ca:40:85:29:0c:a7:8f:ab:ad:03:6d:7b:
+         ba:62:7f:58
+-----BEGIN CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIBAzANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMjEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7qOz+26T3q6z3d6E1F
+g2N46geQOjxPklQrLQIb656Bcmguc/hKod4M1vDCYSaQC0hZqyMlj+RKa8ktuqc1
+xCLfdpnYe/dtypva0u1+yJOyp/bwBWpdxuF50CVZqVAeZescyc1OajoqpBr6gaPn
+rtfeQ9noC1ywa0Y5xZxKbVm/2nAugKzIgOOD0XF7pwuSv6iBrVyy1em5X7VPk0Nn
+cjaz9xe5G9oqE4NwNq5ZAz3wcd6iekGttemiUeQY7IitSPHfFwRDVCqvPMD1hDlD
+0afSUg883e8TWIwd1N0ubRrnc5uL80F7mlNODZLTXT/8w2HcX6CTPAjMtJvOnXjj
+d8kCAwEAAaOByzCByDAdBgNVHQ4EFgQUFZ6mrfWfiqHBCJm/Zm/PynLNDDQwHwYD
+VR0jBBgwFoAUAs72rBo5HoXoctGKxh3oeo+dFWswNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBRm91W8rEr
+5TbILx2pUx+J4SQzvaxWwcMaOKZ+/GGarnwfEz435OapEZ4rbs7dEgzBsrfrSA7H
+pWXwhkmK3M8bbTOvr5ZRSQHkgtbmWtBBxwWfFusGvbyr/qDXrN5i0XF+aYIxA+Ng
+KOYYO+WTK1ju1Qt7tq/yTyLrTbemdGi3gmh/qbbuoCDXxhYOnBw56iReYBL8OWAN
+VD6qs0PhD+/Xjz4JqVWV6T0MT63LwvMsEENnVPlmVIH/YmGUBbBCr/DFrACRKFyq
+o2FEusKmq/gdfgJpM0j+rJN/TJmR2Rg3+XA/ViruSuBN82ASXTDYN7/KQIUpDKeP
+q60DbXu6Yn9Y
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:a6:44:ec:a7:15:00:1f:89:ac:91:f8:ec:7b:03:
+                    46:0b:53:15:ed:23:40:35:94:f3:96:80:27:d3:4a:
+                    84:92:68:c9:0c:e0:14:32:c7:31:67:49:29:58:77:
+                    ea:ce:8a:72:5b:93:b1:a0:a8:e5:84:c6:52:9d:5a:
+                    c0:41:bf:98:5f:18:5d:aa:d1:65:79:fb:e9:b0:84:
+                    92:9d:2c:58:bc:f1:c4:29:59:ed:bc:ac:85:ce:d7:
+                    0e:aa:08:e8:2d:90:25:cb:91:9d:7d:91:74:42:a0:
+                    ae:77:d2:11:7b:57:49:04:24:c0:94:f4:20:54:60:
+                    d9:1b:76:76:0b:2c:23:3c:67:90:8c:06:ed:4e:df:
+                    ac:24:22:26:f7:26:8f:5a:d2:5b:79:8a:6f:6e:53:
+                    27:60:10:cb:c7:b4:9f:60:2d:8f:32:69:4b:01:d1:
+                    f0:6d:69:1a:22:14:06:66:63:97:e8:fc:79:41:8d:
+                    15:44:44:d1:43:2a:37:5e:77:e4:06:e6:a9:85:13:
+                    e9:24:63:9d:09:d0:f5:13:d5:ba:59:2e:1c:d2:70:
+                    06:b1:80:f7:57:d7:30:f7:14:f3:18:06:7f:84:38:
+                    b6:81:46:9f:a2:36:87:0e:5f:1a:45:38:b7:20:16:
+                    b7:c6:e1:91:3b:0e:0c:ab:b7:4e:3d:a4:6d:66:d8:
+                    85:fb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                02:CE:F6:AC:1A:39:1E:85:E8:72:D1:8A:C6:1D:E8:7A:8F:9D:15:6B
+            X509v3 Authority Key Identifier: 
+                keyid:02:CE:F6:AC:1A:39:1E:85:E8:72:D1:8A:C6:1D:E8:7A:8F:9D:15:6B
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         0e:00:56:5c:72:31:88:bd:95:13:f7:64:96:1c:63:c2:1c:11:
+         60:04:d2:c3:5f:7c:a2:d7:d1:33:6d:51:6b:77:61:78:a8:70:
+         2e:50:97:5d:c1:e8:9b:dd:c6:61:a7:d3:e1:2c:83:07:85:5c:
+         c9:d7:1e:22:c2:5f:76:83:19:d7:de:4a:5e:82:0f:43:80:45:
+         02:d7:d0:3d:ca:c3:c0:fc:04:c8:f6:89:32:d7:47:c6:bf:1f:
+         c6:bd:71:e1:07:00:90:12:ec:61:63:1b:6c:e9:58:2c:fc:4c:
+         a9:8f:58:e1:b1:6e:a5:ca:4d:be:7e:32:16:74:5f:fd:35:e4:
+         37:aa:1a:c5:33:21:20:8a:3e:1c:af:da:f3:c7:a2:22:d3:93:
+         6c:5e:ac:0a:65:d5:db:e4:8b:11:5e:ca:eb:8f:da:c4:5d:2f:
+         7a:98:e8:3c:d1:89:15:05:02:86:ef:eb:17:18:81:28:ca:d6:
+         58:87:bd:d4:e2:50:41:92:d9:7f:b1:f7:53:8f:f3:cc:f3:1e:
+         1d:e4:5a:c2:60:1b:17:42:78:53:e9:2d:5d:bb:f9:21:50:ff:
+         87:53:be:5f:e6:d4:8f:25:7f:d7:83:d7:f8:4d:c1:7c:7a:40:
+         0b:11:f1:d9:c6:eb:97:45:00:d6:6b:84:1c:4f:fc:8e:1f:5b:
+         b5:3d:60:0c
+-----BEGIN TRUSTED_CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKZE7KcVAB+JrJH47HsD
+RgtTFe0jQDWU85aAJ9NKhJJoyQzgFDLHMWdJKVh36s6KcluTsaCo5YTGUp1awEG/
+mF8YXarRZXn76bCEkp0sWLzxxClZ7byshc7XDqoI6C2QJcuRnX2RdEKgrnfSEXtX
+SQQkwJT0IFRg2Rt2dgssIzxnkIwG7U7frCQiJvcmj1rSW3mKb25TJ2AQy8e0n2At
+jzJpSwHR8G1pGiIUBmZjl+j8eUGNFURE0UMqN1535AbmqYUT6SRjnQnQ9RPVulku
+HNJwBrGA91fXMPcU8xgGf4Q4toFGn6I2hw5fGkU4tyAWt8bhkTsODKu3Tj2kbWbY
+hfsCAwEAAaOByzCByDAdBgNVHQ4EFgQUAs72rBo5HoXoctGKxh3oeo+dFWswHwYD
+VR0jBBgwFoAUAs72rBo5HoXoctGKxh3oeo+dFWswNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAOAFZccjGI
+vZUT92SWHGPCHBFgBNLDX3yi19EzbVFrd2F4qHAuUJddweib3cZhp9PhLIMHhVzJ
+1x4iwl92gxnX3kpegg9DgEUC19A9ysPA/ATI9oky10fGvx/GvXHhBwCQEuxhYxts
+6Vgs/Eypj1jhsW6lyk2+fjIWdF/9NeQ3qhrFMyEgij4cr9rzx6Ii05NsXqwKZdXb
+5IsRXsrrj9rEXS96mOg80YkVBQKG7+sXGIEoytZYh73U4lBBktl/sfdTj/PM8x4d
+5FrCYBsXQnhT6S1du/khUP+HU75f5tSPJX/Xg9f4TcF8ekALEfHZxuuXRQDWa4Qc
+T/yOH1u1PWAM
+-----END TRUSTED_CERTIFICATE-----
+
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+-----BEGIN VERIFY_RESULT-----
+U1VDQ0VTUw==
+-----END VERIFY_RESULT-----
diff --git a/net/data/verify_certificate_chain_unittest/key-rollover-newchain.pem b/net/data/verify_certificate_chain_unittest/key-rollover-newchain.pem
new file mode 100644
index 0000000..97d56749
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/key-rollover-newchain.pem
@@ -0,0 +1,311 @@
+[Created by: ./generate-key-rollover.py]
+
+A certificate tree with two self-signed root certificates(oldroot, newroot),
+and a third root certificate (newrootrollover) which has the same key as newroot
+but is signed by oldroot, all with the same subject and issuer.
+There are two intermediates with the same key, subject and issuer
+(oldintermediate signed by oldroot, and newintermediate signed by newroot).
+The target certificate is signed by the intermediate key.
+
+
+In graphical form:
+
+   oldroot-------->newrootrollover  newroot
+      |                      |        |
+      v                      v        v
+oldintermediate           newintermediate
+      |                          |
+      +------------+-------------+
+                   |
+                   v
+                 target
+
+
+Several chains are output:
+  key-rollover-oldchain.pem:
+    target<-oldintermediate<-oldroot
+  key-rollover-rolloverchain.pem:
+    target<-newintermediate<-newrootrollover<-oldroot
+  key-rollover-longrolloverchain.pem:
+    target<-newintermediate<-newroot<-newrootrollover<-oldroot
+  key-rollover-newchain.pem:
+    target<-newintermediate<-newroot
+
+All of these chains should verify successfully.
+
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:be:12:1c:48:e4:73:1f:5c:d2:54:a9:7b:58:1c:
+                    37:73:c2:49:26:3e:ed:b5:6b:55:17:c9:4c:52:34:
+                    ce:d9:76:86:32:74:74:ae:11:b2:99:1b:51:a0:33:
+                    48:34:2f:b9:d3:2b:06:c2:5c:29:53:35:ce:7c:a6:
+                    67:b2:6a:d4:33:c3:13:62:30:a1:53:5f:45:78:5b:
+                    bb:47:ad:07:a4:98:9a:e9:e3:b1:3b:e6:33:c2:c1:
+                    5c:95:d7:c8:b9:a6:72:27:7a:79:da:c4:c8:5a:1a:
+                    3e:5e:5e:a6:62:64:c6:72:86:b1:78:98:5b:63:27:
+                    70:15:04:6b:b1:0f:11:9c:4d:3b:5c:e7:8d:c0:be:
+                    d5:84:46:6c:bd:11:1e:21:c1:82:9c:d0:aa:2d:2f:
+                    f8:2a:e9:3b:e4:35:15:6d:c7:4a:dd:a8:65:69:b8:
+                    16:a1:8a:04:a2:44:68:40:b6:99:ae:61:df:9f:6c:
+                    40:ef:79:c9:a3:6d:e4:2d:07:01:68:f1:21:4e:0e:
+                    28:a7:fd:2f:ad:ee:7d:65:cf:36:fd:4f:1b:ba:10:
+                    8e:86:fd:ec:37:67:0c:20:71:66:48:64:f3:82:af:
+                    f5:e1:73:c9:09:36:03:3f:c2:47:7a:f2:33:b9:f9:
+                    9f:53:9b:24:5e:c3:cc:05:d9:a9:ed:d7:b2:2a:c5:
+                    b7:39
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                C9:45:0B:2A:F2:D8:8D:2A:D7:CE:AF:56:BF:82:B0:84:0C:C8:2E:F4
+            X509v3 Authority Key Identifier: 
+                keyid:B1:39:79:13:35:D0:03:6B:E9:C4:63:2B:CC:D6:61:C3:82:EC:14:C1
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         63:66:9e:6c:34:8c:5d:74:ae:90:25:55:ae:86:49:b9:3d:fd:
+         27:bc:4f:69:7b:70:cb:25:0e:a3:8c:7a:7d:9c:4f:0b:7c:f2:
+         85:a5:ea:82:d2:37:c2:74:a2:ae:a8:bf:62:f4:5f:d4:c6:41:
+         45:0c:cc:27:53:aa:8f:66:58:e9:b0:de:ae:98:14:bd:92:df:
+         9b:0f:f2:c5:3b:d2:bc:1c:3e:80:b4:09:0f:c1:9f:d6:3a:29:
+         52:71:b6:1a:92:95:5a:18:dc:b4:30:dc:61:61:93:54:d1:55:
+         83:92:5d:c0:c7:dc:ab:d7:08:dd:8a:44:cf:92:f9:4d:86:25:
+         aa:ac:52:f6:0e:17:99:0b:31:d2:75:5e:33:f9:f5:b6:77:42:
+         07:62:a9:53:cc:f3:79:84:57:d9:14:3f:ab:4c:8b:ae:c7:9f:
+         cf:7a:1f:bf:7e:1d:44:bd:76:b4:cd:8d:c8:1d:75:f7:3b:b5:
+         bc:35:8b:3f:29:b1:cb:67:a4:17:af:a4:ca:9f:2b:e7:15:66:
+         e4:c8:c1:7c:08:78:9e:5d:4b:c3:c6:58:66:96:42:e8:e6:40:
+         fd:dc:24:ce:3b:58:11:38:40:0e:fc:a9:c0:2c:0f:e5:cc:bb:
+         02:32:31:b9:bc:6f:2d:1d:f6:2b:7c:d3:f8:24:f6:60:38:8a:
+         1f:dd:e1:50
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+EhxI
+5HMfXNJUqXtYHDdzwkkmPu21a1UXyUxSNM7ZdoYydHSuEbKZG1GgM0g0L7nTKwbC
+XClTNc58pmeyatQzwxNiMKFTX0V4W7tHrQekmJrp47E75jPCwVyV18i5pnInenna
+xMhaGj5eXqZiZMZyhrF4mFtjJ3AVBGuxDxGcTTtc543AvtWERmy9ER4hwYKc0Kot
+L/gq6TvkNRVtx0rdqGVpuBahigSiRGhAtpmuYd+fbEDvecmjbeQtBwFo8SFODiin
+/S+t7n1lzzb9Txu6EI6G/ew3ZwwgcWZIZPOCr/Xhc8kJNgM/wkd68jO5+Z9TmyRe
+w8wF2ant17Iqxbc5AgMBAAGjgekwgeYwHQYDVR0OBBYEFMlFCyry2I0q186vVr+C
+sIQMyC70MB8GA1UdIwQYMBaAFLE5eRM10ANr6cRjK8zWYcOC7BTBMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAY2aebDSMXXSukCVVroZJ
+uT39J7xPaXtwyyUOo4x6fZxPC3zyhaXqgtI3wnSirqi/YvRf1MZBRQzMJ1Oqj2ZY
+6bDerpgUvZLfmw/yxTvSvBw+gLQJD8Gf1jopUnG2GpKVWhjctDDcYWGTVNFVg5Jd
+wMfcq9cI3YpEz5L5TYYlqqxS9g4XmQsx0nVeM/n1tndCB2KpU8zzeYRX2RQ/q0yL
+rsefz3ofv34dRL12tM2NyB119zu1vDWLPymxy2ekF6+kyp8r5xVm5MjBfAh4nl1L
+w8ZYZpZC6OZA/dwkzjtYEThADvypwCwP5cy7AjIxubxvLR32K3zT+CT2YDiKH93h
+UA==
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  2 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:9c:d0:2e:3b:06:d6:ea:65:bd:dd:b2:d3:e8:88:
+                    ea:08:73:01:42:ac:ca:38:28:17:32:93:5e:16:a8:
+                    c1:79:44:9a:db:24:08:ba:81:52:63:9c:b4:ed:57:
+                    d4:b2:ac:54:64:3b:70:39:7e:37:da:11:e1:8c:ba:
+                    09:bc:1a:9b:e7:fe:6d:75:f8:71:31:f0:ca:52:89:
+                    2a:9e:d5:53:db:b8:c0:76:cf:bf:58:58:e1:bb:81:
+                    de:62:bb:06:58:1f:9b:64:03:75:7d:ee:76:6f:39:
+                    47:cb:8e:34:32:07:83:89:b0:83:2a:78:d0:ac:e2:
+                    86:0a:a8:ab:3b:97:81:de:9d:36:b4:03:b7:d5:06:
+                    05:53:d7:80:03:44:86:53:72:db:7a:5f:c5:20:dd:
+                    c7:44:58:3b:40:7f:0e:39:bc:be:0d:ca:6a:f6:82:
+                    a2:97:a2:17:79:51:6f:42:5d:0d:6a:b7:a0:de:5f:
+                    6a:00:be:e7:5a:b7:91:e9:fc:77:fd:75:88:8d:52:
+                    76:3d:0e:91:4b:c7:db:96:a4:5f:39:59:55:62:65:
+                    3b:15:7a:bc:7b:09:9f:3e:75:d9:9e:c5:00:b3:19:
+                    d4:26:7e:eb:db:62:07:c2:f5:b6:4e:87:2d:eb:56:
+                    8b:5a:68:6c:85:2f:b4:3e:1d:dd:5d:31:49:98:8b:
+                    06:55
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                B1:39:79:13:35:D0:03:6B:E9:C4:63:2B:CC:D6:61:C3:82:EC:14:C1
+            X509v3 Authority Key Identifier: 
+                keyid:15:9E:A6:AD:F5:9F:8A:A1:C1:08:99:BF:66:6F:CF:CA:72:CD:0C:34
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         a8:ad:48:d8:6e:1d:24:09:d2:b3:29:3c:48:60:27:7f:37:64:
+         d5:f1:3b:b3:5c:43:de:7c:b4:5f:ee:3a:f2:1a:25:24:0a:8a:
+         25:6d:19:5b:dc:0c:4e:48:61:2f:60:d3:6b:f3:9c:03:2c:d3:
+         fa:c8:9b:99:e7:2e:c5:43:c0:5f:14:cd:8b:92:62:4f:e5:3a:
+         cd:b5:0a:d8:b2:01:c7:44:b4:3a:86:66:bf:fa:11:a5:f8:24:
+         3f:d1:1a:e8:eb:1e:ad:f0:70:31:6f:bc:21:cf:db:ce:63:4e:
+         84:e9:52:9e:bb:1b:c4:72:ae:e0:6c:88:9e:99:dc:79:d9:fd:
+         83:26:8e:f6:19:70:d9:5e:fc:f4:0c:d1:17:6f:af:10:f6:64:
+         16:08:d8:72:ba:3a:2d:66:28:5a:41:0d:f3:47:87:a7:9c:78:
+         c6:cd:5e:25:71:0b:f2:93:b8:26:17:b2:19:17:cc:03:ed:c0:
+         6e:06:e2:4b:4a:57:5f:23:02:2a:69:06:7a:c4:b7:3e:2f:e8:
+         f0:03:ae:b8:2d:df:63:22:20:73:23:75:d9:3c:d7:22:e4:b4:
+         65:65:ed:b9:e6:02:1d:b5:51:11:9c:db:92:e4:fe:8c:1d:bb:
+         c5:95:87:5b:38:ee:ff:e4:01:d1:5d:84:b9:73:d3:da:23:ca:
+         5e:05:d3:7d
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMjEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNAuOwbW
+6mW93bLT6IjqCHMBQqzKOCgXMpNeFqjBeUSa2yQIuoFSY5y07VfUsqxUZDtwOX43
+2hHhjLoJvBqb5/5tdfhxMfDKUokqntVT27jAds+/WFjhu4HeYrsGWB+bZAN1fe52
+bzlHy440MgeDibCDKnjQrOKGCqirO5eB3p02tAO31QYFU9eAA0SGU3Lbel/FIN3H
+RFg7QH8OOby+Dcpq9oKil6IXeVFvQl0Nareg3l9qAL7nWreR6fx3/XWIjVJ2PQ6R
+S8fblqRfOVlVYmU7FXq8ewmfPnXZnsUAsxnUJn7r22IHwvW2Toct61aLWmhshS+0
+Ph3dXTFJmIsGVQIDAQABo4HLMIHIMB0GA1UdDgQWBBSxOXkTNdADa+nEYyvM1mHD
+guwUwTAfBgNVHSMEGDAWgBQVnqat9Z+KocEImb9mb8/Kcs0MNDA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AKitSNhuHSQJ0rMpPEhgJ383ZNXxO7NcQ958tF/uOvIaJSQKiiVtGVvcDE5IYS9g
+02vznAMs0/rIm5nnLsVDwF8UzYuSYk/lOs21CtiyAcdEtDqGZr/6EaX4JD/RGujr
+Hq3wcDFvvCHP285jToTpUp67G8RyruBsiJ6Z3HnZ/YMmjvYZcNle/PQM0RdvrxD2
+ZBYI2HK6Oi1mKFpBDfNHh6eceMbNXiVxC/KTuCYXshkXzAPtwG4G4ktKV18jAipp
+BnrEtz4v6PADrrgt32MiIHMjddk81yLktGVl7bnmAh21URGc25Lk/owdu8WVh1s4
+7v/kAdFdhLlz09ojyl4F030=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  2 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:ae:ea:3b:3f:b6:e9:3d:ea:eb:3d:dd:e8:4d:45:
+                    83:63:78:ea:07:90:3a:3c:4f:92:54:2b:2d:02:1b:
+                    eb:9e:81:72:68:2e:73:f8:4a:a1:de:0c:d6:f0:c2:
+                    61:26:90:0b:48:59:ab:23:25:8f:e4:4a:6b:c9:2d:
+                    ba:a7:35:c4:22:df:76:99:d8:7b:f7:6d:ca:9b:da:
+                    d2:ed:7e:c8:93:b2:a7:f6:f0:05:6a:5d:c6:e1:79:
+                    d0:25:59:a9:50:1e:65:eb:1c:c9:cd:4e:6a:3a:2a:
+                    a4:1a:fa:81:a3:e7:ae:d7:de:43:d9:e8:0b:5c:b0:
+                    6b:46:39:c5:9c:4a:6d:59:bf:da:70:2e:80:ac:c8:
+                    80:e3:83:d1:71:7b:a7:0b:92:bf:a8:81:ad:5c:b2:
+                    d5:e9:b9:5f:b5:4f:93:43:67:72:36:b3:f7:17:b9:
+                    1b:da:2a:13:83:70:36:ae:59:03:3d:f0:71:de:a2:
+                    7a:41:ad:b5:e9:a2:51:e4:18:ec:88:ad:48:f1:df:
+                    17:04:43:54:2a:af:3c:c0:f5:84:39:43:d1:a7:d2:
+                    52:0f:3c:dd:ef:13:58:8c:1d:d4:dd:2e:6d:1a:e7:
+                    73:9b:8b:f3:41:7b:9a:53:4e:0d:92:d3:5d:3f:fc:
+                    c3:61:dc:5f:a0:93:3c:08:cc:b4:9b:ce:9d:78:e3:
+                    77:c9
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                15:9E:A6:AD:F5:9F:8A:A1:C1:08:99:BF:66:6F:CF:CA:72:CD:0C:34
+            X509v3 Authority Key Identifier: 
+                keyid:15:9E:A6:AD:F5:9F:8A:A1:C1:08:99:BF:66:6F:CF:CA:72:CD:0C:34
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         58:4b:60:7e:0b:c2:3d:20:f3:49:34:91:55:d3:5f:5b:ce:81:
+         31:da:71:9f:72:d7:f4:cc:be:cf:58:b8:97:aa:99:87:3d:34:
+         12:ac:40:1c:8e:02:46:83:ee:04:e1:7e:5e:57:ca:a4:ae:e1:
+         d0:b4:12:e9:65:33:f5:4e:c4:f6:49:00:7f:83:6d:75:67:84:
+         b2:db:52:5a:a2:3e:5d:2d:5c:f3:45:fe:6a:d3:c4:0a:76:52:
+         c6:9b:b0:89:01:b9:b6:be:30:60:d9:b4:2e:1d:1e:bf:ef:d8:
+         12:90:9f:cb:67:29:20:61:9f:1a:67:64:88:4c:43:ec:10:7d:
+         87:11:00:44:6a:ce:37:af:73:f4:fa:d2:22:2f:24:3a:6f:79:
+         09:6c:8d:de:b5:71:0e:6e:b7:64:a9:be:73:a8:c1:c8:50:74:
+         d2:c4:2b:ef:4f:25:20:8f:41:f0:1f:6e:52:77:eb:a0:1a:94:
+         87:7c:35:11:37:5c:33:f5:83:47:e0:f2:0e:97:af:23:61:23:
+         25:0b:92:6c:3b:30:a1:aa:c6:dc:4a:05:6e:43:76:58:82:66:
+         cd:f6:d6:ef:9b:80:36:d6:95:b7:d1:ec:5c:53:f7:78:84:ef:
+         48:6a:2c:f7:93:97:f2:7a:ce:ec:f3:eb:63:e1:5a:e0:69:02:
+         5d:34:36:93
+-----BEGIN TRUSTED_CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMjEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7qOz+26T3q6z3d6E1F
+g2N46geQOjxPklQrLQIb656Bcmguc/hKod4M1vDCYSaQC0hZqyMlj+RKa8ktuqc1
+xCLfdpnYe/dtypva0u1+yJOyp/bwBWpdxuF50CVZqVAeZescyc1OajoqpBr6gaPn
+rtfeQ9noC1ywa0Y5xZxKbVm/2nAugKzIgOOD0XF7pwuSv6iBrVyy1em5X7VPk0Nn
+cjaz9xe5G9oqE4NwNq5ZAz3wcd6iekGttemiUeQY7IitSPHfFwRDVCqvPMD1hDlD
+0afSUg883e8TWIwd1N0ubRrnc5uL80F7mlNODZLTXT/8w2HcX6CTPAjMtJvOnXjj
+d8kCAwEAAaOByzCByDAdBgNVHQ4EFgQUFZ6mrfWfiqHBCJm/Zm/PynLNDDQwHwYD
+VR0jBBgwFoAUFZ6mrfWfiqHBCJm/Zm/PynLNDDQwNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBYS2B+C8I9
+IPNJNJFV019bzoEx2nGfctf0zL7PWLiXqpmHPTQSrEAcjgJGg+4E4X5eV8qkruHQ
+tBLpZTP1TsT2SQB/g211Z4Sy21Jaoj5dLVzzRf5q08QKdlLGm7CJAbm2vjBg2bQu
+HR6/79gSkJ/LZykgYZ8aZ2SITEPsEH2HEQBEas43r3P0+tIiLyQ6b3kJbI3etXEO
+brdkqb5zqMHIUHTSxCvvTyUgj0HwH25Sd+ugGpSHfDURN1wz9YNH4PIOl68jYSMl
+C5JsOzChqsbcSgVuQ3ZYgmbN9tbvm4A21pW30excU/d4hO9Iaiz3k5fyes7s8+tj
+4VrgaQJdNDaT
+-----END TRUSTED_CERTIFICATE-----
+
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+-----BEGIN VERIFY_RESULT-----
+U1VDQ0VTUw==
+-----END VERIFY_RESULT-----
diff --git a/net/data/verify_certificate_chain_unittest/key-rollover-oldchain.pem b/net/data/verify_certificate_chain_unittest/key-rollover-oldchain.pem
new file mode 100644
index 0000000..6669c2a
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/key-rollover-oldchain.pem
@@ -0,0 +1,311 @@
+[Created by: ./generate-key-rollover.py]
+
+A certificate tree with two self-signed root certificates(oldroot, newroot),
+and a third root certificate (newrootrollover) which has the same key as newroot
+but is signed by oldroot, all with the same subject and issuer.
+There are two intermediates with the same key, subject and issuer
+(oldintermediate signed by oldroot, and newintermediate signed by newroot).
+The target certificate is signed by the intermediate key.
+
+
+In graphical form:
+
+   oldroot-------->newrootrollover  newroot
+      |                      |        |
+      v                      v        v
+oldintermediate           newintermediate
+      |                          |
+      +------------+-------------+
+                   |
+                   v
+                 target
+
+
+Several chains are output:
+  key-rollover-oldchain.pem:
+    target<-oldintermediate<-oldroot
+  key-rollover-rolloverchain.pem:
+    target<-newintermediate<-newrootrollover<-oldroot
+  key-rollover-longrolloverchain.pem:
+    target<-newintermediate<-newroot<-newrootrollover<-oldroot
+  key-rollover-newchain.pem:
+    target<-newintermediate<-newroot
+
+All of these chains should verify successfully.
+
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:be:12:1c:48:e4:73:1f:5c:d2:54:a9:7b:58:1c:
+                    37:73:c2:49:26:3e:ed:b5:6b:55:17:c9:4c:52:34:
+                    ce:d9:76:86:32:74:74:ae:11:b2:99:1b:51:a0:33:
+                    48:34:2f:b9:d3:2b:06:c2:5c:29:53:35:ce:7c:a6:
+                    67:b2:6a:d4:33:c3:13:62:30:a1:53:5f:45:78:5b:
+                    bb:47:ad:07:a4:98:9a:e9:e3:b1:3b:e6:33:c2:c1:
+                    5c:95:d7:c8:b9:a6:72:27:7a:79:da:c4:c8:5a:1a:
+                    3e:5e:5e:a6:62:64:c6:72:86:b1:78:98:5b:63:27:
+                    70:15:04:6b:b1:0f:11:9c:4d:3b:5c:e7:8d:c0:be:
+                    d5:84:46:6c:bd:11:1e:21:c1:82:9c:d0:aa:2d:2f:
+                    f8:2a:e9:3b:e4:35:15:6d:c7:4a:dd:a8:65:69:b8:
+                    16:a1:8a:04:a2:44:68:40:b6:99:ae:61:df:9f:6c:
+                    40:ef:79:c9:a3:6d:e4:2d:07:01:68:f1:21:4e:0e:
+                    28:a7:fd:2f:ad:ee:7d:65:cf:36:fd:4f:1b:ba:10:
+                    8e:86:fd:ec:37:67:0c:20:71:66:48:64:f3:82:af:
+                    f5:e1:73:c9:09:36:03:3f:c2:47:7a:f2:33:b9:f9:
+                    9f:53:9b:24:5e:c3:cc:05:d9:a9:ed:d7:b2:2a:c5:
+                    b7:39
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                C9:45:0B:2A:F2:D8:8D:2A:D7:CE:AF:56:BF:82:B0:84:0C:C8:2E:F4
+            X509v3 Authority Key Identifier: 
+                keyid:B1:39:79:13:35:D0:03:6B:E9:C4:63:2B:CC:D6:61:C3:82:EC:14:C1
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         63:66:9e:6c:34:8c:5d:74:ae:90:25:55:ae:86:49:b9:3d:fd:
+         27:bc:4f:69:7b:70:cb:25:0e:a3:8c:7a:7d:9c:4f:0b:7c:f2:
+         85:a5:ea:82:d2:37:c2:74:a2:ae:a8:bf:62:f4:5f:d4:c6:41:
+         45:0c:cc:27:53:aa:8f:66:58:e9:b0:de:ae:98:14:bd:92:df:
+         9b:0f:f2:c5:3b:d2:bc:1c:3e:80:b4:09:0f:c1:9f:d6:3a:29:
+         52:71:b6:1a:92:95:5a:18:dc:b4:30:dc:61:61:93:54:d1:55:
+         83:92:5d:c0:c7:dc:ab:d7:08:dd:8a:44:cf:92:f9:4d:86:25:
+         aa:ac:52:f6:0e:17:99:0b:31:d2:75:5e:33:f9:f5:b6:77:42:
+         07:62:a9:53:cc:f3:79:84:57:d9:14:3f:ab:4c:8b:ae:c7:9f:
+         cf:7a:1f:bf:7e:1d:44:bd:76:b4:cd:8d:c8:1d:75:f7:3b:b5:
+         bc:35:8b:3f:29:b1:cb:67:a4:17:af:a4:ca:9f:2b:e7:15:66:
+         e4:c8:c1:7c:08:78:9e:5d:4b:c3:c6:58:66:96:42:e8:e6:40:
+         fd:dc:24:ce:3b:58:11:38:40:0e:fc:a9:c0:2c:0f:e5:cc:bb:
+         02:32:31:b9:bc:6f:2d:1d:f6:2b:7c:d3:f8:24:f6:60:38:8a:
+         1f:dd:e1:50
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+EhxI
+5HMfXNJUqXtYHDdzwkkmPu21a1UXyUxSNM7ZdoYydHSuEbKZG1GgM0g0L7nTKwbC
+XClTNc58pmeyatQzwxNiMKFTX0V4W7tHrQekmJrp47E75jPCwVyV18i5pnInenna
+xMhaGj5eXqZiZMZyhrF4mFtjJ3AVBGuxDxGcTTtc543AvtWERmy9ER4hwYKc0Kot
+L/gq6TvkNRVtx0rdqGVpuBahigSiRGhAtpmuYd+fbEDvecmjbeQtBwFo8SFODiin
+/S+t7n1lzzb9Txu6EI6G/ew3ZwwgcWZIZPOCr/Xhc8kJNgM/wkd68jO5+Z9TmyRe
+w8wF2ant17Iqxbc5AgMBAAGjgekwgeYwHQYDVR0OBBYEFMlFCyry2I0q186vVr+C
+sIQMyC70MB8GA1UdIwQYMBaAFLE5eRM10ANr6cRjK8zWYcOC7BTBMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAY2aebDSMXXSukCVVroZJ
+uT39J7xPaXtwyyUOo4x6fZxPC3zyhaXqgtI3wnSirqi/YvRf1MZBRQzMJ1Oqj2ZY
+6bDerpgUvZLfmw/yxTvSvBw+gLQJD8Gf1jopUnG2GpKVWhjctDDcYWGTVNFVg5Jd
+wMfcq9cI3YpEz5L5TYYlqqxS9g4XmQsx0nVeM/n1tndCB2KpU8zzeYRX2RQ/q0yL
+rsefz3ofv34dRL12tM2NyB119zu1vDWLPymxy2ekF6+kyp8r5xVm5MjBfAh4nl1L
+w8ZYZpZC6OZA/dwkzjtYEThADvypwCwP5cy7AjIxubxvLR32K3zT+CT2YDiKH93h
+UA==
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:9c:d0:2e:3b:06:d6:ea:65:bd:dd:b2:d3:e8:88:
+                    ea:08:73:01:42:ac:ca:38:28:17:32:93:5e:16:a8:
+                    c1:79:44:9a:db:24:08:ba:81:52:63:9c:b4:ed:57:
+                    d4:b2:ac:54:64:3b:70:39:7e:37:da:11:e1:8c:ba:
+                    09:bc:1a:9b:e7:fe:6d:75:f8:71:31:f0:ca:52:89:
+                    2a:9e:d5:53:db:b8:c0:76:cf:bf:58:58:e1:bb:81:
+                    de:62:bb:06:58:1f:9b:64:03:75:7d:ee:76:6f:39:
+                    47:cb:8e:34:32:07:83:89:b0:83:2a:78:d0:ac:e2:
+                    86:0a:a8:ab:3b:97:81:de:9d:36:b4:03:b7:d5:06:
+                    05:53:d7:80:03:44:86:53:72:db:7a:5f:c5:20:dd:
+                    c7:44:58:3b:40:7f:0e:39:bc:be:0d:ca:6a:f6:82:
+                    a2:97:a2:17:79:51:6f:42:5d:0d:6a:b7:a0:de:5f:
+                    6a:00:be:e7:5a:b7:91:e9:fc:77:fd:75:88:8d:52:
+                    76:3d:0e:91:4b:c7:db:96:a4:5f:39:59:55:62:65:
+                    3b:15:7a:bc:7b:09:9f:3e:75:d9:9e:c5:00:b3:19:
+                    d4:26:7e:eb:db:62:07:c2:f5:b6:4e:87:2d:eb:56:
+                    8b:5a:68:6c:85:2f:b4:3e:1d:dd:5d:31:49:98:8b:
+                    06:55
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                B1:39:79:13:35:D0:03:6B:E9:C4:63:2B:CC:D6:61:C3:82:EC:14:C1
+            X509v3 Authority Key Identifier: 
+                keyid:02:CE:F6:AC:1A:39:1E:85:E8:72:D1:8A:C6:1D:E8:7A:8F:9D:15:6B
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         56:90:dd:ae:50:4b:18:56:7f:e9:1c:e4:29:7c:60:7e:87:27:
+         b5:cd:2b:00:67:2b:f7:a2:32:db:38:92:18:46:00:22:c4:2d:
+         1f:ff:32:5f:62:69:30:db:c8:aa:63:68:6a:0f:db:2d:13:15:
+         8f:74:22:54:67:fb:95:19:35:49:66:1e:a1:65:67:3c:71:02:
+         85:34:f7:3b:20:51:03:e9:a1:b9:7d:9b:3b:21:d4:7a:28:6e:
+         bd:01:50:a6:7e:2b:a0:bc:ad:d5:55:63:3d:04:f3:f0:aa:c3:
+         4e:b3:aa:b8:68:89:a0:8c:b7:9e:38:37:81:2c:01:57:93:7c:
+         3f:aa:54:2e:35:66:a4:c0:81:ca:06:5c:c6:ab:72:f4:38:08:
+         cd:60:40:53:42:48:71:0a:e7:f5:82:3c:bb:51:89:a4:8b:1e:
+         c9:44:4c:7f:10:7d:d3:f4:60:04:07:36:b2:44:82:b2:0a:3c:
+         e9:82:55:4a:37:cf:47:b6:d3:ea:e5:fc:b8:49:4c:6b:77:a6:
+         42:29:1c:1f:97:78:18:f7:88:da:52:42:87:19:e4:13:0d:91:
+         73:cc:98:6a:22:70:e2:5c:54:d0:96:b7:94:37:3c:0f:f6:9a:
+         e3:02:fb:95:63:5a:87:d4:0d:16:16:bd:95:fa:88:c3:a8:e4:
+         d0:91:dd:be
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNAuOwbW
+6mW93bLT6IjqCHMBQqzKOCgXMpNeFqjBeUSa2yQIuoFSY5y07VfUsqxUZDtwOX43
+2hHhjLoJvBqb5/5tdfhxMfDKUokqntVT27jAds+/WFjhu4HeYrsGWB+bZAN1fe52
+bzlHy440MgeDibCDKnjQrOKGCqirO5eB3p02tAO31QYFU9eAA0SGU3Lbel/FIN3H
+RFg7QH8OOby+Dcpq9oKil6IXeVFvQl0Nareg3l9qAL7nWreR6fx3/XWIjVJ2PQ6R
+S8fblqRfOVlVYmU7FXq8ewmfPnXZnsUAsxnUJn7r22IHwvW2Toct61aLWmhshS+0
+Ph3dXTFJmIsGVQIDAQABo4HLMIHIMB0GA1UdDgQWBBSxOXkTNdADa+nEYyvM1mHD
+guwUwTAfBgNVHSMEGDAWgBQCzvasGjkehehy0YrGHeh6j50VazA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AFaQ3a5QSxhWf+kc5Cl8YH6HJ7XNKwBnK/eiMts4khhGACLELR//Ml9iaTDbyKpj
+aGoP2y0TFY90IlRn+5UZNUlmHqFlZzxxAoU09zsgUQPpobl9mzsh1Hoobr0BUKZ+
+K6C8rdVVYz0E8/Cqw06zqrhoiaCMt544N4EsAVeTfD+qVC41ZqTAgcoGXMarcvQ4
+CM1gQFNCSHEK5/WCPLtRiaSLHslETH8QfdP0YAQHNrJEgrIKPOmCVUo3z0e20+rl
+/LhJTGt3pkIpHB+XeBj3iNpSQocZ5BMNkXPMmGoicOJcVNCWt5Q3PA/2muMC+5Vj
+WofUDRYWvZX6iMOo5NCR3b4=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:a6:44:ec:a7:15:00:1f:89:ac:91:f8:ec:7b:03:
+                    46:0b:53:15:ed:23:40:35:94:f3:96:80:27:d3:4a:
+                    84:92:68:c9:0c:e0:14:32:c7:31:67:49:29:58:77:
+                    ea:ce:8a:72:5b:93:b1:a0:a8:e5:84:c6:52:9d:5a:
+                    c0:41:bf:98:5f:18:5d:aa:d1:65:79:fb:e9:b0:84:
+                    92:9d:2c:58:bc:f1:c4:29:59:ed:bc:ac:85:ce:d7:
+                    0e:aa:08:e8:2d:90:25:cb:91:9d:7d:91:74:42:a0:
+                    ae:77:d2:11:7b:57:49:04:24:c0:94:f4:20:54:60:
+                    d9:1b:76:76:0b:2c:23:3c:67:90:8c:06:ed:4e:df:
+                    ac:24:22:26:f7:26:8f:5a:d2:5b:79:8a:6f:6e:53:
+                    27:60:10:cb:c7:b4:9f:60:2d:8f:32:69:4b:01:d1:
+                    f0:6d:69:1a:22:14:06:66:63:97:e8:fc:79:41:8d:
+                    15:44:44:d1:43:2a:37:5e:77:e4:06:e6:a9:85:13:
+                    e9:24:63:9d:09:d0:f5:13:d5:ba:59:2e:1c:d2:70:
+                    06:b1:80:f7:57:d7:30:f7:14:f3:18:06:7f:84:38:
+                    b6:81:46:9f:a2:36:87:0e:5f:1a:45:38:b7:20:16:
+                    b7:c6:e1:91:3b:0e:0c:ab:b7:4e:3d:a4:6d:66:d8:
+                    85:fb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                02:CE:F6:AC:1A:39:1E:85:E8:72:D1:8A:C6:1D:E8:7A:8F:9D:15:6B
+            X509v3 Authority Key Identifier: 
+                keyid:02:CE:F6:AC:1A:39:1E:85:E8:72:D1:8A:C6:1D:E8:7A:8F:9D:15:6B
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         0e:00:56:5c:72:31:88:bd:95:13:f7:64:96:1c:63:c2:1c:11:
+         60:04:d2:c3:5f:7c:a2:d7:d1:33:6d:51:6b:77:61:78:a8:70:
+         2e:50:97:5d:c1:e8:9b:dd:c6:61:a7:d3:e1:2c:83:07:85:5c:
+         c9:d7:1e:22:c2:5f:76:83:19:d7:de:4a:5e:82:0f:43:80:45:
+         02:d7:d0:3d:ca:c3:c0:fc:04:c8:f6:89:32:d7:47:c6:bf:1f:
+         c6:bd:71:e1:07:00:90:12:ec:61:63:1b:6c:e9:58:2c:fc:4c:
+         a9:8f:58:e1:b1:6e:a5:ca:4d:be:7e:32:16:74:5f:fd:35:e4:
+         37:aa:1a:c5:33:21:20:8a:3e:1c:af:da:f3:c7:a2:22:d3:93:
+         6c:5e:ac:0a:65:d5:db:e4:8b:11:5e:ca:eb:8f:da:c4:5d:2f:
+         7a:98:e8:3c:d1:89:15:05:02:86:ef:eb:17:18:81:28:ca:d6:
+         58:87:bd:d4:e2:50:41:92:d9:7f:b1:f7:53:8f:f3:cc:f3:1e:
+         1d:e4:5a:c2:60:1b:17:42:78:53:e9:2d:5d:bb:f9:21:50:ff:
+         87:53:be:5f:e6:d4:8f:25:7f:d7:83:d7:f8:4d:c1:7c:7a:40:
+         0b:11:f1:d9:c6:eb:97:45:00:d6:6b:84:1c:4f:fc:8e:1f:5b:
+         b5:3d:60:0c
+-----BEGIN TRUSTED_CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKZE7KcVAB+JrJH47HsD
+RgtTFe0jQDWU85aAJ9NKhJJoyQzgFDLHMWdJKVh36s6KcluTsaCo5YTGUp1awEG/
+mF8YXarRZXn76bCEkp0sWLzxxClZ7byshc7XDqoI6C2QJcuRnX2RdEKgrnfSEXtX
+SQQkwJT0IFRg2Rt2dgssIzxnkIwG7U7frCQiJvcmj1rSW3mKb25TJ2AQy8e0n2At
+jzJpSwHR8G1pGiIUBmZjl+j8eUGNFURE0UMqN1535AbmqYUT6SRjnQnQ9RPVulku
+HNJwBrGA91fXMPcU8xgGf4Q4toFGn6I2hw5fGkU4tyAWt8bhkTsODKu3Tj2kbWbY
+hfsCAwEAAaOByzCByDAdBgNVHQ4EFgQUAs72rBo5HoXoctGKxh3oeo+dFWswHwYD
+VR0jBBgwFoAUAs72rBo5HoXoctGKxh3oeo+dFWswNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAOAFZccjGI
+vZUT92SWHGPCHBFgBNLDX3yi19EzbVFrd2F4qHAuUJddweib3cZhp9PhLIMHhVzJ
+1x4iwl92gxnX3kpegg9DgEUC19A9ysPA/ATI9oky10fGvx/GvXHhBwCQEuxhYxts
+6Vgs/Eypj1jhsW6lyk2+fjIWdF/9NeQ3qhrFMyEgij4cr9rzx6Ii05NsXqwKZdXb
+5IsRXsrrj9rEXS96mOg80YkVBQKG7+sXGIEoytZYh73U4lBBktl/sfdTj/PM8x4d
+5FrCYBsXQnhT6S1du/khUP+HU75f5tSPJX/Xg9f4TcF8ekALEfHZxuuXRQDWa4Qc
+T/yOH1u1PWAM
+-----END TRUSTED_CERTIFICATE-----
+
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+-----BEGIN VERIFY_RESULT-----
+U1VDQ0VTUw==
+-----END VERIFY_RESULT-----
diff --git a/net/data/verify_certificate_chain_unittest/key-rollover-rolloverchain.pem b/net/data/verify_certificate_chain_unittest/key-rollover-rolloverchain.pem
new file mode 100644
index 0000000..94105ff
--- /dev/null
+++ b/net/data/verify_certificate_chain_unittest/key-rollover-rolloverchain.pem
@@ -0,0 +1,400 @@
+[Created by: ./generate-key-rollover.py]
+
+A certificate tree with two self-signed root certificates(oldroot, newroot),
+and a third root certificate (newrootrollover) which has the same key as newroot
+but is signed by oldroot, all with the same subject and issuer.
+There are two intermediates with the same key, subject and issuer
+(oldintermediate signed by oldroot, and newintermediate signed by newroot).
+The target certificate is signed by the intermediate key.
+
+
+In graphical form:
+
+   oldroot-------->newrootrollover  newroot
+      |                      |        |
+      v                      v        v
+oldintermediate           newintermediate
+      |                          |
+      +------------+-------------+
+                   |
+                   v
+                 target
+
+
+Several chains are output:
+  key-rollover-oldchain.pem:
+    target<-oldintermediate<-oldroot
+  key-rollover-rolloverchain.pem:
+    target<-newintermediate<-newrootrollover<-oldroot
+  key-rollover-longrolloverchain.pem:
+    target<-newintermediate<-newroot<-newrootrollover<-oldroot
+  key-rollover-newchain.pem:
+    target<-newintermediate<-newroot
+
+All of these chains should verify successfully.
+
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Intermediate
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Target
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:be:12:1c:48:e4:73:1f:5c:d2:54:a9:7b:58:1c:
+                    37:73:c2:49:26:3e:ed:b5:6b:55:17:c9:4c:52:34:
+                    ce:d9:76:86:32:74:74:ae:11:b2:99:1b:51:a0:33:
+                    48:34:2f:b9:d3:2b:06:c2:5c:29:53:35:ce:7c:a6:
+                    67:b2:6a:d4:33:c3:13:62:30:a1:53:5f:45:78:5b:
+                    bb:47:ad:07:a4:98:9a:e9:e3:b1:3b:e6:33:c2:c1:
+                    5c:95:d7:c8:b9:a6:72:27:7a:79:da:c4:c8:5a:1a:
+                    3e:5e:5e:a6:62:64:c6:72:86:b1:78:98:5b:63:27:
+                    70:15:04:6b:b1:0f:11:9c:4d:3b:5c:e7:8d:c0:be:
+                    d5:84:46:6c:bd:11:1e:21:c1:82:9c:d0:aa:2d:2f:
+                    f8:2a:e9:3b:e4:35:15:6d:c7:4a:dd:a8:65:69:b8:
+                    16:a1:8a:04:a2:44:68:40:b6:99:ae:61:df:9f:6c:
+                    40:ef:79:c9:a3:6d:e4:2d:07:01:68:f1:21:4e:0e:
+                    28:a7:fd:2f:ad:ee:7d:65:cf:36:fd:4f:1b:ba:10:
+                    8e:86:fd:ec:37:67:0c:20:71:66:48:64:f3:82:af:
+                    f5:e1:73:c9:09:36:03:3f:c2:47:7a:f2:33:b9:f9:
+                    9f:53:9b:24:5e:c3:cc:05:d9:a9:ed:d7:b2:2a:c5:
+                    b7:39
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                C9:45:0B:2A:F2:D8:8D:2A:D7:CE:AF:56:BF:82:B0:84:0C:C8:2E:F4
+            X509v3 Authority Key Identifier: 
+                keyid:B1:39:79:13:35:D0:03:6B:E9:C4:63:2B:CC:D6:61:C3:82:EC:14:C1
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Intermediate.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Intermediate.crl
+
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication, TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         63:66:9e:6c:34:8c:5d:74:ae:90:25:55:ae:86:49:b9:3d:fd:
+         27:bc:4f:69:7b:70:cb:25:0e:a3:8c:7a:7d:9c:4f:0b:7c:f2:
+         85:a5:ea:82:d2:37:c2:74:a2:ae:a8:bf:62:f4:5f:d4:c6:41:
+         45:0c:cc:27:53:aa:8f:66:58:e9:b0:de:ae:98:14:bd:92:df:
+         9b:0f:f2:c5:3b:d2:bc:1c:3e:80:b4:09:0f:c1:9f:d6:3a:29:
+         52:71:b6:1a:92:95:5a:18:dc:b4:30:dc:61:61:93:54:d1:55:
+         83:92:5d:c0:c7:dc:ab:d7:08:dd:8a:44:cf:92:f9:4d:86:25:
+         aa:ac:52:f6:0e:17:99:0b:31:d2:75:5e:33:f9:f5:b6:77:42:
+         07:62:a9:53:cc:f3:79:84:57:d9:14:3f:ab:4c:8b:ae:c7:9f:
+         cf:7a:1f:bf:7e:1d:44:bd:76:b4:cd:8d:c8:1d:75:f7:3b:b5:
+         bc:35:8b:3f:29:b1:cb:67:a4:17:af:a4:ca:9f:2b:e7:15:66:
+         e4:c8:c1:7c:08:78:9e:5d:4b:c3:c6:58:66:96:42:e8:e6:40:
+         fd:dc:24:ce:3b:58:11:38:40:0e:fc:a9:c0:2c:0f:e5:cc:bb:
+         02:32:31:b9:bc:6f:2d:1d:f6:2b:7c:d3:f8:24:f6:60:38:8a:
+         1f:dd:e1:50
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl
+cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD
+VQQDDAZUYXJnZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+EhxI
+5HMfXNJUqXtYHDdzwkkmPu21a1UXyUxSNM7ZdoYydHSuEbKZG1GgM0g0L7nTKwbC
+XClTNc58pmeyatQzwxNiMKFTX0V4W7tHrQekmJrp47E75jPCwVyV18i5pnInenna
+xMhaGj5eXqZiZMZyhrF4mFtjJ3AVBGuxDxGcTTtc543AvtWERmy9ER4hwYKc0Kot
+L/gq6TvkNRVtx0rdqGVpuBahigSiRGhAtpmuYd+fbEDvecmjbeQtBwFo8SFODiin
+/S+t7n1lzzb9Txu6EI6G/ew3ZwwgcWZIZPOCr/Xhc8kJNgM/wkd68jO5+Z9TmyRe
+w8wF2ant17Iqxbc5AgMBAAGjgekwgeYwHQYDVR0OBBYEFMlFCyry2I0q186vVr+C
+sIQMyC70MB8GA1UdIwQYMBaAFLE5eRM10ANr6cRjK8zWYcOC7BTBMD8GCCsGAQUF
+BwEBBDMwMTAvBggrBgEFBQcwAoYjaHR0cDovL3VybC1mb3ItYWlhL0ludGVybWVk
+aWF0ZS5jZXIwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL3VybC1mb3ItY3JsL0lu
+dGVybWVkaWF0ZS5jcmwwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAY2aebDSMXXSukCVVroZJ
+uT39J7xPaXtwyyUOo4x6fZxPC3zyhaXqgtI3wnSirqi/YvRf1MZBRQzMJ1Oqj2ZY
+6bDerpgUvZLfmw/yxTvSvBw+gLQJD8Gf1jopUnG2GpKVWhjctDDcYWGTVNFVg5Jd
+wMfcq9cI3YpEz5L5TYYlqqxS9g4XmQsx0nVeM/n1tndCB2KpU8zzeYRX2RQ/q0yL
+rsefz3ofv34dRL12tM2NyB119zu1vDWLPymxy2ekF6+kyp8r5xVm5MjBfAh4nl1L
+w8ZYZpZC6OZA/dwkzjtYEThADvypwCwP5cy7AjIxubxvLR32K3zT+CT2YDiKH93h
+UA==
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  2 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Intermediate
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:9c:d0:2e:3b:06:d6:ea:65:bd:dd:b2:d3:e8:88:
+                    ea:08:73:01:42:ac:ca:38:28:17:32:93:5e:16:a8:
+                    c1:79:44:9a:db:24:08:ba:81:52:63:9c:b4:ed:57:
+                    d4:b2:ac:54:64:3b:70:39:7e:37:da:11:e1:8c:ba:
+                    09:bc:1a:9b:e7:fe:6d:75:f8:71:31:f0:ca:52:89:
+                    2a:9e:d5:53:db:b8:c0:76:cf:bf:58:58:e1:bb:81:
+                    de:62:bb:06:58:1f:9b:64:03:75:7d:ee:76:6f:39:
+                    47:cb:8e:34:32:07:83:89:b0:83:2a:78:d0:ac:e2:
+                    86:0a:a8:ab:3b:97:81:de:9d:36:b4:03:b7:d5:06:
+                    05:53:d7:80:03:44:86:53:72:db:7a:5f:c5:20:dd:
+                    c7:44:58:3b:40:7f:0e:39:bc:be:0d:ca:6a:f6:82:
+                    a2:97:a2:17:79:51:6f:42:5d:0d:6a:b7:a0:de:5f:
+                    6a:00:be:e7:5a:b7:91:e9:fc:77:fd:75:88:8d:52:
+                    76:3d:0e:91:4b:c7:db:96:a4:5f:39:59:55:62:65:
+                    3b:15:7a:bc:7b:09:9f:3e:75:d9:9e:c5:00:b3:19:
+                    d4:26:7e:eb:db:62:07:c2:f5:b6:4e:87:2d:eb:56:
+                    8b:5a:68:6c:85:2f:b4:3e:1d:dd:5d:31:49:98:8b:
+                    06:55
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                B1:39:79:13:35:D0:03:6B:E9:C4:63:2B:CC:D6:61:C3:82:EC:14:C1
+            X509v3 Authority Key Identifier: 
+                keyid:15:9E:A6:AD:F5:9F:8A:A1:C1:08:99:BF:66:6F:CF:CA:72:CD:0C:34
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         a8:ad:48:d8:6e:1d:24:09:d2:b3:29:3c:48:60:27:7f:37:64:
+         d5:f1:3b:b3:5c:43:de:7c:b4:5f:ee:3a:f2:1a:25:24:0a:8a:
+         25:6d:19:5b:dc:0c:4e:48:61:2f:60:d3:6b:f3:9c:03:2c:d3:
+         fa:c8:9b:99:e7:2e:c5:43:c0:5f:14:cd:8b:92:62:4f:e5:3a:
+         cd:b5:0a:d8:b2:01:c7:44:b4:3a:86:66:bf:fa:11:a5:f8:24:
+         3f:d1:1a:e8:eb:1e:ad:f0:70:31:6f:bc:21:cf:db:ce:63:4e:
+         84:e9:52:9e:bb:1b:c4:72:ae:e0:6c:88:9e:99:dc:79:d9:fd:
+         83:26:8e:f6:19:70:d9:5e:fc:f4:0c:d1:17:6f:af:10:f6:64:
+         16:08:d8:72:ba:3a:2d:66:28:5a:41:0d:f3:47:87:a7:9c:78:
+         c6:cd:5e:25:71:0b:f2:93:b8:26:17:b2:19:17:cc:03:ed:c0:
+         6e:06:e2:4b:4a:57:5f:23:02:2a:69:06:7a:c4:b7:3e:2f:e8:
+         f0:03:ae:b8:2d:df:63:22:20:73:23:75:d9:3c:d7:22:e4:b4:
+         65:65:ed:b9:e6:02:1d:b5:51:11:9c:db:92:e4:fe:8c:1d:bb:
+         c5:95:87:5b:38:ee:ff:e4:01:d1:5d:84:b9:73:d3:da:23:ca:
+         5e:05:d3:7d
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMjEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50
+ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNAuOwbW
+6mW93bLT6IjqCHMBQqzKOCgXMpNeFqjBeUSa2yQIuoFSY5y07VfUsqxUZDtwOX43
+2hHhjLoJvBqb5/5tdfhxMfDKUokqntVT27jAds+/WFjhu4HeYrsGWB+bZAN1fe52
+bzlHy440MgeDibCDKnjQrOKGCqirO5eB3p02tAO31QYFU9eAA0SGU3Lbel/FIN3H
+RFg7QH8OOby+Dcpq9oKil6IXeVFvQl0Nareg3l9qAL7nWreR6fx3/XWIjVJ2PQ6R
+S8fblqRfOVlVYmU7FXq8ewmfPnXZnsUAsxnUJn7r22IHwvW2Toct61aLWmhshS+0
+Ph3dXTFJmIsGVQIDAQABo4HLMIHIMB0GA1UdDgQWBBSxOXkTNdADa+nEYyvM1mHD
+guwUwTAfBgNVHSMEGDAWgBQVnqat9Z+KocEImb9mb8/Kcs0MNDA3BggrBgEFBQcB
+AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs
+BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AKitSNhuHSQJ0rMpPEhgJ383ZNXxO7NcQ958tF/uOvIaJSQKiiVtGVvcDE5IYS9g
+02vznAMs0/rIm5nnLsVDwF8UzYuSYk/lOs21CtiyAcdEtDqGZr/6EaX4JD/RGujr
+Hq3wcDFvvCHP285jToTpUp67G8RyruBsiJ6Z3HnZ/YMmjvYZcNle/PQM0RdvrxD2
+ZBYI2HK6Oi1mKFpBDfNHh6eceMbNXiVxC/KTuCYXshkXzAPtwG4G4ktKV18jAipp
+BnrEtz4v6PADrrgt32MiIHMjddk81yLktGVl7bnmAh21URGc25Lk/owdu8WVh1s4
+7v/kAdFdhLlz09ojyl4F030=
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 3 (0x3)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  2 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:ae:ea:3b:3f:b6:e9:3d:ea:eb:3d:dd:e8:4d:45:
+                    83:63:78:ea:07:90:3a:3c:4f:92:54:2b:2d:02:1b:
+                    eb:9e:81:72:68:2e:73:f8:4a:a1:de:0c:d6:f0:c2:
+                    61:26:90:0b:48:59:ab:23:25:8f:e4:4a:6b:c9:2d:
+                    ba:a7:35:c4:22:df:76:99:d8:7b:f7:6d:ca:9b:da:
+                    d2:ed:7e:c8:93:b2:a7:f6:f0:05:6a:5d:c6:e1:79:
+                    d0:25:59:a9:50:1e:65:eb:1c:c9:cd:4e:6a:3a:2a:
+                    a4:1a:fa:81:a3:e7:ae:d7:de:43:d9:e8:0b:5c:b0:
+                    6b:46:39:c5:9c:4a:6d:59:bf:da:70:2e:80:ac:c8:
+                    80:e3:83:d1:71:7b:a7:0b:92:bf:a8:81:ad:5c:b2:
+                    d5:e9:b9:5f:b5:4f:93:43:67:72:36:b3:f7:17:b9:
+                    1b:da:2a:13:83:70:36:ae:59:03:3d:f0:71:de:a2:
+                    7a:41:ad:b5:e9:a2:51:e4:18:ec:88:ad:48:f1:df:
+                    17:04:43:54:2a:af:3c:c0:f5:84:39:43:d1:a7:d2:
+                    52:0f:3c:dd:ef:13:58:8c:1d:d4:dd:2e:6d:1a:e7:
+                    73:9b:8b:f3:41:7b:9a:53:4e:0d:92:d3:5d:3f:fc:
+                    c3:61:dc:5f:a0:93:3c:08:cc:b4:9b:ce:9d:78:e3:
+                    77:c9
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                15:9E:A6:AD:F5:9F:8A:A1:C1:08:99:BF:66:6F:CF:CA:72:CD:0C:34
+            X509v3 Authority Key Identifier: 
+                keyid:02:CE:F6:AC:1A:39:1E:85:E8:72:D1:8A:C6:1D:E8:7A:8F:9D:15:6B
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         51:9b:dd:56:f2:b1:2b:e5:36:c8:2f:1d:a9:53:1f:89:e1:24:
+         33:bd:ac:56:c1:c3:1a:38:a6:7e:fc:61:9a:ae:7c:1f:13:3e:
+         37:e4:e6:a9:11:9e:2b:6e:ce:dd:12:0c:c1:b2:b7:eb:48:0e:
+         c7:a5:65:f0:86:49:8a:dc:cf:1b:6d:33:af:af:96:51:49:01:
+         e4:82:d6:e6:5a:d0:41:c7:05:9f:16:eb:06:bd:bc:ab:fe:a0:
+         d7:ac:de:62:d1:71:7e:69:82:31:03:e3:60:28:e6:18:3b:e5:
+         93:2b:58:ee:d5:0b:7b:b6:af:f2:4f:22:eb:4d:b7:a6:74:68:
+         b7:82:68:7f:a9:b6:ee:a0:20:d7:c6:16:0e:9c:1c:39:ea:24:
+         5e:60:12:fc:39:60:0d:54:3e:aa:b3:43:e1:0f:ef:d7:8f:3e:
+         09:a9:55:95:e9:3d:0c:4f:ad:cb:c2:f3:2c:10:43:67:54:f9:
+         66:54:81:ff:62:61:94:05:b0:42:af:f0:c5:ac:00:91:28:5c:
+         aa:a3:61:44:ba:c2:a6:ab:f8:1d:7e:02:69:33:48:fe:ac:93:
+         7f:4c:99:91:d9:18:37:f9:70:3f:56:2a:ee:4a:e0:4d:f3:60:
+         12:5d:30:d8:37:bf:ca:40:85:29:0c:a7:8f:ab:ad:03:6d:7b:
+         ba:62:7f:58
+-----BEGIN CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIBAzANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMjEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7qOz+26T3q6z3d6E1F
+g2N46geQOjxPklQrLQIb656Bcmguc/hKod4M1vDCYSaQC0hZqyMlj+RKa8ktuqc1
+xCLfdpnYe/dtypva0u1+yJOyp/bwBWpdxuF50CVZqVAeZescyc1OajoqpBr6gaPn
+rtfeQ9noC1ywa0Y5xZxKbVm/2nAugKzIgOOD0XF7pwuSv6iBrVyy1em5X7VPk0Nn
+cjaz9xe5G9oqE4NwNq5ZAz3wcd6iekGttemiUeQY7IitSPHfFwRDVCqvPMD1hDlD
+0afSUg883e8TWIwd1N0ubRrnc5uL80F7mlNODZLTXT/8w2HcX6CTPAjMtJvOnXjj
+d8kCAwEAAaOByzCByDAdBgNVHQ4EFgQUFZ6mrfWfiqHBCJm/Zm/PynLNDDQwHwYD
+VR0jBBgwFoAUAs72rBo5HoXoctGKxh3oeo+dFWswNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBRm91W8rEr
+5TbILx2pUx+J4SQzvaxWwcMaOKZ+/GGarnwfEz435OapEZ4rbs7dEgzBsrfrSA7H
+pWXwhkmK3M8bbTOvr5ZRSQHkgtbmWtBBxwWfFusGvbyr/qDXrN5i0XF+aYIxA+Ng
+KOYYO+WTK1ju1Qt7tq/yTyLrTbemdGi3gmh/qbbuoCDXxhYOnBw56iReYBL8OWAN
+VD6qs0PhD+/Xjz4JqVWV6T0MT63LwvMsEENnVPlmVIH/YmGUBbBCr/DFrACRKFyq
+o2FEusKmq/gdfgJpM0j+rJN/TJmR2Rg3+XA/ViruSuBN82ASXTDYN7/KQIUpDKeP
+q60DbXu6Yn9Y
+-----END CERTIFICATE-----
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=Root
+        Validity
+            Not Before: Jan  1 12:00:00 2015 GMT
+            Not After : Jan  1 12:00:00 2016 GMT
+        Subject: CN=Root
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:a6:44:ec:a7:15:00:1f:89:ac:91:f8:ec:7b:03:
+                    46:0b:53:15:ed:23:40:35:94:f3:96:80:27:d3:4a:
+                    84:92:68:c9:0c:e0:14:32:c7:31:67:49:29:58:77:
+                    ea:ce:8a:72:5b:93:b1:a0:a8:e5:84:c6:52:9d:5a:
+                    c0:41:bf:98:5f:18:5d:aa:d1:65:79:fb:e9:b0:84:
+                    92:9d:2c:58:bc:f1:c4:29:59:ed:bc:ac:85:ce:d7:
+                    0e:aa:08:e8:2d:90:25:cb:91:9d:7d:91:74:42:a0:
+                    ae:77:d2:11:7b:57:49:04:24:c0:94:f4:20:54:60:
+                    d9:1b:76:76:0b:2c:23:3c:67:90:8c:06:ed:4e:df:
+                    ac:24:22:26:f7:26:8f:5a:d2:5b:79:8a:6f:6e:53:
+                    27:60:10:cb:c7:b4:9f:60:2d:8f:32:69:4b:01:d1:
+                    f0:6d:69:1a:22:14:06:66:63:97:e8:fc:79:41:8d:
+                    15:44:44:d1:43:2a:37:5e:77:e4:06:e6:a9:85:13:
+                    e9:24:63:9d:09:d0:f5:13:d5:ba:59:2e:1c:d2:70:
+                    06:b1:80:f7:57:d7:30:f7:14:f3:18:06:7f:84:38:
+                    b6:81:46:9f:a2:36:87:0e:5f:1a:45:38:b7:20:16:
+                    b7:c6:e1:91:3b:0e:0c:ab:b7:4e:3d:a4:6d:66:d8:
+                    85:fb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                02:CE:F6:AC:1A:39:1E:85:E8:72:D1:8A:C6:1D:E8:7A:8F:9D:15:6B
+            X509v3 Authority Key Identifier: 
+                keyid:02:CE:F6:AC:1A:39:1E:85:E8:72:D1:8A:C6:1D:E8:7A:8F:9D:15:6B
+
+            Authority Information Access: 
+                CA Issuers - URI:http://url-for-aia/Root.cer
+
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://url-for-crl/Root.crl
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         0e:00:56:5c:72:31:88:bd:95:13:f7:64:96:1c:63:c2:1c:11:
+         60:04:d2:c3:5f:7c:a2:d7:d1:33:6d:51:6b:77:61:78:a8:70:
+         2e:50:97:5d:c1:e8:9b:dd:c6:61:a7:d3:e1:2c:83:07:85:5c:
+         c9:d7:1e:22:c2:5f:76:83:19:d7:de:4a:5e:82:0f:43:80:45:
+         02:d7:d0:3d:ca:c3:c0:fc:04:c8:f6:89:32:d7:47:c6:bf:1f:
+         c6:bd:71:e1:07:00:90:12:ec:61:63:1b:6c:e9:58:2c:fc:4c:
+         a9:8f:58:e1:b1:6e:a5:ca:4d:be:7e:32:16:74:5f:fd:35:e4:
+         37:aa:1a:c5:33:21:20:8a:3e:1c:af:da:f3:c7:a2:22:d3:93:
+         6c:5e:ac:0a:65:d5:db:e4:8b:11:5e:ca:eb:8f:da:c4:5d:2f:
+         7a:98:e8:3c:d1:89:15:05:02:86:ef:eb:17:18:81:28:ca:d6:
+         58:87:bd:d4:e2:50:41:92:d9:7f:b1:f7:53:8f:f3:cc:f3:1e:
+         1d:e4:5a:c2:60:1b:17:42:78:53:e9:2d:5d:bb:f9:21:50:ff:
+         87:53:be:5f:e6:d4:8f:25:7f:d7:83:d7:f8:4d:c1:7c:7a:40:
+         0b:11:f1:d9:c6:eb:97:45:00:d6:6b:84:1c:4f:fc:8e:1f:5b:
+         b5:3d:60:0c
+-----BEGIN TRUSTED_CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290
+MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKZE7KcVAB+JrJH47HsD
+RgtTFe0jQDWU85aAJ9NKhJJoyQzgFDLHMWdJKVh36s6KcluTsaCo5YTGUp1awEG/
+mF8YXarRZXn76bCEkp0sWLzxxClZ7byshc7XDqoI6C2QJcuRnX2RdEKgrnfSEXtX
+SQQkwJT0IFRg2Rt2dgssIzxnkIwG7U7frCQiJvcmj1rSW3mKb25TJ2AQy8e0n2At
+jzJpSwHR8G1pGiIUBmZjl+j8eUGNFURE0UMqN1535AbmqYUT6SRjnQnQ9RPVulku
+HNJwBrGA91fXMPcU8xgGf4Q4toFGn6I2hw5fGkU4tyAWt8bhkTsODKu3Tj2kbWbY
+hfsCAwEAAaOByzCByDAdBgNVHQ4EFgQUAs72rBo5HoXoctGKxh3oeo+dFWswHwYD
+VR0jBBgwFoAUAs72rBo5HoXoctGKxh3oeo+dFWswNwYIKwYBBQUHAQEEKzApMCcG
+CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw
+IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE
+AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAOAFZccjGI
+vZUT92SWHGPCHBFgBNLDX3yi19EzbVFrd2F4qHAuUJddweib3cZhp9PhLIMHhVzJ
+1x4iwl92gxnX3kpegg9DgEUC19A9ysPA/ATI9oky10fGvx/GvXHhBwCQEuxhYxts
+6Vgs/Eypj1jhsW6lyk2+fjIWdF/9NeQ3qhrFMyEgij4cr9rzx6Ii05NsXqwKZdXb
+5IsRXsrrj9rEXS96mOg80YkVBQKG7+sXGIEoytZYh73U4lBBktl/sfdTj/PM8x4d
+5FrCYBsXQnhT6S1du/khUP+HU75f5tSPJX/Xg9f4TcF8ekALEfHZxuuXRQDWa4Qc
+T/yOH1u1PWAM
+-----END TRUSTED_CERTIFICATE-----
+
+-----BEGIN TIME-----
+MTUwMzAyMTIwMDAwWg==
+-----END TIME-----
+
+-----BEGIN VERIFY_RESULT-----
+U1VDQ0VTUw==
+-----END VERIFY_RESULT-----
diff --git a/net/net.gypi b/net/net.gypi
index 7dabdaa..8a4a799 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -105,6 +105,8 @@
       'cert/internal/parse_ocsp.h',
       'cert/internal/parsed_certificate.cc',
       'cert/internal/parsed_certificate.h',
+      'cert/internal/path_builder.cc',
+      'cert/internal/path_builder.h',
       'cert/internal/signature_algorithm.cc',
       'cert/internal/signature_algorithm.h',
       'cert/internal/signature_policy.cc',
@@ -1405,11 +1407,15 @@
       'cert/internal/parse_certificate_unittest.cc',
       'cert/internal/parse_name_unittest.cc',
       'cert/internal/parse_ocsp_unittest.cc',
+      'cert/internal/path_builder_pkits_unittest.cc',
+      'cert/internal/path_builder_unittest.cc',
+      'cert/internal/path_builder_verify_certificate_chain_unittest.cc',
       'cert/internal/signature_algorithm_unittest.cc',
       'cert/internal/test_helpers.cc',
       'cert/internal/test_helpers.h',
-      'cert/internal/verify_certificate_chain_unittest.cc',
       'cert/internal/verify_certificate_chain_pkits_unittest.cc',
+      'cert/internal/verify_certificate_chain_typed_unittest.h',
+      'cert/internal/verify_certificate_chain_unittest.cc',
       'cert/internal/verify_name_match_unittest.cc',
       'cert/internal/verify_signed_data_unittest.cc',
       'cert/jwk_serializer_unittest.cc',
@@ -2465,6 +2471,10 @@
       'data/verify_certificate_chain_unittest/intermediary-unknown-non-critical-extension.pem',
       'data/verify_certificate_chain_unittest/issuer-and-subject-not-byte-for-byte-equal-anchor.pem',
       'data/verify_certificate_chain_unittest/issuer-and-subject-not-byte-for-byte-equal.pem',
+      'data/verify_certificate_chain_unittest/key-rollover-longrolloverchain.pem',
+      'data/verify_certificate_chain_unittest/key-rollover-newchain.pem',
+      'data/verify_certificate_chain_unittest/key-rollover-oldchain.pem',
+      'data/verify_certificate_chain_unittest/key-rollover-rolloverchain.pem',
       'data/verify_certificate_chain_unittest/non-self-signed-root.pem',
       'data/verify_certificate_chain_unittest/target-and-intermediary.pem',
       'data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca.pem',