| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/cdm/renderer/widevine_key_system_info.h" |
| |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "build/build_config.h" |
| #include "media/base/media_switches.h" |
| #include "media/media_buildflags.h" |
| #include "third_party/widevine/cdm/buildflags.h" |
| #include "third_party/widevine/cdm/widevine_cdm_common.h" |
| |
| #if !BUILDFLAG(ENABLE_WIDEVINE) |
| #error This file should only be built when Widevine is enabled. |
| #endif |
| |
| using media::CdmSessionType; |
| using media::EmeConfig; |
| using media::EmeConfigRuleState; |
| using media::EmeFeatureSupport; |
| using media::EmeInitDataType; |
| using media::EmeMediaType; |
| using media::EncryptionScheme; |
| using media::SupportedCodecs; |
| using Robustness = cdm::WidevineKeySystemInfo::Robustness; |
| |
| namespace cdm { |
| namespace { |
| |
| Robustness ConvertRobustness(const std::string& robustness) { |
| if (robustness.empty()) |
| return Robustness::EMPTY; |
| if (robustness == "SW_SECURE_CRYPTO") |
| return Robustness::SW_SECURE_CRYPTO; |
| if (robustness == "SW_SECURE_DECODE") |
| return Robustness::SW_SECURE_DECODE; |
| if (robustness == "HW_SECURE_CRYPTO") |
| return Robustness::HW_SECURE_CRYPTO; |
| if (robustness == "HW_SECURE_DECODE") |
| return Robustness::HW_SECURE_DECODE; |
| if (robustness == "HW_SECURE_ALL") |
| return Robustness::HW_SECURE_ALL; |
| return Robustness::INVALID; |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| bool IsHardwareSecurityEnabledForKeySystem(const std::string& key_system) { |
| return (key_system == kWidevineExperimentKeySystem) && |
| base::FeatureList::IsEnabled( |
| media::kHardwareSecureDecryptionExperiment); |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| } // namespace |
| |
| WidevineKeySystemInfo::WidevineKeySystemInfo( |
| SupportedCodecs codecs, |
| base::flat_set<EncryptionScheme> encryption_schemes, |
| base::flat_set<CdmSessionType> session_types, |
| SupportedCodecs hw_secure_codecs, |
| base::flat_set<EncryptionScheme> hw_secure_encryption_schemes, |
| base::flat_set<CdmSessionType> hw_secure_session_types, |
| Robustness max_audio_robustness, |
| Robustness max_video_robustness, |
| EmeFeatureSupport persistent_state_support, |
| EmeFeatureSupport distinctive_identifier_support) |
| : codecs_(codecs), |
| encryption_schemes_(std::move(encryption_schemes)), |
| session_types_(std::move(session_types)), |
| hw_secure_codecs_(hw_secure_codecs), |
| hw_secure_encryption_schemes_(std::move(hw_secure_encryption_schemes)), |
| hw_secure_session_types_(std::move(hw_secure_session_types)), |
| max_audio_robustness_(max_audio_robustness), |
| max_video_robustness_(max_video_robustness), |
| persistent_state_support_(persistent_state_support), |
| distinctive_identifier_support_(distinctive_identifier_support) {} |
| |
| WidevineKeySystemInfo::~WidevineKeySystemInfo() = default; |
| |
| std::string WidevineKeySystemInfo::GetBaseKeySystemName() const { |
| return kWidevineKeySystem; |
| } |
| |
| bool WidevineKeySystemInfo::IsSupportedKeySystem( |
| const std::string& key_system) const { |
| #if BUILDFLAG(IS_WIN) |
| if (is_experimental_) { |
| return key_system == kWidevineExperimentKeySystem; |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| return key_system == kWidevineKeySystem; |
| } |
| |
| bool WidevineKeySystemInfo::ShouldUseBaseKeySystemName() const { |
| // Internally Widevine CDM only supports kWidevineKeySystem. |
| return true; |
| } |
| |
| bool WidevineKeySystemInfo::IsSupportedInitDataType( |
| EmeInitDataType init_data_type) const { |
| // Here we assume that support for a container implies support for the |
| // associated initialization data type. KeySystems handles validating |
| // |init_data_type| x |container| pairings. |
| if (init_data_type == EmeInitDataType::WEBM) |
| return (codecs_ & media::EME_CODEC_WEBM_ALL) != 0; |
| if (init_data_type == EmeInitDataType::CENC) |
| return (codecs_ & media::EME_CODEC_MP4_ALL) != 0; |
| |
| return false; |
| } |
| |
| EmeConfig::Rule WidevineKeySystemInfo::GetEncryptionSchemeConfigRule( |
| EncryptionScheme encryption_scheme) const { |
| bool is_supported = encryption_schemes_.contains(encryption_scheme); |
| bool is_hw_secure_supported = |
| hw_secure_encryption_schemes_.contains(encryption_scheme); |
| if (is_supported && is_hw_secure_supported) { |
| return EmeConfig::SupportedRule(); |
| } else if (is_supported && !is_hw_secure_supported) { |
| return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kNotAllowed}; |
| } else if (!is_supported && is_hw_secure_supported) { |
| return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kRequired}; |
| } else { |
| return media::EmeConfig::UnsupportedRule(); |
| } |
| } |
| |
| SupportedCodecs WidevineKeySystemInfo::GetSupportedCodecs() const { |
| return codecs_; |
| } |
| |
| SupportedCodecs WidevineKeySystemInfo::GetSupportedHwSecureCodecs() const { |
| return hw_secure_codecs_; |
| } |
| |
| EmeConfig::Rule WidevineKeySystemInfo::GetRobustnessConfigRule( |
| const std::string& key_system, |
| EmeMediaType media_type, |
| const std::string& requested_robustness, |
| const bool* hw_secure_requirement) const { |
| Robustness robustness = ConvertRobustness(requested_robustness); |
| if (robustness == Robustness::INVALID) { |
| return EmeConfig::UnsupportedRule(); |
| } |
| |
| Robustness max_robustness = Robustness::INVALID; |
| switch (media_type) { |
| case EmeMediaType::AUDIO: |
| max_robustness = max_audio_robustness_; |
| break; |
| case EmeMediaType::VIDEO: |
| max_robustness = max_video_robustness_; |
| break; |
| } |
| |
| // We can compare robustness levels whenever they are not HW_SECURE_CRYPTO |
| // and SW_SECURE_DECODE in some order. If they are exactly those two then the |
| // robustness requirement is not supported. |
| if ((max_robustness == Robustness::HW_SECURE_CRYPTO && |
| robustness == Robustness::SW_SECURE_DECODE) || |
| (max_robustness == Robustness::SW_SECURE_DECODE && |
| robustness == Robustness::HW_SECURE_CRYPTO) || |
| robustness > max_robustness) { |
| return media::EmeConfig::UnsupportedRule(); |
| } |
| |
| [[maybe_unused]] bool hw_secure_codecs_required = |
| hw_secure_requirement && *hw_secure_requirement; |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // Hardware security requires HWDRM or remote attestation, both of these |
| // require an identifier. |
| if (robustness >= Robustness::HW_SECURE_CRYPTO || hw_secure_codecs_required) { |
| #if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA) |
| return EmeConfig{.identifier = EmeConfigRuleState::kRequired, |
| .hw_secure_codecs = EmeConfigRuleState::kRequired}; |
| #else |
| return EmeConfig{.identifier = EmeConfigRuleState::kRequired}; |
| #endif |
| } |
| |
| // For video, recommend remote attestation if HW_SECURE_ALL is available, |
| // regardless of the value of |robustness|, because it enables hardware |
| // accelerated decoding. |
| // TODO(sandersd): Only do this when hardware accelerated decoding is |
| // available for the requested codecs. |
| if (media_type == EmeMediaType::VIDEO && |
| max_robustness == Robustness::HW_SECURE_ALL) { |
| return EmeConfig{.identifier = EmeConfigRuleState::kRecommended}; |
| } |
| #elif BUILDFLAG(IS_ANDROID) |
| // On Android, require hardware secure codecs for SW_SECURE_DECODE and above. |
| if (robustness >= Robustness::SW_SECURE_DECODE || hw_secure_codecs_required) { |
| return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kRequired}; |
| } |
| |
| #elif BUILDFLAG(IS_WIN) |
| if (robustness >= Robustness::HW_SECURE_CRYPTO) { |
| // On Windows, hardware security uses MediaFoundation-based CDM which |
| // requires identifier and persistent state. |
| |
| if (IsHardwareSecurityEnabledForKeySystem(key_system)) { |
| return EmeConfig{.identifier = EmeConfigRuleState::kRequired, |
| .persistence = EmeConfigRuleState::kRequired, |
| .hw_secure_codecs = EmeConfigRuleState::kRequired}; |
| } else { |
| return media::EmeConfig::UnsupportedRule(); |
| } |
| } else if (robustness < Robustness::HW_SECURE_CRYPTO) { |
| // On Windows, when software security is queried, explicitly not allow |
| // hardware secure codecs to prevent robustness level upgrade, for stability |
| // and compatibility reasons. See https://crbug.com/1327043. |
| // Also explicitly not allow identifier to prevent permission request. |
| // https://crbug.com/432054935. |
| return EmeConfig{.identifier = EmeConfigRuleState::kNotAllowed, |
| .hw_secure_codecs = EmeConfigRuleState::kNotAllowed}; |
| } |
| #else |
| // On other platforms, require hardware secure codecs for HW_SECURE_CRYPTO and |
| // above. |
| if (robustness >= Robustness::HW_SECURE_CRYPTO) { |
| return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kRequired}; |
| } |
| |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| return media::EmeConfig::SupportedRule(); |
| } |
| |
| EmeConfig::Rule WidevineKeySystemInfo::GetPersistentLicenseSessionSupport() |
| const { |
| bool is_supported = |
| session_types_.contains(CdmSessionType::kPersistentLicense); |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // The logic around hardware/software security support is complicated on |
| // ChromeOS. This code is to preserve the original logic, by deciding the |
| // support only based on `is_supported` and ignore `is_hw_secure_supported`. |
| // Note: On ChromeOS, platform verification (similar to CDM host verification) |
| // is required for persistent license support, which requires identifier. |
| // TODO(crbug.com/40839176): Fix the logic after refactoring EmeConfig. |
| if (is_supported) { |
| return EmeConfig{.identifier = EmeConfigRuleState::kRequired, |
| .persistence = EmeConfigRuleState::kRequired}; |
| |
| } else { |
| return media::EmeConfig::UnsupportedRule(); |
| } |
| #else // BUILDFLAG(IS_CHROMEOS) |
| bool is_hw_secure_supported = |
| hw_secure_session_types_.contains(CdmSessionType::kPersistentLicense); |
| |
| // Per GetPersistentLicenseSessionSupport() API, there's no need to specify |
| // the PERSISTENCE requirement here, which is implicitly assumed and enforced |
| // by `KeySystemConfigSelector`. |
| if (is_supported && is_hw_secure_supported) { |
| return EmeConfig::SupportedRule(); |
| } else if (is_supported && !is_hw_secure_supported) { |
| return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kNotAllowed}; |
| } else if (!is_supported && is_hw_secure_supported) { |
| return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kRequired}; |
| } else { |
| return media::EmeConfig::UnsupportedRule(); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| } |
| |
| EmeFeatureSupport WidevineKeySystemInfo::GetPersistentStateSupport() const { |
| return persistent_state_support_; |
| } |
| |
| EmeFeatureSupport WidevineKeySystemInfo::GetDistinctiveIdentifierSupport() |
| const { |
| return distinctive_identifier_support_; |
| } |
| |
| } // namespace cdm |