| Index: net/cert/internal/path_builder.cc
|
| diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..863d8506b22d38673eaf18db08302484d934c34f
|
| --- /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
|
|
|