123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- // 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/containers/contains.h"
- #include "base/containers/span.h"
- #include "base/logging.h"
- #include "base/no_destructor.h"
- #include "base/strings/string_piece.h"
- #include "base/synchronization/lock.h"
- #include "base/task/task_traits.h"
- #include "base/task/thread_pool.h"
- #include "components/cast_certificate/cast_crl.h"
- #include "net/cert/pki/cert_issuer_source_static.h"
- #include "net/cert/pki/certificate_policies.h"
- #include "net/cert/pki/common_cert_errors.h"
- #include "net/cert/pki/parse_certificate.h"
- #include "net/cert/pki/parse_name.h"
- #include "net/cert/pki/parsed_certificate.h"
- #include "net/cert/pki/path_builder.h"
- #include "net/cert/pki/simple_path_builder_delegate.h"
- #include "net/cert/pki/trust_store_in_memory.h"
- #include "net/cert/x509_util.h"
- #include "net/der/encode_values.h"
- #include "net/der/input.h"
- #include "third_party/boringssl/src/include/openssl/bytestring.h"
- #include "third_party/boringssl/src/include/openssl/digest.h"
- #include "third_party/boringssl/src/include/openssl/evp.h"
- // Used specifically when CAST_ALLOW_DEVELOPER_CERTIFICATE is true:
- #include "base/command_line.h"
- #include "base/memory/weak_ptr.h"
- #include "base/path_service.h"
- #include "components/cast_certificate/cast_cert_reader.h"
- #include "components/cast_certificate/switches.h"
- namespace cast_certificate {
- namespace {
- #define RETURN_STRING_LITERAL(x) \
- 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"
- class CastTrustStore {
- public:
- using AccessCallback = base::OnceCallback<void(net::TrustStore*)>;
- CastTrustStore(const CastTrustStore&) = delete;
- CastTrustStore& operator=(const CastTrustStore&) = delete;
- static void AccessInstance(AccessCallback callback) {
- CastTrustStore* instance = GetInstance();
- const base::AutoLock guard(instance->lock_);
- std::move(callback).Run(&instance->store_);
- }
- private:
- friend class base::NoDestructor<CastTrustStore>;
- static CastTrustStore* GetInstance() {
- static base::NoDestructor<CastTrustStore> instance;
- return instance.get();
- }
- CastTrustStore() {
- AddAnchor(kCastRootCaDer);
- AddAnchor(kEurekaRootCaDer);
- // Adding developer certificates must be done off of the IO thread due
- // to blocking file access.
- #if defined(CAST_ALLOW_DEVELOPER_CERTIFICATE)
- base::ThreadPool::PostTask(
- FROM_HERE, {base::MayBlock()},
- // NOTE: the singleton instance is never destroyed, so we can use
- // Unretained here instead of a weak pointer.
- base::BindOnce(&CastTrustStore::AddDeveloperCertificates,
- base::Unretained(this)));
- }
- // Check for custom root developer certificate and create a trust store
- // from it if present and enabled.
- void AddDeveloperCertificates() {
- base::AutoLock guard(lock_);
- auto* command_line = base::CommandLine::ForCurrentProcess();
- std::string cert_path_arg = command_line->GetSwitchValueASCII(
- switches::kCastDeveloperCertificatePath);
- if (!cert_path_arg.empty()) {
- base::FilePath cert_path(cert_path_arg);
- if (!cert_path.IsAbsolute()) {
- base::FilePath path;
- base::PathService::Get(base::DIR_CURRENT, &path);
- cert_path = path.Append(cert_path);
- }
- VLOG(1) << "Using cast developer certificate path " << cert_path;
- if (!PopulateStoreWithCertsFromPath(&store_, cert_path)) {
- LOG(WARNING) << "No developer certs added to store, only official"
- "Google root CA certificates will work.";
- }
- }
- #endif
- }
- // 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::Create(
- net::x509_util::CreateCryptoBufferFromStaticDataUnsafe(data), {},
- &errors);
- CHECK(cert) << errors.ToDebugString();
- // Enforce pathlen constraints and policies defined on the root certificate.
- base::AutoLock guard(lock_);
- store_.AddTrustAnchorWithConstraints(std::move(cert));
- }
- base::Lock lock_;
- net::TrustStoreInMemory store_ GUARDED_BY(lock_);
- };
- // Returns the OID for the Audio-Only Cast policy
- // (1.3.6.1.4.1.11129.2.5.2) 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:
- //
- // * ECDSA, RSA-SSA, and RSA-PSS
- // * 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 {
- public:
- CastPathBuilderDelegate()
- : SimplePathBuilderDelegate(
- 2048,
- SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1) {}
- };
- class CertVerificationContextImpl : public CertVerificationContext {
- public:
- // Save a copy of the passed in public key and common name (text).
- CertVerificationContextImpl(bssl::UniquePtr<EVP_PKEY> key,
- base::StringPiece common_name)
- : key_(std::move(key)), common_name_(common_name) {}
- bool VerifySignatureOverData(
- const base::StringPiece& signature,
- const base::StringPiece& data,
- CastDigestAlgorithm digest_algorithm) const override {
- const EVP_MD* digest = nullptr;
- switch (digest_algorithm) {
- case CastDigestAlgorithm::SHA1:
- digest = EVP_sha1();
- break;
- case CastDigestAlgorithm::SHA256:
- digest = EVP_sha256();
- break;
- };
- // Verify with RSASSA-PKCS1-v1_5 and |digest|.
- auto signature_bytes = base::as_bytes(base::make_span(signature));
- auto data_bytes = base::as_bytes(base::make_span(data));
- bssl::ScopedEVP_MD_CTX ctx;
- return EVP_PKEY_id(key_.get()) == EVP_PKEY_RSA &&
- EVP_DigestVerifyInit(ctx.get(), nullptr, digest, nullptr,
- key_.get()) &&
- EVP_DigestVerify(ctx.get(), signature_bytes.data(),
- signature_bytes.size(), data_bytes.data(),
- data_bytes.size());
- }
- std::string GetCommonName() const override { return common_name_; }
- private:
- bssl::UniquePtr<EVP_PKEY> key_;
- 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::der::Input(net::kTypeCommonNameOid)) {
- return atv.ValueAsString(common_name);
- }
- }
- }
- return false;
- }
- // Cast device certificates use the policy 1.3.6.1.4.1.11129.2.5.2 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::Contains(policies, AudioOnlyPolicyOid())) {
- audio_only = true;
- break;
- }
- }
- }
- *policy = audio_only ? CastDeviceCertPolicy::AUDIO_ONLY
- : CastDeviceCertPolicy::NONE;
- }
- // Checks properties on the target certificate.
- //
- // * The Key Usage must include Digital Signature
- [[nodiscard]] 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;
- // Get the public key for the certificate.
- CBS spki;
- CBS_init(&spki, cert->tbs().spki_tlv.UnsafeData(),
- cert->tbs().spki_tlv.Length());
- bssl::UniquePtr<EVP_PKEY> key(EVP_parse_public_key(&spki));
- if (!key || CBS_len(&spki) != 0)
- return false;
- *context = std::make_unique<CertVerificationContextImpl>(std::move(key),
- 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) {
- DCHECK(!result.HasValidPath());
- if (result.paths.empty())
- return CastCertError::ERR_CERTS_VERIFY_GENERIC;
- const net::CertPathErrors& path_errors =
- result.paths.at(result.best_result_index)->errors;
- if (path_errors.ContainsError(net::cert_errors::kValidityFailedNotAfter) ||
- path_errors.ContainsError(net::cert_errors::kValidityFailedNotBefore)) {
- return CastCertError::ERR_CERTS_DATE_INVALID;
- }
- return CastCertError::ERR_CERTS_VERIFY_GENERIC;
- }
- } // 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) {
- CastCertError verification_result;
- CastTrustStore::AccessInstance(base::BindOnce(
- [](const std::vector<std::string>& certs, const base::Time& time,
- std::unique_ptr<CertVerificationContext>* context,
- CastDeviceCertPolicy* policy, const CastCRL* crl, CRLPolicy crl_policy,
- CastCertError* result, net::TrustStore* store) {
- *result = VerifyDeviceCertUsingCustomTrustStore(
- certs, time, context, policy, crl, crl_policy, store);
- },
- certs, time, context, policy, crl, crl_policy, &verification_result));
- return verification_result;
- }
- 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(),
- &errors));
- if (!cert)
- return CastCertError::ERR_CERTS_PARSE;
- if (i == 0)
- target_cert = std::move(cert);
- else
- intermediate_cert_issuer_source.AddCert(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 path_builder(
- target_cert.get(), trust_store, &path_builder_delegate, verification_time,
- net::KeyPurpose::CLIENT_AUTH, net::InitialExplicitPolicy::kFalse,
- {net::der::Input(net::kAnyPolicyOid)},
- net::InitialPolicyMappingInhibit::kFalse,
- net::InitialAnyPolicyInhibit::kFalse);
- path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
- net::CertPathBuilder::Result result = path_builder.Run();
- 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))
- return CastCertError::ERR_CERTS_RESTRICTIONS;
- // Check for revocation.
- if (crl && !crl->CheckRevocation(result.GetBestValidPath()->certs, time))
- return CastCertError::ERR_CERTS_REVOKED;
- return CastCertError::OK;
- }
- std::string CastCertErrorToString(CastCertError error) {
- switch (error) {
- RETURN_STRING_LITERAL(CastCertError::ERR_CERTS_MISSING);
- RETURN_STRING_LITERAL(CastCertError::ERR_CERTS_PARSE);
- RETURN_STRING_LITERAL(CastCertError::ERR_CERTS_DATE_INVALID);
- RETURN_STRING_LITERAL(CastCertError::ERR_CERTS_VERIFY_GENERIC);
- RETURN_STRING_LITERAL(CastCertError::ERR_CERTS_RESTRICTIONS);
- RETURN_STRING_LITERAL(CastCertError::ERR_CRL_INVALID);
- RETURN_STRING_LITERAL(CastCertError::ERR_CERTS_REVOKED);
- RETURN_STRING_LITERAL(CastCertError::ERR_UNEXPECTED);
- RETURN_STRING_LITERAL(CastCertError::OK);
- }
- return "CastCertError::UNKNOWN";
- }
- } // namespace cast_certificate
|