diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 91db0f50..da61b878 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -34,7 +34,9 @@ class CdmEngine { CdmSessionId* session_id); virtual CdmResponseType CloseSession(const CdmSessionId& session_id); - virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); + virtual CdmResponseType OpenKeySetSession( + const CdmKeySetId& key_set_id, + const CdmClientPropertySet* property_set); virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); // License related methods diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 94051bb7..c3a866e3 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -108,7 +108,9 @@ CdmResponseType CdmEngine::OpenSession( return NO_ERROR; } -CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) { +CdmResponseType CdmEngine::OpenKeySetSession( + const CdmKeySetId& key_set_id, + const CdmClientPropertySet* property_set) { LOGI("CdmEngine::OpenKeySetSession"); if (key_set_id.empty()) { @@ -117,7 +119,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) { } CdmSessionId session_id; - CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id); + CdmResponseType sts = OpenSession(KEY_SYSTEM, property_set, &session_id); if (sts != NO_ERROR) return sts; diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 9a698f77..828d6eed 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -869,7 +869,11 @@ bool CdmLicense::RestoreLicenseForRelease( return false; } - key_request_ = signed_request.msg(); + if (Properties::use_certificates_as_identification()) { + key_request_ = signed_request.msg(); + } else { + if (!session_->GenerateDerivedKeys(signed_request.msg())) return false; + } SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { @@ -885,19 +889,6 @@ bool CdmLicense::RestoreLicenseForRelease( return false; } - if (Properties::use_certificates_as_identification()) { - if (!signed_response.has_session_key()) { - LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present"); - return false; - } - - if (!session_->GenerateDerivedKeys(key_request_, - signed_response.session_key())) - return false; - } else { - if (!session_->GenerateDerivedKeys(key_request_)) return false; - } - if (!signed_response.has_signature()) { LOGE("CdmLicense::RestoreLicenseForRelease: license response is not" " signed"); @@ -914,6 +905,21 @@ bool CdmLicense::RestoreLicenseForRelease( if (license.id().has_provider_session_token()) provider_session_token_ = license.id().provider_session_token(); + if (Properties::use_certificates_as_identification()) { + if (!signed_response.has_session_key()) { + LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present"); + return false; + } + + if (license.id().has_provider_session_token()) { + if (!session_->GenerateDerivedKeys(key_request_, + signed_response.session_key())) + return false; + } else { + return KEY_ADDED == HandleKeyResponse(license_response); + } + } + if (license.policy().has_renewal_server_url()) server_url_ = license.policy().renewal_server_url(); diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index d62034a7..730ce0a6 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -778,12 +778,14 @@ extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t *pst, const FunctionPointers* fcn1 = kAdapter->get(kLevelDefault); const FunctionPointers* fcn3 = kAdapter->get(kLevel3); OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED; - if (fcn3 && fcn3->version > 8) { - sts = fcn3->DeactivateUsageEntry(pst, pst_length); - } - if (fcn1 && fcn1 != fcn3 && fcn1->version > 8) { + if (fcn1 && fcn1->version > 8) { sts = fcn1->DeactivateUsageEntry(pst, pst_length); } + if (OEMCrypto_SUCCESS != sts) { + if (fcn3 && fcn1 != fcn3 && fcn3->version > 8) { + sts = fcn3->DeactivateUsageEntry(pst, pst_length); + } + } return sts; } diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 2f88876a..c64d156a 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -39,6 +39,7 @@ class WvContentDecryptionModule : public TimerHandler { const CdmInitData& init_data, const CdmLicenseType license_type, CdmAppParameterMap& app_parameters, + CdmClientPropertySet* property_set, CdmKeyMessage* key_request, std::string* server_url); diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index de7ed587..2c07020c 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -66,11 +66,12 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( const CdmInitData& init_data, const CdmLicenseType license_type, CdmAppParameterMap& app_parameters, + CdmClientPropertySet* property_set, CdmKeyMessage* key_request, std::string* server_url) { CdmResponseType sts; if (license_type == kLicenseTypeRelease) { - sts = cdm_engine_->OpenKeySetSession(key_set_id); + sts = cdm_engine_->OpenKeySetSession(key_set_id, property_set); if (sts != NO_ERROR) return sts; } diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index 47fed1e1..b95f10ae 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -243,7 +243,7 @@ class WvCdmExtendedDurationTest : public testing::Test { EXPECT_EQ(wvcdm::KEY_MESSAGE, decryptor_.GenerateKeyRequest( session_id_, key_set_id_, "video/mp4", init_data, - license_type, app_parameters, &key_msg_, &server_url)); + license_type, app_parameters, NULL, &key_msg_, &server_url)); EXPECT_EQ(0u, server_url.size()); } @@ -256,7 +256,7 @@ class WvCdmExtendedDurationTest : public testing::Test { EXPECT_EQ(wvcdm::KEY_MESSAGE, decryptor_.GenerateKeyRequest( session_id_, key_set_id_, "video/mp4", init_data, - license_type, app_parameters, &key_msg_, server_url)); + license_type, app_parameters, NULL, &key_msg_, server_url)); // TODO(edwinwong, rfrias): Add tests cases for when license server url // is empty on renewal. Need appropriate key id at the server. EXPECT_NE(0u, server_url->size()); @@ -270,7 +270,8 @@ class WvCdmExtendedDurationTest : public testing::Test { EXPECT_EQ(wvcdm::KEY_MESSAGE, decryptor_.GenerateKeyRequest( session_id, key_set_id, "video/mp4", init_data, - kLicenseTypeRelease, app_parameters, &key_msg_, &server_url)); + kLicenseTypeRelease, app_parameters, NULL, &key_msg_, + &server_url)); } // Post a request and extract the drm message from the response diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 1b552b38..8a90dd9f 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -371,12 +371,16 @@ SessionSharingSubSampleInfo session_sharing_sub_samples[] = { struct UsageInfoSubSampleInfo { SubSampleInfo* sub_sample; uint32_t usage_info; + wvcdm::SecurityLevel security_level; }; UsageInfoSubSampleInfo usage_info_sub_sample_info[] = { - {&usage_info_sub_samples_icp[0], 1}, - {&usage_info_sub_samples_icp[0], 3}, - {&usage_info_sub_samples_icp[0], 5}}; + {&usage_info_sub_samples_icp[0], 1, wvcdm::kLevelDefault}, + {&usage_info_sub_samples_icp[0], 3, wvcdm::kLevelDefault}, + {&usage_info_sub_samples_icp[0], 5, wvcdm::kLevelDefault}, + {&usage_info_sub_samples_icp[0], 1, wvcdm::kLevel3}, + {&usage_info_sub_samples_icp[0], 3, wvcdm::kLevel3}, + {&usage_info_sub_samples_icp[0], 5, wvcdm::kLevel3}}; } // namespace @@ -460,18 +464,30 @@ class WvCdmRequestLicenseTest : public testing::Test { void GenerateKeyRequest(const std::string& init_data, CdmLicenseType license_type) { + GenerateKeyRequest(init_data, license_type, NULL); + } + + void GenerateKeyRequest(const std::string& init_data, + CdmLicenseType license_type, + CdmClientPropertySet* property_set) { wvcdm::CdmAppParameterMap app_parameters; std::string server_url; std::string key_set_id; EXPECT_EQ(wvcdm::KEY_MESSAGE, decryptor_.GenerateKeyRequest( session_id_, key_set_id, "video/mp4", init_data, license_type, - app_parameters, &key_msg_, &server_url)); + app_parameters, property_set, &key_msg_, &server_url)); EXPECT_EQ(0u, server_url.size()); } void GenerateRenewalRequest(CdmLicenseType license_type, std::string* server_url) { + GenerateRenewalRequest(license_type, server_url, NULL); + } + + void GenerateRenewalRequest(CdmLicenseType license_type, + std::string* server_url, + CdmClientPropertySet* property_set) { // TODO application makes a license request, CDM will renew the license // when appropriate. std::string init_data; @@ -479,13 +495,19 @@ class WvCdmRequestLicenseTest : public testing::Test { EXPECT_EQ(wvcdm::KEY_MESSAGE, decryptor_.GenerateKeyRequest( session_id_, key_set_id_, "video/mp4", init_data, - license_type, app_parameters, &key_msg_, server_url)); + license_type, app_parameters, property_set, &key_msg_, + server_url)); // TODO(edwinwong, rfrias): Add tests cases for when license server url // is empty on renewal. Need appropriate key id at the server. EXPECT_NE(0u, server_url->size()); } void GenerateKeyRelease(CdmKeySetId key_set_id) { + GenerateKeyRelease(key_set_id, NULL); + } + + void GenerateKeyRelease(CdmKeySetId key_set_id, + CdmClientPropertySet* property_set) { CdmSessionId session_id; CdmInitData init_data; wvcdm::CdmAppParameterMap app_parameters; @@ -493,7 +515,8 @@ class WvCdmRequestLicenseTest : public testing::Test { EXPECT_EQ(wvcdm::KEY_MESSAGE, decryptor_.GenerateKeyRequest( session_id, key_set_id, "video/mp4", init_data, - kLicenseTypeRelease, app_parameters, &key_msg_, &server_url)); + kLicenseTypeRelease, app_parameters, property_set, &key_msg_, + &server_url)); } // Post a request and extract the drm message from the response @@ -1019,6 +1042,70 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseRetryOfflineKeyTest) { VerifyKeyRequestResponse(g_license_server, client_auth, false); } +TEST_F(WvCdmRequestLicenseTest, ReleaseRetryL3OfflineKeyTest) { + Unprovision(); + + TestWvCdmClientPropertySet property_set; + property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); + + // override default settings unless configured through the command line + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); + + CdmResponseType sts = + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + + if (NEED_PROVISIONING == sts) { + std::string provisioning_server_url; + CdmCertificateType cert_type = kCertificateWidevine; + std::string cert_authority, cert, wrapped_key; + EXPECT_EQ(NO_ERROR, decryptor_.GetProvisioningRequest( + cert_type, cert_authority, &key_msg_, + &provisioning_server_url)); + EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); + std::string response = + GetCertRequestResponse(g_config->provisioning_server_url()); + EXPECT_NE(0, static_cast(response.size())); + EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response, &cert, + &wrapped_key)); + EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set, + &session_id_)); + } else { + EXPECT_EQ(NO_ERROR, sts); + } + + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + GenerateKeyRequest(key_id, kLicenseTypeOffline, &property_set); + VerifyKeyRequestResponse(g_license_server, client_auth, false); + + CdmKeySetId key_set_id = key_set_id_; + EXPECT_FALSE(key_set_id_.empty()); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + key_set_id_.clear(); + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + key_set_id_.clear(); + GenerateKeyRelease(key_set_id, &property_set); + + session_id_.clear(); + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + EXPECT_EQ( + wvcdm::UNKNOWN_ERROR, decryptor_.RestoreKey(session_id_, key_set_id)); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + key_set_id_.clear(); + GenerateKeyRelease(key_set_id, &property_set); + key_set_id_ = key_set_id; + VerifyKeyRequestResponse(g_license_server, client_auth, false); +} + TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { Unprovision(); Provision(kLevelDefault); @@ -1101,6 +1188,15 @@ class WvCdmUsageInfoTest TEST_P(WvCdmUsageInfoTest, UsageInfo) { Unprovision(); Provision(kLevelDefault); + + UsageInfoSubSampleInfo* usage_info_data = GetParam(); + TestWvCdmClientPropertySet client_property_set; + TestWvCdmClientPropertySet* property_set = NULL; + if (kLevel3 == usage_info_data->security_level) { + client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); + property_set = &client_property_set; + } + CdmSecurityLevel security_level = GetDefaultSecurityLevel(); DeviceFiles handle; EXPECT_TRUE(handle.Init(security_level)); @@ -1108,10 +1204,9 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) { handle.SetTestFile(&file); EXPECT_TRUE(handle.DeleteUsageInfo()); - UsageInfoSubSampleInfo* usage_info_data = GetParam(); for (size_t i = 0; i < usage_info_data->usage_info; ++i) { SubSampleInfo* data = usage_info_data->sub_sample + i; - decryptor_.OpenSession(g_key_system, NULL, &session_id_); + decryptor_.OpenSession(g_key_system, property_set, &session_id_); std::string key_id = a2bs_hex( "000000427073736800000000" // blob size and pssh "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id @@ -1121,7 +1216,7 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) { char ch = 0x33 + i; key_id.append(1, ch); - GenerateKeyRequest(key_id, kLicenseTypeStreaming); + GenerateKeyRequest(key_id, kLicenseTypeStreaming, property_set); VerifyKeyRequestResponse(g_license_server, g_client_auth, false); std::vector decrypt_buffer(data->encrypt_data.size()); diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index d42b5f10..7cef174a 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -209,7 +209,8 @@ status_t WVDrmPlugin::getKeyRequest( cdmInitDataType, processedInitData, cdmLicenseType, cdmParameters, - &keyRequest, &cdmDefaultUrl); + &mPropertySet, &keyRequest, + &cdmDefaultUrl); if (isCdmResponseTypeSuccess(res)) { defaultUrl.clear(); diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index e0a10dfe..b53b5624 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -31,12 +31,13 @@ class MockCDM : public WvContentDecryptionModule { MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&)); - MOCK_METHOD8(GenerateKeyRequest, CdmResponseType(const CdmSessionId&, + MOCK_METHOD9(GenerateKeyRequest, CdmResponseType(const CdmSessionId&, const CdmKeySetId&, const std::string&, const CdmInitData&, const CdmLicenseType, CdmAppParameterMap&, + CdmClientPropertySet*, CdmKeyMessage*, string*)); MOCK_METHOD3(AddKey, CdmResponseType(const CdmSessionId&, @@ -272,23 +273,23 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", mimeType, initData, kLicenseTypeOffline, cdmParameters, _, - _)) - .WillOnce(DoAll(SetArgPointee<6>(cdmRequest), - SetArgPointee<7>(kDefaultUrl), + _, _)) + .WillOnce(DoAll(SetArgPointee<7>(cdmRequest), + SetArgPointee<8>(kDefaultUrl), Return(wvcdm::KEY_MESSAGE))); EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", mimeType, initData, kLicenseTypeStreaming, cdmParameters, _, - _)) - .WillOnce(DoAll(SetArgPointee<6>(cdmRequest), - SetArgPointee<7>(kDefaultUrl), + _, _)) + .WillOnce(DoAll(SetArgPointee<7>(cdmRequest), + SetArgPointee<8>(kDefaultUrl), Return(wvcdm::KEY_MESSAGE))); EXPECT_CALL(cdm, GenerateKeyRequest("", cdmKeySetId, mimeType, initData, - kLicenseTypeRelease, cdmParameters, _, - _)) - .WillOnce(DoAll(SetArgPointee<6>(cdmRequest), - SetArgPointee<7>(kDefaultUrl), + kLicenseTypeRelease, cdmParameters, + NotNull(), _, _)) + .WillOnce(DoAll(SetArgPointee<7>(cdmRequest), + SetArgPointee<8>(kDefaultUrl), Return(wvcdm::KEY_MESSAGE))); } }