Merge "Enforce OEMCrypto insufficient resources error reporting"

This commit is contained in:
Rahul Frias
2018-12-12 16:47:11 +00:00
committed by Android (Google) Code Review
5 changed files with 385 additions and 1113 deletions

View File

@@ -71,6 +71,14 @@ class UsageTableHeader {
DeviceFiles* handle,
metrics::CryptoMetrics* metrics);
// Test only method. This method emulates the behavior of DeleteEntry
// without actually invoking OEMCrypto (through CryptoSession)
// or storage (through DeviceFiles). It modifies internal data structures
// when DeleteEntry is mocked. This allows one to test methods that are
// dependent on DeleteEntry without having to set expectations
// for the objects that DeleteEntry depends on.
void DeleteEntryForTest(uint32_t usage_entry_number);
private:
CdmResponseType MoveEntry(uint32_t from /* usage entry number */,
const CdmUsageEntry& from_usage_entry,
@@ -119,6 +127,9 @@ class UsageTableHeader {
metrics::CryptoMetrics alternate_crypto_metrics_;
// TODO(rfrias): Move to utility class
uint32_t GetRandomInRange(size_t upper_bound_inclusive);
// Test related declarations
friend class UsageTableHeaderTest;

View File

@@ -56,7 +56,8 @@ CdmSession::CdmSession(FileSystem* file_system,
CdmSession::~CdmSession() {
if (usage_support_type_ == kUsageEntrySupport &&
has_provider_session_token() &&
usage_table_header_ != NULL) {
usage_table_header_ != NULL &&
!is_release_) {
UpdateUsageEntryInformation();
}

View File

@@ -74,9 +74,28 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
if (status == NO_ERROR) {
if (usage_entry_info_.size() > kMinUsageEntriesSupported) {
uint32_t temporary_usage_entry_number;
CdmResponseType result = AddEntry(crypto_session, true,
kDummyKeySetId, kEmptyString,
&temporary_usage_entry_number);
// Create a new temporary usage entry, close the session and then
// try to delete it.
CdmResponseType result = NO_ERROR;
{
// |local_crypto_session| points to an object whose scope is this
// method or a test object whose scope is the lifetime of this class
std::unique_ptr<CryptoSession> scoped_crypto_session;
CryptoSession* local_crypto_session = test_crypto_session_.get();
if (local_crypto_session == NULL) {
scoped_crypto_session.reset(
(CryptoSession::MakeCryptoSession(metrics)));
local_crypto_session = scoped_crypto_session.get();
}
result = local_crypto_session->Open(requested_security_level_);
if (result == NO_ERROR) {
result = AddEntry(local_crypto_session, true,
kDummyKeySetId, kEmptyString,
&temporary_usage_entry_number);
}
}
if (result == NO_ERROR) {
result = DeleteEntry(temporary_usage_entry_number,
file_handle_.get(), metrics);
@@ -125,17 +144,20 @@ CdmResponseType UsageTableHeader::AddEntry(
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
if (metrics == NULL) metrics = &alternate_crypto_metrics_;
uint32_t retry_count = 0;
CdmResponseType status = NO_ERROR;
do {
{
AutoLock auto_lock(usage_table_header_lock_);
status = crypto_session->CreateUsageEntry(usage_entry_number);
}
if (status == INSUFFICIENT_CRYPTO_RESOURCES_3)
DeleteEntry(retry_count, file_handle_.get(), metrics);
} while (status == INSUFFICIENT_CRYPTO_RESOURCES_3 &&
++retry_count < kMaxCryptoRetries);
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number);
// If usage entry creation fails due to insufficient resources, release a
// random entry and try again.
for (uint32_t retry_count = 0;
retry_count < kMaxCryptoRetries &&
status == INSUFFICIENT_CRYPTO_RESOURCES_3;
++retry_count) {
uint32_t entry_number_to_delete =
GetRandomInRange(usage_entry_info_.size());
DeleteEntry(entry_number_to_delete, file_handle_.get(), metrics);
status = crypto_session->CreateUsageEntry(usage_entry_number);
}
if (status != NO_ERROR) return status;
@@ -189,17 +211,22 @@ CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session,
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
if (metrics == NULL) metrics = &alternate_crypto_metrics_;
uint32_t retry_count = 0;
CdmResponseType status = NO_ERROR;
do {
{
AutoLock auto_lock(usage_table_header_lock_);
CdmResponseType status =
crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
// If loading a usage entry fails due to insufficient resources, release a
// random entry and try again.
for (uint32_t retry_count = 0;
retry_count < kMaxCryptoRetries &&
status == INSUFFICIENT_CRYPTO_RESOURCES_3;
++retry_count) {
uint32_t entry_number_to_delete =
GetRandomInRange(usage_entry_info_.size());
if (usage_entry_number != entry_number_to_delete) {
DeleteEntry(entry_number_to_delete, file_handle_.get(), metrics);
status = crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
}
if (status == INSUFFICIENT_CRYPTO_RESOURCES_3)
DeleteEntry(retry_count, file_handle_.get(), metrics);
} while (status == INSUFFICIENT_CRYPTO_RESOURCES_3 &&
++retry_count < kMaxCryptoRetries);
}
return status;
}
@@ -680,6 +707,12 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable(
return NO_ERROR;
}
uint32_t UsageTableHeader::GetRandomInRange(size_t upper_bound_inclusive) {
if (upper_bound_inclusive == 0) return 0;
return rand() / (RAND_MAX / upper_bound_inclusive + 1);
}
// TODO(fredgc): remove when b/65730828 is addressed
bool UsageTableHeader::CreateDummyOldUsageEntry(CryptoSession* crypto_session) {
return crypto_session->CreateOldUsageEntry(
kOldUsageEntryTimeSinceLicenseReceived,
@@ -691,4 +724,20 @@ bool UsageTableHeader::CreateDummyOldUsageEntry(CryptoSession* crypto_session) {
kOldUsageEntryPoviderSessionToken);
}
// Test only method.
void UsageTableHeader::DeleteEntryForTest(uint32_t usage_entry_number) {
LOGV("UsageTableHeader::DeleteEntryForTest: usage_entry_number: %d",
usage_entry_number);
if (usage_entry_number >= usage_entry_info_.size()) {
LOGE("UsageTableHeader::DeleteEntryForTest: usage entry number %d larger "
"than usage entry size %d", usage_entry_number,
usage_entry_info_.size());
return;
}
// Move last entry into deleted location and shrink usage entries
usage_entry_info_[usage_entry_number] =
usage_entry_info_[usage_entry_info_.size() - 1];
usage_entry_info_.resize(usage_entry_info_.size() - 1);
}
} // namespace wvcdm

File diff suppressed because it is too large Load Diff

View File

@@ -1604,41 +1604,61 @@ TEST_F(WvCdmExtendedDurationTest, MaxUsageEntryOfflineRecoveryTest) {
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
std::vector<CdmKeySetId> key_set_ids;
// Download large number of offline licenses. If OEMCrypto returns
// OEMCrypto_ERROR_INSUFFICIENT_RESOURCES when usage table is at capacity,
// licenses will be deleted internally to make space and we might
// licenses will be deleted internally to make space and we will
// not encounter an error.
CdmResponseType response = NO_ERROR;
for (size_t i = 0; i < 2000; ++i) {
decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier,
NULL, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline, &response);
if (response != KEY_MESSAGE) {
decryptor_.CloseSession(session_id_);
break;
}
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false, &response);
if (response != KEY_ADDED) {
decryptor_.CloseSession(session_id_);
break;
}
EXPECT_EQ(KEY_ADDED, response);
GenerateKeyRequest(kOfflineClip2PstInitData, kLicenseTypeOffline);
VerifyKeyRequestResponse(config_.license_server(), client_auth, false);
key_set_ids.push_back(key_set_id_);
decryptor_.CloseSession(session_id_);
}
// If we encountered an error, verify that on UsageTableHeader creation
// the usage entries will be deleted and that we can add new ones.
if (response != KEY_ADDED && response != KEY_MESSAGE) {
Provision();
for (size_t i = 0; i < 10; ++i) {
decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier,
NULL, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
uint32_t number_of_valid_offline_sessions = 0;
for (size_t i = 0; i < key_set_ids.size(); ++i) {
session_id_.clear();
decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier,
NULL, &session_id_);
CdmResponseType result = decryptor_.RestoreKey(session_id_, key_set_ids[i]);
if (result == KEY_ADDED) {
++number_of_valid_offline_sessions;
// Decrypt data
SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
std::vector<uint8_t> 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_EQ(data->decrypt_data, decrypt_buffer);
decryptor_.CloseSession(session_id_);
// Release the license
GenerateKeyRelease(key_set_ids[i]);
key_set_id_ = key_set_ids[i];
VerifyKeyRequestResponse(config_.license_server(), client_auth, false);
} else {
decryptor_.CloseSession(session_id_);
}
}
EXPECT_GE(number_of_valid_offline_sessions, 200u);
}
} // namespace wvcdm