From 9bc13a07a05ab65c706b12ac3721143358de9549 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Thu, 4 Sep 2014 00:23:03 -0700 Subject: [PATCH] Corrections for license release on fallback to L3 When falling back to L3, release requests were failing. Information requesting falling back to L3 is passed along when the session is opened. Licenses however are released using the key set ID and information requesting fallback to L3(CdmClientPropertySet) at that point is unavailable. The release was actually attempting to release a license at the default security level which is incorrect. In addition, the mac keys were not being setup correctly and the release message was signed with keys derived from the license request and not the response. Both these issues have been addressed and unit tests added to track release of offline licenses and usage reporting scenarios. [ Merge of https://widevine-internal-review.googlesource.com/#/c/11062 from wv cdm repo ] b/17073910 Change-Id: I5cd95a7dfe58ebae7ae27ece6c92e67755c1d665 --- libwvdrmengine/cdm/core/include/cdm_engine.h | 4 +- libwvdrmengine/cdm/core/src/cdm_engine.cpp | 6 +- libwvdrmengine/cdm/core/src/license.cpp | 34 +++--- .../core/src/oemcrypto_adapter_dynamic.cpp | 10 +- .../include/wv_content_decryption_module.h | 1 + .../cdm/src/wv_content_decryption_module.cpp | 3 +- .../cdm/test/cdm_extended_duration_test.cpp | 7 +- .../cdm/test/request_license_test.cpp | 113 ++++++++++++++++-- libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 3 +- .../mediadrm/test/WVDrmPlugin_test.cpp | 23 ++-- 10 files changed, 158 insertions(+), 46 deletions(-) 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))); } }