Allow selective removal of Usage Table Entries by PST
This is a merge of http://go/wvgerrit/13693 in the Widevine repository. This adds level 3 and mock implementation and unit tests for the OEMCrypto function OEMCrypto_ForceDeleteUsageEntry. It also plumbs this function up into CdmEngine, CdmSession, and CryptoSession so that deleting all usage information for a given app id will now delete the entries in OEMCrypto, too. b/18194071 Change-Id: Iaea4034a507b323878657215784edfe95876386a
This commit is contained in:
@@ -90,7 +90,9 @@ class CdmSession {
|
||||
}
|
||||
virtual CdmSecurityLevel GetSecurityLevel() { return security_level_; }
|
||||
|
||||
virtual CdmResponseType DeleteUsageInformation(const std::string& app_id);
|
||||
// Delete usage information for the list of tokens, |provider_session_tokens|.
|
||||
virtual CdmResponseType DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens);
|
||||
virtual CdmResponseType UpdateUsageInformation();
|
||||
|
||||
virtual bool is_initial_usage_update() { return is_initial_usage_update_; }
|
||||
|
||||
@@ -96,6 +96,14 @@ class CryptoSession {
|
||||
virtual CdmResponseType ReleaseUsageInformation(
|
||||
const std::string& message, const std::string& signature,
|
||||
const std::string& provider_session_token);
|
||||
// Delete a usage information for a single token. This does not require
|
||||
// a signed message from the server.
|
||||
virtual CdmResponseType DeleteUsageInformation(
|
||||
const std::string& provider_session_token);
|
||||
// Delete usage information for a list of tokens. This does not require
|
||||
// a signed message from the server.
|
||||
virtual CdmResponseType DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens);
|
||||
virtual CdmResponseType DeleteAllUsageReports();
|
||||
virtual bool IsAntiRollbackHwPresent();
|
||||
|
||||
|
||||
@@ -63,7 +63,11 @@ class DeviceFiles {
|
||||
const std::string& app_id);
|
||||
virtual bool DeleteUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token);
|
||||
virtual bool DeleteAllUsageInfoForApp(const std::string& app_id);
|
||||
// Delete usage information from the file system. Puts a list of all the
|
||||
// psts that were deleted from the file into |provider_session_tokens|.
|
||||
virtual bool DeleteAllUsageInfoForApp(
|
||||
const std::string& app_id,
|
||||
std::vector<std::string>* provider_session_tokens);
|
||||
// Retrieve one usage info from the file. Subsequent calls will retrieve
|
||||
// subsequent entries in the table for this app_id.
|
||||
virtual bool RetrieveUsageInfo(
|
||||
@@ -104,6 +108,7 @@ class DeviceFiles {
|
||||
FRIEND_TEST(DeviceFilesTest, StoreLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, UpdateLicenseState);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Delete);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, DeleteAll);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Read);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Store);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest);
|
||||
|
||||
@@ -732,13 +732,20 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
|
||||
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
|
||||
CdmResponseType status = NO_ERROR;
|
||||
DeviceFiles handle[kSecurityLevelUnknown - kSecurityLevelL1];
|
||||
for (int i = 0, j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++i, ++j) {
|
||||
if (handle[i].Init(static_cast<CdmSecurityLevel>(j))) {
|
||||
if (!handle[i].DeleteAllUsageInfoForApp(app_id)) {
|
||||
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
|
||||
DeviceFiles handle;
|
||||
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
|
||||
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete L%d secure"
|
||||
"stops", j);
|
||||
status = UNKNOWN_ERROR;
|
||||
} else {
|
||||
CdmResponseType status2 = usage_session_->
|
||||
DeleteMultipleUsageInformation(provider_session_tokens);
|
||||
if (status2 != NO_ERROR) {
|
||||
status = status2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to initialize L%d device"
|
||||
|
||||
@@ -526,11 +526,16 @@ bool CdmSession::DeleteLicense() {
|
||||
if (!is_offline_ && license_parser_->provider_session_token().empty())
|
||||
return false;
|
||||
|
||||
if (!license_parser_->provider_session_token().empty()) {
|
||||
if(crypto_session_->DeleteUsageInformation(
|
||||
license_parser_->provider_session_token()) != NO_ERROR) {
|
||||
LOGE("CdmSession::DeleteLicense: error deleting usage info");
|
||||
}
|
||||
}
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::DeleteLicense: Unable to initialize device files");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_offline_) {
|
||||
return file_handle_->DeleteLicense(key_set_id_);
|
||||
} else {
|
||||
@@ -590,18 +595,10 @@ void CdmSession::GetApplicationId(std::string* app_id) {
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::DeleteUsageInformation(const std::string& app_id) {
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to initialize device files");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (file_handle_->DeleteAllUsageInfoForApp(app_id)) {
|
||||
return NO_ERROR;
|
||||
} else {
|
||||
LOGE("CdmSession::DeleteUsageInformation: failed");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
CdmResponseType CdmSession::DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens) {
|
||||
return crypto_session_
|
||||
->DeleteMultipleUsageInformation(provider_session_tokens);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::UpdateUsageInformation() {
|
||||
|
||||
@@ -846,6 +846,50 @@ CdmResponseType CryptoSession::ReleaseUsageInformation(
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::DeleteUsageInformation(
|
||||
const std::string& provider_session_token) {
|
||||
CdmResponseType response = NO_ERROR;
|
||||
LOGV("CryptoSession::DeleteUsageInformation");
|
||||
OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry(
|
||||
reinterpret_cast<const uint8_t*>(provider_session_token.c_str()),
|
||||
provider_session_token.length());
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGE("CryptoSession::DeleteUsageInformation: Delete Usage Table error =%ld",
|
||||
status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::DeleteUsageInformation: update table error=%ld",
|
||||
status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens) {
|
||||
LOGV("CryptoSession::DeleteMultipleUsageInformation");
|
||||
CdmResponseType response = NO_ERROR;
|
||||
for (size_t i=0; i < provider_session_tokens.size(); ++i) {
|
||||
OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry(
|
||||
reinterpret_cast<const uint8_t*>(provider_session_tokens[i].c_str()),
|
||||
provider_session_tokens[i].length());
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("CryptoSession::DeleteMultipleUsageInformation: "
|
||||
"Delete Usage Table error =%ld", status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
OEMCryptoResult status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::DeleteMultipleUsageInformation: update table error=%ld",
|
||||
status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::DeleteAllUsageReports() {
|
||||
LOGV("DeleteAllUsageReports");
|
||||
OEMCryptoResult status = OEMCrypto_DeleteUsageTable();
|
||||
@@ -859,8 +903,8 @@ CdmResponseType CryptoSession::DeleteAllUsageReports() {
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::DeletaAllUsageReports: update table error=%ld",
|
||||
status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -378,7 +378,6 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!RetrieveHashedFile(file_name.c_str(), &serialized_file)) return false;
|
||||
@@ -418,12 +417,18 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
|
||||
return StoreFileWithHash(file_name.c_str(), serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteAllUsageInfoForApp(const std::string& app_id) {
|
||||
bool DeviceFiles::DeleteAllUsageInfoForApp(
|
||||
const std::string& app_id,
|
||||
std::vector<std::string>* provider_session_tokens) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NULL == provider_session_tokens) {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: pst destination not provided");
|
||||
return false;
|
||||
}
|
||||
provider_session_tokens->clear();
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to get base path");
|
||||
@@ -431,7 +436,21 @@ bool DeviceFiles::DeleteAllUsageInfoForApp(const std::string& app_id) {
|
||||
}
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
path.append(file_name);
|
||||
|
||||
if (!file_->Exists(path)) return true;
|
||||
std::string serialized_file;
|
||||
if (RetrieveHashedFile(file_name.c_str(), &serialized_file)) {
|
||||
video_widevine_client::sdk::File file_proto;
|
||||
if (!file_proto.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to parse file");
|
||||
} else {
|
||||
for (int i = 0; i < file_proto.usage_info().sessions_size(); ++i) {
|
||||
provider_session_tokens->push_back(
|
||||
file_proto.usage_info().sessions(i).token());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to retrieve file");
|
||||
}
|
||||
return file_->Remove(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -325,6 +325,7 @@ class Adapter {
|
||||
LOOKUP(IsAntiRollbackHwPresent, OEMCrypto_IsAntiRollbackHwPresent);
|
||||
LOOKUP(GetNumberOfOpenSessions, OEMCrypto_GetNumberOfOpenSessions);
|
||||
LOOKUP(GetMaxNumberOfSessions, OEMCrypto_GetMaxNumberOfSessions);
|
||||
LOOKUP(ForceDeleteUsageEntry, OEMCrypto_ForceDeleteUsageEntry);
|
||||
}
|
||||
}
|
||||
if (OEMCrypto_SUCCESS == level1_.IsKeyboxValid()) {
|
||||
@@ -924,6 +925,24 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(
|
||||
const uint8_t* pst, size_t pst_length) {
|
||||
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
const FunctionPointers* fcn1 = kAdapter->get(kLevelDefault);
|
||||
const FunctionPointers* fcn3 = kAdapter->get(kLevel3);
|
||||
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (fcn3 && fcn3->version > 9) {
|
||||
sts = fcn3->ForceDeleteUsageEntry(pst, pst_length);
|
||||
}
|
||||
if (fcn1 && fcn1 != fcn3 && fcn1->version > 9) {
|
||||
OEMCryptoResult sts1 = fcn1->ForceDeleteUsageEntry(pst, pst_length);
|
||||
if ((sts1 != OEMCrypto_SUCCESS) && (sts == OEMCrypto_SUCCESS)) {
|
||||
sts = sts1;
|
||||
}
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_DeleteUsageTable() {
|
||||
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
const FunctionPointers* fcn1 = kAdapter->get(kLevelDefault);
|
||||
|
||||
@@ -1938,6 +1938,52 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DeviceFilesUsageInfoTest, DeleteAll) {
|
||||
MockFile file;
|
||||
std::string app_id; // TODO(fredgc): expand tests.
|
||||
std::string path =
|
||||
device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id);
|
||||
|
||||
int index = GetParam();
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
|
||||
std::string data;
|
||||
if (index < 0) {
|
||||
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(false));
|
||||
} else {
|
||||
data = kUsageInfoTestData[index].file_data;
|
||||
EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
|
||||
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
|
||||
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
|
||||
Return(data.size())));
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file, Remove(StrEq(path))).WillOnce(Return(true));
|
||||
}
|
||||
|
||||
DeviceFiles device_files;
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
|
||||
std::vector<std::string> psts;
|
||||
ASSERT_TRUE(device_files.DeleteAllUsageInfoForApp(app_id, &psts));
|
||||
if (index < 0) {
|
||||
EXPECT_EQ(0u, psts.size());
|
||||
} else {
|
||||
// DeleteAllUsageInfoForApp returns a list of all psts that
|
||||
// should be deleted by oemcrypto.
|
||||
EXPECT_EQ(static_cast<unsigned int>(index), psts.size());
|
||||
for(int i=0; i < index; i++) {
|
||||
EXPECT_EQ(kUsageInfoTestData[i+1].provider_session_token, psts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(UsageInfo, DeviceFilesUsageInfoTest,
|
||||
::testing::Values(-1, 0, 1, 2, 3));
|
||||
|
||||
|
||||
@@ -845,7 +845,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_LicenseStartTimeNotSet) {
|
||||
|
||||
CdmQueryMap query_info;
|
||||
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
|
||||
EXPECT_EQ(0, query_info.size());
|
||||
EXPECT_EQ(0u, query_info.size());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, QuerySuccess) {
|
||||
|
||||
@@ -834,7 +834,8 @@ TEST_F(WvCdmExtendedDurationTest, UsageOverflowTest) {
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
File file;
|
||||
handle.SetTestFile(&file);
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(""));
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp("", &provider_session_tokens));
|
||||
|
||||
for (size_t i = 0; i < kMaxUsageTableSize + 100; ++i) {
|
||||
decryptor_.OpenSession(g_key_system, property_set, &session_id_);
|
||||
|
||||
@@ -1210,7 +1210,8 @@ TEST_F(WvCdmRequestLicenseTest, UsageInfoRetryTest) {
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
File file;
|
||||
handle.SetTestFile(&file);
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(app_id));
|
||||
std::vector<std::string> psts;
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(app_id, &psts));
|
||||
|
||||
SubSampleInfo* data = &usage_info_sub_samples_icp[0];
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
@@ -1286,7 +1287,8 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) {
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
File file;
|
||||
handle.SetTestFile(&file);
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(usage_info_data->app_id));
|
||||
std::vector<std::string> psts;
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(usage_info_data->app_id, &psts));
|
||||
|
||||
for (size_t i = 0; i < usage_info_data->usage_info; ++i) {
|
||||
SubSampleInfo* data = usage_info_data->sub_sample + i;
|
||||
|
||||
@@ -1316,6 +1316,22 @@ OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session,
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry()\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("pst", pst, pst_length);
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> pstv(pst, pst + pst_length);
|
||||
if (crypto_engine->usage_table()->DeleteEntry(pstv)) {
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_DeleteUsageTable() {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
|
||||
@@ -1522,6 +1522,12 @@ class Session {
|
||||
&signature_[0], signature_.size()));
|
||||
}
|
||||
|
||||
void ForceDeleteEntry(const std::string& pst) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_ForceDeleteUsageEntry(
|
||||
reinterpret_cast<const uint8_t *>(pst.c_str()), pst.length()));
|
||||
}
|
||||
|
||||
MessageData& license() { return license_; }
|
||||
MessageData& encrypted_license() { return encrypted_license_; }
|
||||
const uint8_t* message_ptr() {
|
||||
@@ -4877,6 +4883,32 @@ TEST_P(DISABLED_UsageTableTest, DeleteActiveEntry) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DISABLED_UsageTableTest, ForceDeleteActiveEntry) {
|
||||
if (OEMCrypto_SupportsUsageTable()) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
|
||||
Session s;
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
std::string pst = "my pst";
|
||||
s.FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired,
|
||||
s.get_nonce(), pst);
|
||||
s.EncryptAndSign();
|
||||
s.LoadTestKeys(pst, new_mac_keys_);
|
||||
s.TestDecryptCTR();
|
||||
s.GenerateReport(pst);
|
||||
s.close();
|
||||
|
||||
s.ForceDeleteEntry(pst);
|
||||
|
||||
// Now that session is deleted, we can't generate a report for it.
|
||||
Session s3;
|
||||
s3.open();
|
||||
s3.GenerateReport(pst, false);
|
||||
s3.close();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DISABLED_UsageTableTest, DeleteInactiveEntry) {
|
||||
if (OEMCrypto_SupportsUsageTable()) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
|
||||
|
||||
Reference in New Issue
Block a user