| 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
|
|
|