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_
...@@ -9,9 +9,10 @@ ...@@ -9,9 +9,10 @@
#include "base/logging.h" #include "base/logging.h"
#include "net/cert/internal/name_constraints.h" #include "net/cert/internal/name_constraints.h"
#include "net/cert/internal/parse_certificate.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_algorithm.h"
#include "net/cert/internal/signature_policy.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/cert/internal/verify_signed_data.h"
#include "net/der/input.h" #include "net/der/input.h"
#include "net/der/parser.h" #include "net/der/parser.h"
...@@ -20,172 +21,17 @@ namespace net { ...@@ -20,172 +21,17 @@ namespace net {
namespace { 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_ // Returns true if the certificate does not contain any unconsumed _critical_
// extensions. // extensions.
WARN_UNUSED_RESULT bool VerifyNoUnconsumedCriticalExtensions( WARN_UNUSED_RESULT bool VerifyNoUnconsumedCriticalExtensions(
const FullyParsedCert& cert) { const ParsedCertificate& cert) {
for (const auto& entry : cert.unconsumed_extensions) { for (const auto& entry : cert.unparsed_extensions()) {
if (entry.second.critical) if (entry.second.critical)
return false; return false;
} }
return true; 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 // Returns true if |cert| was self-issued. The definition of self-issuance
// comes from RFC 5280 section 6.1: // comes from RFC 5280 section 6.1:
// //
...@@ -197,8 +43,8 @@ WARN_UNUSED_RESULT bool NameMatches(const der::Input& name1_tlv, ...@@ -197,8 +43,8 @@ WARN_UNUSED_RESULT bool NameMatches(const der::Input& name1_tlv,
// support key rollover or changes in certificate policies. These // support key rollover or changes in certificate policies. These
// self-issued certificates are not counted when evaluating path length // self-issued certificates are not counted when evaluating path length
// or name constraints. // or name constraints.
WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) { WARN_UNUSED_RESULT bool IsSelfIssued(const ParsedCertificate& cert) {
return NameMatches(cert.tbs.subject_tlv, cert.tbs.issuer_tlv); return cert.normalized_subject() == cert.normalized_issuer();
} }
// Returns true if |cert| is valid at time |time|. // Returns true if |cert| is valid at time |time|.
...@@ -208,10 +54,10 @@ WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) { ...@@ -208,10 +54,10 @@ WARN_UNUSED_RESULT bool IsSelfIssued(const FullyParsedCert& cert) {
// //
// The validity period for a certificate is the period of time from // The validity period for a certificate is the period of time from
// notBefore through notAfter, inclusive. // notBefore through notAfter, inclusive.
WARN_UNUSED_RESULT bool VerifyTimeValidity(const FullyParsedCert& cert, WARN_UNUSED_RESULT bool VerifyTimeValidity(const ParsedCertificate& cert,
const der::GeneralizedTime time) { const der::GeneralizedTime time) {
return !(time < cert.tbs.validity_not_before) && return !(time < cert.tbs().validity_not_before) &&
!(cert.tbs.validity_not_after < time); !(cert.tbs().validity_not_after < time);
} }
// Returns true if |signature_algorithm_tlv| is a valid algorithm encoding for // Returns true if |signature_algorithm_tlv| is a valid algorithm encoding for
...@@ -246,9 +92,9 @@ WARN_UNUSED_RESULT bool IsRsaWithSha1SignatureAlgorithm( ...@@ -246,9 +92,9 @@ WARN_UNUSED_RESULT bool IsRsaWithSha1SignatureAlgorithm(
// specifying RSA with SHA1 (different OIDs). This is special-cased for // specifying RSA with SHA1 (different OIDs). This is special-cased for
// compatibility sake. // compatibility sake.
WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch( WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch(
const FullyParsedCert& cert) { const ParsedCertificate& cert) {
const der::Input& alg1_tlv = cert.signature_algorithm_tlv; const der::Input& alg1_tlv = cert.signature_algorithm_tlv();
const der::Input& alg2_tlv = cert.tbs.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 // Ensure that the two DER-encoded signature algorithms are byte-for-byte
// equal, but make a compatibility concession for RSA with SHA1. // equal, but make a compatibility concession for RSA with SHA1.
...@@ -261,18 +107,17 @@ WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch( ...@@ -261,18 +107,17 @@ WARN_UNUSED_RESULT bool VerifySignatureAlgorithmsMatch(
// //
// |skip_issuer_checks| controls whether the function will skip: // |skip_issuer_checks| controls whether the function will skip:
// - Checking that |cert|'s signature using |working_spki| // - 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. // This should be set to true only when verifying a trusted root certificate.
WARN_UNUSED_RESULT bool BasicCertificateProcessing( WARN_UNUSED_RESULT bool BasicCertificateProcessing(
const FullyParsedCert& cert, const ParsedCertificate& cert,
bool is_target_cert, bool is_target_cert,
bool skip_issuer_checks, bool skip_issuer_checks,
const SignaturePolicy* signature_policy, const SignaturePolicy* signature_policy,
const der::GeneralizedTime& time, const der::GeneralizedTime& time,
const der::Input& working_spki, const der::Input& working_spki,
const der::Input& working_issuer_name, const der::Input& working_normalized_issuer_name,
const std::vector<std::unique_ptr<NameConstraints>>& const std::vector<const NameConstraints*>& name_constraints_list) {
name_constraints_list) {
// Check that the signature algorithms in Certificate vs TBSCertificate // 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 // 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. // sections 4.1.1.2 and 4.1.2.3.
...@@ -282,9 +127,10 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing( ...@@ -282,9 +127,10 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
// Verify the digital signature using the previous certificate's key (RFC // Verify the digital signature using the previous certificate's key (RFC
// 5280 section 6.1.3 step a.1). // 5280 section 6.1.3 step a.1).
if (!skip_issuer_checks) { if (!skip_issuer_checks) {
if (!VerifySignedData(*cert.signature_algorithm, cert.tbs_certificate_tlv, if (!cert.has_valid_supported_signature_algorithm() ||
cert.signature_value, working_spki, !VerifySignedData(cert.signature_algorithm(),
signature_policy)) { cert.tbs_certificate_tlv(), cert.signature_value(),
working_spki, signature_policy)) {
return false; return false;
} }
} }
...@@ -300,7 +146,7 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing( ...@@ -300,7 +146,7 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
// Verify the certificate's issuer name matches the issuing certificate's // Verify the certificate's issuer name matches the issuing certificate's
// subject name. (RFC 5280 section 6.1.3 step a.4) // subject name. (RFC 5280 section 6.1.3 step a.4)
if (!skip_issuer_checks) { if (!skip_issuer_checks) {
if (!NameMatches(cert.tbs.issuer_tlv, working_issuer_name)) if (cert.normalized_issuer() != working_normalized_issuer_name)
return false; return false;
} }
...@@ -309,14 +155,13 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing( ...@@ -309,14 +155,13 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
// path, skip this step for certificate i. // path, skip this step for certificate i.
if (!name_constraints_list.empty() && if (!name_constraints_list.empty() &&
(!IsSelfIssued(cert) || is_target_cert)) { (!IsSelfIssued(cert) || is_target_cert)) {
der::Input subject_value; for (const NameConstraints* nc : name_constraints_list) {
if (!GetSequenceValue(cert.tbs.subject_tlv, &subject_value)) if (!nc->IsPermittedCert(cert.normalized_subject(),
return false; cert.subject_alt_names())) {
for (const auto& nc : name_constraints_list) {
if (!nc->IsPermittedCert(subject_value, cert.subject_alt_names.get()))
return false; return false;
} }
} }
}
// TODO(eroman): Steps d-f are omitted, as policy constraints are not yet // TODO(eroman): Steps d-f are omitted, as policy constraints are not yet
// implemented. // implemented.
...@@ -327,38 +172,31 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing( ...@@ -327,38 +172,31 @@ WARN_UNUSED_RESULT bool BasicCertificateProcessing(
// This function corresponds to RFC 5280 section 6.1.4's "Preparation for // This function corresponds to RFC 5280 section 6.1.4's "Preparation for
// Certificate i+1" procedure. |cert| is expected to be an intermediary. // Certificate i+1" procedure. |cert| is expected to be an intermediary.
WARN_UNUSED_RESULT bool PrepareForNextCertificate( WARN_UNUSED_RESULT bool PrepareForNextCertificate(
const FullyParsedCert& cert, const ParsedCertificate& cert,
size_t* max_path_length_ptr, size_t* max_path_length_ptr,
der::Input* working_spki, der::Input* working_spki,
der::Input* working_issuer_name, der::Input* working_normalized_issuer_name,
std::vector<std::unique_ptr<NameConstraints>>* name_constraints_list) { std::vector<const NameConstraints*>* name_constraints_list) {
// TODO(eroman): Steps a-b are omitted, as policy constraints are not yet // TODO(eroman): Steps a-b are omitted, as policy constraints are not yet
// implemented. // implemented.
// From RFC 5280 section 6.1.4 step c: // From RFC 5280 section 6.1.4 step c:
// //
// Assign the certificate subject name to working_issuer_name. // Assign the certificate subject name to working_normalized_issuer_name.
*working_issuer_name = cert.tbs.subject_tlv; *working_normalized_issuer_name = cert.normalized_subject();
// From RFC 5280 section 6.1.4 step d: // From RFC 5280 section 6.1.4 step d:
// //
// Assign the certificate subjectPublicKey to working_public_key. // 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 // Note that steps e and f are omitted as they are handled by
// the assignment to |working_spki| above. See the definition // the assignment to |working_spki| above. See the definition
// of |working_spki|. // of |working_spki|.
// From RFC 5280 section 6.1.4 step g: // From RFC 5280 section 6.1.4 step g:
if (cert.has_name_constraints) { if (cert.has_name_constraints())
std::unique_ptr<NameConstraints> name_constraints( name_constraints_list->push_back(&cert.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));
}
// TODO(eroman): Steps h-j are omitted as policy constraints are not yet // TODO(eroman): Steps h-j are omitted as policy constraints are not yet
// implemented. // implemented.
...@@ -376,7 +214,7 @@ WARN_UNUSED_RESULT bool PrepareForNextCertificate( ...@@ -376,7 +214,7 @@ WARN_UNUSED_RESULT bool PrepareForNextCertificate(
// //
// This code implicitly rejects non version 3 intermediaries, since they // This code implicitly rejects non version 3 intermediaries, since they
// can't contain a BasicConstraints extension. // 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; return false;
// From RFC 5280 section 6.1.4 step l: // From RFC 5280 section 6.1.4 step l:
...@@ -395,17 +233,17 @@ WARN_UNUSED_RESULT bool PrepareForNextCertificate( ...@@ -395,17 +233,17 @@ WARN_UNUSED_RESULT bool PrepareForNextCertificate(
// If pathLenConstraint is present in the certificate and is // If pathLenConstraint is present in the certificate and is
// less than max_path_length, set max_path_length to the value // less than max_path_length, set max_path_length to the value
// of pathLenConstraint. // of pathLenConstraint.
if (cert.basic_constraints.has_path_len && if (cert.basic_constraints().has_path_len &&
cert.basic_constraints.path_len < *max_path_length_ptr) { cert.basic_constraints().path_len < *max_path_length_ptr) {
*max_path_length_ptr = cert.basic_constraints.path_len; *max_path_length_ptr = cert.basic_constraints().path_len;
} }
// From RFC 5280 section 6.1.4 step n: // From RFC 5280 section 6.1.4 step n:
// //
// If a key usage extension is present, verify that the // If a key usage extension is present, verify that the
// keyCertSign bit is set. // keyCertSign bit is set.
if (cert.has_key_usage && if (cert.has_key_usage() &&
!cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)) { !cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)) {
return false; return false;
} }
...@@ -444,20 +282,21 @@ WARN_UNUSED_RESULT bool PrepareForNextCertificate( ...@@ -444,20 +282,21 @@ WARN_UNUSED_RESULT bool PrepareForNextCertificate(
// for compatibility reasons. Investigate if we need to similarly relax this // for compatibility reasons. Investigate if we need to similarly relax this
// constraint. // constraint.
WARN_UNUSED_RESULT bool VerifyTargetCertHasConsistentCaBits( WARN_UNUSED_RESULT bool VerifyTargetCertHasConsistentCaBits(
const FullyParsedCert& cert) { const ParsedCertificate& cert) {
// Check if the certificate contains any property specific to CAs. // Check if the certificate contains any property specific to CAs.
bool has_ca_property = bool has_ca_property =
(cert.has_basic_constraints && (cert.has_basic_constraints() &&
(cert.basic_constraints.is_ca || cert.basic_constraints.has_path_len)) || (cert.basic_constraints().is_ca ||
(cert.has_key_usage && cert.basic_constraints().has_path_len)) ||
cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)); (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 // 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. // it sets ALL the properties expected of a CA.
if (has_ca_property) { if (has_ca_property) {
return cert.has_basic_constraints && cert.basic_constraints.is_ca && return cert.has_basic_constraints() && cert.basic_constraints().is_ca &&
(!cert.has_key_usage || (!cert.has_key_usage() ||
cert.key_usage.AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)); cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN));
} }
return true; return true;
...@@ -465,7 +304,7 @@ WARN_UNUSED_RESULT bool VerifyTargetCertHasConsistentCaBits( ...@@ -465,7 +304,7 @@ WARN_UNUSED_RESULT bool VerifyTargetCertHasConsistentCaBits(
// This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up Procedure". // This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up Procedure".
// It does processing for the final certificate (the target cert). // 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 // TODO(eroman): Steps a-b are omitted as policy constraints are not yet
// implemented. // implemented.
...@@ -497,102 +336,6 @@ WARN_UNUSED_RESULT bool WrapUp(const FullyParsedCert& cert) { ...@@ -497,102 +336,6 @@ WARN_UNUSED_RESULT bool WrapUp(const FullyParsedCert& cert) {
} // namespace } // 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. // TODO(eroman): Move this into existing anonymous namespace.
namespace { namespace {
...@@ -603,24 +346,24 @@ namespace { ...@@ -603,24 +346,24 @@ namespace {
// the chain. This root certificate is assumed to be trusted, and neither its // 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). // signature nor issuer name are verified. (It needn't be self-signed).
bool VerifyCertificateChainAssumingTrustedRoot( bool VerifyCertificateChainAssumingTrustedRoot(
const std::vector<der::Input>& certs_der, const std::vector<scoped_refptr<ParsedCertificate>>& certs,
// The trust store is only used for assertions. // The trust store is only used for assertions.
const TrustStore& trust_store, const TrustStore& trust_store,
const SignaturePolicy* signature_policy, const SignaturePolicy* signature_policy,
const der::GeneralizedTime& time) { const der::GeneralizedTime& time) {
// An empty chain is necessarily invalid. // An empty chain is necessarily invalid.
if (certs_der.empty()) if (certs.empty())
return false; return false;
// IMPORTANT: the assumption being made is that the root certificate in // IMPORTANT: the assumption being made is that the root certificate in
// the given path is the trust anchor (and has already been verified as // the given path is the trust anchor (and has already been verified as
// such). // 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 // Will contain a NameConstraints for each previous cert in the chain which
// had nameConstraints. This corresponds to the permitted_subtrees and // had nameConstraints. This corresponds to the permitted_subtrees and
// excluded_subtrees state variables from RFC 5280. // 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_spki| is an amalgamation of 3 separate variables from RFC 5280:
// * working_public_key // * working_public_key
...@@ -638,12 +381,12 @@ bool VerifyCertificateChainAssumingTrustedRoot( ...@@ -638,12 +381,12 @@ bool VerifyCertificateChainAssumingTrustedRoot(
// signature of a certificate. // signature of a certificate.
der::Input working_spki; der::Input working_spki;
// |working_issuer_name| corresponds with the same named variable in RFC 5280 // |working_normalized_issuer_name| is the normalized value of the
// section 6.1.2: // working_issuer_name variable in RFC 5280 section 6.1.2:
// //
// working_issuer_name: the issuer distinguished name expected // working_issuer_name: the issuer distinguished name expected
// in the next certificate in the chain. // 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 // |max_path_length| corresponds with the same named variable in RFC 5280
// section 6.1.2: // section 6.1.2:
...@@ -653,7 +396,7 @@ bool VerifyCertificateChainAssumingTrustedRoot( ...@@ -653,7 +396,7 @@ bool VerifyCertificateChainAssumingTrustedRoot(
// and may be reduced to the value in the path length constraint // and may be reduced to the value in the path length constraint
// field within the basic constraints extension of a CA // field within the basic constraints extension of a CA
// certificate. // 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 // Iterate over all the certificates in the reverse direction: starting from
// the trust anchor and progressing towards the target certificate. // the trust anchor and progressing towards the target certificate.
...@@ -662,37 +405,34 @@ bool VerifyCertificateChainAssumingTrustedRoot( ...@@ -662,37 +405,34 @@ bool VerifyCertificateChainAssumingTrustedRoot(
// //
// * i=0 : Trust anchor. // * i=0 : Trust anchor.
// * i=N-1 : Target certificate. // * i=N-1 : Target certificate.
for (size_t i = 0; i < certs_der.size(); ++i) { for (size_t i = 0; i < certs.size(); ++i) {
const size_t index_into_certs_der = certs_der.size() - i - 1; const size_t index_into_certs = certs.size() - i - 1;
// |is_target_cert| is true if the current certificate is the target // |is_target_cert| is true if the current certificate is the target
// certificate being verified. The target certificate isn't necessarily an // certificate being verified. The target certificate isn't necessarily an
// end-entity certificate. // 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 // |is_trust_anchor| is true if the current certificate is the trust
// anchor. This certificate is implicitly trusted. // anchor. This certificate is implicitly trusted.
const bool is_trust_anchor = i == 0; const bool is_trust_anchor = i == 0;
// Parse the current certificate into |cert|. const ParsedCertificate& cert = *certs[index_into_certs];
FullyParsedCert cert;
const der::Input& cert_der = certs_der[index_into_certs_der];
if (!FullyParseCertificate(cert_der, &cert))
return false;
// Per RFC 5280 section 6.1: // Per RFC 5280 section 6.1:
// * Do basic processing for each certificate // * Do basic processing for each certificate
// * If it is the last certificate in the path (target certificate) // * If it is the last certificate in the path (target certificate)
// - Then run "Wrap up" // - Then run "Wrap up"
// - Otherwise run "Prepare for Next cert" // - Otherwise run "Prepare for Next cert"
if (!BasicCertificateProcessing( if (!BasicCertificateProcessing(cert, is_target_cert, is_trust_anchor,
cert, is_target_cert, is_trust_anchor, signature_policy, time, signature_policy, time, working_spki,
working_spki, working_issuer_name, name_constraints_list)) { working_normalized_issuer_name,
name_constraints_list)) {
return false; return false;
} }
if (!is_target_cert) { if (!is_target_cert) {
if (!PrepareForNextCertificate(cert, &max_path_length, &working_spki, if (!PrepareForNextCertificate(cert, &max_path_length, &working_spki,
&working_issuer_name, &working_normalized_issuer_name,
&name_constraints_list)) { &name_constraints_list)) {
return false; return false;
} }
...@@ -717,56 +457,45 @@ bool VerifyCertificateChainAssumingTrustedRoot( ...@@ -717,56 +457,45 @@ bool VerifyCertificateChainAssumingTrustedRoot(
// Beyond this no other verification is done on the chain. The caller is // Beyond this no other verification is done on the chain. The caller is
// responsible for verifying the subsequent chain's correctness. // responsible for verifying the subsequent chain's correctness.
WARN_UNUSED_RESULT bool BuildSimplePathToTrustAnchor( WARN_UNUSED_RESULT bool BuildSimplePathToTrustAnchor(
const std::vector<der::Input>& certs_der,
const TrustStore& trust_store, const TrustStore& trust_store,
std::vector<der::Input>* certs_der_trusted_root) { std::vector<scoped_refptr<ParsedCertificate>>* certs) {
// Copy the input chain. if (certs->empty())
*certs_der_trusted_root = certs_der;
if (certs_der.empty())
return false; return false;
// Check if the current root certificate is trusted. If it is then no // Check if the current root certificate is trusted. If it is then no
// extra work is needed. // extra work is needed.
if (trust_store.IsTrustedCertificate(certs_der_trusted_root->back())) if (trust_store.IsTrustedCertificate(certs->back().get()))
return true; return true;
// Otherwise if it is not trusted, check whether its issuer is trusted. If std::vector<scoped_refptr<ParsedCertificate>> trust_anchors;
// so, make *that* trusted certificate the root. If the issuer is not in trust_store.FindTrustAnchorsByNormalizedName(
// the trust store then give up and fail (this is not full path building). certs->back()->normalized_issuer(), &trust_anchors);
der::Input tbs_certificate_tlv; if (trust_anchors.empty())
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)) {
return false; return false;
} // TODO(mattm): this only tries the first match, even if there are multiple.
certs->push_back(std::move(trust_anchors[0]));
auto trust_anchor = trust_store.FindTrustAnchorByName(tbs.issuer_tlv);
if (!trust_anchor)
return false;
certs_der_trusted_root->push_back(trust_anchor->cert());
return true; return true;
} }
} // namespace } // namespace
bool VerifyCertificateChain(const std::vector<der::Input>& certs_der, bool VerifyCertificateChain(
const std::vector<scoped_refptr<ParsedCertificate>>& cert_chain,
const TrustStore& trust_store, const TrustStore& trust_store,
const SignaturePolicy* signature_policy, const SignaturePolicy* signature_policy,
const der::GeneralizedTime& time) { 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. // Modify the certificate chain so that its root is a trusted certificate.
std::vector<der::Input> certs_der_trusted_root; if (!BuildSimplePathToTrustAnchor(trust_store, &full_chain))
if (!BuildSimplePathToTrustAnchor(certs_der, trust_store,
&certs_der_trusted_root)) {
return false; return false;
}
// Verify the chain. // Verify the chain.
return VerifyCertificateChainAssumingTrustedRoot( return VerifyCertificateChainAssumingTrustedRoot(full_chain, trust_store,
certs_der_trusted_root, trust_store, signature_policy, time); signature_policy, time);
} }
} // namespace net } // namespace net
...@@ -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 std::vector<scoped_refptr<ParsedCertificate>>& cert_chain,
const TrustStore& trust_store, const TrustStore& trust_store,
const SignaturePolicy* signature_policy, const SignaturePolicy* signature_policy,
const der::GeneralizedTime& time) const der::GeneralizedTime& time) WARN_UNUSED_RESULT;
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