From 20eeb1e5461d3dfbb99ec8cecd4b1842d3d853a9 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Sat, 16 Sep 2017 21:26:44 -0700 Subject: [PATCH] Fixes for usage table upgrades [ Merge of http://go/wvgerrit/34060 ] License were not being upgraded successfully from usage tables to usage table headers and entries (big usage tables). Bug: 65730713 Test: WV unit/integration tests Test: GTSMediaDrmTests Test: Playback using netflix and play movies Test: Manual upgrade from N (L3) Change-Id: I7ef127204104fa36dd1ee385bc80ed6a81172b4b --- .../cdm/core/include/crypto_session.h | 9 ++ .../cdm/core/include/usage_table_header.h | 2 + .../cdm/core/src/crypto_session.cpp | 72 ++++++++++-- .../cdm/core/src/usage_table_header.cpp | 68 ++++++++--- .../core/test/usage_table_header_unittest.cpp | 108 +++++++++++++++++- libwvdrmengine/cdm/src/file_utils.cpp | 17 --- 6 files changed, 230 insertions(+), 46 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 42bf7e37..f0ae42e8 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -165,8 +165,17 @@ class CryptoSession { virtual CdmResponseType ShrinkUsageTableHeader( uint32_t new_entry_count, CdmUsageTableHeader* usage_table_header); virtual CdmResponseType MoveUsageEntry(uint32_t new_entry_number); + virtual bool CreateOldUsageEntry( + uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + UsageDurationStatus status, + const std::string& server_mac_key, + const std::string& client_mac_key, + const std::string& provider_session_token); virtual CdmResponseType CopyOldUsageEntry( const std::string& provider_session_token); + virtual metrics::CryptoMetrics* GetCryptoMetrics() { return metrics_; } private: friend class CryptoSessionForTest; diff --git a/libwvdrmengine/cdm/core/include/usage_table_header.h b/libwvdrmengine/cdm/core/include/usage_table_header.h index 0fb8f748..78f3b9ec 100644 --- a/libwvdrmengine/cdm/core/include/usage_table_header.h +++ b/libwvdrmengine/cdm/core/include/usage_table_header.h @@ -92,6 +92,8 @@ class UsageTableHeader { virtual bool is_inited() { return is_inited_; } + virtual bool CreateDummyOldUsageEntry(CryptoSession* crypto_session); + // This handle and file system is only to be used when accessing // usage_table_header. Usage entries should use the file system provided // by CdmSession. diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 34fe5090..d86778bc 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -586,24 +586,25 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { CdmSecurityLevel security_level = GetSecurityLevel(); if (security_level == kSecurityLevelL1 || security_level == kSecurityLevelL3) { - UsageTableHeader* header = security_level == kSecurityLevelL1 ? - usage_table_header_l1_ : usage_table_header_l3_; - if (header == NULL) { - header = new UsageTableHeader(); + UsageTableHeader** header = security_level == kSecurityLevelL1 ? + &usage_table_header_l1_ : &usage_table_header_l3_; + if (*header == NULL) { + *header = new UsageTableHeader(); // Ignore errors since we do not know when a session is opened, // if it is intended to be used for offline/usage session related // or otherwise. - if (!header->Init(security_level, this)) { - delete header; + crypto_lock_.Release(); + bool is_usage_table_header_inited = + (*header)->Init(security_level, this); + crypto_lock_.Acquire(); + if (!is_usage_table_header_inited) { + delete *header; + *header = NULL; usage_table_header_ = NULL; return NO_ERROR; } - if (security_level == kSecurityLevelL1) - usage_table_header_l1_ = header; - else - usage_table_header_l3_ = header; } - usage_table_header_ = header; + usage_table_header_ = *header; } } } @@ -2262,6 +2263,55 @@ CdmResponseType CryptoSession::MoveUsageEntry(uint32_t new_entry_number) { return NO_ERROR; } +bool CryptoSession::CreateOldUsageEntry( + uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + UsageDurationStatus usage_duration_status, + const std::string& server_mac_key, + const std::string& client_mac_key, + const std::string& provider_session_token) { + LOGV("CreateOldUsageEntry: Lock"); + AutoLock auto_lock(crypto_lock_); + + if (server_mac_key.size() < MAC_KEY_SIZE || + client_mac_key.size() < MAC_KEY_SIZE) { + LOGE("CreateOldUsageEntry: Invalid mac key size: server mac key size %d, " + "client mac key size: %d", server_mac_key.size(), + client_mac_key.size()); + return false; + } + + OEMCrypto_Usage_Entry_Status status; + switch (usage_duration_status) { + case kUsageDurationsInvalid: status = kUnused; break; + case kUsageDurationPlaybackNotBegun: status = kInactiveUnused; break; + case kUsageDurationsValid: status = kActive; break; + default: + LOGE("CreateOldUsageEntry: Unrecognized usage entry status: %d", status); + status = kUnused; + return false; + } + + OEMCryptoResult result = + OEMCrypto_CreateOldUsageEntry( + requested_security_level_, time_since_license_received, + time_since_first_decrypt, time_since_last_decrypt, status, + reinterpret_cast( + const_cast(server_mac_key.data())), + reinterpret_cast( + const_cast(client_mac_key.data())), + reinterpret_cast(provider_session_token.data()), + provider_session_token.size()); + + if (result != OEMCrypto_SUCCESS) { + LOGE("CreateOldUsageEntry: OEMCrypto_CreateOldUsageEntry error: %d", + result); + return false; + } + return true; +} + CdmResponseType CryptoSession::CopyOldUsageEntry( const std::string& provider_session_token) { LOGV("CopyOldUsageEntry: id=%ld", (uint32_t)oec_session_id_); diff --git a/libwvdrmengine/cdm/core/src/usage_table_header.cpp b/libwvdrmengine/cdm/core/src/usage_table_header.cpp index d34b26bb..0f66ace9 100644 --- a/libwvdrmengine/cdm/core/src/usage_table_header.cpp +++ b/libwvdrmengine/cdm/core/src/usage_table_header.cpp @@ -5,9 +5,17 @@ #include "crypto_session.h" #include "license.h" #include "log.h" +#include "wv_cdm_constants.h" namespace { std::string kEmptyString; +uint64_t kOldUsageEntryTimeSinceLicenseReceived = 0; +uint64_t kOldUsageEntryTimeSinceFirstDecrypt = 0; +uint64_t kOldUsageEntryTimeSinceLastDecrypt = 0; +std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0); +std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0); +std::string kOldUsageEntryPoviderSessionToken = + "nahZ6achSheiqua3TohQuei0ahwohv"; } namespace wvcdm { @@ -39,6 +47,8 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level, } security_level_ = security_level; + requested_security_level_ = + security_level_ == kSecurityLevelL3 ? kLevel3 : kLevelDefault; if (!file_handle_->Init(security_level)) { LOGE("UsageTableHeader::Init: device files initialization failed"); @@ -53,18 +63,27 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level, LOGE( "UsageTableHeader::Init: load usage table failed, security level: %d", security_level); + file_handle_->DeleteAllLicenses(); + usage_entry_info_.clear(); + usage_table_header_.clear(); + status = crypto_session->CreateUsageTableHeader(&usage_table_header_); + if (status != NO_ERROR) return false; + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); } - } - if (status != NO_ERROR) { - file_handle_->DeleteAllLicenses(); - usage_entry_info_.clear(); + } else { status = crypto_session->CreateUsageTableHeader(&usage_table_header_); if (status != NO_ERROR) return false; file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + + metrics::CryptoMetrics alternate_metrics; + metrics::CryptoMetrics* metrics = + crypto_session->GetCryptoMetrics() != nullptr ? + crypto_session->GetCryptoMetrics() : &alternate_metrics; + + UpgradeFromUsageTable(file_handle_.get(), metrics); + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); } - requested_security_level_ = - security_level_ == kSecurityLevelL3 ? kLevel3 : kLevelDefault; is_inited_ = true; return true; } @@ -144,8 +163,11 @@ CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number, metrics::CryptoMetrics* metrics) { LOGV("UsageTableHeader::DeleteEntry: Lock"); AutoLock auto_lock(usage_table_header_lock_); - if (usage_entry_number >= usage_entry_info_.size()) + if (usage_entry_number >= usage_entry_info_.size()) { + LOGE("UsageTableHeader::DeleteEntry: usage entry number %d larger than " + "usage entry size %d", usage_entry_number, usage_entry_info_.size()); return USAGE_INVALID_PARAMETERS_1; + } // Find the last valid entry number, in order to swap size_t swap_entry_number = usage_entry_info_.size() - 1; @@ -430,7 +452,7 @@ bool UsageTableHeader::UpgradeLicensesFromUsageTable( // * save the usage table header and store the usage entry number and // usage entry along with the license to persistent memory std::vector key_set_ids; - if (handle->ListLicenses(&key_set_ids)) { + if (!handle->ListLicenses(&key_set_ids)) { LOGW( "UpgradeUsageTableHeader::UpgradeLicensesFromUsageTable: unable to " "retrieve list of licenses"); @@ -473,6 +495,9 @@ bool UsageTableHeader::UpgradeLicensesFromUsageTable( if (status != NO_ERROR) continue; + // TODO(fredgc): remove when b/65730828 is addressed + if (!CreateDummyOldUsageEntry(&crypto_session)) continue; + status = AddEntry(&crypto_session, true /* persistent license */, key_set_ids[i], kEmptyString, &usage_entry_number); @@ -521,7 +546,7 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable( // information to persistent memory along with usage entry number and usage // entry. std::vector usage_info_file_names; - if (handle->ListUsageInfoFiles(&usage_info_file_names)) { + if (!handle->ListUsageInfoFiles(&usage_info_file_names)) { LOGW( "UpgradeUsageTableHeader::UpgradeUsageInfoFromUsageTable: Unable to " "retrieve list of usage info file names"); @@ -538,7 +563,7 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable( continue; } - for (size_t j = 0; j < usage_data.size(); --j) { + for (size_t j = 0; j < usage_data.size(); ++j) { if (usage_data[j].provider_session_token.empty()) { LOGW( "UsageTableHeader::UpgradeUsageInfoFromUsageTable: Provider " @@ -551,16 +576,19 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable( if (status != NO_ERROR) continue; + // TODO(fredgc): remove when b/65730828 is addressed + if (!CreateDummyOldUsageEntry(&crypto_session)) continue; + // TODO(rfrias): We need to fill in the app id, but it is hashed // and we have no way to extract. Use the hased filename instead? status = AddEntry(&crypto_session, false /* usage info */, - usage_data[j].key_set_id, kEmptyString, - &(usage_data[i].usage_entry_number)); + usage_data[j].key_set_id, usage_info_file_names[i], + &(usage_data[j].usage_entry_number)); if (status != NO_ERROR) continue; status = crypto_session.CopyOldUsageEntry( - usage_data[i].provider_session_token); + usage_data[j].provider_session_token); if (status != NO_ERROR) { crypto_session.Close(); @@ -568,7 +596,7 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable( continue; } - status = UpdateEntry(&crypto_session, &(usage_data[i].usage_entry)); + status = UpdateEntry(&crypto_session, &(usage_data[j].usage_entry)); if (status != NO_ERROR) { crypto_session.Close(); @@ -589,4 +617,16 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable( return NO_ERROR; } +// TODO(fredgc): remove when b/65730828 is addressed +bool UsageTableHeader::CreateDummyOldUsageEntry(CryptoSession* crypto_session) { + return crypto_session->CreateOldUsageEntry( + kOldUsageEntryTimeSinceLicenseReceived, + kOldUsageEntryTimeSinceFirstDecrypt, + kOldUsageEntryTimeSinceLastDecrypt, + CryptoSession::kUsageDurationsInvalid, + kOldUsageEntryServerMacKey, + kOldUsageEntryClientMacKey, + kOldUsageEntryPoviderSessionToken); +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp index 7b197d58..fcb870de 100644 --- a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp @@ -50,6 +50,46 @@ const CdmUsageEntryInfo kUsageEntryInfoStorageTypeUnknown = { .storage_type = kStorageUnknown, .key_set_id = "", .usage_info_file_name = ""}; +const std::vector kEmptyLicenseList; +const std::vector kLicenseList = { + kUsageEntryInfoOfflineLicense1.key_set_id, + kUsageEntryInfoOfflineLicense2.key_set_id, + kUsageEntryInfoOfflineLicense3.key_set_id, +}; +const std::vector kEmptyUsageInfoFilesList; +const std::vector kUsageInfoFileList = { + kUsageEntryInfoSecureStop1.usage_info_file_name, + kUsageEntryInfoSecureStop2.usage_info_file_name, + kUsageEntryInfoSecureStop3.usage_info_file_name, +}; +const DeviceFiles::CdmUsageData kCdmUsageData1 = { + .provider_session_token = "provider_session_token_1", + .license_request = "license_request_1", + .license = "license_1", + .key_set_id = "key_set_id_1", + .usage_entry = "usage_entry_1", + .usage_entry_number = 0, +}; +const DeviceFiles::CdmUsageData kCdmUsageData2 = { + .provider_session_token = "provider_session_token_2", + .license_request = "license_request_2", + .license = "license_2", + .key_set_id = "key_set_id_2", + .usage_entry = "usage_entry_2", + .usage_entry_number = 0, +}; +const DeviceFiles::CdmUsageData kCdmUsageData3 = { + .provider_session_token = "provider_session_token_3", + .license_request = "license_request_3", + .license = "license_3", + .key_set_id = "key_set_id_3", + .usage_entry = "usage_entry_3", + .usage_entry_number = 0, +}; +const std::vector kEmptyUsageInfoUsageDataList; +const std::vector kUsageInfoUsageDataList = { + kCdmUsageData1, kCdmUsageData2, kCdmUsageData3, +}; const std::vector kEmptyUsageEntryInfoVector; const std::vector kUsageEntryInfoVector = { kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, @@ -89,6 +129,10 @@ class MockDeviceFiles : public DeviceFiles { const std::string&, const CdmUsageEntry&, uint32_t)); MOCK_METHOD2(RetrieveUsageInfo, bool(const std::string&, std::vector*)); + MOCK_METHOD1(ListLicenses, + bool(std::vector* key_set_ids)); + MOCK_METHOD1(ListUsageInfoFiles, + bool(std::vector* usage_info_files)); private: FileSystem file_system_; @@ -173,19 +217,75 @@ class UsageTableHeaderInitializationTest public ::testing::WithParamInterface {}; TEST_P(UsageTableHeaderInitializationTest, CreateUsageTableHeader) { - std::vector empty_usage_entry_info; - EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), SetArgPointee<1>(kEmptyUsageEntryInfoVector), Return(false))); + EXPECT_CALL(*device_files_, ListLicenses(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyLicenseList), + Return(false))); + EXPECT_CALL(*device_files_, ListUsageInfoFiles(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageInfoFilesList), + Return(false))); EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull())) .WillOnce( DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true)); EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, kEmptyUsageEntryInfoVector)) - .WillOnce(Return(true)); + .Times(2) + .WillRepeatedly(Return(true)); + + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveLicenses) { + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), + SetArgPointee<1>(kEmptyUsageEntryInfoVector), + Return(false))); + EXPECT_CALL(*device_files_, ListLicenses(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kLicenseList), + Return(true))); + EXPECT_CALL(*device_files_, ListUsageInfoFiles(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageInfoFilesList), + Return(false))); + EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, + kEmptyUsageEntryInfoVector)) + .Times(2) + .WillRepeatedly(Return(true)); + + for (size_t i = 0; i < kLicenseList.size(); ++i) + device_files_->DeleteLicense(kLicenseList[i]); + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveUsageInfo) { + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), + SetArgPointee<1>(kEmptyUsageEntryInfoVector), + Return(false))); + EXPECT_CALL(*device_files_, ListLicenses(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyLicenseList), + Return(false))); + EXPECT_CALL(*device_files_, ListUsageInfoFiles(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageInfoFileList), + Return(true))); + EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, + kEmptyUsageEntryInfoVector)) + .Times(2) + .WillRepeatedly(Return(true)); + for (size_t i = 0; i < kUsageInfoFileList.size(); ++i) { + EXPECT_CALL(*device_files_, + RetrieveUsageInfo(kUsageInfoFileList[i], NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(kEmptyUsageInfoUsageDataList), + Return(false))); + } EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); } diff --git a/libwvdrmengine/cdm/src/file_utils.cpp b/libwvdrmengine/cdm/src/file_utils.cpp index 05fd7782..fa00760c 100644 --- a/libwvdrmengine/cdm/src/file_utils.cpp +++ b/libwvdrmengine/cdm/src/file_utils.cpp @@ -15,14 +15,6 @@ #include "log.h" #include "properties.h" -namespace { -const char* kSecurityLevelPathCompatibilityExclusionList[] = { - "ay64.dat", "ay64.dat2", "ay64.dat3"}; -size_t kSecurityLevelPathCompatibilityExclusionListSize = - sizeof(kSecurityLevelPathCompatibilityExclusionList) / - sizeof(*kSecurityLevelPathCompatibilityExclusionList); -} // namespace - namespace wvcdm { bool IsCurrentOrParentDirectory(char* dir) { @@ -258,15 +250,6 @@ void FileUtils::SecurityLevelPathBackwardCompatibility( for (size_t i = 0; i < files.size(); ++i) { std::string from = from_dir + files[i]; - bool exclude = false; - for (size_t j = 0; j < kSecurityLevelPathCompatibilityExclusionListSize; - ++j) { - if (files[i] == kSecurityLevelPathCompatibilityExclusionList[j]) { - exclude = true; - break; - } - } - if (exclude) continue; if (!FileUtils::IsRegularFile(from)) continue; for (size_t j = 0; j < security_dirs.size(); ++j) {