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 |