blob: ffcd2b118417e59ff9d13315df5a5440f16d22be [file] [log] [blame]
// Copyright 2014 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 "components/cast_certificate/cast_cert_validator.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <memory>
#include <utility>
#include "base/memory/singleton.h"
#include "base/stl_util.h"
#include "components/cast_certificate/cast_crl.h"
#include "net/cert/internal/cert_issuer_source_static.h"
#include "net/cert/internal/certificate_policies.h"
#include "net/cert/internal/common_cert_errors.h"
#include "net/cert/internal/extended_key_usage.h"
#include "net/cert/internal/parse_certificate.h"
#include "net/cert/internal/parse_name.h"
#include "net/cert/internal/parsed_certificate.h"
#include "net/cert/internal/path_builder.h"
#include "net/cert/internal/signature_algorithm.h"
#include "net/cert/internal/simple_path_builder_delegate.h"
#include "net/cert/internal/trust_store_in_memory.h"
#include "net/cert/internal/verify_signed_data.h"
#include "net/cert/x509_util.h"
#include "net/der/encode_values.h"
#include "net/der/input.h"
namespace cast_certificate {
namespace {
case x: \
return #x;
// -------------------------------------------------------------------------
// Cast trust anchors.
// -------------------------------------------------------------------------
// There are two trusted roots for Cast certificate chains:
// (1) CN=Cast Root CA (kCastRootCaDer)
// (2) CN=Eureka Root CA (kEurekaRootCaDer)
// These constants are defined by the files included next:
#include "components/cast_certificate/cast_root_ca_cert_der-inc.h"
#include "components/cast_certificate/eureka_root_ca_der-inc.h"
// Singleton for the Cast trust store.
class CastTrustStore {
static CastTrustStore* GetInstance() {
return base::Singleton<CastTrustStore,
static net::TrustStore& Get() { return GetInstance()->store_; }
friend struct base::DefaultSingletonTraits<CastTrustStore>;
CastTrustStore() {
// Adds a trust anchor given a DER-encoded certificate from static
// storage.
template <size_t N>
void AddAnchor(const uint8_t (&data)[N]) {
net::CertErrors errors;
scoped_refptr<net::ParsedCertificate> cert =
net::ParsedCertificate::CreateWithoutCopyingUnsafe(data, N, {},
CHECK(cert) << errors.ToDebugString();
// Enforce pathlen constraints and policies defined on the root certificate.
net::TrustStoreInMemory store_;
// Returns the OID for the Audio-Only Cast policy
// ( in DER form.
net::der::Input AudioOnlyPolicyOid() {
static const uint8_t kAudioOnlyPolicy[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
0xD6, 0x79, 0x02, 0x05, 0x02};
return net::der::Input(kAudioOnlyPolicy);
// Cast certificates rely on RSASSA-PKCS#1 v1.5 with SHA-1 for signatures.
// The following delegate will allow signature algorithms of:
// * Supported EC curves: P-256, P-384, P-521.
// * Hashes: All SHA hashes including SHA-1 (despite being known weak).
// It will also require RSA keys have a modulus at least 2048-bits long.
class CastPathBuilderDelegate : public net::SimplePathBuilderDelegate {
: SimplePathBuilderDelegate(
SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1) {}
class CertVerificationContextImpl : public CertVerificationContext {
// Save a copy of the passed in public key (DER) and common name (text).
CertVerificationContextImpl(const net::der::Input& spki,
const base::StringPiece& common_name)
: spki_(spki.AsString()), common_name_(common_name.as_string()) {}
bool VerifySignatureOverData(
const base::StringPiece& signature,
const base::StringPiece& data,
net::DigestAlgorithm digest_algorithm) const override {
// This code assumes the signature algorithm was RSASSA PKCS#1 v1.5 with
// |digest_algorithm|.
auto signature_algorithm =
return net::VerifySignedData(
*signature_algorithm, net::der::Input(data),
net::der::BitString(net::der::Input(signature), 0),
std::string GetCommonName() const override { return common_name_; }
std::string spki_;
std::string common_name_;
// Helper that extracts the Common Name from a certificate's subject field. On
// success |common_name| contains the text for the attribute (UTF-8, but for
// Cast device certs it should be ASCII).
bool GetCommonNameFromSubject(const net::der::Input& subject_tlv,
std::string* common_name) {
net::RDNSequence rdn_sequence;
if (!net::ParseName(subject_tlv, &rdn_sequence))
return false;
for (const net::RelativeDistinguishedName& rdn : rdn_sequence) {
for (const auto& atv : rdn) {
if (atv.type == net::TypeCommonNameOid()) {
return atv.ValueAsString(common_name);
return false;
// Cast device certificates use the policy to indicate
// it is *restricted* to an audio-only device whereas the absence of a policy
// means it is unrestricted.
// This is somewhat different than RFC 5280's notion of policies, so policies
// are checked separately outside of path building.
// See the unit-tests VerifyCastDeviceCertTest.Policies* for some
// concrete examples of how this works.
void DetermineDeviceCertificatePolicy(
const net::CertPathBuilderResultPath* result_path,
CastDeviceCertPolicy* policy) {
// Iterate over all the certificates, including the root certificate. If any
// certificate contains the audio-only policy, the whole chain is considered
// constrained to audio-only device certificates.
// Policy mappings are not accounted for. The expectation is that top-level
// intermediates issued with audio-only will have no mappings. If subsequent
// certificates in the chain do, it won't matter as the chain is already
// restricted to being audio-only.
bool audio_only = false;
for (const auto& cert : result_path->certs) {
if (cert->has_policy_oids()) {
const std::vector<net::der::Input>& policies = cert->policy_oids();
if (base::ContainsValue(policies, AudioOnlyPolicyOid())) {
audio_only = true;
*policy = audio_only ? CastDeviceCertPolicy::AUDIO_ONLY
: CastDeviceCertPolicy::NONE;
// Checks properties on the target certificate.
// * The Key Usage must include Digital Signature
WARN_UNUSED_RESULT bool CheckTargetCertificate(
const net::ParsedCertificate* cert,
std::unique_ptr<CertVerificationContext>* context) {
// Get the Key Usage extension.
if (!cert->has_key_usage())
return false;
// Ensure Key Usage contains digitalSignature.
if (!cert->key_usage().AssertsBit(net::KEY_USAGE_BIT_DIGITAL_SIGNATURE))
return false;
// Get the Common Name for the certificate.
std::string common_name;
if (!GetCommonNameFromSubject(cert->tbs().subject_tlv, &common_name))
return false;
new CertVerificationContextImpl(cert->tbs().spki_tlv, common_name));
return true;
// Returns the parsing options used for Cast certificates.
net::ParseCertificateOptions GetCertParsingOptions() {
net::ParseCertificateOptions options;
// Some cast intermediate certificates contain serial numbers that are
// 21 octets long, and might also not use valid DER encoding for an
// INTEGER (non-minimal encoding).
// Allow these sorts of serial numbers.
// TODO(eroman): At some point in the future this workaround will no longer be
// necessary. Should revisit this for removal in 2017 if not earlier.
options.allow_invalid_serial_numbers = true;
return options;
// Returns the CastCertError for the failed path building.
// This function must only be called if path building failed.
CastCertError MapToCastError(const net::CertPathBuilder::Result& result) {
if (result.paths.empty())
const net::CertPathErrors& path_errors =>errors;
if (path_errors.ContainsError(net::cert_errors::kValidityFailedNotAfter) ||
path_errors.ContainsError(net::cert_errors::kValidityFailedNotBefore)) {
return CastCertError::ERR_CERTS_DATE_INVALID;
} // namespace
CastCertError VerifyDeviceCert(
const std::vector<std::string>& certs,
const base::Time& time,
std::unique_ptr<CertVerificationContext>* context,
CastDeviceCertPolicy* policy,
const CastCRL* crl,
CRLPolicy crl_policy) {
return VerifyDeviceCertUsingCustomTrustStore(
certs, time, context, policy, crl, crl_policy, &CastTrustStore::Get());
CastCertError VerifyDeviceCertUsingCustomTrustStore(
const std::vector<std::string>& certs,
const base::Time& time,
std::unique_ptr<CertVerificationContext>* context,
CastDeviceCertPolicy* policy,
const CastCRL* crl,
CRLPolicy crl_policy,
net::TrustStore* trust_store) {
if (!trust_store)
return VerifyDeviceCert(certs, time, context, policy, crl, crl_policy);
if (certs.empty())
return CastCertError::ERR_CERTS_MISSING;
// Fail early if CRL is required but not provided.
if (!crl && crl_policy == CRLPolicy::CRL_REQUIRED)
return CastCertError::ERR_CRL_INVALID;
net::CertErrors errors;
scoped_refptr<net::ParsedCertificate> target_cert;
net::CertIssuerSourceStatic intermediate_cert_issuer_source;
for (size_t i = 0; i < certs.size(); ++i) {
scoped_refptr<net::ParsedCertificate> cert(net::ParsedCertificate::Create(
net::x509_util::CreateCryptoBuffer(certs[i]), GetCertParsingOptions(),
if (!cert)
return CastCertError::ERR_CERTS_PARSE;
if (i == 0)
target_cert = std::move(cert);
CastPathBuilderDelegate path_builder_delegate;
// Do path building and RFC 5280 compatible certificate verification using the
// two Cast trust anchors and Cast signature policy.
net::der::GeneralizedTime verification_time;
if (!net::der::EncodeTimeAsGeneralizedTime(time, &verification_time))
return CastCertError::ERR_UNEXPECTED;
net::CertPathBuilder::Result result;
net::CertPathBuilder path_builder(
target_cert.get(), trust_store, &path_builder_delegate, verification_time,
net::KeyPurpose::CLIENT_AUTH, net::InitialExplicitPolicy::kFalse,
{net::AnyPolicy()}, net::InitialPolicyMappingInhibit::kFalse,
net::InitialAnyPolicyInhibit::kFalse, &result);
if (!result.HasValidPath())
return MapToCastError(result);
// Determine whether this device certificate is restricted to audio-only.
DetermineDeviceCertificatePolicy(result.GetBestValidPath(), policy);
// Check properties of the leaf certificate not already verified by path
// building (key usage), and construct a CertVerificationContext that uses
// its public key.
if (!CheckTargetCertificate(target_cert.get(), context))
// Check for revocation.
if (crl && !crl->CheckRevocation(result.GetBestValidPath()->certs, time))
return CastCertError::ERR_CERTS_REVOKED;
return CastCertError::OK;
std::unique_ptr<CertVerificationContext> CertVerificationContextImplForTest(
const base::StringPiece& spki) {
// Use a bogus CommonName, since this is just exposed for testing signature
// verification by unittests.
return std::make_unique<CertVerificationContextImpl>(net::der::Input(spki),
std::string CastCertErrorToString(CastCertError error) {
switch (error) {
return "CastCertError::UNKNOWN";
} // namespace cast_certificate