diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index cfb1e3eb..599ea806 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -1203,6 +1203,9 @@ bool DeviceFiles::RetrieveHashedFile( if (bytes != static_cast(serialized_hash_file.size())) { LOGW("DeviceFiles::RetrieveHashedFile: read failed"); + // Remove the corrupted file so the caller will not get the same error + // when trying to access the file repeatedly, causing the system to stall. + file_system_->Remove(path); return false; } @@ -1212,6 +1215,9 @@ bool DeviceFiles::RetrieveHashedFile( HashedFile hash_file; if (!hash_file.ParseFromString(serialized_hash_file)) { LOGW("DeviceFiles::RetrieveHashedFile: Unable to parse hash file"); + // Remove the corrupted file so the caller will not get the same error + // when trying to access the file repeatedly, causing the system to stall. + file_system_->Remove(path); return false; } @@ -1231,6 +1237,9 @@ bool DeviceFiles::RetrieveHashedFile( if (!deserialized_file->ParseFromString(hash_file.file())) { LOGW("DeviceFiles::RetrieveHashedFile: Unable to parse file"); + // Remove the corrupted file so the caller will not get the same error + // when trying to access the file repeatedly, causing the system to stall. + file_system_->Remove(path); return false; } return true; diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 3ca36bed..76dc3400 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -13,6 +13,7 @@ #include "cdm_identifier.h" #include "config_test_env.h" #include "device_files.h" +#include "device_files.pb.h" #include "file_store.h" #include "file_utils.h" #include "license_protocol.pb.h" @@ -3419,6 +3420,170 @@ TEST_F(WvCdmRequestLicenseTest, RemoveCorruptedUsageInfoTest) { EXPECT_TRUE(usage_info.empty()); } +TEST_F(WvCdmRequestLicenseTest, RemoveCorruptedUsageInfoTest2) { + 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 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 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(); + + video_widevine_client::sdk::HashedFile hash_file; + + EXPECT_TRUE(hash_file.ParseFromString(file_data)); + size_t pos = file_data.find(hash_file.hash()); + EXPECT_NE(std::string::npos, pos); + EXPECT_NE(0u, pos); + + // Corrupt the protobuf key field of the hash of the usage info file + // with non-empty app id and write it back out + file_data[pos-1] = 0xff; + + 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(); + + EXPECT_TRUE(hash_file.ParseFromString(file_data)); + pos = file_data.find(hash_file.hash()); + EXPECT_NE(std::string::npos, pos); + EXPECT_NE(0u, pos); + + // Corrupt the protobuf key field of the hash of the usage info file + // with empty app id and write it back out + file_data[pos-1] = 0xff; + + 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();