| Index: net/cert/internal/verify_certificate_chain.cc
|
| diff --git a/net/cert/internal/verify_certificate_chain.cc b/net/cert/internal/verify_certificate_chain.cc
|
| index 506fb434befec20aca8d118e49021cf624313e87..e701c9f14ae046a2b665ad9e6738cc36c7fd85be 100644
|
| --- a/net/cert/internal/verify_certificate_chain.cc
|
| +++ b/net/cert/internal/verify_certificate_chain.cc
|
| @@ -26,8 +26,10 @@ using ExtensionsMap = std::map<der::Input, ParsedExtension>;
|
| // Describes all parsed properties of a certificate that are relevant for
|
| // certificate verification.
|
| struct FullyParsedCert {
|
| - ParsedCertificate cert;
|
| - ParsedTbsCertificate tbs;
|
| + // XXX better naming.. this results in lots of non-obvious cert.cert->foo code
|
| + const CertThing* cert;
|
| +
|
| + // XXX should some of this be moved into CertThing?
|
|
|
| std::unique_ptr<SignatureAlgorithm> signature_algorithm;
|
|
|
| @@ -79,26 +81,23 @@ WARN_UNUSED_RESULT bool GetSequenceValue(const der::Input& tlv,
|
| return parser.ReadTag(der::kSequence, value) && !parser.HasMore();
|
| }
|
|
|
| +// XXX update doc
|
| // Parses an X.509 Certificate fully (including the TBSCertificate and
|
| // standard extensions), saving all the properties to |out_|.
|
| -WARN_UNUSED_RESULT bool FullyParseCertificate(const der::Input& cert_tlv,
|
| +WARN_UNUSED_RESULT bool FullyParseCertificate(const CertThing& cert,
|
| FullyParsedCert* out) {
|
| - // Parse the outer Certificate.
|
| - if (!ParseCertificate(cert_tlv, &out->cert))
|
| - return false;
|
| + // XXX move more of this into CertThing?
|
| +
|
| + out->cert = &cert;
|
|
|
| // Parse the signature algorithm contained in the Certificate (there is
|
| // another one in the TBSCertificate, which is checked later by
|
| // VerifySignatureAlgorithmsMatch)
|
| - out->signature_algorithm =
|
| - SignatureAlgorithm::CreateFromDer(out->cert.signature_algorithm_tlv);
|
| + out->signature_algorithm = SignatureAlgorithm::CreateFromDer(
|
| + cert.parsed_cert().signature_algorithm_tlv);
|
| if (!out->signature_algorithm)
|
| return false;
|
|
|
| - // Parse the TBSCertificate.
|
| - if (!ParseTbsCertificate(out->cert.tbs_certificate_tlv, &out->tbs))
|
| - return false;
|
| -
|
| // Reset state relating to extensions (which may not get overwritten). This is
|
| // just a precaution, since in practice |out| will already be default
|
| // initialize.
|
| @@ -110,10 +109,11 @@ WARN_UNUSED_RESULT bool FullyParseCertificate(const der::Input& cert_tlv,
|
|
|
| // Parse the standard X.509 extensions and remove them from
|
| // |unconsumed_extensions|.
|
| - if (out->tbs.has_extensions) {
|
| + if (cert.parsed_tbs().has_extensions) {
|
| // ParseExtensions() ensures there are no duplicates, and maps the (unique)
|
| // OID to the extension value.
|
| - if (!ParseExtensions(out->tbs.extensions_tlv, &out->unconsumed_extensions))
|
| + if (!ParseExtensions(cert.parsed_tbs().extensions_tlv,
|
| + &out->unconsumed_extensions))
|
| return false;
|
|
|
| ParsedExtension extension;
|
| @@ -149,7 +149,7 @@ WARN_UNUSED_RESULT bool FullyParseCertificate(const der::Input& cert_tlv,
|
| // MUST be critical.
|
| if (!extension.critical) {
|
| der::Input subject_value;
|
| - if (!GetSequenceValue(out->tbs.subject_tlv, &subject_value))
|
| + if (!GetSequenceValue(cert.parsed_tbs().subject_tlv, &subject_value))
|
| return false;
|
| if (subject_value.Length() == 0)
|
| return false;
|
| @@ -195,7 +195,7 @@ WARN_UNUSED_RESULT bool NameMatches(const der::Input& name1_tlv,
|
| // self-issued certificates are not counted when evaluating path length
|
| // or name constraints.
|
| WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) {
|
| - return NameMatches(cert.tbs.subject_tlv, cert.tbs.issuer_tlv);
|
| + return cert.cert->normalized_subject() == cert.cert->normalized_issuer();
|
| }
|
|
|
| // Returns true if |cert| is valid at time |time|.
|
| @@ -207,8 +207,8 @@ WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) {
|
| // notBefore through notAfter, inclusive.
|
| WARN_UNUSED_RESULT bool VerifyTimeValidity(const FullyParsedCert& cert,
|
| const der::GeneralizedTime time) {
|
| - return !(time < cert.tbs.validity_not_before) &&
|
| - !(cert.tbs.validity_not_after < time);
|
| + return !(time < cert.cert->parsed_tbs().validity_not_before) &&
|
| + !(cert.cert->parsed_tbs().validity_not_after < time);
|
| }
|
|
|
| // Returns true if |signature_algorithm_tlv| is a valid algorithm encoding for
|
| @@ -244,8 +244,8 @@ WARN_UNUSED_RESULT bool IsRsaWithSha1SignatureAlgorithm(
|
| // compatibility sake.
|
| WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch(
|
| const FullyParsedCert& cert) {
|
| - const der::Input& alg1_tlv = cert.cert.signature_algorithm_tlv;
|
| - const der::Input& alg2_tlv = cert.tbs.signature_algorithm_tlv;
|
| + const der::Input& alg1_tlv = cert.cert->parsed_cert().signature_algorithm_tlv;
|
| + const der::Input& alg2_tlv = cert.cert->parsed_tbs().signature_algorithm_tlv;
|
|
|
| // Ensure that the two DER-encoded signature algorithms are byte-for-byte
|
| // equal, but make a compatibility concession for RSA with SHA1.
|
| @@ -258,7 +258,7 @@ WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch(
|
| //
|
| // |skip_issuer_checks| controls whether the function will skip:
|
| // - Checking that |cert|'s signature using |working_spki|
|
| -// - Checkinging that |cert|'s issuer matches |working_issuer_name|
|
| +// - Checkinging that |cert|'s issuer matches |working_normalized_issuer_name|
|
| // This should be set to true only when verifying a trusted root certificate.
|
| WARN_UNUSED_RESULT bool BasicCertificateProcessing(
|
| const FullyParsedCert& cert,
|
| @@ -267,7 +267,7 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
|
| const SignaturePolicy* signature_policy,
|
| const der::GeneralizedTime& time,
|
| const der::Input& working_spki,
|
| - const der::Input& working_issuer_name,
|
| + const der::Input& working_normalized_issuer_name,
|
| const std::vector<std::unique_ptr<NameConstraints>>&
|
| name_constraints_list) {
|
| // Check that the signature algorithms in Certificate vs TBSCertificate
|
| @@ -279,9 +279,10 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
|
| // Verify the digital signature using the previous certificate's key (RFC
|
| // 5280 section 6.1.3 step a.1).
|
| if (!skip_issuer_checks) {
|
| - if (!VerifySignedData(
|
| - *cert.signature_algorithm, cert.cert.tbs_certificate_tlv,
|
| - cert.cert.signature_value, working_spki, signature_policy)) {
|
| + if (!VerifySignedData(*cert.signature_algorithm,
|
| + cert.cert->parsed_cert().tbs_certificate_tlv,
|
| + cert.cert->parsed_cert().signature_value,
|
| + working_spki, signature_policy)) {
|
| return false;
|
| }
|
| }
|
| @@ -297,7 +298,8 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
|
| // Verify the certificate's issuer name matches the issuing certificate's
|
| // subject name. (RFC 5280 section 6.1.3 step a.4)
|
| if (!skip_issuer_checks) {
|
| - if (!NameMatches(cert.tbs.issuer_tlv, working_issuer_name))
|
| + if (der::Input(&cert.cert->normalized_issuer()) !=
|
| + working_normalized_issuer_name)
|
| return false;
|
| }
|
|
|
| @@ -306,8 +308,9 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
|
| // path, skip this step for certificate i.
|
| if (!name_constraints_list.empty() &&
|
| (!IsSelfIssued(cert) || is_target_cert)) {
|
| + // XXX used normalized_subject here
|
| der::Input subject_value;
|
| - if (!GetSequenceValue(cert.tbs.subject_tlv, &subject_value))
|
| + if (!GetSequenceValue(cert.cert->parsed_tbs().subject_tlv, &subject_value))
|
| return false;
|
| for (const auto& nc : name_constraints_list) {
|
| if (!nc->IsPermittedCert(subject_value, cert.subject_alt_names.get()))
|
| @@ -327,20 +330,21 @@ WARN_UNUSED_RESULT bool PrepareForNextCertificate(
|
| const FullyParsedCert& cert,
|
| size_t* max_path_length_ptr,
|
| der::Input* working_spki,
|
| - der::Input* working_issuer_name,
|
| + der::Input* working_normalized_issuer_name,
|
| std::vector<std::unique_ptr<NameConstraints>>* name_constraints_list) {
|
| // TODO(eroman): Steps a-b are omitted, as policy constraints are not yet
|
| // implemented.
|
|
|
| // From RFC 5280 section 6.1.4 step c:
|
| //
|
| - // Assign the certificate subject name to working_issuer_name.
|
| - *working_issuer_name = cert.tbs.subject_tlv;
|
| + // Assign the certificate subject name to working_normalized_issuer_name.
|
| + *working_normalized_issuer_name =
|
| + der::Input(&cert.cert->normalized_subject());
|
|
|
| // From RFC 5280 section 6.1.4 step d:
|
| //
|
| // Assign the certificate subjectPublicKey to working_public_key.
|
| - *working_spki = cert.tbs.spki_tlv;
|
| + *working_spki = cert.cert->parsed_tbs().spki_tlv;
|
|
|
| // Note that steps e and f are omitted as they are handled by
|
| // the assignment to |working_spki| above. See the definition
|
| @@ -494,14 +498,14 @@ WARN_UNUSED_RESULT bool WrapUp(const FullyParsedCert& cert) {
|
|
|
| } // namespace
|
|
|
| -TrustAnchor::TrustAnchor() {}
|
| -TrustAnchor::~TrustAnchor() {}
|
| +CertThing::CertThing() {}
|
| +CertThing::~CertThing() {}
|
|
|
| -std::unique_ptr<TrustAnchor> TrustAnchor::CreateFromCertificateData(
|
| +std::unique_ptr<CertThing> CertThing::CreateFromCertificateData(
|
| const uint8_t* data,
|
| size_t length,
|
| DataSource source) {
|
| - std::unique_ptr<TrustAnchor> result(new TrustAnchor);
|
| + std::unique_ptr<CertThing> result(new CertThing);
|
|
|
| switch (source) {
|
| case DataSource::INTERNAL_COPY:
|
| @@ -515,25 +519,70 @@ std::unique_ptr<TrustAnchor> TrustAnchor::CreateFromCertificateData(
|
| }
|
|
|
| // Parse the certificate to get its name.
|
| - ParsedCertificate cert;
|
| - if (!ParseCertificate(result->cert(), &cert))
|
| + if (!ParseCertificate(result->cert_, &result->parsed_cert_))
|
| return nullptr;
|
|
|
| - ParsedTbsCertificate tbs;
|
| - if (!ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs))
|
| + if (!ParseTbsCertificate(result->parsed_cert_.tbs_certificate_tlv,
|
| + &result->parsed_tbs_))
|
| + return nullptr;
|
| +
|
| + der::Input subject_value;
|
| + if (!GetSequenceValue(result->parsed_tbs_.subject_tlv, &subject_value) ||
|
| + !NormalizeName(subject_value, &result->normalized_subject_))
|
| + return nullptr;
|
| + der::Input issuer_value;
|
| + if (!GetSequenceValue(result->parsed_tbs_.issuer_tlv, &issuer_value) ||
|
| + !NormalizeName(issuer_value, &result->normalized_issuer_))
|
| return nullptr;
|
|
|
| - result->name_ = tbs.subject_tlv;
|
| + return result;
|
| +}
|
|
|
| - // TODO(eroman): If adding a self-signed certificate, check that its
|
| - // signature is correct? This check will not otherwise be done during
|
| - // verification.
|
| +std::unique_ptr<CertThing> CertThing::CreateFromCertificateCopy(
|
| + const base::StringPiece& data) {
|
| + return CertThing::CreateFromCertificateData(
|
| + reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
| + DataSource::INTERNAL_COPY);
|
| +}
|
| +
|
| +std::unique_ptr<CertThing> CertThing::Clone() const {
|
| + // XXX Should it always INTERNAL_COPY, or should it continue with what the
|
| + // previous had?.. lifetimes could get weird with that. (Eg, someone created a
|
| + // Cert, passed it to a function they could gaurantee it would outlive, but
|
| + // that function internall makes a clone and returns it as part of the results
|
| + // or error info..)
|
| + /*std::unique_ptr<CertThing>
|
| + result(CreateFromCertificateData(cert_.UnsafeData(), cert_.Length(),
|
| + CertThing::DataSource::INTERNAL_COPY));
|
| + // If it parsed to make this object, it should never fail to parse again.
|
| + CHECK(result);
|
| + return result;*/
|
| +
|
| + std::unique_ptr<CertThing> result(new CertThing);
|
| +
|
| + result->cert_data_.assign(
|
| + reinterpret_cast<const char*>(cert_.UnsafeData()),
|
| + reinterpret_cast<const char*>(cert_.UnsafeData()) + cert_.Length());
|
| + result->cert_ =
|
| + der::Input(result->cert_data_.data(), result->cert_data_.size());
|
| +
|
| + result->parsed_cert_ = ParsedCertificate(parsed_cert_, cert_, result->cert_);
|
| + result->parsed_tbs_ = ParsedTbsCertificate(parsed_tbs_, cert_, result->cert_);
|
| +
|
| + result->normalized_subject_ = normalized_subject_;
|
| + result->normalized_issuer_ = normalized_issuer_;
|
| +
|
| + // XXX clean up
|
| + /*result->parsed_cert_.tbs_certificate_tlv = der::Input(
|
| + result->cert_.UnsafeData() +
|
| + (parsed_cert_.tbs_certificate_tlv.UnsafeData() - cert_.UnsafeData()),
|
| + parsed_cert_.tbs_certificate_tlv.Length());*/
|
|
|
| return result;
|
| }
|
|
|
| -bool TrustAnchor::MatchesName(const der::Input& name) const {
|
| - return NameMatches(name, name_);
|
| +bool CertThing::MatchesName(const der::Input& name) const {
|
| + return NameMatches(name, parsed_tbs_.subject_tlv); // XXX don't re-normalize
|
| }
|
|
|
| TrustStore::TrustStore() {}
|
| @@ -544,8 +593,11 @@ void TrustStore::Clear() {
|
| }
|
|
|
| bool TrustStore::AddTrustedCertificate(const uint8_t* data, size_t length) {
|
| + // TODO(eroman): If adding a self-signed certificate, check that its
|
| + // signature is correct? This check will not otherwise be done during
|
| + // verification.
|
| return AddTrustedCertificate(data, length,
|
| - TrustAnchor::DataSource::INTERNAL_COPY);
|
| + CertThing::DataSource::INTERNAL_COPY);
|
| }
|
|
|
| bool TrustStore::AddTrustedCertificate(const base::StringPiece& data) {
|
| @@ -556,10 +608,10 @@ bool TrustStore::AddTrustedCertificate(const base::StringPiece& data) {
|
| bool TrustStore::AddTrustedCertificateWithoutCopying(const uint8_t* data,
|
| size_t length) {
|
| return AddTrustedCertificate(data, length,
|
| - TrustAnchor::DataSource::EXTERNAL_REFERENCE);
|
| + CertThing::DataSource::EXTERNAL_REFERENCE);
|
| }
|
|
|
| -const TrustAnchor* TrustStore::FindTrustAnchorByName(
|
| +const CertThing* TrustStore::FindTrustAnchorByName(
|
| const der::Input& name) const {
|
| for (const auto& anchor : anchors_) {
|
| if (anchor->MatchesName(name)) {
|
| @@ -569,9 +621,20 @@ const TrustAnchor* TrustStore::FindTrustAnchorByName(
|
| return nullptr;
|
| }
|
|
|
| +std::vector<const CertThing*> TrustStore::FindTrustAnchorsByNormalizedName(
|
| + const std::string& normalized_name) const {
|
| + std::vector<const CertThing*> result;
|
| + for (const auto& anchor : anchors_) {
|
| + if (anchor->normalized_subject() == normalized_name) {
|
| + result.push_back(anchor.get());
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| bool TrustStore::IsTrustedCertificate(const der::Input& cert_der) const {
|
| for (const auto& anchor : anchors_) {
|
| - if (anchor->cert() == cert_der)
|
| + if (anchor->der_cert() == cert_der)
|
| return true;
|
| }
|
| return false;
|
| @@ -579,17 +642,14 @@ bool TrustStore::IsTrustedCertificate(const der::Input& cert_der) const {
|
|
|
| bool TrustStore::AddTrustedCertificate(const uint8_t* data,
|
| size_t length,
|
| - TrustAnchor::DataSource source) {
|
| - auto anchor = TrustAnchor::CreateFromCertificateData(data, length, source);
|
| + CertThing::DataSource source) {
|
| + auto anchor = CertThing::CreateFromCertificateData(data, length, source);
|
| if (!anchor)
|
| return false;
|
| anchors_.push_back(std::move(anchor));
|
| return true;
|
| }
|
|
|
| -// 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.
|
| //
|
| @@ -597,19 +657,19 @@ namespace {
|
| // 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<der::Input>& certs_der,
|
| + const std::vector<std::unique_ptr<CertThing>>& certs,
|
| // The trust store is only used for assertions.
|
| const TrustStore& trust_store,
|
| const SignaturePolicy* signature_policy,
|
| const der::GeneralizedTime& time) {
|
| // An empty chain is necessarily invalid.
|
| - if (certs_der.empty())
|
| + if (certs.empty())
|
| return false;
|
|
|
| // IMPORTANT: the assumption being made is that the root certificate in
|
| // the given path is the trust anchor (and has already been verified as
|
| // such).
|
| - DCHECK(trust_store.IsTrustedCertificate(certs_der.back()));
|
| + DCHECK(trust_store.IsTrustedCertificate(certs.back()->der_cert())); // XXX
|
|
|
| // Will contain a NameConstraints for each previous cert in the chain which
|
| // had nameConstraints. This corresponds to the permitted_subtrees and
|
| @@ -632,12 +692,12 @@ bool VerifyCertificateChainAssumingTrustedRoot(
|
| // signature of a certificate.
|
| der::Input working_spki;
|
|
|
| - // |working_issuer_name| corresponds with the same named variable in RFC 5280
|
| - // section 6.1.2:
|
| + // |working_normalized_issuer_name| is the normalized value of the
|
| + // working_issuer_name variable in RFC 5280 section 6.1.2:
|
| //
|
| // working_issuer_name: the issuer distinguished name expected
|
| // in the next certificate in the chain.
|
| - der::Input working_issuer_name;
|
| + der::Input working_normalized_issuer_name;
|
|
|
| // |max_path_length| corresponds with the same named variable in RFC 5280
|
| // section 6.1.2:
|
| @@ -647,7 +707,7 @@ bool VerifyCertificateChainAssumingTrustedRoot(
|
| // and may be reduced to the value in the path length constraint
|
| // field within the basic constraints extension of a CA
|
| // certificate.
|
| - size_t max_path_length = certs_der.size();
|
| + size_t max_path_length = certs.size();
|
|
|
| // Iterate over all the certificates in the reverse direction: starting from
|
| // the trust anchor and progressing towards the target certificate.
|
| @@ -656,13 +716,13 @@ bool VerifyCertificateChainAssumingTrustedRoot(
|
| //
|
| // * i=0 : Trust anchor.
|
| // * i=N-1 : Target certificate.
|
| - for (size_t i = 0; i < certs_der.size(); ++i) {
|
| - const size_t index_into_certs_der = certs_der.size() - i - 1;
|
| + for (size_t i = 0; i < certs.size(); ++i) {
|
| + const size_t index_into_certs = certs.size() - i - 1;
|
|
|
| // |is_target_cert| is true if the current certificate is the target
|
| // certificate being verified. The target certificate isn't necessarily an
|
| // end-entity certificate.
|
| - const bool is_target_cert = index_into_certs_der == 0;
|
| + const bool is_target_cert = index_into_certs == 0;
|
|
|
| // |is_trust_anchor| is true if the current certificate is the trust
|
| // anchor. This certificate is implicitly trusted.
|
| @@ -670,8 +730,8 @@ bool VerifyCertificateChainAssumingTrustedRoot(
|
|
|
| // Parse the current certificate into |cert|.
|
| FullyParsedCert cert;
|
| - const der::Input& cert_der = certs_der[index_into_certs_der];
|
| - if (!FullyParseCertificate(cert_der, &cert))
|
| + const CertThing& cert_thing = *certs[index_into_certs];
|
| + if (!FullyParseCertificate(cert_thing, &cert))
|
| return false;
|
|
|
| // Per RFC 5280 section 6.1:
|
| @@ -679,14 +739,15 @@ bool VerifyCertificateChainAssumingTrustedRoot(
|
| // * If it is the last certificate in the path (target certificate)
|
| // - Then run "Wrap up"
|
| // - Otherwise run "Prepare for Next cert"
|
| - if (!BasicCertificateProcessing(
|
| - cert, is_target_cert, is_trust_anchor, signature_policy, time,
|
| - working_spki, working_issuer_name, name_constraints_list)) {
|
| + if (!BasicCertificateProcessing(cert, is_target_cert, is_trust_anchor,
|
| + signature_policy, time, working_spki,
|
| + working_normalized_issuer_name,
|
| + name_constraints_list)) {
|
| return false;
|
| }
|
| if (!is_target_cert) {
|
| if (!PrepareForNextCertificate(cert, &max_path_length, &working_spki,
|
| - &working_issuer_name,
|
| + &working_normalized_issuer_name,
|
| &name_constraints_list)) {
|
| return false;
|
| }
|
| @@ -704,6 +765,9 @@ bool VerifyCertificateChainAssumingTrustedRoot(
|
| return true;
|
| }
|
|
|
| +// TODO(eroman): Move this into existing anonymous namespace.
|
| +namespace {
|
| +
|
| // 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.
|
| @@ -738,7 +802,7 @@ WARN_UNUSED_RESULT bool BuildSimplePathToTrustAnchor(
|
| auto trust_anchor = trust_store.FindTrustAnchorByName(tbs.issuer_tlv);
|
| if (!trust_anchor)
|
| return false;
|
| - certs_der_trusted_root->push_back(trust_anchor->cert());
|
| + certs_der_trusted_root->push_back(trust_anchor->der_cert());
|
| return true;
|
| }
|
|
|
| @@ -755,9 +819,19 @@ bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
|
| return false;
|
| }
|
|
|
| + std::vector<std::unique_ptr<CertThing>> certs_trusted_root;
|
| + for (const auto& cert_der : certs_der_trusted_root) {
|
| + std::unique_ptr<CertThing> cert(CertThing::CreateFromCertificateData(
|
| + cert_der.UnsafeData(), cert_der.Length(),
|
| + CertThing::DataSource::EXTERNAL_REFERENCE));
|
| + if (!cert)
|
| + return false;
|
| + certs_trusted_root.push_back(std::move(cert));
|
| + }
|
| +
|
| // Verify the chain.
|
| return VerifyCertificateChainAssumingTrustedRoot(
|
| - certs_der_trusted_root, trust_store, signature_policy, time);
|
| + certs_trusted_root, trust_store, signature_policy, time);
|
| }
|
|
|
| } // namespace net
|
|
|