From 6cbd75bb6cd2a8210c1c0e88442d9b6aec760851 Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Thu, 30 Mar 2023 23:11:28 -0700 Subject: [PATCH] Added support for additional HDCP levels. [ Merge of http://go/wvgerrit/169450 ] OEMCrypto v17 introduced several new HDCP levels that OEMCrypto may report; however, the CDM never updated to support them. The enum values of the additional levels are no longer sequential with their level of support (v1.1 is 7, and v2.1 is 3), this requires more considerations when comparing the required HDCP levels (as specified by the license) and current HDCP level supported by OEMCrypto. The following rules were used: 1) HDCP_NONE is the absolute lowest level 2) HDCP_NO_DIGITAL_OUTPUT is the absolute highest level 3) HDCP_V1 is treated as equal to all V1.x levels 4) All other versions are based on their major-minor pairs Bug: 269671291 Test: license_unittest Test: policy_engine_constraints_unittest Test: policy_engine_unittest Test: GtsMediaTestCases Change-Id: Ibecfcb981d7e019c68cb8e0c7286222253d18369 --- libwvdrmengine/cdm/core/include/cdm_engine.h | 2 - .../cdm/core/include/license_key_status.h | 10 +-- .../cdm/core/include/wv_cdm_constants.h | 5 ++ libwvdrmengine/cdm/core/src/cdm_engine.cpp | 52 +++++++++------- .../cdm/core/src/crypto_session.cpp | 5 +- .../cdm/core/src/license_key_status.cpp | 62 ++++++++++++++----- libwvdrmengine/cdm/core/src/policy_engine.cpp | 6 +- 7 files changed, 91 insertions(+), 51 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 154d2dd8..50ad2246 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -415,8 +415,6 @@ class CdmEngine { void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); - std::string MapHdcpVersion(CryptoSession::HdcpCapability version); - void CloseExpiredReleaseSessions(); // Returns "true" if |okp_provisioner_| should be checked. diff --git a/libwvdrmengine/cdm/core/include/license_key_status.h b/libwvdrmengine/cdm/core/include/license_key_status.h index db7b9525..d07cbffc 100644 --- a/libwvdrmengine/cdm/core/include/license_key_status.h +++ b/libwvdrmengine/cdm/core/include/license_key_status.h @@ -155,12 +155,12 @@ class LicenseKeyStatus { void SetConstraints(const ConstraintList& constraints); - bool is_content_key_; - CdmKeyStatus key_status_; - bool meets_constraints_; - bool meets_security_level_constraints_; + bool is_content_key_ = false; + CdmKeyStatus key_status_ = kKeyStatusInternalError; + bool meets_constraints_ = true; + bool meets_security_level_constraints_ = true; CdmKeyAllowedUsage allowed_usage_; - CryptoSession::HdcpCapability default_hdcp_level_; + CryptoSession::HdcpCapability default_hdcp_level_ = HDCP_NONE; ConstraintList constraints_; CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeyStatus); diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 1e40068a..800c55b1 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -142,6 +142,11 @@ static const std::string QUERY_VALUE_HDCP_V2_0 = "HDCP-2.0"; static const std::string QUERY_VALUE_HDCP_V2_1 = "HDCP-2.1"; static const std::string QUERY_VALUE_HDCP_V2_2 = "HDCP-2.2"; static const std::string QUERY_VALUE_HDCP_V2_3 = "HDCP-2.3"; +static const std::string QUERY_VALUE_HDCP_V1_0 = "HDCP-1.0"; +static const std::string QUERY_VALUE_HDCP_V1_1 = "HDCP-1.1"; +static const std::string QUERY_VALUE_HDCP_V1_2 = "HDCP-1.2"; +static const std::string QUERY_VALUE_HDCP_V1_3 = "HDCP-1.3"; +static const std::string QUERY_VALUE_HDCP_V1_4 = "HDCP-1.4"; static const std::string QUERY_VALUE_HDCP_LEVEL_UNKNOWN = "HDCP-LevelUnknown"; static const std::string QUERY_VALUE_DRM_CERTIFICATE = "DrmCertificate"; static const std::string QUERY_VALUE_KEYBOX = "Keybox"; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 069a7842..1b554180 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -33,6 +33,37 @@ namespace wvcdm { namespace { const uint64_t kReleaseSessionTimeToLive = 60; // seconds const uint32_t kUpdateUsageInformationPeriod = 60; // seconds + +std::string MapHdcpVersion(CryptoSession::HdcpCapability version) { + switch (version) { + case HDCP_NONE: + return QUERY_VALUE_HDCP_NONE; + case HDCP_V1: + return QUERY_VALUE_HDCP_V1; + case HDCP_V2: + return QUERY_VALUE_HDCP_V2_0; + case HDCP_V2_1: + return QUERY_VALUE_HDCP_V2_1; + case HDCP_V2_2: + return QUERY_VALUE_HDCP_V2_2; + case HDCP_V2_3: + return QUERY_VALUE_HDCP_V2_3; + // V17 and forward. + case HDCP_V1_0: + return QUERY_VALUE_HDCP_V1_0; + case HDCP_V1_1: + return QUERY_VALUE_HDCP_V1_1; + case HDCP_V1_2: + return QUERY_VALUE_HDCP_V1_2; + case HDCP_V1_3: + return QUERY_VALUE_HDCP_V1_3; + case HDCP_V1_4: + return QUERY_VALUE_HDCP_V1_4; + case HDCP_NO_DIGITAL_OUTPUT: + return QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT; + } + return QUERY_VALUE_HDCP_LEVEL_UNKNOWN; +} } // namespace class UsagePropertySet : public CdmClientPropertySet { @@ -2190,27 +2221,6 @@ CdmResponseType CdmEngine::SetPlaybackId(const CdmSessionId& session_id, return CdmResponseType(NO_ERROR); } -std::string CdmEngine::MapHdcpVersion(CryptoSession::HdcpCapability version) { - switch (version) { - case HDCP_NONE: - return QUERY_VALUE_HDCP_NONE; - case HDCP_V1: - return QUERY_VALUE_HDCP_V1; - case HDCP_V2: - return QUERY_VALUE_HDCP_V2_0; - case HDCP_V2_1: - return QUERY_VALUE_HDCP_V2_1; - case HDCP_V2_2: - return QUERY_VALUE_HDCP_V2_2; - case HDCP_V2_3: - return QUERY_VALUE_HDCP_V2_3; - case HDCP_NO_DIGITAL_OUTPUT: - return QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT; - default: - return QUERY_VALUE_HDCP_LEVEL_UNKNOWN; - } -} - void CdmEngine::CloseExpiredReleaseSessions() { const int64_t current_time = clock_.GetCurrentTime(); std::set close_session_set; diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 6d9ca395..4aa4df07 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -2101,9 +2101,8 @@ CdmResponseType CryptoSession::GetHdcpCapabilities( RETURN_IF_NULL(current, PARAMETER_NULL); RETURN_IF_NULL(max, PARAMETER_NULL); - OEMCryptoResult status; - WithOecReadLock("GetHdcpCapabilities", [&] { - status = OEMCrypto_GetHDCPCapability(security_level, current, max); + const OEMCryptoResult status = WithOecReadLock("GetHdcpCapabilities", [&] { + return OEMCrypto_GetHDCPCapability(security_level, current, max); }); if (OEMCrypto_SUCCESS == status) { diff --git a/libwvdrmengine/cdm/core/src/license_key_status.cpp b/libwvdrmengine/cdm/core/src/license_key_status.cpp index a4fc365b..4e3bff28 100644 --- a/libwvdrmengine/cdm/core/src/license_key_status.cpp +++ b/libwvdrmengine/cdm/core/src/license_key_status.cpp @@ -9,6 +9,7 @@ #include "log.h" #include "wv_cdm_constants.h" +namespace wvcdm { namespace { // License protocol aliases using KeyContainer = ::video_widevine::License::KeyContainer; @@ -19,7 +20,7 @@ using ConstraintList = // Map the HDCP protection associated with a key in the license to // an equivalent OEMCrypto HDCP protection level -wvcdm::CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp( +CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp( const OutputProtection::HDCP& input) { switch (input) { case OutputProtection::HDCP_NONE: @@ -36,11 +37,46 @@ wvcdm::CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp( return HDCP_V2_3; case OutputProtection::HDCP_NO_DIGITAL_OUTPUT: return HDCP_NO_DIGITAL_OUTPUT; - default: - LOGE("Unknown HDCP Level, returning HDCP_NO_DIGITAL_OUTPUT: input = %d", - static_cast(input)); - return HDCP_NO_DIGITAL_OUTPUT; } + LOGW("Unknown HDCP Level, returning HDCP_NO_DIGITAL_OUTPUT: input = %d", + static_cast(input)); + return HDCP_NO_DIGITAL_OUTPUT; +} + +bool MeetsHdcpRequirements(const CryptoSession::HdcpCapability& required, + const CryptoSession::HdcpCapability& provided) { + if (provided == HDCP_NO_DIGITAL_OUTPUT) return true; // Always met. + switch (required) { + case HDCP_NONE: + return true; // Always met. + case HDCP_V1: // Means v1.x + // Can be any HDCP level other than NONE. + return provided != HDCP_NONE; + case HDCP_V2: // Means v2.0 + case HDCP_V2_1: + case HDCP_V2_2: + case HDCP_V2_3: + // Must be equal to or higher than the required v2.x version. + return provided >= required && provided <= HDCP_V2_3; + case HDCP_V1_0: + case HDCP_V1_1: + case HDCP_V1_2: + case HDCP_V1_3: + case HDCP_V1_4: + // Must be equal to or higher than the required v1.x version, + // be exactly v1.x, or within the 2.x range. OEMCrypto may + // behave differently. + return (provided == HDCP_V1) || + (provided >= required && provided <= HDCP_V1_4) || + (provided >= HDCP_V2 && provided <= HDCP_V2_3); + case HDCP_NO_DIGITAL_OUTPUT: + // If |provided| was HDCP_NO_DIGITAL_OUTPUT, it would have been caught + // at the top of this function. + return false; + } + LOGE("Unknown required HDCP level: required = %d, provided = %d", + static_cast(required), static_cast(provided)); + return false; } // Returns the constraint from a set of constraints that matches the @@ -63,8 +99,6 @@ VideoResolutionConstraint* GetConstraintForRes( } // namespace -namespace wvcdm { - bool LicenseKeys::IsContentKey(const std::string& key_id) { if (key_statuses_.count(key_id) > 0) { return key_statuses_[key_id]->IsContentKey(); @@ -199,12 +233,7 @@ void LicenseKeys::SetEntitledKeys( } LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key, - const CdmSecurityLevel security_level) - : is_content_key_(false), - key_status_(kKeyStatusInternalError), - meets_constraints_(true), - meets_security_level_constraints_(true), - default_hdcp_level_(HDCP_NONE) { + const CdmSecurityLevel security_level) { allowed_usage_.Clear(); constraints_.Clear(); @@ -370,15 +399,14 @@ void LicenseKeyStatus::ApplyConstraints( } } - CryptoSession::HdcpCapability desired_hdcp_level; + CryptoSession::HdcpCapability desired_hdcp_level = default_hdcp_level_; if (current_constraint && current_constraint->has_required_protection()) { desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp( current_constraint->required_protection().hdcp()); - } else { - desired_hdcp_level = default_hdcp_level_; } - meets_constraints_ = (new_hdcp_level >= desired_hdcp_level); + meets_constraints_ = + MeetsHdcpRequirements(desired_hdcp_level, new_hdcp_level); } void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) { diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index 6306dbfc..bfde62a8 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -72,9 +72,9 @@ void PolicyEngine::CheckDeviceHdcpStatusOnTimer(int64_t current_time) { void PolicyEngine::CheckDeviceHdcpStatus() { if (!license_keys_->Empty()) { - CryptoSession::HdcpCapability current_hdcp_level; - CryptoSession::HdcpCapability ignored; - CdmResponseType status = + CryptoSession::HdcpCapability current_hdcp_level = HDCP_NONE; + CryptoSession::HdcpCapability ignored = HDCP_NONE; + const CdmResponseType status = crypto_session_->GetHdcpCapabilities(¤t_hdcp_level, &ignored); if (status != NO_ERROR) { current_hdcp_level = HDCP_NONE;