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

View File

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

View File

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

View File

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

View File

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