Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Unified Diff: net/cert/internal/verify_certificate_chain.cc

Issue 1890193003: Make Cast certificate verification enforce constraints specified in the trusted root certificate. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: list datafiles for ios (needed following the rebase) Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « net/cert/internal/verify_certificate_chain.h ('k') | net/cert/internal/verify_certificate_chain_pkits_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698