| 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 | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..6a7f9701415b88e6950f148c3af4353f02fd8c47 | 
| --- /dev/null | 
| +++ b/net/cert/internal/verify_certificate_chain.cc | 
| @@ -0,0 +1,205 @@ | 
| +// Copyright 2015 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/verify_certificate_chain.h" | 
| + | 
| +#include <utility> | 
| + | 
| +#include "net/cert/internal/parse_certificate.h" | 
| +#include "net/cert/internal/signature_algorithm.h" | 
| +#include "net/cert/internal/signature_policy.h" | 
| +#include "net/cert/internal/verify_signed_data.h" | 
| +#include "net/der/input.h" | 
| + | 
| +namespace net { | 
| + | 
| +TrustedRoot::~TrustedRoot() {} | 
| + | 
| +TrustStore::TrustStore() {} | 
| +TrustStore::~TrustStore() {} | 
| + | 
| +// TODO(eroman): Move into net/der (duplicated this from test_helpers.cc). | 
| +der::Input InputFromString(const std::string* s) { | 
| +  return der::Input(reinterpret_cast<const uint8_t*>(s->data()), s->size()); | 
| +} | 
| + | 
| +struct FullyParsedCert { | 
| +  CertificateVersion version; | 
| +  scoped_ptr<SignatureAlgorithm> signature_algorithm; | 
| +  scoped_ptr<SignatureAlgorithm> tbs_signature_algorithm; | 
| +  der::BitString signature_value; | 
| +  der::Input tbs_tlv; | 
| + | 
| +  // TODO(eroman): Everywhere this is consumed should also consider | 
| +  // issuerAltName. | 
| +  der::Input issuer_tlv; | 
| +  der::GeneralizedTime validity_not_before; | 
| +  der::GeneralizedTime validity_not_after; | 
| + | 
| +  // TODO(eroman): Everywhere this is consumed should also consider | 
| +  // subjectAltName. | 
| +  der::Input subject_tlv; | 
| +  der::Input spki_tlv; | 
| + | 
| +  // Extensions | 
| +}; | 
| + | 
| +WARN_UNUSED_RESULT bool FullyParseCertificate(const der::Input& cert_tlv, | 
| +                                              FullyParsedCert* out) { | 
| +  ParsedCertificate cert; | 
| +  if (!ParseCertificate(cert_tlv, &cert)) | 
| +    return false; | 
| + | 
| +  out->tbs_tlv = cert.tbs_certificate_tlv; | 
| +  out->signature_value = cert.signature_value; | 
| +  out->signature_algorithm = | 
| +      SignatureAlgorithm::CreateFromDer(cert.signature_algorithm_tlv); | 
| + | 
| +  if (!out->signature_algorithm) | 
| +    return false; | 
| + | 
| +  ParsedTbsCertificate tbs; | 
| +  if (!ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs)) | 
| +    return false; | 
| + | 
| +  out->tbs_signature_algorithm = | 
| +      SignatureAlgorithm::CreateFromDer(tbs.signature_algorithm_tlv); | 
| +  if (!out->tbs_signature_algorithm) | 
| +    return false; | 
| + | 
| +  out->issuer_tlv = tbs.issuer_tlv; | 
| +  out->version = tbs.version; | 
| +  out->spki_tlv = tbs.spki_tlv; | 
| +  out->subject_tlv = tbs.subject_tlv; | 
| +  out->validity_not_after = tbs.validity_not_after; | 
| +  out->validity_not_before = tbs.validity_not_before; | 
| + | 
| +  // Note that the serial_number, issuer_unique_id and subject_unique_id are | 
| +  // unused by verification at this time. Invalid encodings will be rejected | 
| +  // while parsing, but other then that are unused. | 
| + | 
| +  return true; | 
| +} | 
| + | 
| +WARN_UNUSED_RESULT bool IsValidAtTime(const der::GeneralizedTime& time, | 
| +                                      const der::GeneralizedTime& not_before, | 
| +                                      const der::GeneralizedTime& not_after) { | 
| +  // TODO(eroman): IMPORTANT: Return true if time >= not_before && time <= | 
| +  // not_after | 
| +  return true; | 
| +} | 
| + | 
| +WARN_UNUSED_RESULT bool NameMatches(const der::Input& name1, | 
| +                                    const der::Input& name2) { | 
| +  // TODO(eroman): Should account for normalization. | 
| +  return name1.Equals(name2); | 
| +} | 
| + | 
| +WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) { | 
| +  return NameMatches(cert.subject_tlv, cert.issuer_tlv); | 
| +} | 
| + | 
| +// Finds a mapping in the trust for matching |name|, or returns nullptr. | 
| +// | 
| +// TODO(eroman): This implementation is linear in the size of the trust store, | 
| +// and also presumes that all names are unique (in practice could have multiple | 
| +// SPKIs with the same name). | 
| +WARN_UNUSED_RESULT const TrustedRoot* FindTrustedRootByName( | 
| +    const TrustStore& trust_store, | 
| +    const der::Input& name) { | 
| +  for (const auto& root : trust_store.roots) { | 
| +    if (NameMatches(name, InputFromString(&root.name))) | 
| +      return &root; | 
| +  } | 
| + | 
| +  return nullptr; | 
| +} | 
| + | 
| +bool VerifyCertificateChain(const std::vector<der::Input>& certs_der, | 
| +                            const TrustStore& trust_store, | 
| +                            const SignaturePolicy* signature_policy, | 
| +                            const der::GeneralizedTime time) { | 
| +  // An empty chain is invalid input. Fail early since the rest of the code | 
| +  // assumes a non-empty chain. | 
| +  if (certs_der.empty()) | 
| +    return false; | 
| + | 
| +  // Fully parse all of the certificates. | 
| +  std::vector<FullyParsedCert> certs(certs_der.size()); | 
| +  for (size_t i = 0; i < certs_der.size(); ++i) { | 
| +    if (!FullyParseCertificate(certs_der[i], &certs[i])) | 
| +      return false; | 
| +  } | 
| + | 
| +  // Walk the chain in the forward direction (from end entity towards trust | 
| +  // anchor) and check all properties of the certificate except for name | 
| +  // constraints. | 
| +  size_t num_prev_self_issued_certs = 0; | 
| +  for (size_t i = 0; i < certs_der.size(); ++i) { | 
| +    const auto& cert = certs[i]; | 
| + | 
| +    // Check for expiration. | 
| +    if (!IsValidAtTime(time, cert.validity_not_before, cert.validity_not_after)) | 
| +      return false; | 
| + | 
| +    // Verify that the signature algorithm provided in tbsCertificate | 
| +    // matches that provided in the certificate. This requirement comes from | 
| +    // RFC 5280 section 4.1.1.2. | 
| +    if (!cert.signature_algorithm->Equals(*cert.tbs_signature_algorithm)) | 
| +      return false; | 
| + | 
| +    // TODO: use is_self_issued | 
| + | 
| +    // TODO(eroman): the usage must allow signing if i > 0. Otherwise .... | 
| + | 
| +    // Verify that the previous certificate (i-1) was signed by the current one | 
| +    // (i). | 
| +    // Note that the signature for the final certificate in the chain is checked | 
| +    // separately outside this loop. | 
| +    if (i > 0) { | 
| +      const auto& subordinate_cert = certs[i - 1]; | 
| + | 
| +      // The issuer and subject must match. | 
| +      if (!NameMatches(cert.subject_tlv, subordinate_cert.issuer_tlv)) | 
| +        return false; | 
| + | 
| +      // The digital signature must be correct. | 
| +      if (!VerifySignedData(*subordinate_cert.signature_algorithm, | 
| +                            subordinate_cert.tbs_tlv, | 
| +                            subordinate_cert.signature_value, cert.spki_tlv, | 
| +                            signature_policy)) { | 
| +        return false; | 
| +      } | 
| + | 
| +      // The last certificate must chain up to a trust anchor. | 
| +      if (i + 1 == certs.size()) { | 
| +        const TrustedRoot* trusted_root = | 
| +            FindTrustedRootByName(trust_store, cert.issuer_tlv); | 
| + | 
| +        if (!trusted_root) | 
| +          return false; | 
| + | 
| +        if (!VerifySignedData( | 
| +                *cert.signature_algorithm, cert.tbs_tlv, cert.signature_value, | 
| +                InputFromString(&trusted_root->spki), signature_policy)) { | 
| +          return false; | 
| +        } | 
| +      } | 
| +    } | 
| + | 
| +    // Keep track of how many certificates were self-issued, since some of the | 
| +    // rules are different for self-issued certificates. | 
| +    bool is_self_issued = IsSelfIssued(cert); | 
| +    if (is_self_issued) | 
| +      num_prev_self_issued_certs++; | 
| +  } | 
| + | 
| +  // TODO(eroman): Verify that no certificate in the chain violates the name | 
| +  // constraints extension. This can be done by walking the chain in the | 
| +  // reverse direction. | 
| + | 
| +  return true; | 
| +} | 
| + | 
| +}  // namespace net | 
|  |