Recovery from usage info corruption
[ Merge of http://go/wvgerrit/46623 ] If corruption of the usage information file is detected while saving a streaming license with a PST, usage information file is deleted, so that a subsequent load keys may succeed. Also when calling the MediaDrm API releaseAllSecureStops(), an error would be returned if usage info file was corrupted. Since this file is deleted successfully, errors have been replaced with warnings. Bug: 73447733 Test: wv unit/integration tests Change-Id: Ie4a63ac202fd6009609105f38ffa8a3b23ed334e
This commit is contained in:
@@ -1475,13 +1475,11 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
||||
|
||||
void VerifyKeyRequestResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
std::string response;
|
||||
VerifyKeyRequestResponse(server_url, client_auth, false);
|
||||
}
|
||||
|
||||
void VerifyUsageKeyRequestResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
std::string response;
|
||||
VerifyKeyRequestResponse(server_url, client_auth, true);
|
||||
}
|
||||
|
||||
@@ -1496,10 +1494,19 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
||||
const std::string& client_auth,
|
||||
bool is_usage,
|
||||
std::string* response) {
|
||||
VerifyKeyRequestResponse(wvcdm::KEY_ADDED, server_url, client_auth,
|
||||
is_usage, response);
|
||||
}
|
||||
|
||||
void VerifyKeyRequestResponse(CdmResponseType expected_response,
|
||||
const std::string& server_url,
|
||||
const std::string& client_auth,
|
||||
bool is_usage,
|
||||
std::string* response) {
|
||||
*response = GetKeyRequestResponse(server_url, client_auth);
|
||||
|
||||
EXPECT_EQ(decryptor_.AddKey(session_id_, *response, &key_set_id_),
|
||||
wvcdm::KEY_ADDED);
|
||||
expected_response);
|
||||
EXPECT_EQ(is_usage || license_type_ == kLicenseTypeOffline,
|
||||
key_set_id_.size() > 0);
|
||||
}
|
||||
@@ -3116,6 +3123,156 @@ TEST_F(WvCdmRequestLicenseTest, UsageRemoveAllTest) {
|
||||
EXPECT_TRUE(usage_info.empty());
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, RemoveCorruptedUsageInfoTest) {
|
||||
Unprovision();
|
||||
|
||||
std::string app_id_empty = "";
|
||||
std::string app_id_not_empty = "not empty";
|
||||
|
||||
TestWvCdmClientPropertySet property_set;
|
||||
Provision(kLevelDefault);
|
||||
|
||||
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
||||
FileSystem file_system;
|
||||
DeviceFiles handle(&file_system);
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
std::vector<std::string> psts;
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
||||
|
||||
for (size_t i = 0; i < N_ELEM(usage_info_sub_samples_icp); ++i) {
|
||||
SubSampleInfo* data = usage_info_sub_samples_icp + i;
|
||||
property_set.set_app_id(i % 2 == 0 ? app_id_empty : app_id_not_empty);
|
||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
std::string key_id = a2bs_hex(
|
||||
"000000427073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
||||
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
||||
"747265616d696e675f636c6970");
|
||||
|
||||
char ch = 0x33 + i;
|
||||
key_id.append(1, ch);
|
||||
|
||||
GenerateKeyRequest(key_id, kLicenseTypeStreaming, &property_set);
|
||||
|
||||
// TODO(rfrias): streaming_clip6 is a streaming license without a pst
|
||||
if (ch == '6')
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, false);
|
||||
else
|
||||
VerifyUsageKeyRequestResponse(g_license_server, g_client_auth);
|
||||
|
||||
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
||||
CdmDecryptionParameters decryption_parameters(
|
||||
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
||||
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
||||
decryption_parameters.is_encrypted = data->is_encrypted;
|
||||
decryption_parameters.is_secure = data->is_secure;
|
||||
decryption_parameters.subsample_flags = data->subsample_flags;
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data->validate_key_id,
|
||||
decryption_parameters));
|
||||
|
||||
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
||||
decrypt_buffer.begin()));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
CdmUsageInfo usage_info;
|
||||
EXPECT_EQ(
|
||||
KEY_MESSAGE,
|
||||
decryptor_.GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.size() > 0);
|
||||
EXPECT_EQ(
|
||||
KEY_MESSAGE,
|
||||
decryptor_.GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.size() > 0);
|
||||
|
||||
// Read in usage info file
|
||||
std::string path;
|
||||
EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path));
|
||||
std::string usage_info_not_empty_app_id_file_name =
|
||||
path + DeviceFiles::GetUsageInfoFileName(app_id_not_empty);
|
||||
|
||||
ssize_t file_size =
|
||||
file_system.FileSize(usage_info_not_empty_app_id_file_name);
|
||||
EXPECT_LT(4, file_size);
|
||||
File* file =
|
||||
file_system.Open(usage_info_not_empty_app_id_file_name,
|
||||
FileSystem::kReadOnly);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
std::string file_data;
|
||||
file_data.resize(file_size);
|
||||
ssize_t bytes = file->Read(&file_data[0], file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
// Corrupt the hash of the usage info file with non-empty app id and write
|
||||
// it back out
|
||||
memset(&file_data[0]+bytes-4, 0, 4);
|
||||
file = file_system.Open(usage_info_not_empty_app_id_file_name,
|
||||
FileSystem::kCreate | FileSystem::kTruncate);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
bytes = file->Write(file_data.data(), file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.RemoveAllUsageInfo(app_id_not_empty, kDefaultCdmIdentifier));
|
||||
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.empty());
|
||||
EXPECT_EQ(
|
||||
KEY_MESSAGE,
|
||||
decryptor_.GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.size() > 0);
|
||||
|
||||
// Read in usage info file
|
||||
std::string usage_info_empty_app_id_file_name =
|
||||
path + DeviceFiles::GetUsageInfoFileName(app_id_empty);
|
||||
|
||||
file_size = file_system.FileSize(usage_info_empty_app_id_file_name);
|
||||
EXPECT_LT(4, file_size);
|
||||
file = file_system.Open(usage_info_empty_app_id_file_name,
|
||||
FileSystem::kReadOnly);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
file_data.resize(file_size);
|
||||
bytes = file->Read(&file_data[0], file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
// Corrupt the hash of the usage info file with empty app id and write it
|
||||
// back out
|
||||
memset(&file_data[0]+bytes-4, 0, 4);
|
||||
file = file_system.Open(usage_info_empty_app_id_file_name,
|
||||
FileSystem::kCreate | FileSystem::kTruncate);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
bytes = file->Write(file_data.data(), file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.RemoveAllUsageInfo(app_id_empty, kDefaultCdmIdentifier));
|
||||
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.empty());
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.empty());
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, GetSecureStopIdsTest) {
|
||||
Unprovision();
|
||||
|
||||
@@ -3276,6 +3433,115 @@ TEST_F(WvCdmRequestLicenseTest, GetSecureStopIdsTest) {
|
||||
EXPECT_TRUE(retrieved_secure_stop_ids.empty());
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, UsageRecoveryTest) {
|
||||
Unprovision();
|
||||
|
||||
std::string app_id_empty = "";
|
||||
|
||||
TestWvCdmClientPropertySet property_set;
|
||||
Provision(kLevelDefault);
|
||||
|
||||
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
||||
FileSystem file_system;
|
||||
DeviceFiles handle(&file_system);
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
std::vector<std::string> psts;
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
||||
|
||||
// Fetch a usage license
|
||||
SubSampleInfo* data = kUsageLicenseTestVector1[0].sub_sample;
|
||||
|
||||
property_set.set_app_id(app_id_empty);
|
||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
GenerateKeyRequest(kUsageLicenseTestVector1[0].pssh, kLicenseTypeStreaming,
|
||||
&property_set);
|
||||
|
||||
VerifyUsageKeyRequestResponse(g_license_server, g_client_auth);
|
||||
|
||||
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
||||
CdmDecryptionParameters decryption_parameters(
|
||||
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
||||
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
||||
decryption_parameters.is_encrypted = data->is_encrypted;
|
||||
decryption_parameters.is_secure = data->is_secure;
|
||||
decryption_parameters.subsample_flags = data->subsample_flags;
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data->validate_key_id,
|
||||
decryption_parameters));
|
||||
|
||||
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
||||
decrypt_buffer.begin()));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
std::string path;
|
||||
EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path));
|
||||
std::string usage_info_file_name =
|
||||
path + DeviceFiles::GetUsageInfoFileName(app_id_empty);
|
||||
|
||||
// Read in usage info file
|
||||
ssize_t file_size = file_system.FileSize(usage_info_file_name);
|
||||
EXPECT_LT(4, file_size);
|
||||
File* file =
|
||||
file_system.Open(usage_info_file_name, FileSystem::kReadOnly);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
std::string file_data;
|
||||
file_data.resize(file_size);
|
||||
ssize_t bytes = file->Read(&file_data[0], file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
// Corrupt the hash of the usage info file and write it back out
|
||||
memset(&file_data[0]+bytes-4, 0, 4);
|
||||
file = file_system.Open(usage_info_file_name,
|
||||
FileSystem::kCreate | FileSystem::kTruncate);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
bytes = file->Write(file_data.data(), file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
// Fetch a second usage license, this should fail as the usage table is
|
||||
// corrupt
|
||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
GenerateKeyRequest(kUsageLicenseTestVector1[1].pssh, kLicenseTypeStreaming,
|
||||
&property_set);
|
||||
|
||||
std::string response;
|
||||
VerifyKeyRequestResponse(wvcdm::STORE_USAGE_INFO_ERROR, g_license_server,
|
||||
g_client_auth, true, &response);
|
||||
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
// Fetch the second usage license and verify that it is usable
|
||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
GenerateKeyRequest(kUsageLicenseTestVector1[1].pssh, kLicenseTypeStreaming,
|
||||
&property_set);
|
||||
|
||||
VerifyUsageKeyRequestResponse(g_license_server, g_client_auth);
|
||||
|
||||
data = kUsageLicenseTestVector1[1].sub_sample;
|
||||
|
||||
decrypt_buffer.resize(data->encrypt_data.size());
|
||||
decryption_parameters.key_id = &data->key_id;
|
||||
decryption_parameters.encrypt_buffer = &data->encrypt_data.front();
|
||||
decryption_parameters.encrypt_length = data->encrypt_data.size();
|
||||
decryption_parameters.iv = &data->iv;
|
||||
decryption_parameters.block_offset = data->block_offset;
|
||||
decryption_parameters.decrypt_buffer = &decrypt_buffer[0];
|
||||
decryption_parameters.decrypt_buffer_length = data->encrypt_data.size();
|
||||
decryption_parameters.is_encrypted = data->is_encrypted;
|
||||
decryption_parameters.is_secure = data->is_secure;
|
||||
decryption_parameters.subsample_flags = data->subsample_flags;
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data->validate_key_id,
|
||||
decryption_parameters));
|
||||
|
||||
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
||||
decrypt_buffer.begin()));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, UsageRemoveSecureStopTest) {
|
||||
Unprovision();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user