Commit cd67ff33 authored by mattm's avatar mattm Committed by Commit bot

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}
parent dbf2020a
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
#include "net/cert/internal/extended_key_usage.h" #include "net/cert/internal/extended_key_usage.h"
#include "net/cert/internal/parse_certificate.h" #include "net/cert/internal/parse_certificate.h"
#include "net/cert/internal/parse_name.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_algorithm.h"
#include "net/cert/internal/signature_policy.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_certificate_chain.h"
#include "net/cert/internal/verify_signed_data.h" #include "net/cert/internal/verify_signed_data.h"
#include "net/der/input.h" #include "net/der/input.h"
...@@ -55,10 +57,18 @@ class CastTrustStore { ...@@ -55,10 +57,18 @@ class CastTrustStore {
CastTrustStore() { CastTrustStore() {
// Initialize the trust store with two root certificates. // Initialize the trust store with two root certificates.
CHECK(store_.AddTrustedCertificateWithoutCopying(kCastRootCaDer, scoped_refptr<net::ParsedCertificate> root =
sizeof(kCastRootCaDer))); net::ParsedCertificate::CreateFromCertificateData(
CHECK(store_.AddTrustedCertificateWithoutCopying(kEurekaRootCaDer, kCastRootCaDer, sizeof(kCastRootCaDer),
sizeof(kEurekaRootCaDer))); 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_; net::TrustStore store_;
...@@ -167,50 +177,27 @@ bool HasClientAuth(const std::vector<net::der::Input>& ekus) { ...@@ -167,50 +177,27 @@ bool HasClientAuth(const std::vector<net::der::Input>& ekus) {
// Checks properties on the target certificate. // Checks properties on the target certificate.
// //
// * The Key Usage must include Digital Signature // * 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 // * May have the policy 1.3.6.1.4.1.11129.2.5.2 to indicate it
// is an audio-only device. // is an audio-only device.
WARN_UNUSED_RESULT bool CheckTargetCertificate( WARN_UNUSED_RESULT bool CheckTargetCertificate(
const net::der::Input& cert_der, const net::ParsedCertificate* cert,
std::unique_ptr<CertVerificationContext>* context, std::unique_ptr<CertVerificationContext>* context,
CastDeviceCertPolicy* policy) { 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. // Get the Key Usage extension.
if (!GetExtensionValue(extensions, net::KeyUsageOid(), &extension_value)) if (!cert->has_key_usage())
return false;
net::der::BitString key_usage;
if (!net::ParseKeyUsage(extension_value, &key_usage))
return false; return false;
// Ensure Key Usage contains digitalSignature. // 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; return false;
// Get the Extended Key Usage extension. // 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; return false;
}
std::vector<net::der::Input> ekus; std::vector<net::der::Input> ekus;
if (!net::ParseEKUExtension(extension_value, &ekus)) if (!net::ParseEKUExtension(extension_value, &ekus))
return false; return false;
...@@ -221,8 +208,8 @@ WARN_UNUSED_RESULT bool CheckTargetCertificate( ...@@ -221,8 +208,8 @@ WARN_UNUSED_RESULT bool CheckTargetCertificate(
// Check for an optional audio-only policy extension. // Check for an optional audio-only policy extension.
*policy = CastDeviceCertPolicy::NONE; *policy = CastDeviceCertPolicy::NONE;
if (GetExtensionValue(extensions, net::CertificatePoliciesOid(), if (GetExtensionValue(cert->unparsed_extensions(),
&extension_value)) { net::CertificatePoliciesOid(), &extension_value)) {
std::vector<net::der::Input> policies; std::vector<net::der::Input> policies;
if (!net::ParseCertificatePoliciesExtension(extension_value, &policies)) if (!net::ParseCertificatePoliciesExtension(extension_value, &policies))
return false; return false;
...@@ -236,10 +223,11 @@ WARN_UNUSED_RESULT bool CheckTargetCertificate( ...@@ -236,10 +223,11 @@ WARN_UNUSED_RESULT bool CheckTargetCertificate(
// Get the Common Name for the certificate. // Get the Common Name for the certificate.
std::string common_name; std::string common_name;
if (!GetCommonNameFromSubject(tbs.subject_tlv, &common_name)) if (!GetCommonNameFromSubject(cert->tbs().subject_tlv, &common_name))
return false; return false;
context->reset(new CertVerificationContextImpl(tbs.spki_tlv, common_name)); context->reset(
new CertVerificationContextImpl(cert->tbs().spki_tlv, common_name));
return true; return true;
} }
...@@ -256,6 +244,20 @@ net::der::GeneralizedTime ConvertExplodedTime( ...@@ -256,6 +244,20 @@ net::der::GeneralizedTime ConvertExplodedTime(
return result; 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 } // namespace
bool VerifyDeviceCert(const std::vector<std::string>& certs, bool VerifyDeviceCert(const std::vector<std::string>& certs,
...@@ -263,10 +265,22 @@ bool VerifyDeviceCert(const std::vector<std::string>& certs, ...@@ -263,10 +265,22 @@ bool VerifyDeviceCert(const std::vector<std::string>& certs,
std::unique_ptr<CertVerificationContext>* context, std::unique_ptr<CertVerificationContext>* context,
CastDeviceCertPolicy* policy) { CastDeviceCertPolicy* policy) {
// The underlying verification function expects a sequence of // The underlying verification function expects a sequence of
// der::Input, so wrap the data in it (cheap). // ParsedCertificate.
std::vector<net::der::Input> input_chain; std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
for (const auto& cert : certs) // Verify that nothing saves a reference to the input certs, since the backing
input_chain.push_back(net::der::Input(&cert)); // 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. // Use a signature policy compatible with Cast's PKI.
auto signature_policy = CreateCastSignaturePolicy(); auto signature_policy = CreateCastSignaturePolicy();
...@@ -281,7 +295,7 @@ bool VerifyDeviceCert(const std::vector<std::string>& certs, ...@@ -281,7 +295,7 @@ bool VerifyDeviceCert(const std::vector<std::string>& certs,
// Check properties of the leaf certificate (key usage, policy), and construct // Check properties of the leaf certificate (key usage, policy), and construct
// a CertVerificationContext that uses its public key. // 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( std::unique_ptr<CertVerificationContext> CertVerificationContextImplForTest(
...@@ -293,8 +307,14 @@ std::unique_ptr<CertVerificationContext> CertVerificationContextImplForTest( ...@@ -293,8 +307,14 @@ std::unique_ptr<CertVerificationContext> CertVerificationContextImplForTest(
} }
bool AddTrustAnchorForTest(const uint8_t* data, size_t length) { bool AddTrustAnchorForTest(const uint8_t* data, size_t length) {
return CastTrustStore::Get().AddTrustedCertificateWithoutCopying(data, scoped_refptr<net::ParsedCertificate> anchor(
length); 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 } // namespace cast_certificate
...@@ -508,6 +508,19 @@ NET_EXPORT bool ParseExtensions( ...@@ -508,6 +508,19 @@ NET_EXPORT bool ParseExtensions(
return true; 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, bool ParseBasicConstraints(const der::Input& basic_constraints_tlv,
ParsedBasicConstraints* out) { ParsedBasicConstraints* out) {
der::Parser parser(basic_constraints_tlv); der::Parser parser(basic_constraints_tlv);
......
...@@ -324,6 +324,14 @@ NET_EXPORT bool ParseExtensions( ...@@ -324,6 +324,14 @@ NET_EXPORT bool ParseExtensions(
const der::Input& extensions_tlv, const der::Input& extensions_tlv,
std::map<der::Input, ParsedExtension>* extensions) WARN_UNUSED_RESULT; 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 { struct ParsedBasicConstraints {
bool is_ca = false; bool is_ca = false;
bool has_path_len = false; bool has_path_len = false;
......
// 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
// 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_
// 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
// 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_
...@@ -5,15 +5,11 @@ ...@@ -5,15 +5,11 @@
#ifndef NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_H_ #ifndef NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_H_
#define 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 <vector>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "net/base/net_export.h" #include "net/base/net_export.h"
#include "net/cert/internal/parse_certificate.h"
#include "net/der/input.h" #include "net/der/input.h"
namespace net { namespace net {
...@@ -22,99 +18,9 @@ namespace der { ...@@ -22,99 +18,9 @@ namespace der {
struct GeneralizedTime; struct GeneralizedTime;
} }
class ParsedCertificate;
class SignaturePolicy; class SignaturePolicy;
class TrustStore;
// 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);
};
// VerifyCertificateChain() verifies a certificate path (chain) based on the // VerifyCertificateChain() verifies a certificate path (chain) based on the
// rules in RFC 5280. // rules in RFC 5280.
...@@ -150,11 +56,11 @@ class NET_EXPORT TrustStore { ...@@ -150,11 +56,11 @@ class NET_EXPORT TrustStore {
// --------- // ---------
// //
// Returns true if the target certificate can be verified. // Returns true if the target certificate can be verified.
NET_EXPORT bool VerifyCertificateChain(const std::vector<der::Input>& certs_der, NET_EXPORT bool VerifyCertificateChain(
const TrustStore& trust_store, const std::vector<scoped_refptr<ParsedCertificate>>& cert_chain,
const SignaturePolicy* signature_policy, const TrustStore& trust_store,
const der::GeneralizedTime& time) const SignaturePolicy* signature_policy,
WARN_UNUSED_RESULT; const der::GeneralizedTime& time) WARN_UNUSED_RESULT;
} // namespace net } // namespace net
......
...@@ -4,8 +4,9 @@ ...@@ -4,8 +4,9 @@
#include "net/cert/internal/verify_certificate_chain.h" #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/signature_policy.h"
#include "net/cert/internal/trust_store.h"
#include "net/der/input.h" #include "net/der/input.h"
// Disable tests that require DSA signatures (DSA signatures are intentionally // Disable tests that require DSA signatures (DSA signatures are intentionally
...@@ -53,13 +54,25 @@ class VerifyCertificateChainPkitsTestDelegate { ...@@ -53,13 +54,25 @@ class VerifyCertificateChainPkitsTestDelegate {
} }
// First entry in the PKITS chain is the trust anchor. // First entry in the PKITS chain is the trust anchor.
TrustStore trust_store; 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 // PKITS lists chains from trust anchor to target, VerifyCertificateChain
// takes them starting with the target and not including the trust anchor. // takes them starting with the target and not including the trust anchor.
std::vector<der::Input> input_chain; std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
for (size_t i = cert_ders.size() - 1; i > 0; --i) for (size_t i = cert_ders.size() - 1; i > 0; --i) {
input_chain.push_back(der::Input(&cert_ders[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); SimpleSignaturePolicy signature_policy(1024);
......
...@@ -10,9 +10,10 @@ ...@@ -10,9 +10,10 @@
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.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/signature_policy.h"
#include "net/cert/internal/test_helpers.h" #include "net/cert/internal/test_helpers.h"
#include "net/cert/internal/trust_store.h"
#include "net/cert/pem_tokenizer.h" #include "net/cert/pem_tokenizer.h"
#include "net/der/input.h" #include "net/der/input.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -75,7 +76,10 @@ void ReadTestFromFile(const std::string& file_name, ...@@ -75,7 +76,10 @@ void ReadTestFromFile(const std::string& file_name,
if (block_type == kCertificateHeader) { if (block_type == kCertificateHeader) {
chain->push_back(block_data); chain->push_back(block_data);
} else if (block_type == kTrustedCertificateHeader) { } 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) { } else if (block_type == kTimeHeader) {
ASSERT_FALSE(has_time) << "Duplicate " << kTimeHeader; ASSERT_FALSE(has_time) << "Duplicate " << kTimeHeader;
has_time = true; has_time = true;
...@@ -101,9 +105,12 @@ void RunTest(const char* file_name) { ...@@ -101,9 +105,12 @@ void RunTest(const char* file_name) {
ReadTestFromFile(file_name, &chain, &trust_store, &time, &expected_result); ReadTestFromFile(file_name, &chain, &trust_store, &time, &expected_result);
std::vector<der::Input> input_chain; std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
for (const auto& cert_str : chain) for (const auto& cert_der : chain) {
input_chain.push_back(der::Input(&cert_str)); 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); SimpleSignaturePolicy signature_policy(1024);
...@@ -225,7 +232,7 @@ TEST(VerifyCertificateChainTest, NonSelfSignedRoot) { ...@@ -225,7 +232,7 @@ TEST(VerifyCertificateChainTest, NonSelfSignedRoot) {
TEST(VerifyCertificateChainTest, EmptyChainIsInvalid) { TEST(VerifyCertificateChainTest, EmptyChainIsInvalid) {
TrustStore trust_store; TrustStore trust_store;
der::GeneralizedTime time; der::GeneralizedTime time;
std::vector<der::Input> chain; std::vector<scoped_refptr<ParsedCertificate>> chain;
SimpleSignaturePolicy signature_policy(2048); SimpleSignaturePolicy signature_policy(2048);
ASSERT_FALSE( ASSERT_FALSE(
......
...@@ -98,10 +98,14 @@ ...@@ -98,10 +98,14 @@
'cert/internal/parse_name.h', 'cert/internal/parse_name.h',
'cert/internal/parse_ocsp.cc', 'cert/internal/parse_ocsp.cc',
'cert/internal/parse_ocsp.h', 'cert/internal/parse_ocsp.h',
'cert/internal/parsed_certificate.cc',
'cert/internal/parsed_certificate.h',
'cert/internal/signature_algorithm.cc', 'cert/internal/signature_algorithm.cc',
'cert/internal/signature_algorithm.h', 'cert/internal/signature_algorithm.h',
'cert/internal/signature_policy.cc', 'cert/internal/signature_policy.cc',
'cert/internal/signature_policy.h', '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.cc',
'cert/internal/verify_certificate_chain.h', 'cert/internal/verify_certificate_chain.h',
'cert/internal/verify_name_match.cc', 'cert/internal/verify_name_match.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment