| 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 a6e0f4a8515889fc1d496cbcecbc98d9fcaaff60..506fb434befec20aca8d118e49021cf624313e87 100644
|
| --- a/net/cert/internal/verify_certificate_chain.cc
|
| +++ b/net/cert/internal/verify_certificate_chain.cc
|
| @@ -198,24 +198,6 @@ WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) {
|
| return NameMatches(cert.tbs.subject_tlv, cert.tbs.issuer_tlv);
|
| }
|
|
|
| -// Finds a trust anchor that matches |name| in |trust_store| or returns
|
| -// nullptr. The returned pointer references data in |trust_store|.
|
| -//
|
| -// TODO(eroman): This implementation is linear in the size of the trust store,
|
| -// and also presumes that all names are unique. In practice it is possible to
|
| -// have multiple SPKIs with the same name. Also this mechanism of
|
| -// searching is fairly primitive, and does not take advantage of other
|
| -// properties like the authority key id.
|
| -WARN_UNUSED_RESULT const TrustAnchor* FindTrustAnchorByName(
|
| - const TrustStore& trust_store,
|
| - const der::Input& name) {
|
| - for (const auto& anchor : trust_store.anchors) {
|
| - if (NameMatches(name, der::Input(&anchor.name)))
|
| - return &anchor;
|
| - }
|
| - return nullptr;
|
| -}
|
| -
|
| // Returns true if |cert| is valid at time |time|.
|
| //
|
| // The certificate's validity requirements are described by RFC 5280 section
|
| @@ -273,9 +255,15 @@ WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch(
|
|
|
| // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
|
| // Processing" procedure.
|
| +//
|
| +// |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|
|
| +// This should be set to true only when verifying a trusted root certificate.
|
| WARN_UNUSED_RESULT bool BasicCertificateProcessing(
|
| const FullyParsedCert& cert,
|
| bool is_target_cert,
|
| + bool skip_issuer_checks,
|
| const SignaturePolicy* signature_policy,
|
| const der::GeneralizedTime& time,
|
| const der::Input& working_spki,
|
| @@ -288,12 +276,14 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
|
| if (!VerifySignatureAlgorithmsMatch(cert))
|
| return false;
|
|
|
| - // Verify the digital signature using the previous certificate's (or trust
|
| - // anchor's) key (RFC 5280 section 6.1.3 step a.1).
|
| - if (!VerifySignedData(
|
| - *cert.signature_algorithm, cert.cert.tbs_certificate_tlv,
|
| - cert.cert.signature_value, working_spki, signature_policy)) {
|
| - return false;
|
| + // 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)) {
|
| + return false;
|
| + }
|
| }
|
|
|
| // Check the time range for the certificate's validity, ensuring it is valid
|
| @@ -304,10 +294,12 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
|
|
|
| // TODO(eroman): Check revocation (RFC 5280 section 6.1.3 step a.3)
|
|
|
| - // Verify the certificate's issuer name matches the issuing certificate's (or
|
| - // trust anchor's) subject name. (RFC 5280 section 6.1.3 step a.4)
|
| - if (!NameMatches(cert.tbs.issuer_tlv, working_issuer_name))
|
| - return false;
|
| + // 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))
|
| + return false;
|
| + }
|
|
|
| // Name constraints (RFC 5280 section 6.1.3 step b & c)
|
| // If certificate i is self-issued and it is not the final certificate in the
|
| @@ -502,22 +494,123 @@ WARN_UNUSED_RESULT bool WrapUp(const FullyParsedCert& cert) {
|
|
|
| } // namespace
|
|
|
| +TrustAnchor::TrustAnchor() {}
|
| TrustAnchor::~TrustAnchor() {}
|
|
|
| +std::unique_ptr<TrustAnchor> TrustAnchor::CreateFromCertificateData(
|
| + const uint8_t* data,
|
| + size_t length,
|
| + DataSource source) {
|
| + std::unique_ptr<TrustAnchor> result(new TrustAnchor);
|
| +
|
| + switch (source) {
|
| + case DataSource::INTERNAL_COPY:
|
| + result->cert_data_.assign(data, data + length);
|
| + result->cert_ =
|
| + der::Input(result->cert_data_.data(), result->cert_data_.size());
|
| + break;
|
| + case DataSource::EXTERNAL_REFERENCE:
|
| + result->cert_ = der::Input(data, length);
|
| + break;
|
| + }
|
| +
|
| + // Parse the certificate to get its name.
|
| + ParsedCertificate cert;
|
| + if (!ParseCertificate(result->cert(), &cert))
|
| + return nullptr;
|
| +
|
| + ParsedTbsCertificate tbs;
|
| + if (!ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs))
|
| + return nullptr;
|
| +
|
| + result->name_ = tbs.subject_tlv;
|
| +
|
| + // TODO(eroman): If adding a self-signed certificate, check that its
|
| + // signature is correct? This check will not otherwise be done during
|
| + // verification.
|
| +
|
| + return result;
|
| +}
|
| +
|
| +bool TrustAnchor::MatchesName(const der::Input& name) const {
|
| + return NameMatches(name, name_);
|
| +}
|
| +
|
| TrustStore::TrustStore() {}
|
| -TrustStore::TrustStore(const TrustStore& other) = default;
|
| TrustStore::~TrustStore() {}
|
|
|
| +void TrustStore::Clear() {
|
| + anchors_.clear();
|
| +}
|
| +
|
| +bool TrustStore::AddTrustedCertificate(const uint8_t* data, size_t length) {
|
| + return AddTrustedCertificate(data, length,
|
| + TrustAnchor::DataSource::INTERNAL_COPY);
|
| +}
|
| +
|
| +bool TrustStore::AddTrustedCertificate(const base::StringPiece& data) {
|
| + return AddTrustedCertificate(reinterpret_cast<const uint8_t*>(data.data()),
|
| + data.size());
|
| +}
|
| +
|
| +bool TrustStore::AddTrustedCertificateWithoutCopying(const uint8_t* data,
|
| + size_t length) {
|
| + return AddTrustedCertificate(data, length,
|
| + TrustAnchor::DataSource::EXTERNAL_REFERENCE);
|
| +}
|
| +
|
| +const TrustAnchor* TrustStore::FindTrustAnchorByName(
|
| + const der::Input& name) const {
|
| + for (const auto& anchor : anchors_) {
|
| + if (anchor->MatchesName(name)) {
|
| + return anchor.get();
|
| + }
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +bool TrustStore::IsTrustedCertificate(const der::Input& cert_der) const {
|
| + for (const auto& anchor : anchors_) {
|
| + if (anchor->cert() == cert_der)
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +bool TrustStore::AddTrustedCertificate(const uint8_t* data,
|
| + size_t length,
|
| + TrustAnchor::DataSource source) {
|
| + auto anchor = TrustAnchor::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.
|
| -bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
|
| - const TrustStore& trust_store,
|
| - const SignaturePolicy* signature_policy,
|
| - const der::GeneralizedTime& time) {
|
| +//
|
| +// Unlike RFC 5280, the trust anchor is specified as the root certificate in
|
| +// 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,
|
| + // 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())
|
| 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()));
|
| +
|
| // Will contain a NameConstraints for each previous cert in the chain which
|
| // had nameConstraints. This corresponds to the permitted_subtrees and
|
| // excluded_subtrees state variables from RFC 5280.
|
| @@ -536,18 +629,14 @@ bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
|
| // 5280 section 6.1.2:
|
| //
|
| // working_public_key: the public key used to verify the
|
| - // signature of a certificate. The working_public_key is
|
| - // initialized from the trusted public key provided in the trust
|
| - // anchor information.
|
| + // 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_issuer_name: the issuer distinguished name expected
|
| - // in the next certificate in the chain. The
|
| - // working_issuer_name is initialized to the trusted issuer name
|
| - // provided in the trust anchor information.
|
| + // in the next certificate in the chain.
|
| der::Input working_issuer_name;
|
|
|
| // |max_path_length| corresponds with the same named variable in RFC 5280
|
| @@ -565,39 +654,34 @@ bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
|
| //
|
| // Note that |i| uses 0-based indexing whereas in RFC 5280 it is 1-based.
|
| //
|
| - // * i=0 : Certificate signed by a trust anchor.
|
| + // * 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;
|
| +
|
| + // |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;
|
|
|
| + // |is_trust_anchor| is true if the current certificate is the trust
|
| + // anchor. This certificate is implicitly trusted.
|
| + const bool is_trust_anchor = i == 0;
|
| +
|
| // Parse the current certificate into |cert|.
|
| FullyParsedCert cert;
|
| const der::Input& cert_der = certs_der[index_into_certs_der];
|
| if (!FullyParseCertificate(cert_der, &cert))
|
| return false;
|
|
|
| - // When processing the first certificate, initialize |working_spki|
|
| - // and |working_issuer_name| to the trust anchor per RFC 5280 section 6.1.2.
|
| - // This is done inside the loop in order to have access to the parsed
|
| - // certificate.
|
| - if (i == 0) {
|
| - const TrustAnchor* trust_anchor =
|
| - FindTrustAnchorByName(trust_store, cert.tbs.issuer_tlv);
|
| - if (!trust_anchor)
|
| - return false;
|
| - working_spki = der::Input(&trust_anchor->spki);
|
| - working_issuer_name = der::Input(&trust_anchor->name);
|
| - }
|
| -
|
| // Per RFC 5280 section 6.1:
|
| // * Do basic processing for each certificate
|
| // * 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, 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_issuer_name, name_constraints_list)) {
|
| return false;
|
| }
|
| if (!is_target_cert) {
|
| @@ -620,4 +704,60 @@ bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
|
| 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 std::vector<der::Input>& certs_der,
|
| + const TrustStore& trust_store,
|
| + std::vector<der::Input>* certs_der_trusted_root) {
|
| + // Copy the input chain.
|
| + *certs_der_trusted_root = certs_der;
|
| +
|
| + if (certs_der.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_der_trusted_root->back()))
|
| + return true;
|
| +
|
| + // Otherwise if it is not trusted, check whether its issuer is trusted. If
|
| + // so, make *that* trusted certificate the root. If the issuer is not in
|
| + // the trust store then give up and fail (this is not full path building).
|
| + ParsedCertificate cert;
|
| + ParsedTbsCertificate tbs;
|
| + if (!ParseCertificate(certs_der.back(), &cert) ||
|
| + !ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs)) {
|
| + return false;
|
| + }
|
| +
|
| + auto trust_anchor = trust_store.FindTrustAnchorByName(tbs.issuer_tlv);
|
| + if (!trust_anchor)
|
| + return false;
|
| + certs_der_trusted_root->push_back(trust_anchor->cert());
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
|
| + const TrustStore& trust_store,
|
| + const SignaturePolicy* signature_policy,
|
| + const der::GeneralizedTime& time) {
|
| + // Modify the certificate chain so that its root is a trusted certificate.
|
| + std::vector<der::Input> certs_der_trusted_root;
|
| + if (!BuildSimplePathToTrustAnchor(certs_der, trust_store,
|
| + &certs_der_trusted_root)) {
|
| + return false;
|
| + }
|
| +
|
| + // Verify the chain.
|
| + return VerifyCertificateChainAssumingTrustedRoot(
|
| + certs_der_trusted_root, trust_store, signature_policy, time);
|
| +}
|
| +
|
| } // namespace net
|
|
|