Add new ParsedCertificate class, move TrustStore to own file.
This consolidates the certificate parsing from various places in
verify_certificate_chain.cc into a single class that pre-parses all the
important information.
The relevant places are all changed to use the new ParsedCertificate
class, and TrustStore is separated into its own file.
BUG=410574
Review-Url: https://codereview.chromium.org/1976433002
Cr-Commit-Position: refs/heads/master@{#397863}
diff --git a/components/cast_certificate/cast_cert_validator.cc b/components/cast_certificate/cast_cert_validator.cc
index b83ad54..5fb0ea1 100644
--- a/components/cast_certificate/cast_cert_validator.cc
+++ b/components/cast_certificate/cast_cert_validator.cc
@@ -17,8 +17,10 @@
#include "net/cert/internal/extended_key_usage.h"
#include "net/cert/internal/parse_certificate.h"
#include "net/cert/internal/parse_name.h"
+#include "net/cert/internal/parsed_certificate.h"
#include "net/cert/internal/signature_algorithm.h"
#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/trust_store.h"
#include "net/cert/internal/verify_certificate_chain.h"
#include "net/cert/internal/verify_signed_data.h"
#include "net/der/input.h"
@@ -55,10 +57,18 @@
CastTrustStore() {
// Initialize the trust store with two root certificates.
- CHECK(store_.AddTrustedCertificateWithoutCopying(kCastRootCaDer,
- sizeof(kCastRootCaDer)));
- CHECK(store_.AddTrustedCertificateWithoutCopying(kEurekaRootCaDer,
- sizeof(kEurekaRootCaDer)));
+ scoped_refptr<net::ParsedCertificate> root =
+ net::ParsedCertificate::CreateFromCertificateData(
+ kCastRootCaDer, sizeof(kCastRootCaDer),
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
+ CHECK(root);
+ store_.AddTrustedCertificate(std::move(root));
+
+ root = net::ParsedCertificate::CreateFromCertificateData(
+ kEurekaRootCaDer, sizeof(kEurekaRootCaDer),
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE);
+ CHECK(root);
+ store_.AddTrustedCertificate(std::move(root));
}
net::TrustStore store_;
@@ -167,50 +177,27 @@
// Checks properties on the target certificate.
//
// * The Key Usage must include Digital Signature
-// * THe Extended Key Usage must includ TLS Client Auth
+// * The Extended Key Usage must include TLS Client Auth
// * May have the policy 1.3.6.1.4.1.11129.2.5.2 to indicate it
// is an audio-only device.
WARN_UNUSED_RESULT bool CheckTargetCertificate(
- const net::der::Input& cert_der,
+ const net::ParsedCertificate* cert,
std::unique_ptr<CertVerificationContext>* context,
CastDeviceCertPolicy* policy) {
- // TODO(eroman): Simplify this. The certificate chain verification
- // function already parses this stuff, awkward to re-do it here.
-
- net::der::Input tbs_certificate_tlv;
- net::der::Input signature_algorithm_tlv;
- net::der::BitString signature_value;
- if (!net::ParseCertificate(cert_der, &tbs_certificate_tlv,
- &signature_algorithm_tlv, &signature_value))
- return false;
-
- net::ParsedTbsCertificate tbs;
- if (!net::ParseTbsCertificate(tbs_certificate_tlv, &tbs))
- return false;
-
- // Get the extensions.
- if (!tbs.has_extensions)
- return false;
- ExtensionsMap extensions;
- if (!net::ParseExtensions(tbs.extensions_tlv, &extensions))
- return false;
-
- net::der::Input extension_value;
-
// Get the Key Usage extension.
- if (!GetExtensionValue(extensions, net::KeyUsageOid(), &extension_value))
- return false;
- net::der::BitString key_usage;
- if (!net::ParseKeyUsage(extension_value, &key_usage))
+ if (!cert->has_key_usage())
return false;
// Ensure Key Usage contains digitalSignature.
- if (!key_usage.AssertsBit(net::KEY_USAGE_BIT_DIGITAL_SIGNATURE))
+ if (!cert->key_usage().AssertsBit(net::KEY_USAGE_BIT_DIGITAL_SIGNATURE))
return false;
// Get the Extended Key Usage extension.
- if (!GetExtensionValue(extensions, net::ExtKeyUsageOid(), &extension_value))
+ net::der::Input extension_value;
+ if (!GetExtensionValue(cert->unparsed_extensions(), net::ExtKeyUsageOid(),
+ &extension_value)) {
return false;
+ }
std::vector<net::der::Input> ekus;
if (!net::ParseEKUExtension(extension_value, &ekus))
return false;
@@ -221,8 +208,8 @@
// Check for an optional audio-only policy extension.
*policy = CastDeviceCertPolicy::NONE;
- if (GetExtensionValue(extensions, net::CertificatePoliciesOid(),
- &extension_value)) {
+ if (GetExtensionValue(cert->unparsed_extensions(),
+ net::CertificatePoliciesOid(), &extension_value)) {
std::vector<net::der::Input> policies;
if (!net::ParseCertificatePoliciesExtension(extension_value, &policies))
return false;
@@ -236,10 +223,11 @@
// Get the Common Name for the certificate.
std::string common_name;
- if (!GetCommonNameFromSubject(tbs.subject_tlv, &common_name))
+ if (!GetCommonNameFromSubject(cert->tbs().subject_tlv, &common_name))
return false;
- context->reset(new CertVerificationContextImpl(tbs.spki_tlv, common_name));
+ context->reset(
+ new CertVerificationContextImpl(cert->tbs().spki_tlv, common_name));
return true;
}
@@ -256,6 +244,20 @@
return result;
}
+class ScopedCheckUnreferencedCerts {
+ public:
+ explicit ScopedCheckUnreferencedCerts(
+ std::vector<scoped_refptr<net::ParsedCertificate>>* certs)
+ : certs_(certs) {}
+ ~ScopedCheckUnreferencedCerts() {
+ for (const auto& cert : *certs_)
+ DCHECK(cert->HasOneRef());
+ }
+
+ private:
+ std::vector<scoped_refptr<net::ParsedCertificate>>* certs_;
+};
+
} // namespace
bool VerifyDeviceCert(const std::vector<std::string>& certs,
@@ -263,10 +265,22 @@
std::unique_ptr<CertVerificationContext>* context,
CastDeviceCertPolicy* policy) {
// The underlying verification function expects a sequence of
- // der::Input, so wrap the data in it (cheap).
- std::vector<net::der::Input> input_chain;
- for (const auto& cert : certs)
- input_chain.push_back(net::der::Input(&cert));
+ // ParsedCertificate.
+ std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
+ // Verify that nothing saves a reference to the input certs, since the backing
+ // data will go out of scope when the function finishes.
+ ScopedCheckUnreferencedCerts ref_checker(&input_chain);
+
+ for (const auto& cert_der : certs) {
+ // No reference to the ParsedCertificate is kept past the end of this
+ // function, so using EXTERNAL_REFERENCE here is safe.
+ if (!net::ParsedCertificate::CreateAndAddToVector(
+ reinterpret_cast<const uint8_t*>(cert_der.data()), cert_der.size(),
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE,
+ &input_chain)) {
+ return false;
+ }
+ }
// Use a signature policy compatible with Cast's PKI.
auto signature_policy = CreateCastSignaturePolicy();
@@ -281,7 +295,7 @@
// Check properties of the leaf certificate (key usage, policy), and construct
// a CertVerificationContext that uses its public key.
- return CheckTargetCertificate(input_chain[0], context, policy);
+ return CheckTargetCertificate(input_chain[0].get(), context, policy);
}
std::unique_ptr<CertVerificationContext> CertVerificationContextImplForTest(
@@ -293,8 +307,14 @@
}
bool AddTrustAnchorForTest(const uint8_t* data, size_t length) {
- return CastTrustStore::Get().AddTrustedCertificateWithoutCopying(data,
- length);
+ scoped_refptr<net::ParsedCertificate> anchor(
+ net::ParsedCertificate::CreateFromCertificateData(
+ data, length,
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE));
+ if (!anchor)
+ return false;
+ CastTrustStore::Get().AddTrustedCertificate(std::move(anchor));
+ return true;
}
} // namespace cast_certificate
diff --git a/net/cert/internal/parse_certificate.cc b/net/cert/internal/parse_certificate.cc
index 6bf3e3a..2a69926 100644
--- a/net/cert/internal/parse_certificate.cc
+++ b/net/cert/internal/parse_certificate.cc
@@ -508,6 +508,19 @@
return true;
}
+NET_EXPORT bool ConsumeExtension(
+ const der::Input& oid,
+ std::map<der::Input, ParsedExtension>* unconsumed_extensions,
+ ParsedExtension* extension) {
+ auto it = unconsumed_extensions->find(oid);
+ if (it == unconsumed_extensions->end())
+ return false;
+
+ *extension = it->second;
+ unconsumed_extensions->erase(it);
+ return true;
+}
+
bool ParseBasicConstraints(const der::Input& basic_constraints_tlv,
ParsedBasicConstraints* out) {
der::Parser parser(basic_constraints_tlv);
diff --git a/net/cert/internal/parse_certificate.h b/net/cert/internal/parse_certificate.h
index d3c4d97..1414d2e0 100644
--- a/net/cert/internal/parse_certificate.h
+++ b/net/cert/internal/parse_certificate.h
@@ -324,6 +324,14 @@
const der::Input& extensions_tlv,
std::map<der::Input, ParsedExtension>* extensions) WARN_UNUSED_RESULT;
+// Removes the extension with OID |oid| from |unconsumed_extensions| and fills
+// |extension| with the matching extension value. If there was no extension
+// matching |oid| then returns |false|.
+NET_EXPORT bool ConsumeExtension(
+ const der::Input& oid,
+ std::map<der::Input, ParsedExtension>* unconsumed_extensions,
+ ParsedExtension* extension) WARN_UNUSED_RESULT;
+
struct ParsedBasicConstraints {
bool is_ca = false;
bool has_path_len = false;
diff --git a/net/cert/internal/parsed_certificate.cc b/net/cert/internal/parsed_certificate.cc
new file mode 100644
index 0000000..0f836b20
--- /dev/null
+++ b/net/cert/internal/parsed_certificate.cc
@@ -0,0 +1,158 @@
+// Copyright 2016 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/parsed_certificate.h"
+
+#include "net/cert/internal/name_constraints.h"
+#include "net/cert/internal/signature_algorithm.h"
+#include "net/cert/internal/verify_name_match.h"
+#include "net/der/parser.h"
+
+namespace net {
+
+namespace {
+
+WARN_UNUSED_RESULT bool GetSequenceValue(const der::Input& tlv,
+ der::Input* value) {
+ der::Parser parser(tlv);
+ return parser.ReadTag(der::kSequence, value) && !parser.HasMore();
+}
+
+} // namespace
+
+ParsedCertificate::ParsedCertificate() {}
+ParsedCertificate::~ParsedCertificate() {}
+
+scoped_refptr<ParsedCertificate> ParsedCertificate::CreateFromCertificateData(
+ const uint8_t* data,
+ size_t length,
+ DataSource source) {
+ scoped_refptr<ParsedCertificate> result(new ParsedCertificate);
+
+ 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;
+ }
+
+ if (!ParseCertificate(result->cert_, &result->tbs_certificate_tlv_,
+ &result->signature_algorithm_tlv_,
+ &result->signature_value_)) {
+ return nullptr;
+ }
+
+ if (!ParseTbsCertificate(result->tbs_certificate_tlv_, &result->tbs_))
+ return nullptr;
+
+ // Attempt to parse the signature algorithm contained in the Certificate.
+ // Do not give up on failure here, since SignatureAlgorithm::CreateFromDer
+ // will fail on valid but unsupported signature algorithms.
+ // TODO(mattm): should distinguish between unsupported algorithms and parsing
+ // errors.
+ result->signature_algorithm_ =
+ SignatureAlgorithm::CreateFromDer(result->signature_algorithm_tlv_);
+
+ der::Input subject_value;
+ if (!GetSequenceValue(result->tbs_.subject_tlv, &subject_value) ||
+ !NormalizeName(subject_value, &result->normalized_subject_)) {
+ return nullptr;
+ }
+ der::Input issuer_value;
+ if (!GetSequenceValue(result->tbs_.issuer_tlv, &issuer_value) ||
+ !NormalizeName(issuer_value, &result->normalized_issuer_)) {
+ return nullptr;
+ }
+
+ // Parse the standard X.509 extensions and remove them from
+ // |unparsed_extensions|.
+ if (result->tbs_.has_extensions) {
+ // ParseExtensions() ensures there are no duplicates, and maps the (unique)
+ // OID to the extension value.
+ if (!ParseExtensions(result->tbs_.extensions_tlv,
+ &result->unparsed_extensions_)) {
+ return nullptr;
+ }
+
+ ParsedExtension extension;
+
+ // Basic constraints.
+ if (ConsumeExtension(BasicConstraintsOid(), &result->unparsed_extensions_,
+ &extension)) {
+ result->has_basic_constraints_ = true;
+ if (!ParseBasicConstraints(extension.value, &result->basic_constraints_))
+ return nullptr;
+ }
+
+ // KeyUsage.
+ if (ConsumeExtension(KeyUsageOid(), &result->unparsed_extensions_,
+ &extension)) {
+ result->has_key_usage_ = true;
+ if (!ParseKeyUsage(extension.value, &result->key_usage_))
+ return nullptr;
+ }
+
+ // Subject alternative name.
+ if (ConsumeExtension(SubjectAltNameOid(), &result->unparsed_extensions_,
+ &result->subject_alt_names_extension_)) {
+ // RFC 5280 section 4.2.1.6:
+ // SubjectAltName ::= GeneralNames
+ result->subject_alt_names_ = GeneralNames::CreateFromDer(
+ result->subject_alt_names_extension_.value);
+ if (!result->subject_alt_names_)
+ return nullptr;
+ // RFC 5280 section 4.1.2.6:
+ // If subject naming information is present only in the subjectAltName
+ // extension (e.g., a key bound only to an email address or URI), then the
+ // subject name MUST be an empty sequence and the subjectAltName extension
+ // MUST be critical.
+ if (subject_value.Length() == 0 &&
+ !result->subject_alt_names_extension_.critical) {
+ return nullptr;
+ }
+ }
+
+ // Name constraints.
+ if (ConsumeExtension(NameConstraintsOid(), &result->unparsed_extensions_,
+ &extension)) {
+ result->name_constraints_ =
+ NameConstraints::CreateFromDer(extension.value, extension.critical);
+ if (!result->name_constraints_)
+ return nullptr;
+ }
+
+ // NOTE: if additional extensions are consumed here, the verification code
+ // must be updated to process those extensions, since the
+ // VerifyNoUnconsumedCriticalExtensions uses the unparsed_extensions_
+ // variable to tell which extensions were processed.
+ }
+
+ return result;
+}
+
+scoped_refptr<ParsedCertificate> ParsedCertificate::CreateFromCertificateCopy(
+ const base::StringPiece& data) {
+ return ParsedCertificate::CreateFromCertificateData(
+ reinterpret_cast<const uint8_t*>(data.data()), data.size(),
+ DataSource::INTERNAL_COPY);
+}
+
+bool ParsedCertificate::CreateAndAddToVector(
+ const uint8_t* data,
+ size_t length,
+ DataSource source,
+ std::vector<scoped_refptr<ParsedCertificate>>* chain) {
+ scoped_refptr<ParsedCertificate> cert(
+ CreateFromCertificateData(data, length, source));
+ if (!cert)
+ return false;
+ chain->push_back(std::move(cert));
+ return true;
+}
+
+} // namespace net
diff --git a/net/cert/internal/parsed_certificate.h b/net/cert/internal/parsed_certificate.h
new file mode 100644
index 0000000..7c9ae3c
--- /dev/null
+++ b/net/cert/internal/parsed_certificate.h
@@ -0,0 +1,213 @@
+// Copyright 2016 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.
+
+#ifndef NET_CERT_INTERNAL_PARSED_CERTIFICATE_H_
+#define NET_CERT_INTERNAL_PARSED_CERTIFICATE_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "net/base/net_export.h"
+#include "net/cert/internal/parse_certificate.h"
+#include "net/der/input.h"
+
+namespace net {
+
+struct GeneralNames;
+class NameConstraints;
+class SignatureAlgorithm;
+
+// Represents an X.509 certificate, including Certificate, TBSCertificate, and
+// standard extensions.
+// Creating a ParsedCertificate does not completely parse and validate the
+// certificate data. Presence of a member in this class implies the DER was
+// parsed successfully to that level, but does not imply the contents of that
+// member are valid, unless otherwise specified. See the documentation for each
+// member or the documentation of the type it returns.
+class NET_EXPORT ParsedCertificate
+ : public base::RefCountedThreadSafe<ParsedCertificate> {
+ public:
+ // Map from OID to ParsedExtension.
+ using ExtensionsMap = std::map<der::Input, ParsedExtension>;
+
+ // The certificate data for may either be owned internally (INTERNAL_COPY) or
+ // owned externally (EXTERNAL_REFERENCE). When it is owned internally the data
+ // is held by |cert_data_|
+ enum class DataSource {
+ INTERNAL_COPY,
+ EXTERNAL_REFERENCE,
+ };
+
+ // Creates a ParsedCertificate given a DER-encoded Certificate. Returns
+ // nullptr on failure. Failure will occur if the standard certificate fields
+ // and supported extensions cannot be parsed.
+ //
+ // The provided certificate data is either copied, or aliased, depending on
+ // the value of |source|. See the comments for DataSource for details.
+ static scoped_refptr<ParsedCertificate> CreateFromCertificateData(
+ const uint8_t* data,
+ size_t length,
+ DataSource source);
+
+ // Creates a ParsedCertificate and appends it to |chain|. Returns true if the
+ // certificate was successfully parsed and added. If false is return, |chain|
+ // is unmodified.
+ static bool CreateAndAddToVector(
+ const uint8_t* data,
+ size_t length,
+ DataSource source,
+ std::vector<scoped_refptr<net::ParsedCertificate>>* chain);
+
+ // Creates a ParsedCertificate, copying the data from |data|.
+ static scoped_refptr<ParsedCertificate> CreateFromCertificateCopy(
+ const base::StringPiece& data);
+
+ // Returns the DER-encoded certificate data for this cert.
+ const der::Input& der_cert() const { return cert_; }
+
+ // Accessors for raw fields of the Certificate.
+ const der::Input& tbs_certificate_tlv() const { return tbs_certificate_tlv_; }
+
+ const der::Input& signature_algorithm_tlv() const {
+ return signature_algorithm_tlv_;
+ }
+
+ const der::BitString& signature_value() const { return signature_value_; }
+
+ // Accessor for struct containing raw fields of the TbsCertificate.
+ const ParsedTbsCertificate& tbs() const { return tbs_; }
+
+ // Returns true if the signatureAlgorithm of the Certificate is supported and
+ // valid.
+ bool has_valid_supported_signature_algorithm() const {
+ return signature_algorithm_ != nullptr;
+ }
+
+ // Returns the signatureAlgorithm of the Certificate (not the tbsCertificate).
+ // Must not be called if has_valid_supported_signature_algorithm() is false.
+ const SignatureAlgorithm& signature_algorithm() const {
+ DCHECK(signature_algorithm_);
+ return *signature_algorithm_;
+ }
+
+ // Returns the DER-encoded normalized subject value (not including outer
+ // Sequence tag). This is gauranteed to be valid DER, though the contents of
+ // unhandled string types are treated as raw bytes.
+ der::Input normalized_subject() const {
+ return der::Input(&normalized_subject_);
+ }
+ // Returns the DER-encoded normalized issuer value (not including outer
+ // Sequence tag). This is gauranteed to be valid DER, though the contents of
+ // unhandled string types are treated as raw bytes.
+ der::Input normalized_issuer() const {
+ return der::Input(&normalized_issuer_);
+ }
+
+ // Returns true if the certificate has a BasicConstraints extension.
+ bool has_basic_constraints() const { return has_basic_constraints_; }
+
+ // Returns the ParsedBasicConstraints struct. Caller must check
+ // has_basic_constraints() before accessing this.
+ const ParsedBasicConstraints& basic_constraints() const {
+ DCHECK(has_basic_constraints_);
+ return basic_constraints_;
+ }
+
+ // Returns true if the certificate has a KeyUsage extension.
+ bool has_key_usage() const { return has_key_usage_; }
+
+ // Returns the KeyUsage BitString. Caller must check
+ // has_key_usage() before accessing this.
+ const der::BitString& key_usage() const {
+ DCHECK(has_key_usage_);
+ return key_usage_;
+ }
+
+ // Returns true if the certificate has a SubjectAltName extension.
+ bool has_subject_alt_names() const { return subject_alt_names_ != nullptr; }
+
+ // Returns the ParsedExtension struct for the SubjectAltName extension.
+ // If the cert did not have a SubjectAltName extension, this will be a
+ // default-initialized ParsedExtension struct.
+ const ParsedExtension& subject_alt_names_extension() const {
+ return subject_alt_names_extension_;
+ }
+
+ // Returns the GeneralNames class parsed from SubjectAltName extension, or
+ // nullptr if no SubjectAltName extension was present.
+ const GeneralNames* subject_alt_names() const {
+ return subject_alt_names_.get();
+ }
+
+ // Returns true if the certificate has a NameConstraints extension.
+ bool has_name_constraints() const { return name_constraints_ != nullptr; }
+
+ // Returns the parsed NameConstraints extension. Must not be called if
+ // has_name_constraints() is false.
+ const NameConstraints& name_constraints() const {
+ DCHECK(name_constraints_);
+ return *name_constraints_;
+ }
+
+ // Returns a map of unhandled extensions (excludes the ones above).
+ const ExtensionsMap& unparsed_extensions() const {
+ return unparsed_extensions_;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<ParsedCertificate>;
+ ParsedCertificate();
+ ~ParsedCertificate();
+
+ // The backing store for the certificate data. This is only applicable when
+ // the ParsedCertificate was initialized using DataSource::INTERNAL_COPY.
+ std::vector<uint8_t> cert_data_;
+
+ // Note that the backing data for |cert_| (and its may come either from
+ // |cert_data_| or some external buffer (depending on how the
+ // ParsedCertificate was created).
+
+ // Points to the raw certificate DER.
+ der::Input cert_;
+
+ der::Input tbs_certificate_tlv_;
+ der::Input signature_algorithm_tlv_;
+ der::BitString signature_value_;
+ ParsedTbsCertificate tbs_;
+
+ // The signatureAlgorithm from the Certificate.
+ std::unique_ptr<SignatureAlgorithm> signature_algorithm_;
+
+ // Normalized DER-encoded Subject (not including outer Sequence tag).
+ std::string normalized_subject_;
+ // Normalized DER-encoded Issuer (not including outer Sequence tag).
+ std::string normalized_issuer_;
+
+ // BasicConstraints extension.
+ bool has_basic_constraints_ = false;
+ ParsedBasicConstraints basic_constraints_;
+
+ // KeyUsage extension.
+ bool has_key_usage_ = false;
+ der::BitString key_usage_;
+
+ // Raw SubjectAltName extension.
+ ParsedExtension subject_alt_names_extension_;
+ // Parsed SubjectAltName extension.
+ std::unique_ptr<GeneralNames> subject_alt_names_;
+
+ // NameConstraints extension.
+ std::unique_ptr<NameConstraints> name_constraints_;
+
+ // The remaining extensions (excludes the standard ones above).
+ ExtensionsMap unparsed_extensions_;
+
+ DISALLOW_COPY_AND_ASSIGN(ParsedCertificate);
+};
+
+} // namespace net
+
+#endif // NET_CERT_INTERNAL_PARSED_CERTIFICATE_H_
diff --git a/net/cert/internal/trust_store.cc b/net/cert/internal/trust_store.cc
new file mode 100644
index 0000000..892698ba5
--- /dev/null
+++ b/net/cert/internal/trust_store.cc
@@ -0,0 +1,44 @@
+// Copyright 2016 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/trust_store.h"
+
+#include "net/cert/internal/parsed_certificate.h"
+
+namespace net {
+
+TrustStore::TrustStore() {}
+TrustStore::~TrustStore() {}
+
+void TrustStore::Clear() {
+ anchors_.clear();
+}
+
+void TrustStore::AddTrustedCertificate(
+ scoped_refptr<ParsedCertificate> anchor) {
+ // TODO(mattm): should this check for duplicate certs?
+ anchors_.insert(std::make_pair(anchor->normalized_subject().AsStringPiece(),
+ std::move(anchor)));
+}
+
+void TrustStore::FindTrustAnchorsByNormalizedName(
+ const der::Input& normalized_name,
+ std::vector<scoped_refptr<ParsedCertificate>>* matches) const {
+ auto range = anchors_.equal_range(normalized_name.AsStringPiece());
+ for (auto it = range.first; it != range.second; ++it)
+ matches->push_back(it->second);
+}
+
+bool TrustStore::IsTrustedCertificate(const ParsedCertificate* cert) const {
+ auto range = anchors_.equal_range(cert->normalized_subject().AsStringPiece());
+ for (auto it = range.first; it != range.second; ++it) {
+ // First compare the ParsedCertificate pointers as an optimization, fall
+ // back to comparing full DER encoding.
+ if (it->second == cert || it->second->der_cert() == cert->der_cert())
+ return true;
+ }
+ return false;
+}
+
+} // namespace net
diff --git a/net/cert/internal/trust_store.h b/net/cert/internal/trust_store.h
new file mode 100644
index 0000000..611af1c
--- /dev/null
+++ b/net/cert/internal/trust_store.h
@@ -0,0 +1,59 @@
+// Copyright 2016 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.
+
+#ifndef NET_CERT_INTERNAL_TRUST_STORE_H_
+#define NET_CERT_INTERNAL_TRUST_STORE_H_
+
+#include <unordered_map>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+namespace der {
+class Input;
+}
+
+class ParsedCertificate;
+
+// A very simple implementation of a TrustStore, which contains a set of
+// trusted certificates.
+// TODO(mattm): convert this into an interface, provide implementations that
+// interface with OS trust store.
+class NET_EXPORT TrustStore {
+ public:
+ TrustStore();
+ ~TrustStore();
+
+ // Empties the trust store, resetting it to original state.
+ void Clear();
+
+ // Adds a trusted certificate to the store.
+ void AddTrustedCertificate(scoped_refptr<ParsedCertificate> anchor);
+
+ // Returns the trust anchors that match |name| in |*matches|, if any.
+ void FindTrustAnchorsByNormalizedName(
+ const der::Input& normalized_name,
+ std::vector<scoped_refptr<ParsedCertificate>>* matches) const;
+
+ // Returns true if |cert| matches a certificate in the TrustStore.
+ bool IsTrustedCertificate(const ParsedCertificate* cert) const
+ WARN_UNUSED_RESULT;
+
+ private:
+ // Multimap from normalized subject -> ParsedCertificate.
+ std::unordered_multimap<base::StringPiece,
+ scoped_refptr<ParsedCertificate>,
+ base::StringPieceHash>
+ anchors_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrustStore);
+};
+
+} // namespace net
+
+#endif // NET_CERT_INTERNAL_TRUST_STORE_H_
diff --git a/net/cert/internal/verify_certificate_chain.cc b/net/cert/internal/verify_certificate_chain.cc
index edfbfcb5..f6a45b24 100644
--- a/net/cert/internal/verify_certificate_chain.cc
+++ b/net/cert/internal/verify_certificate_chain.cc
@@ -9,9 +9,10 @@
#include "base/logging.h"
#include "net/cert/internal/name_constraints.h"
#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/internal/parsed_certificate.h"
#include "net/cert/internal/signature_algorithm.h"
#include "net/cert/internal/signature_policy.h"
-#include "net/cert/internal/verify_name_match.h"
+#include "net/cert/internal/trust_store.h"
#include "net/cert/internal/verify_signed_data.h"
#include "net/der/input.h"
#include "net/der/parser.h"
@@ -20,172 +21,17 @@
namespace {
-// Map from OID to ParsedExtension.
-using ExtensionsMap = std::map<der::Input, ParsedExtension>;
-
-// Describes all parsed properties of a certificate that are relevant for
-// certificate verification.
-struct FullyParsedCert {
- der::Input tbs_certificate_tlv;
- der::Input signature_algorithm_tlv;
- der::BitString signature_value;
- ParsedTbsCertificate tbs;
-
- std::unique_ptr<SignatureAlgorithm> signature_algorithm;
-
- // Standard extensions that were parsed.
- bool has_basic_constraints = false;
- ParsedBasicConstraints basic_constraints;
-
- bool has_key_usage = false;
- der::BitString key_usage;
-
- std::unique_ptr<GeneralNames> subject_alt_names;
-
- bool has_name_constraints = false;
- ParsedExtension name_constraints_extension;
-
- // The remaining extensions (excludes the standard ones above).
- ExtensionsMap unconsumed_extensions;
-};
-
-// Removes the extension with OID |oid| from |unconsumed_extensions| and fills
-// |extension| with the matching extension value. If there was no extension
-// matching |oid| then returns |false|.
-WARN_UNUSED_RESULT bool ConsumeExtension(const der::Input& oid,
- ExtensionsMap* unconsumed_extensions,
- ParsedExtension* extension) {
- auto it = unconsumed_extensions->find(oid);
- if (it == unconsumed_extensions->end())
- return false;
-
- *extension = it->second;
- unconsumed_extensions->erase(it);
- return true;
-}
-
// Returns true if the certificate does not contain any unconsumed _critical_
// extensions.
WARN_UNUSED_RESULT bool VerifyNoUnconsumedCriticalExtensions(
- const FullyParsedCert& cert) {
- for (const auto& entry : cert.unconsumed_extensions) {
+ const ParsedCertificate& cert) {
+ for (const auto& entry : cert.unparsed_extensions()) {
if (entry.second.critical)
return false;
}
return true;
}
-WARN_UNUSED_RESULT bool GetSequenceValue(const der::Input& tlv,
- der::Input* value) {
- der::Parser parser(tlv);
- return parser.ReadTag(der::kSequence, value) && !parser.HasMore();
-}
-
-// Parses an X.509 Certificate fully (including the TBSCertificate and
-// standard extensions), saving all the properties to |out_|.
-WARN_UNUSED_RESULT bool FullyParseCertificate(const der::Input& cert_tlv,
- FullyParsedCert* out) {
- // Parse the outer Certificate.
- if (!ParseCertificate(cert_tlv, &out->tbs_certificate_tlv,
- &out->signature_algorithm_tlv, &out->signature_value))
- return false;
-
- // Parse the signature algorithm contained in the Certificate (there is
- // another one in the TBSCertificate, which is checked later by
- // VerifySignatureAlgorithmsMatch)
- out->signature_algorithm =
- SignatureAlgorithm::CreateFromDer(out->signature_algorithm_tlv);
- if (!out->signature_algorithm)
- return false;
-
- // Parse the TBSCertificate.
- if (!ParseTbsCertificate(out->tbs_certificate_tlv, &out->tbs))
- return false;
-
- // Reset state relating to extensions (which may not get overwritten). This is
- // just a precaution, since in practice |out| will already be default
- // initialize.
- out->has_basic_constraints = false;
- out->has_key_usage = false;
- out->unconsumed_extensions.clear();
- out->subject_alt_names.reset();
- out->has_name_constraints = false;
-
- // Parse the standard X.509 extensions and remove them from
- // |unconsumed_extensions|.
- if (out->tbs.has_extensions) {
- // ParseExtensions() ensures there are no duplicates, and maps the (unique)
- // OID to the extension value.
- if (!ParseExtensions(out->tbs.extensions_tlv, &out->unconsumed_extensions))
- return false;
-
- ParsedExtension extension;
-
- // Basic constraints.
- if (ConsumeExtension(BasicConstraintsOid(), &out->unconsumed_extensions,
- &extension)) {
- out->has_basic_constraints = true;
- if (!ParseBasicConstraints(extension.value, &out->basic_constraints))
- return false;
- }
-
- // KeyUsage.
- if (ConsumeExtension(KeyUsageOid(), &out->unconsumed_extensions,
- &extension)) {
- out->has_key_usage = true;
- if (!ParseKeyUsage(extension.value, &out->key_usage))
- return false;
- }
-
- // Subject alternative name.
- if (ConsumeExtension(SubjectAltNameOid(), &out->unconsumed_extensions,
- &extension)) {
- // RFC 5280 section 4.2.1.6:
- // SubjectAltName ::= GeneralNames
- out->subject_alt_names = GeneralNames::CreateFromDer(extension.value);
- if (!out->subject_alt_names)
- return false;
- // RFC 5280 section 4.1.2.6:
- // If subject naming information is present only in the subjectAltName
- // extension (e.g., a key bound only to an email address or URI), then the
- // subject name MUST be an empty sequence and the subjectAltName extension
- // MUST be critical.
- if (!extension.critical) {
- der::Input subject_value;
- if (!GetSequenceValue(out->tbs.subject_tlv, &subject_value))
- return false;
- if (subject_value.Length() == 0)
- return false;
- }
- }
-
- // Name constraints.
- if (ConsumeExtension(NameConstraintsOid(), &out->unconsumed_extensions,
- &out->name_constraints_extension)) {
- out->has_name_constraints = true;
- }
- }
-
- return true;
-}
-
-// Returns true if |name1_tlv| matches |name2_tlv|. The two inputs must be
-// tag-length-value for RFC 5280's Name.
-WARN_UNUSED_RESULT bool NameMatches(const der::Input& name1_tlv,
- const der::Input& name2_tlv) {
- der::Input name1_value;
- der::Input name2_value;
-
- // Assume that the Name is an RDNSequence. VerifyNameMatch() expects the
- // value from a SEQUENCE, so strip off the tag.
- if (!GetSequenceValue(name1_tlv, &name1_value) ||
- !GetSequenceValue(name2_tlv, &name2_value)) {
- return false;
- }
-
- return VerifyNameMatch(name1_value, name2_value);
-}
-
// Returns true if |cert| was self-issued. The definition of self-issuance
// comes from RFC 5280 section 6.1:
//
@@ -197,8 +43,8 @@
// support key rollover or changes in certificate policies. These
// self-issued certificates are not counted when evaluating path length
// or name constraints.
-WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) {
- return NameMatches(cert.tbs.subject_tlv, cert.tbs.issuer_tlv);
+WARN_UNUSED_RESULT bool IsSelfIssued(const ParsedCertificate& cert) {
+ return cert.normalized_subject() == cert.normalized_issuer();
}
// Returns true if |cert| is valid at time |time|.
@@ -208,10 +54,10 @@
//
// The validity period for a certificate is the period of time from
// notBefore through notAfter, inclusive.
-WARN_UNUSED_RESULT bool VerifyTimeValidity(const FullyParsedCert& cert,
+WARN_UNUSED_RESULT bool VerifyTimeValidity(const ParsedCertificate& cert,
const der::GeneralizedTime time) {
- return !(time < cert.tbs.validity_not_before) &&
- !(cert.tbs.validity_not_after < time);
+ return !(time < cert.tbs().validity_not_before) &&
+ !(cert.tbs().validity_not_after < time);
}
// Returns true if |signature_algorithm_tlv| is a valid algorithm encoding for
@@ -246,9 +92,9 @@
// specifying RSA with SHA1 (different OIDs). This is special-cased for
// compatibility sake.
WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch(
- const FullyParsedCert& cert) {
- const der::Input& alg1_tlv = cert.signature_algorithm_tlv;
- const der::Input& alg2_tlv = cert.tbs.signature_algorithm_tlv;
+ const ParsedCertificate& cert) {
+ const der::Input& alg1_tlv = cert.signature_algorithm_tlv();
+ const der::Input& alg2_tlv = cert.tbs().signature_algorithm_tlv;
// Ensure that the two DER-encoded signature algorithms are byte-for-byte
// equal, but make a compatibility concession for RSA with SHA1.
@@ -261,18 +107,17 @@
//
// |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|
+// - Checkinging that |cert|'s issuer matches |working_normalized_issuer_name|
// This should be set to true only when verifying a trusted root certificate.
WARN_UNUSED_RESULT bool BasicCertificateProcessing(
- const FullyParsedCert& cert,
+ const ParsedCertificate& cert,
bool is_target_cert,
bool skip_issuer_checks,
const SignaturePolicy* signature_policy,
const der::GeneralizedTime& time,
const der::Input& working_spki,
- const der::Input& working_issuer_name,
- const std::vector<std::unique_ptr<NameConstraints>>&
- name_constraints_list) {
+ const der::Input& working_normalized_issuer_name,
+ const std::vector<const NameConstraints*>& name_constraints_list) {
// Check that the signature algorithms in Certificate vs TBSCertificate
// match. This isn't part of RFC 5280 section 6.1.3, but is mandated by
// sections 4.1.1.2 and 4.1.2.3.
@@ -282,9 +127,10 @@
// 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.tbs_certificate_tlv,
- cert.signature_value, working_spki,
- signature_policy)) {
+ if (!cert.has_valid_supported_signature_algorithm() ||
+ !VerifySignedData(cert.signature_algorithm(),
+ cert.tbs_certificate_tlv(), cert.signature_value(),
+ working_spki, signature_policy)) {
return false;
}
}
@@ -300,7 +146,7 @@
// 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))
+ if (cert.normalized_issuer() != working_normalized_issuer_name)
return false;
}
@@ -309,12 +155,11 @@
// path, skip this step for certificate i.
if (!name_constraints_list.empty() &&
(!IsSelfIssued(cert) || is_target_cert)) {
- der::Input subject_value;
- if (!GetSequenceValue(cert.tbs.subject_tlv, &subject_value))
- return false;
- for (const auto& nc : name_constraints_list) {
- if (!nc->IsPermittedCert(subject_value, cert.subject_alt_names.get()))
+ for (const NameConstraints* nc : name_constraints_list) {
+ if (!nc->IsPermittedCert(cert.normalized_subject(),
+ cert.subject_alt_names())) {
return false;
+ }
}
}
@@ -327,38 +172,31 @@
// This function corresponds to RFC 5280 section 6.1.4's "Preparation for
// Certificate i+1" procedure. |cert| is expected to be an intermediary.
WARN_UNUSED_RESULT bool PrepareForNextCertificate(
- const FullyParsedCert& cert,
+ const ParsedCertificate& cert,
size_t* max_path_length_ptr,
der::Input* working_spki,
- der::Input* working_issuer_name,
- std::vector<std::unique_ptr<NameConstraints>>* name_constraints_list) {
+ der::Input* working_normalized_issuer_name,
+ std::vector<const NameConstraints*>* name_constraints_list) {
// TODO(eroman): Steps a-b are omitted, as policy constraints are not yet
// implemented.
// From RFC 5280 section 6.1.4 step c:
//
- // Assign the certificate subject name to working_issuer_name.
- *working_issuer_name = cert.tbs.subject_tlv;
+ // Assign the certificate subject name to working_normalized_issuer_name.
+ *working_normalized_issuer_name = cert.normalized_subject();
// From RFC 5280 section 6.1.4 step d:
//
// Assign the certificate subjectPublicKey to working_public_key.
- *working_spki = cert.tbs.spki_tlv;
+ *working_spki = cert.tbs().spki_tlv;
// Note that steps e and f are omitted as they are handled by
// the assignment to |working_spki| above. See the definition
// of |working_spki|.
// From RFC 5280 section 6.1.4 step g:
- if (cert.has_name_constraints) {
- std::unique_ptr<NameConstraints> name_constraints(
- NameConstraints::CreateFromDer(
- cert.name_constraints_extension.value,
- cert.name_constraints_extension.critical));
- if (!name_constraints)
- return false;
- name_constraints_list->push_back(std::move(name_constraints));
- }
+ if (cert.has_name_constraints())
+ name_constraints_list->push_back(&cert.name_constraints());
// TODO(eroman): Steps h-j are omitted as policy constraints are not yet
// implemented.
@@ -376,7 +214,7 @@
//
// This code implicitly rejects non version 3 intermediaries, since they
// can't contain a BasicConstraints extension.
- if (!cert.has_basic_constraints || !cert.basic_constraints.is_ca)
+ if (!cert.has_basic_constraints() || !cert.basic_constraints().is_ca)
return false;
// From RFC 5280 section 6.1.4 step l:
@@ -395,17 +233,17 @@
// If pathLenConstraint is present in the certificate and is
// less than max_path_length, set max_path_length to the value
// of pathLenConstraint.
- if (cert.basic_constraints.has_path_len &&
- cert.basic_constraints.path_len < *max_path_length_ptr) {
- *max_path_length_ptr = cert.basic_constraints.path_len;
+ if (cert.basic_constraints().has_path_len &&
+ cert.basic_constraints().path_len < *max_path_length_ptr) {
+ *max_path_length_ptr = cert.basic_constraints().path_len;
}
// From RFC 5280 section 6.1.4 step n:
//
// If a key usage extension is present, verify that the
// keyCertSign bit is set.
- if (cert.has_key_usage &&
- !cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)) {
+ if (cert.has_key_usage() &&
+ !cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)) {
return false;
}
@@ -444,20 +282,21 @@
// for compatibility reasons. Investigate if we need to similarly relax this
// constraint.
WARN_UNUSED_RESULT bool VerifyTargetCertHasConsistentCaBits(
- const FullyParsedCert& cert) {
+ const ParsedCertificate& cert) {
// Check if the certificate contains any property specific to CAs.
bool has_ca_property =
- (cert.has_basic_constraints &&
- (cert.basic_constraints.is_ca || cert.basic_constraints.has_path_len)) ||
- (cert.has_key_usage &&
- cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
+ (cert.has_basic_constraints() &&
+ (cert.basic_constraints().is_ca ||
+ cert.basic_constraints().has_path_len)) ||
+ (cert.has_key_usage() &&
+ cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
// If it "looks" like a CA because it has a CA-only property, then check that
// it sets ALL the properties expected of a CA.
if (has_ca_property) {
- return cert.has_basic_constraints && cert.basic_constraints.is_ca &&
- (!cert.has_key_usage ||
- cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
+ return cert.has_basic_constraints() && cert.basic_constraints().is_ca &&
+ (!cert.has_key_usage() ||
+ cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
}
return true;
@@ -465,7 +304,7 @@
// This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up Procedure".
// It does processing for the final certificate (the target cert).
-WARN_UNUSED_RESULT bool WrapUp(const FullyParsedCert& cert) {
+WARN_UNUSED_RESULT bool WrapUp(const ParsedCertificate& cert) {
// TODO(eroman): Steps a-b are omitted as policy constraints are not yet
// implemented.
@@ -497,102 +336,6 @@
} // 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.
- der::Input tbs_certificate_tlv;
- der::Input signature_algorithm_tlv;
- der::BitString signature_value;
- if (!ParseCertificate(result->cert(), &tbs_certificate_tlv,
- &signature_algorithm_tlv, &signature_value))
- return nullptr;
-
- ParsedTbsCertificate tbs;
- if (!ParseTbsCertificate(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() {}
-
-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 {
@@ -603,24 +346,24 @@
// 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,
+ const std::vector<scoped_refptr<ParsedCertificate>>& certs,
// 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())
+ if (certs.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()));
+ DCHECK(trust_store.IsTrustedCertificate(certs.back().get()));
// 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.
- std::vector<std::unique_ptr<NameConstraints>> name_constraints_list;
+ std::vector<const NameConstraints*> name_constraints_list;
// |working_spki| is an amalgamation of 3 separate variables from RFC 5280:
// * working_public_key
@@ -638,12 +381,12 @@
// 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_normalized_issuer_name| is the normalized value of the
+ // working_issuer_name variable in RFC 5280 section 6.1.2:
//
// working_issuer_name: the issuer distinguished name expected
// in the next certificate in the chain.
- der::Input working_issuer_name;
+ der::Input working_normalized_issuer_name;
// |max_path_length| corresponds with the same named variable in RFC 5280
// section 6.1.2:
@@ -653,7 +396,7 @@
// and may be reduced to the value in the path length constraint
// field within the basic constraints extension of a CA
// certificate.
- size_t max_path_length = certs_der.size();
+ size_t max_path_length = certs.size();
// Iterate over all the certificates in the reverse direction: starting from
// the trust anchor and progressing towards the target certificate.
@@ -662,37 +405,34 @@
//
// * 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;
+ for (size_t i = 0; i < certs.size(); ++i) {
+ const size_t index_into_certs = certs.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;
+ const bool is_target_cert = index_into_certs == 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;
+ const ParsedCertificate& cert = *certs[index_into_certs];
// 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, is_trust_anchor, 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_normalized_issuer_name,
+ name_constraints_list)) {
return false;
}
if (!is_target_cert) {
if (!PrepareForNextCertificate(cert, &max_path_length, &working_spki,
- &working_issuer_name,
+ &working_normalized_issuer_name,
&name_constraints_list)) {
return false;
}
@@ -717,56 +457,45 @@
// 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())
+ std::vector<scoped_refptr<ParsedCertificate>>* certs) {
+ if (certs->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()))
+ if (trust_store.IsTrustedCertificate(certs->back().get()))
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).
- der::Input tbs_certificate_tlv;
- der::Input signature_algorithm_tlv;
- der::BitString signature_value;
- ParsedTbsCertificate tbs;
- if (!ParseCertificate(certs_der.back(), &tbs_certificate_tlv,
- &signature_algorithm_tlv, &signature_value) ||
- !ParseTbsCertificate(tbs_certificate_tlv, &tbs)) {
+ std::vector<scoped_refptr<ParsedCertificate>> trust_anchors;
+ trust_store.FindTrustAnchorsByNormalizedName(
+ certs->back()->normalized_issuer(), &trust_anchors);
+ if (trust_anchors.empty())
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());
+ // TODO(mattm): this only tries the first match, even if there are multiple.
+ certs->push_back(std::move(trust_anchors[0]));
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)) {
+bool VerifyCertificateChain(
+ const std::vector<scoped_refptr<ParsedCertificate>>& cert_chain,
+ const TrustStore& trust_store,
+ const SignaturePolicy* signature_policy,
+ const der::GeneralizedTime& time) {
+ if (cert_chain.empty())
return false;
- }
+
+ std::vector<scoped_refptr<ParsedCertificate>> full_chain = cert_chain;
+
+ // Modify the certificate chain so that its root is a trusted certificate.
+ if (!BuildSimplePathToTrustAnchor(trust_store, &full_chain))
+ return false;
// Verify the chain.
- return VerifyCertificateChainAssumingTrustedRoot(
- certs_der_trusted_root, trust_store, signature_policy, time);
+ return VerifyCertificateChainAssumingTrustedRoot(full_chain, trust_store,
+ signature_policy, time);
}
} // namespace net
diff --git a/net/cert/internal/verify_certificate_chain.h b/net/cert/internal/verify_certificate_chain.h
index c2f83ebe..4d85eb6 100644
--- a/net/cert/internal/verify_certificate_chain.h
+++ b/net/cert/internal/verify_certificate_chain.h
@@ -5,15 +5,11 @@
#ifndef NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_H_
#define NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_H_
-#include <stdint.h>
-
-#include <memory>
-#include <string>
#include <vector>
#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
#include "net/base/net_export.h"
-#include "net/cert/internal/parse_certificate.h"
#include "net/der/input.h"
namespace net {
@@ -22,99 +18,9 @@
struct GeneralizedTime;
}
+class ParsedCertificate;
class SignaturePolicy;
-
-// Represents a trust anchor (i.e. a trusted root certificate).
-class NET_EXPORT TrustAnchor {
- public:
- // The certificate data for this trust anchor may either be owned internally
- // (INTERNAL_COPY) or owned externally (EXTERNAL_REFERENCE). When it is
- // owned internally the data is held by |cert_data_|
- enum class DataSource {
- INTERNAL_COPY,
- EXTERNAL_REFERENCE,
- };
-
- TrustAnchor();
- ~TrustAnchor();
-
- // Creates a TrustAnchor given a DER-encoded certificate. Returns nullptr on
- // failure. Failure will occur if the certificate data cannot be parsed to
- // find a subject.
- //
- // The provided certificate data is either copied, or aliased, depending on
- // the value of |source|. See the comments for DataSource for details.
- static std::unique_ptr<TrustAnchor> CreateFromCertificateData(
- const uint8_t* data,
- size_t length,
- DataSource source);
-
- // Returns true if the trust anchor matches |name|. In other words, returns
- // true if the certificate's subject matches |name|.
- bool MatchesName(const der::Input& name) const;
-
- // Returns the DER-encoded certificate data for this trust anchor.
- const der::Input& cert() const { return cert_; }
-
- private:
- // The backing store for the certificate data. This is only applicable when
- // the trust anchor was initialized using DataSource::INTERNAL_COPY.
- std::vector<uint8_t> cert_data_;
-
- // Note that the backing data for |cert_| and |name_| may come either form
- // |cert_data_| or some external buffer (depending on how the anchor was
- // created).
-
- // Points to the raw certificate DER.
- der::Input cert_;
-
- // Points to the subject TLV for the certificate.
- der::Input name_;
-
- DISALLOW_COPY_AND_ASSIGN(TrustAnchor);
-};
-
-// A very simple implementation of a TrustStore, which contains a set of
-// trusted certificates.
-class NET_EXPORT TrustStore {
- public:
- TrustStore();
- ~TrustStore();
-
- // Empties the trust store, resetting it to original state.
- void Clear();
-
- // Adds a trusted certificate to the store. The trust store makes a copy of
- // the provided certificate data.
- bool AddTrustedCertificate(const uint8_t* data,
- size_t length) WARN_UNUSED_RESULT;
- bool AddTrustedCertificate(const base::StringPiece& data) WARN_UNUSED_RESULT;
-
- // This function is the same as AddTrustedCertificate() except the underlying
- // data is not copied. The caller is responsible for ensuring that the data
- // pointer remains alive and is not mutated for the lifetime of the
- // TrustStore.
- bool AddTrustedCertificateWithoutCopying(const uint8_t* data,
- size_t length) WARN_UNUSED_RESULT;
-
- // Returns the trust anchor that matches |name|, or nullptr if there is none.
- // TODO(eroman): There may be multiple matches.
- const TrustAnchor* FindTrustAnchorByName(const der::Input& name) const
- WARN_UNUSED_RESULT;
-
- // Returns true if |cert_der| matches a certificate in the TrustStore.
- bool IsTrustedCertificate(const der::Input& cert_der) const
- WARN_UNUSED_RESULT;
-
- private:
- bool AddTrustedCertificate(const uint8_t* data,
- size_t length,
- TrustAnchor::DataSource source) WARN_UNUSED_RESULT;
-
- std::vector<std::unique_ptr<TrustAnchor>> anchors_;
-
- DISALLOW_COPY_AND_ASSIGN(TrustStore);
-};
+class TrustStore;
// VerifyCertificateChain() verifies a certificate path (chain) based on the
// rules in RFC 5280.
@@ -150,11 +56,11 @@
// ---------
//
// Returns true if the target certificate can be verified.
-NET_EXPORT bool VerifyCertificateChain(const std::vector<der::Input>& certs_der,
- const TrustStore& trust_store,
- const SignaturePolicy* signature_policy,
- const der::GeneralizedTime& time)
- WARN_UNUSED_RESULT;
+NET_EXPORT bool VerifyCertificateChain(
+ const std::vector<scoped_refptr<ParsedCertificate>>& cert_chain,
+ const TrustStore& trust_store,
+ const SignaturePolicy* signature_policy,
+ const der::GeneralizedTime& time) WARN_UNUSED_RESULT;
} // namespace net
diff --git a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
index 60656370..1a2fc78 100644
--- a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
@@ -4,8 +4,9 @@
#include "net/cert/internal/verify_certificate_chain.h"
-#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/internal/parsed_certificate.h"
#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/trust_store.h"
#include "net/der/input.h"
// Disable tests that require DSA signatures (DSA signatures are intentionally
@@ -53,13 +54,25 @@
}
// First entry in the PKITS chain is the trust anchor.
TrustStore trust_store;
- EXPECT_TRUE(trust_store.AddTrustedCertificate(cert_ders[0]));
+ scoped_refptr<ParsedCertificate> anchor(
+ ParsedCertificate::CreateFromCertificateCopy(cert_ders[0]));
+ EXPECT_TRUE(anchor);
+ if (anchor)
+ trust_store.AddTrustedCertificate(std::move(anchor));
// PKITS lists chains from trust anchor to target, VerifyCertificateChain
// takes them starting with the target and not including the trust anchor.
- std::vector<der::Input> input_chain;
- for (size_t i = cert_ders.size() - 1; i > 0; --i)
- input_chain.push_back(der::Input(&cert_ders[i]));
+ std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
+ for (size_t i = cert_ders.size() - 1; i > 0; --i) {
+ if (!net::ParsedCertificate::CreateAndAddToVector(
+ reinterpret_cast<const uint8_t*>(cert_ders[i].data()),
+ cert_ders[i].size(),
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE,
+ &input_chain)) {
+ ADD_FAILURE() << "cert " << i << " failed to parse";
+ return false;
+ }
+ }
SimpleSignaturePolicy signature_policy(1024);
diff --git a/net/cert/internal/verify_certificate_chain_unittest.cc b/net/cert/internal/verify_certificate_chain_unittest.cc
index a4443ca..11323f8 100644
--- a/net/cert/internal/verify_certificate_chain_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_unittest.cc
@@ -10,9 +10,10 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/internal/parsed_certificate.h"
#include "net/cert/internal/signature_policy.h"
#include "net/cert/internal/test_helpers.h"
+#include "net/cert/internal/trust_store.h"
#include "net/cert/pem_tokenizer.h"
#include "net/der/input.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -75,7 +76,10 @@
if (block_type == kCertificateHeader) {
chain->push_back(block_data);
} else if (block_type == kTrustedCertificateHeader) {
- ASSERT_TRUE(trust_store->AddTrustedCertificate(block_data));
+ scoped_refptr<ParsedCertificate> cert(
+ ParsedCertificate::CreateFromCertificateCopy(block_data));
+ ASSERT_TRUE(cert);
+ trust_store->AddTrustedCertificate(std::move(cert));
} else if (block_type == kTimeHeader) {
ASSERT_FALSE(has_time) << "Duplicate " << kTimeHeader;
has_time = true;
@@ -101,9 +105,12 @@
ReadTestFromFile(file_name, &chain, &trust_store, &time, &expected_result);
- std::vector<der::Input> input_chain;
- for (const auto& cert_str : chain)
- input_chain.push_back(der::Input(&cert_str));
+ std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
+ for (const auto& cert_der : chain) {
+ ASSERT_TRUE(net::ParsedCertificate::CreateAndAddToVector(
+ reinterpret_cast<const uint8_t*>(cert_der.data()), cert_der.size(),
+ net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, &input_chain));
+ }
SimpleSignaturePolicy signature_policy(1024);
@@ -225,7 +232,7 @@
TEST(VerifyCertificateChainTest, EmptyChainIsInvalid) {
TrustStore trust_store;
der::GeneralizedTime time;
- std::vector<der::Input> chain;
+ std::vector<scoped_refptr<ParsedCertificate>> chain;
SimpleSignaturePolicy signature_policy(2048);
ASSERT_FALSE(
diff --git a/net/net.gypi b/net/net.gypi
index 868c64c3..ea7bc65 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -98,10 +98,14 @@
'cert/internal/parse_name.h',
'cert/internal/parse_ocsp.cc',
'cert/internal/parse_ocsp.h',
+ 'cert/internal/parsed_certificate.cc',
+ 'cert/internal/parsed_certificate.h',
'cert/internal/signature_algorithm.cc',
'cert/internal/signature_algorithm.h',
'cert/internal/signature_policy.cc',
'cert/internal/signature_policy.h',
+ 'cert/internal/trust_store.cc',
+ 'cert/internal/trust_store.h',
'cert/internal/verify_certificate_chain.cc',
'cert/internal/verify_certificate_chain.h',
'cert/internal/verify_name_match.cc',