From 4e201c2700c39b09863884cf61d855b547782a81 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Sat, 31 Mar 2018 03:37:36 -0700 Subject: [PATCH 1/2] Correct segfault in WvCdmRequestLicenseTest.UsageRemoveSecureStopTest [ Merge of http://go/wvgerrit/46622 ] Secure stop API related changes introduced in b/69674645 caused segfaults on taimen/walleye but not other devices due to a difference in OEMCrypto version. Bug: 77294890 Test: WV unit/integration tests on sailfish and walleye Change-Id: I8523ef283334d7d32d180e902072fe1dd6e665c1 --- libwvdrmengine/cdm/core/src/cdm_engine.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index d849da28..5929e6d4 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -1315,8 +1315,7 @@ CdmResponseType CdmEngine::RemoveUsageInfo( switch (usage_session_->get_usage_support_type()) { case kUsageEntrySupport: { - status = usage_session_->DeleteUsageEntry( - usage_data[0].usage_entry_number); + status = usage_session_->DeleteUsageEntry(usage_entry_number); if (!handle.DeleteUsageInfo( DeviceFiles::GetUsageInfoFileName(app_id), From d7d8940174bee5e8c96ece7c8215354ee6ef963a Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Sun, 1 Apr 2018 00:27:49 -0700 Subject: [PATCH 2/2] 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 --- .../cdm/core/include/wv_cdm_types.h | 2 +- libwvdrmengine/cdm/core/src/cdm_engine.cpp | 15 +- libwvdrmengine/cdm/core/src/cdm_session.cpp | 19 ++ .../cdm/core/test/test_printers.cpp | 3 - .../cdm/test/request_license_test.cpp | 272 +++++++++++++++++- libwvdrmengine/include/WVErrors.h | 1 - libwvdrmengine/include/mapErrors-inl.h | 2 - libwvdrmengine/include_hidl/mapErrors-inl.h | 1 - 8 files changed, 299 insertions(+), 16 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 5685878e..ec2f78c4 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -289,7 +289,7 @@ enum CdmResponseType { USAGE_STORE_LICENSE_FAILED = 247, USAGE_STORE_USAGE_INFO_FAILED = 248, USAGE_INVALID_LOAD_ENTRY = 249, - REMOVE_ALL_USAGE_INFO_ERROR_4 = 250, + /* previously REMOVE_ALL_USAGE_INFO_ERROR_4 = 250, */ REMOVE_ALL_USAGE_INFO_ERROR_5 = 251, RELEASE_USAGE_INFO_FAILED = 252, INCORRECT_USAGE_SUPPORT_TYPE_1 = 253, diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 5929e6d4..034729af 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -1223,24 +1223,29 @@ CdmResponseType CdmEngine::RemoveAllUsageInfo(const std::string& app_id) { if (!handle.RetrieveUsageInfo( DeviceFiles::GetUsageInfoFileName(app_id), &usage_data)) { - status = REMOVE_ALL_USAGE_INFO_ERROR_4; + LOGW("CdmEngine::RemoveAllUsageInfo: failed to retrieve usage info"); break; } if (usage_data.empty()) break; - status = usage_session_->DeleteUsageEntry( + CdmResponseType res = usage_session_->DeleteUsageEntry( usage_data[0].usage_entry_number); - if (status != NO_ERROR) break; + if (res != NO_ERROR) { + LOGW("CdmEngine::RemoveAllUsageInfo: failed to delete usage " + "entry: error: %d", res); + break; + } if (!handle.DeleteUsageInfo( DeviceFiles::GetUsageInfoFileName(app_id), usage_data[0].provider_session_token)) { - status = REMOVE_ALL_USAGE_INFO_ERROR_6; + LOGW("CdmEngine::RemoveAllUsageInfo: failed to delete usage " + "info"); break; } - } while (status == NO_ERROR && !usage_data.empty()); + } while (!usage_data.empty()); std::vector provider_session_tokens; if (!handle.DeleteAllUsageInfoForApp( diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 5cb77013..ffac81bc 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -841,6 +841,25 @@ CdmResponseType CdmSession::StoreLicense() { key_set_id_, usage_entry_, usage_entry_number_)) { LOGE("CdmSession::StoreLicense: Unable to store usage info"); + // Usage info file is corrupt. Delete current usage entry and file. + switch (usage_support_type_) { + case kUsageEntrySupport: + DeleteUsageEntry(usage_entry_number_); + break; + case kUsageTableSupport: + crypto_session_->DeleteUsageInformation(provider_session_token); + crypto_session_->UpdateUsageInformation(); + break; + default: + LOGW("CdmSession::StoreLicense: unexpected usage support type: %d", + usage_support_type_); + break; + } + std::vector provider_session_tokens; + file_handle_->DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(app_id), + &provider_session_tokens); + return STORE_USAGE_INFO_ERROR; } return NO_ERROR; diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index 7da8bbf5..863f3425 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -227,9 +227,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case REMOVE_ALL_USAGE_INFO_ERROR_2: *os << "REMOVE_ALL_USAGE_INFO_ERROR_2"; break; - case REMOVE_ALL_USAGE_INFO_ERROR_4: - *os << "REMOVE_ALL_USAGE_INFO_ERROR_4"; - break; case REMOVE_ALL_USAGE_INFO_ERROR_5: *os << "REMOVE_ALL_USAGE_INFO_ERROR_5"; break; diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 9af2eea2..3aa74ab4 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -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 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(); + + // 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 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 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(); diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index 0e373979..631e4d0a 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -231,7 +231,6 @@ enum { kUsageStoreLicenseFailed = ERROR_DRM_VENDOR_MIN + 241, kUsageStoreUsageInfoFailed = ERROR_DRM_VENDOR_MIN + 242, kUsageInvalidLoadEntry = ERROR_DRM_VENDOR_MIN + 243, - kRemoveAllUsageInfoError4 = ERROR_DRM_VENDOR_MIN + 244, kRemoveAllUsageInfoError5 = ERROR_DRM_VENDOR_MIN + 245, kReleaseUsageInfoFailed = ERROR_DRM_VENDOR_MIN + 246, kIncorrectUsageSupportType1 = ERROR_DRM_VENDOR_MIN + 247, diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index e9d82b20..b4aa175c 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -447,8 +447,6 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) { return kIncorrectUsageSupportType1; case wvcdm::INCORRECT_USAGE_SUPPORT_TYPE_2: return kIncorrectUsageSupportType2; - case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_4: - return kRemoveAllUsageInfoError4; case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_5: return kRemoveAllUsageInfoError5; case wvcdm::NO_USAGE_ENTRIES: diff --git a/libwvdrmengine/include_hidl/mapErrors-inl.h b/libwvdrmengine/include_hidl/mapErrors-inl.h index e64601c7..81b571fc 100644 --- a/libwvdrmengine/include_hidl/mapErrors-inl.h +++ b/libwvdrmengine/include_hidl/mapErrors-inl.h @@ -264,7 +264,6 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) { case wvcdm::USAGE_STORE_LICENSE_FAILED: case wvcdm::USAGE_STORE_USAGE_INFO_FAILED: case wvcdm::USAGE_INVALID_LOAD_ENTRY: - case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_4: case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_5: case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_6: case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_7: