diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index ed9d9b27..c127405b 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -149,6 +149,7 @@ class CryptoSession { virtual bool IsSrmUpdateSupported(); virtual bool LoadSrm(const std::string& srm); + virtual bool GetResourceRatingTier(uint32_t* tier); virtual bool GetBuildInformation(std::string* info); virtual CdmResponseType GenericEncrypt(const std::string& in_buffer, diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 5c04c8f0..fa808fb4 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -26,6 +26,14 @@ static const size_t CERTIFICATE_DATA_SIZE = 4 * 1024; // (NaN in JS translates to 0 in unix timestamp). static const int64_t NEVER_EXPIRES = 0; +// Resource rating tiers +static const uint32_t RESOURCE_RATING_TIER_LOW = 1u; +static const uint32_t RESOURCE_RATING_TIER_MEDIUM = 2u; +static const uint32_t RESOURCE_RATING_TIER_HIGH = 3u; + +// OEMCrypto features by version +static const uint32_t OEM_CRYPTO_API_VERSION_SUPPORTS_RESOURCE_RATING_TIER = 15; + static const char SESSION_ID_PREFIX[] = "sid"; static const char KEY_SET_ID_PREFIX[] = "ksid"; static const char KEY_SYSTEM[] = "com.widevine"; @@ -71,6 +79,7 @@ static const std::string QUERY_KEY_CURRENT_SRM_VERSION = "CurrentSRMVersion"; static const std::string QUERY_KEY_SRM_UPDATE_SUPPORT = "SRMUpdateSupport"; // whether OEM supports SRM update static const std::string QUERY_KEY_WVCDM_VERSION = "WidevineCdmVersion"; +static const std::string QUERY_KEY_RESOURCE_RATING_TIER = "ResourceRatingTier"; static const std::string QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION = "OemCryptoBuildInformation"; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index bb42d485..3b39dea9 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -642,6 +642,16 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, } *query_response = cdm_version; + } else if (query_token == QUERY_KEY_RESOURCE_RATING_TIER) { + uint32_t tier; + if (!crypto_session->GetResourceRatingTier(&tier)) { + LOGW("CdmEngine::QueryStatus: GetResourceRatingTier failed"); + return UNKNOWN_ERROR; + } + + std::ostringstream tier_stream; + tier_stream << tier; + *query_response = tier_stream.str(); } else if (query_token == QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION) { if (!crypto_session->GetBuildInformation(query_response)) { LOGW("CdmEngine::QueryStatus: GetBuildInformation failed"); diff --git a/libwvdrmengine/cdm/core/src/client_identification.cpp b/libwvdrmengine/cdm/core/src/client_identification.cpp index 6901400f..250d5194 100644 --- a/libwvdrmengine/cdm/core/src/client_identification.cpp +++ b/libwvdrmengine/cdm/core/src/client_identification.cpp @@ -11,6 +11,7 @@ #include "log.h" #include "properties.h" #include "string_conversions.h" +#include "wv_cdm_constants.h" namespace { const std::string kKeyCompanyName = "company_name"; @@ -254,6 +255,15 @@ CdmResponseType ClientIdentification::Prepare( client_capabilities->set_analog_output_capabilities(video_widevine::ClientIdentification_ClientCapabilities_AnalogOutputCapabilities_ANALOG_OUTPUT_UNKNOWN); } + uint32_t version, tier; + if (crypto_session_->GetApiVersion(&version)) { + if (version >= OEM_CRYPTO_API_VERSION_SUPPORTS_RESOURCE_RATING_TIER) { + if (crypto_session_->GetResourceRatingTier(&tier)) { + client_capabilities->set_resource_rating_tier(tier); + } + } + } + return NO_ERROR; } diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 3e49ec33..8df9c4bb 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -1773,6 +1773,25 @@ bool CryptoSession::LoadSrm(const std::string& srm) { return true; } +bool CryptoSession::GetResourceRatingTier(uint32_t* tier) { + LOGV("GetResourceRatingTier"); + if (!initialized_) return false; + if (tier == nullptr) { + LOGE("tier destination not provided"); + return false; + } + *tier = OEMCrypto_ResourceRatingTier(requested_security_level_); + if (*tier < RESOURCE_RATING_TIER_LOW || *tier > RESOURCE_RATING_TIER_HIGH) { + uint32_t api_version; + if (GetApiVersion(&api_version)) { + if (api_version >= OEM_CRYPTO_API_VERSION_SUPPORTS_RESOURCE_RATING_TIER) { + LOGW("GetResourceRatingTier: invalid resource rating tier: %d", *tier); + } + } + } + return true; +} + bool CryptoSession::GetBuildInformation(std::string* info) { LOGV("GetBuildInformation"); if (!initialized_) return false; diff --git a/libwvdrmengine/cdm/core/test/license_unittest.cpp b/libwvdrmengine/cdm/core/test/license_unittest.cpp index 6e528fd7..deb1d6b0 100644 --- a/libwvdrmengine/cdm/core/test/license_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/license_unittest.cpp @@ -147,6 +147,7 @@ class MockCryptoSession : public TestCryptoSession { MOCK_METHOD3(PrepareRequest, bool(const std::string&, bool, std::string*)); MOCK_METHOD1(LoadEntitledContentKeys, CdmResponseType(const std::vector& key_array)); + MOCK_METHOD1(GetResourceRatingTier, bool(uint32_t*)); }; class MockPolicyEngine : public PolicyEngine { @@ -319,7 +320,8 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { SetArgPointee<1>(max_hdcp_version), Return(true))); EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())); EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())) - .WillOnce( + .Times(2) + .WillRepeatedly( DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true))); EXPECT_CALL(*clock_, GetCurrentTime()).WillOnce(Return(kLicenseStartTime)); EXPECT_CALL(*crypto_session_, GenerateNonce(NotNull())) @@ -393,6 +395,131 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_2048, video_widevine:: ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_3072)); + EXPECT_FALSE(client_capabilities.has_resource_rating_tier()); + + // Verify Content Identification + const LicenseRequest_ContentIdentification& content_id = + license_request.content_id(); + ASSERT_TRUE(content_id.has_cenc_id_deprecated()); + EXPECT_FALSE(content_id.has_webm_id_deprecated()); + EXPECT_FALSE(content_id.has_existing_license()); + + const ::video_widevine::LicenseRequest_ContentIdentification_CencDeprecated& + cenc_id = content_id.cenc_id_deprecated(); + EXPECT_TRUE(std::equal(cenc_id.pssh(0).begin(), cenc_id.pssh(0).end(), + kCencPssh.begin())); + EXPECT_EQ(video_widevine::STREAMING, cenc_id.license_type()); + EXPECT_TRUE(std::equal(cenc_id.request_id().begin(), + cenc_id.request_id().end(), kCryptoRequestId.begin())); + + // Verify other license request fields + EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_NEW, + license_request.type()); + EXPECT_EQ(kLicenseStartTime, license_request.request_time()); + EXPECT_EQ(video_widevine::VERSION_2_1, + license_request.protocol_version()); + EXPECT_EQ(kNonce, license_request.key_control_nonce()); +} + +TEST_F(CdmLicenseTest, PrepareKeyRequestValidationV15) { + bool usage_information_support = true; + CryptoSession::HdcpCapability current_hdcp_version = HDCP_NO_DIGITAL_OUTPUT; + CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1; + uint32_t crypto_session_api_version = 15; + uint32_t resource_rating_tier = RESOURCE_RATING_TIER_LOW; + + EXPECT_CALL(*crypto_session_, IsOpen()) + .WillOnce(Return(true)); + EXPECT_CALL(*crypto_session_, GenerateRequestId(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kCryptoRequestId), Return(true))); + EXPECT_CALL(*crypto_session_, UsageInformationSupport(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(usage_information_support), Return(true))); + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(current_hdcp_version), + SetArgPointee<1>(max_hdcp_version), Return(true))); + EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())); + EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())) + .Times(2) + .WillRepeatedly( + DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true))); + EXPECT_CALL(*crypto_session_, GetResourceRatingTier(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(resource_rating_tier), Return(true))); + EXPECT_CALL(*clock_, GetCurrentTime()).WillOnce(Return(kLicenseStartTime)); + EXPECT_CALL(*crypto_session_, GenerateNonce(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kNonce), Return(true))); + EXPECT_CALL(*crypto_session_, PrepareRequest(_, Eq(false), NotNull())) + .WillOnce( + DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true))); + + CreateCdmLicense(); + EXPECT_TRUE(cdm_license_->Init( + kToken, kClientTokenDrmCert, kEmptyString, true, + kDefaultServiceCertificate, crypto_session_, policy_engine_)); + + CdmAppParameterMap app_parameters; + CdmKeyMessage signed_request; + std::string server_url; + EXPECT_EQ(cdm_license_->PrepareKeyRequest( + *init_data_, kLicenseTypeStreaming, app_parameters, + &signed_request, &server_url), KEY_MESSAGE); + + EXPECT_TRUE(!signed_request.empty()); + + // Verify signed message + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(signed_request)); + EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()); + EXPECT_TRUE(signed_message.has_signature()); + EXPECT_TRUE(std::equal(signed_message.signature().begin(), + signed_message.signature().end(), + kLicenseRequestSignature.begin())); + EXPECT_TRUE(!signed_message.msg().empty()); + + // Verify license request + LicenseRequest license_request; + EXPECT_TRUE(license_request.ParseFromString(signed_message.msg())); + + // Verify Client Identification + const ClientIdentification& client_id = license_request.client_id(); + EXPECT_EQ(video_widevine:: + ClientIdentification_TokenType_DRM_DEVICE_CERTIFICATE, + client_id.type()); + EXPECT_TRUE(std::equal(client_id.token().begin(), client_id.token().end(), + kToken.begin())); + + EXPECT_LT(0, client_id.client_info_size()); + for (int i = 0; i < client_id.client_info_size(); ++i) { + const ::video_widevine::ClientIdentification_NameValue& + name_value = client_id.client_info(i); + EXPECT_TRUE(!name_value.name().empty()); + EXPECT_TRUE(!name_value.value().empty()); + } + + EXPECT_FALSE(client_id.has_provider_client_token()); + EXPECT_FALSE(client_id.has_license_counter()); + + const ::video_widevine::ClientIdentification_ClientCapabilities& + client_capabilities = client_id.client_capabilities(); + EXPECT_FALSE(client_capabilities.has_client_token()); + EXPECT_TRUE(client_capabilities.has_session_token()); + EXPECT_FALSE(client_capabilities.video_resolution_constraints()); + EXPECT_EQ(video_widevine:: + ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1, + client_capabilities.max_hdcp_version()); + EXPECT_EQ(crypto_session_api_version, + client_capabilities.oem_crypto_api_version()); + + EXPECT_THAT( + client_capabilities.supported_certificate_key_type(), + UnorderedElementsAre( + video_widevine:: + ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_2048, + video_widevine:: + ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_3072)); + EXPECT_EQ(resource_rating_tier, + client_capabilities.resource_rating_tier()); // Verify Content Identification const LicenseRequest_ContentIdentification& content_id = diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 5b42273a..ce555a80 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -4140,8 +4140,27 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) { EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryStatus( - kLevelDefault, wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, - &value)); + kLevelDefault, wvcdm::QUERY_KEY_RESOURCE_RATING_TIER, &value)); + ss.clear(); + ss.str(value); + uint32_t resource_rating_tier; + ss >> resource_rating_tier; + ASSERT_FALSE(ss.fail()); + EXPECT_TRUE(ss.eof()); + + if (api_version >= OEM_CRYPTO_API_VERSION_SUPPORTS_RESOURCE_RATING_TIER) { + EXPECT_LE(RESOURCE_RATING_TIER_LOW, resource_rating_tier); + EXPECT_GE(RESOURCE_RATING_TIER_HIGH, resource_rating_tier); + } else { + EXPECT_TRUE(resource_rating_tier < RESOURCE_RATING_TIER_LOW || + resource_rating_tier > RESOURCE_RATING_TIER_HIGH) + << "resource rating tier: " << resource_rating_tier; + } + + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_.QueryStatus(kLevelDefault, + wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, + &value)); EXPECT_TRUE(!value.empty()); } diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index e85381bc..ad9e9de3 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -524,6 +524,8 @@ status_t WVDrmPlugin::getPropertyString(const String8& name, return queryProperty(QUERY_KEY_CURRENT_SRM_VERSION, value); } else if (name == "SRMUpdateSupport") { return queryProperty(QUERY_KEY_SRM_UPDATE_SUPPORT, value); + } else if (name == "resourceRatingTier") { + return queryProperty(QUERY_KEY_RESOURCE_RATING_TIER, value); } else if (name == "oemCryptoBuildInformation") { return queryProperty(QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, value); } else { diff --git a/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp index 0ad9f46b..895f3cb8 100644 --- a/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp @@ -1020,6 +1020,8 @@ Return WVDrmPlugin::getPropertyString(const hidl_string& propertyName, status = queryProperty(wvcdm::QUERY_KEY_CURRENT_SRM_VERSION, value); } else if (name == "SRMUpdateSupport") { status = queryProperty(wvcdm::QUERY_KEY_SRM_UPDATE_SUPPORT, value); + } else if (name == "resourceRatingTier") { + status = queryProperty(wvcdm::QUERY_KEY_RESOURCE_RATING_TIER, value); } else if (name == "oemCryptoBuildInformation") { status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, value); } else { diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index 2e110ec4..1477dbaa 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -109,6 +109,7 @@ using wvcdm::QUERY_KEY_SECURITY_LEVEL; using wvcdm::QUERY_KEY_SRM_UPDATE_SUPPORT; using wvcdm::QUERY_KEY_SYSTEM_ID; using wvcdm::QUERY_KEY_WVCDM_VERSION; +using wvcdm::QUERY_KEY_RESOURCE_RATING_TIER; using wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION; using wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1; using wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3; @@ -1135,6 +1136,7 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) { static const std::string oemCryptoApiVersion = "13"; static const std::string currentSRMVersion = "1"; static const std::string cdmVersion = "Infinity Minus 1"; + static const std::string resourceRatingTier = "1"; static const std::string oemCryptoBuildInformation = "Mostly Harmless"; drm_metrics::WvCdmMetrics expected_metrics; std::string serialized_metrics = wvcdm::a2bs_hex(kSerializedMetricsHex); @@ -1182,6 +1184,10 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) { .WillOnce(DoAll(SetArgPointee<2>(cdmVersion), testing::Return(wvcdm::NO_ERROR))); + EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_RESOURCE_RATING_TIER, _)) + .WillOnce(DoAll(SetArgPointee<2>(resourceRatingTier), + testing::Return(wvcdm::NO_ERROR))); + EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, _)) .WillOnce(DoAll(SetArgPointee<2>(oemCryptoBuildInformation), testing::Return(wvcdm::NO_ERROR))); @@ -1291,6 +1297,20 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) { EXPECT_STREQ(currentSRMVersion.c_str(), stringResult.c_str()); }); + plugin.getPropertyString( + hidl_string("resourceRatingTier"), + [&](Status status, hidl_string stringResult) { + ASSERT_EQ(Status::OK, status); + EXPECT_STREQ(resourceRatingTier.c_str(), stringResult.c_str()); + }); + + plugin.getPropertyString( + hidl_string("oemCryptoBuildInformation"), + [&](Status status, hidl_string stringResult) { + ASSERT_EQ(Status::OK, status); + EXPECT_STREQ(oemCryptoBuildInformation.c_str(), stringResult.c_str()); + }); + // This call occurs before any open session or other call. This means // that the cdm identifer is not yet sealed, and metrics return empty // metrics data.