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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<int>(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<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
||||
|
||||
@@ -209,7 +209,8 @@ status_t WVDrmPlugin::getKeyRequest(
|
||||
cdmInitDataType,
|
||||
processedInitData,
|
||||
cdmLicenseType, cdmParameters,
|
||||
&keyRequest, &cdmDefaultUrl);
|
||||
&mPropertySet, &keyRequest,
|
||||
&cdmDefaultUrl);
|
||||
|
||||
if (isCdmResponseTypeSuccess(res)) {
|
||||
defaultUrl.clear();
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user