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:
Fred Gylys-Colwell
2015-03-18 18:09:37 -07:00
parent a7887f60fc
commit d2ba3a3f91
14 changed files with 226 additions and 28 deletions

View File

@@ -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_; }

View File

@@ -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();

View File

@@ -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);

View File

@@ -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"

View File

@@ -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() {

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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));

View File

@@ -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) {