Merge cherrypicks of [4902692, 4902722, 4902585, 4902586, 4902656, 4902657] into pi-release
Change-Id: I5f09f051e7adf9cd5f097193d976519ad45739e9
This commit is contained in:
@@ -129,6 +129,8 @@ class DeviceFiles {
|
|||||||
const std::string& usage_info_file_name,
|
const std::string& usage_info_file_name,
|
||||||
std::vector<std::string>* provider_session_tokens);
|
std::vector<std::string>* provider_session_tokens);
|
||||||
|
|
||||||
|
virtual bool DeleteAllUsageInfo();
|
||||||
|
|
||||||
// Retrieve one usage info from the file. Subsequent calls will retrieve
|
// Retrieve one usage info from the file. Subsequent calls will retrieve
|
||||||
// subsequent entries in the table for this app_id.
|
// subsequent entries in the table for this app_id.
|
||||||
virtual bool RetrieveUsageInfo(
|
virtual bool RetrieveUsageInfo(
|
||||||
@@ -186,6 +188,8 @@ class DeviceFiles {
|
|||||||
CdmUsageTableHeader* usage_table_header,
|
CdmUsageTableHeader* usage_table_header,
|
||||||
std::vector<CdmUsageEntryInfo>* usage_entry_info);
|
std::vector<CdmUsageEntryInfo>* usage_entry_info);
|
||||||
|
|
||||||
|
virtual bool DeleteUsageTableInfo();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Extract serial number and system ID from DRM Device certificate
|
// Extract serial number and system ID from DRM Device certificate
|
||||||
bool ExtractDeviceInfo(const std::string& device_certificate,
|
bool ExtractDeviceInfo(const std::string& device_certificate,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "crypto_session.h"
|
||||||
#include "device_files.h"
|
#include "device_files.h"
|
||||||
#include "file_store.h"
|
#include "file_store.h"
|
||||||
#include "lock.h"
|
#include "lock.h"
|
||||||
@@ -17,8 +18,6 @@
|
|||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
class CryptoSession;
|
|
||||||
|
|
||||||
// Offline licenses/secure stops may be securely tracked using usage
|
// Offline licenses/secure stops may be securely tracked using usage
|
||||||
// tables (OEMCrypto v9-12) or usage table headers+usage entries
|
// tables (OEMCrypto v9-12) or usage table headers+usage entries
|
||||||
// (OEMCrypto v13+). This class assists with the latter, synchronizing
|
// (OEMCrypto v13+). This class assists with the latter, synchronizing
|
||||||
@@ -117,6 +116,8 @@ class UsageTableHeader {
|
|||||||
// data-structures
|
// data-structures
|
||||||
Lock usage_table_header_lock_;
|
Lock usage_table_header_lock_;
|
||||||
|
|
||||||
|
metrics::CryptoMetrics alternate_crypto_metrics_;
|
||||||
|
|
||||||
// Test related declarations
|
// Test related declarations
|
||||||
friend class UsageTableHeaderTest;
|
friend class UsageTableHeaderTest;
|
||||||
|
|
||||||
|
|||||||
@@ -2085,6 +2085,8 @@ CdmResponseType CryptoSession::LoadUsageEntry(
|
|||||||
return LOAD_USAGE_ENTRY_GENERATION_SKEW;
|
return LOAD_USAGE_ENTRY_GENERATION_SKEW;
|
||||||
case OEMCrypto_ERROR_SIGNATURE_FAILURE:
|
case OEMCrypto_ERROR_SIGNATURE_FAILURE:
|
||||||
return LOAD_USAGE_ENTRY_SIGNATURE_FAILURE;
|
return LOAD_USAGE_ENTRY_SIGNATURE_FAILURE;
|
||||||
|
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
|
||||||
|
return INSUFFICIENT_CRYPTO_RESOURCES_3;
|
||||||
default:
|
default:
|
||||||
return LOAD_USAGE_ENTRY_UNKNOWN_ERROR;
|
return LOAD_USAGE_ENTRY_UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -611,6 +611,15 @@ bool DeviceFiles::DeleteAllUsageInfoForApp(
|
|||||||
return RemoveFile(usage_info_file_name);
|
return RemoveFile(usage_info_file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeviceFiles::DeleteAllUsageInfo() {
|
||||||
|
if (!initialized_) {
|
||||||
|
LOGW("DeviceFiles::DeleteAllUsageInfo: not initialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return RemoveFile(kUsageInfoFileNamePrefix + std::string(kWildcard) +
|
||||||
|
kUsageInfoFileNameExt);
|
||||||
|
}
|
||||||
|
|
||||||
bool DeviceFiles::RetrieveUsageInfo(
|
bool DeviceFiles::RetrieveUsageInfo(
|
||||||
const std::string& usage_info_file_name,
|
const std::string& usage_info_file_name,
|
||||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
|
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
|
||||||
@@ -1105,6 +1114,14 @@ bool DeviceFiles::RetrieveUsageTableInfo(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeviceFiles::DeleteUsageTableInfo() {
|
||||||
|
if (!initialized_) {
|
||||||
|
LOGW("DeviceFiles::DeleteUsageTableInfo: not initialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return RemoveFile(GetUsageTableFileName());
|
||||||
|
}
|
||||||
|
|
||||||
bool DeviceFiles::StoreFileWithHash(const std::string& name,
|
bool DeviceFiles::StoreFileWithHash(const std::string& name,
|
||||||
const std::string& serialized_file) {
|
const std::string& serialized_file) {
|
||||||
// calculate SHA hash
|
// calculate SHA hash
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::string kEmptyString;
|
std::string kEmptyString;
|
||||||
|
size_t kMaxCryptoRetries = 3;
|
||||||
|
size_t kMinUsageEntriesSupported = 200;
|
||||||
|
wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
|
||||||
uint64_t kOldUsageEntryTimeSinceLicenseReceived = 0;
|
uint64_t kOldUsageEntryTimeSinceLicenseReceived = 0;
|
||||||
uint64_t kOldUsageEntryTimeSinceFirstDecrypt = 0;
|
uint64_t kOldUsageEntryTimeSinceFirstDecrypt = 0;
|
||||||
uint64_t kOldUsageEntryTimeSinceLastDecrypt = 0;
|
uint64_t kOldUsageEntryTimeSinceLastDecrypt = 0;
|
||||||
@@ -58,14 +61,42 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
|||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType status = USAGE_INFO_NOT_FOUND;
|
CdmResponseType status = USAGE_INFO_NOT_FOUND;
|
||||||
|
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
|
||||||
|
if (metrics == NULL) metrics = &alternate_crypto_metrics_;
|
||||||
|
|
||||||
if (file_handle_->RetrieveUsageTableInfo(&usage_table_header_,
|
if (file_handle_->RetrieveUsageTableInfo(&usage_table_header_,
|
||||||
&usage_entry_info_)) {
|
&usage_entry_info_)) {
|
||||||
status = crypto_session->LoadUsageTableHeader(usage_table_header_);
|
status = crypto_session->LoadUsageTableHeader(usage_table_header_);
|
||||||
|
|
||||||
|
// If the usage table header has been successfully loaded, and is at
|
||||||
|
// minimum capacity (>200), we need to make sure we can still add and
|
||||||
|
// remove entries. If not, clear files/data and recreate usage header table.
|
||||||
|
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);
|
||||||
|
if (result == NO_ERROR) {
|
||||||
|
result = DeleteEntry(temporary_usage_entry_number,
|
||||||
|
file_handle_.get(), metrics);
|
||||||
|
}
|
||||||
|
if (result != NO_ERROR) {
|
||||||
|
LOGE("UsageTableHeader::Init: Unable to create/delete new entry. "
|
||||||
|
"Clear usage entries, security level: %d, usage entries: %d",
|
||||||
|
security_level, usage_entry_info_.size());
|
||||||
|
status = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (status != NO_ERROR) {
|
if (status != NO_ERROR) {
|
||||||
LOGE(
|
LOGE(
|
||||||
"UsageTableHeader::Init: load usage table failed, security level: %d",
|
"UsageTableHeader::Init: load usage table failed, security level: %d",
|
||||||
security_level);
|
security_level);
|
||||||
file_handle_->DeleteAllLicenses();
|
file_handle_->DeleteAllLicenses();
|
||||||
|
file_handle_->DeleteAllUsageInfo();
|
||||||
|
file_handle_->DeleteUsageTableInfo();
|
||||||
usage_entry_info_.clear();
|
usage_entry_info_.clear();
|
||||||
usage_table_header_.clear();
|
usage_table_header_.clear();
|
||||||
status = crypto_session->CreateUsageTableHeader(&usage_table_header_);
|
status = crypto_session->CreateUsageTableHeader(&usage_table_header_);
|
||||||
@@ -77,11 +108,6 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
|||||||
if (status != NO_ERROR) return false;
|
if (status != NO_ERROR) return false;
|
||||||
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
|
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
|
||||||
|
|
||||||
metrics::CryptoMetrics alternate_metrics;
|
|
||||||
metrics::CryptoMetrics* metrics =
|
|
||||||
crypto_session->GetCryptoMetrics() != NULL ?
|
|
||||||
crypto_session->GetCryptoMetrics() : &alternate_metrics;
|
|
||||||
|
|
||||||
UpgradeFromUsageTable(file_handle_.get(), metrics);
|
UpgradeFromUsageTable(file_handle_.get(), metrics);
|
||||||
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
|
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
|
||||||
}
|
}
|
||||||
@@ -95,11 +121,25 @@ CdmResponseType UsageTableHeader::AddEntry(
|
|||||||
const CdmKeySetId& key_set_id, const std::string& usage_info_file_name,
|
const CdmKeySetId& key_set_id, const std::string& usage_info_file_name,
|
||||||
uint32_t* usage_entry_number) {
|
uint32_t* usage_entry_number) {
|
||||||
LOGV("UsageTableHeader::AddEntry: Lock");
|
LOGV("UsageTableHeader::AddEntry: Lock");
|
||||||
|
|
||||||
|
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_);
|
AutoLock auto_lock(usage_table_header_lock_);
|
||||||
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number);
|
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);
|
||||||
|
|
||||||
if (status != NO_ERROR) return status;
|
if (status != NO_ERROR) return status;
|
||||||
|
|
||||||
|
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::AddEntry: new entry %d smaller than table size: %d",
|
LOGE("UsageTableHeader::AddEntry: new entry %d smaller than table size: %d",
|
||||||
*usage_entry_number, usage_entry_info_.size());
|
*usage_entry_number, usage_entry_info_.size());
|
||||||
@@ -134,17 +174,34 @@ CdmResponseType UsageTableHeader::AddEntry(
|
|||||||
CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session,
|
CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session,
|
||||||
const CdmUsageEntry& usage_entry,
|
const CdmUsageEntry& usage_entry,
|
||||||
uint32_t usage_entry_number) {
|
uint32_t usage_entry_number) {
|
||||||
|
{
|
||||||
LOGV("UsageTableHeader::LoadEntry: Lock");
|
LOGV("UsageTableHeader::LoadEntry: Lock");
|
||||||
AutoLock auto_lock(usage_table_header_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(
|
LOGE(
|
||||||
"UsageTableHeader::LoadEntry: usage entry number %d larger than table "
|
"UsageTableHeader::LoadEntry: usage entry number %d larger than "
|
||||||
"size: %d",
|
"table size: %d",
|
||||||
usage_entry_number, usage_entry_info_.size());
|
usage_entry_number, usage_entry_info_.size());
|
||||||
return USAGE_INVALID_LOAD_ENTRY;
|
return USAGE_INVALID_LOAD_ENTRY;
|
||||||
}
|
}
|
||||||
return crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
|
}
|
||||||
|
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->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;
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType UsageTableHeader::UpdateEntry(CryptoSession* crypto_session,
|
CdmResponseType UsageTableHeader::UpdateEntry(CryptoSession* crypto_session,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -268,17 +268,27 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
|
|||||||
|
|
||||||
void GenerateKeyRequest(const std::string& init_data,
|
void GenerateKeyRequest(const std::string& init_data,
|
||||||
CdmLicenseType license_type) {
|
CdmLicenseType license_type) {
|
||||||
|
CdmResponseType response;
|
||||||
|
GenerateKeyRequest(init_data, license_type, &response);
|
||||||
|
EXPECT_EQ(KEY_MESSAGE, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateKeyRequest(const std::string& init_data,
|
||||||
|
CdmLicenseType license_type,
|
||||||
|
CdmResponseType *response) {
|
||||||
CdmAppParameterMap app_parameters;
|
CdmAppParameterMap app_parameters;
|
||||||
CdmKeyRequest key_request;
|
CdmKeyRequest key_request;
|
||||||
|
|
||||||
EXPECT_EQ(KEY_MESSAGE, decryptor_.GenerateKeyRequest(
|
*response = decryptor_.GenerateKeyRequest(
|
||||||
session_id_, key_set_id_, "video/mp4", init_data,
|
session_id_, key_set_id_, "video/mp4", init_data,
|
||||||
license_type, app_parameters, NULL,
|
license_type, app_parameters, NULL,
|
||||||
kDefaultCdmIdentifier, &key_request));
|
kDefaultCdmIdentifier, &key_request);
|
||||||
|
if (*response == KEY_MESSAGE) {
|
||||||
EXPECT_EQ(kKeyRequestTypeInitial, key_request.type);
|
EXPECT_EQ(kKeyRequestTypeInitial, key_request.type);
|
||||||
key_msg_ = key_request.message;
|
key_msg_ = key_request.message;
|
||||||
EXPECT_EQ(0u, key_request.url.size());
|
EXPECT_EQ(0u, key_request.url.size());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GenerateRenewalRequest(CdmLicenseType license_type,
|
void GenerateRenewalRequest(CdmLicenseType license_type,
|
||||||
std::string* server_url) {
|
std::string* server_url) {
|
||||||
@@ -406,14 +416,21 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
|
|||||||
void VerifyKeyRequestResponse(const std::string& server_url,
|
void VerifyKeyRequestResponse(const std::string& server_url,
|
||||||
const std::string& client_auth,
|
const std::string& client_auth,
|
||||||
bool is_renewal) {
|
bool is_renewal) {
|
||||||
std::string resp = GetKeyRequestResponse(server_url, client_auth);
|
VerifyKeyRequestResponse(server_url, client_auth, is_renewal, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_renewal) {
|
void VerifyKeyRequestResponse(const std::string& server_url,
|
||||||
// TODO application makes a license request, CDM will renew the license
|
const std::string& client_auth,
|
||||||
// when appropriate
|
bool /* is_renewal */,
|
||||||
EXPECT_EQ(KEY_ADDED, decryptor_.AddKey(session_id_, resp, &key_set_id_));
|
CdmResponseType* status) {
|
||||||
|
std::string resp = GetKeyRequestResponse(server_url, client_auth);
|
||||||
|
CdmResponseType sts =
|
||||||
|
decryptor_.AddKey(session_id_, resp, &key_set_id_);
|
||||||
|
|
||||||
|
if (status == NULL) {
|
||||||
|
EXPECT_EQ(KEY_ADDED, sts);
|
||||||
} else {
|
} else {
|
||||||
EXPECT_EQ(KEY_ADDED, decryptor_.AddKey(session_id_, resp, &key_set_id_));
|
*status = sts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1574,6 +1591,55 @@ TEST_P(WvCdmOfflineUsageReportTest, UsageTest) {
|
|||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmOfflineUsageReportTest,
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmOfflineUsageReportTest,
|
||||||
::testing::Values(0, 1, 2));
|
::testing::Values(0, 1, 2));
|
||||||
|
|
||||||
|
// This test verifies that a device can still work if the maximum capacity
|
||||||
|
// of the usage entry table is reached. New usage entries, for offline
|
||||||
|
// licenses, can still be added and existing licenses can still be played back.
|
||||||
|
TEST_F(WvCdmExtendedDurationTest, MaxUsageEntryOfflineRecoveryTest) {
|
||||||
|
Unprovision();
|
||||||
|
Provision();
|
||||||
|
|
||||||
|
// override default settings unless configured through the command line
|
||||||
|
std::string key_id;
|
||||||
|
std::string client_auth;
|
||||||
|
GetOfflineConfiguration(&key_id, &client_auth);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// not encounter an error.
|
||||||
|
CdmResponseType response = NO_ERROR;
|
||||||
|
for (size_t i = 0; i < 2000; ++i) {
|
||||||
|
decryptor_.OpenSession(g_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);
|
||||||
|
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(g_key_system, NULL, kDefaultCdmIdentifier, NULL,
|
||||||
|
&session_id_);
|
||||||
|
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
||||||
|
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
|
||||||
|
decryptor_.CloseSession(session_id_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|
||||||
void show_menu(char* prog_name) {
|
void show_menu(char* prog_name) {
|
||||||
|
|||||||
@@ -1096,9 +1096,14 @@ void Session::InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key) {
|
|||||||
GenerateDerivedKeysFromSessionKey();
|
GenerateDerivedKeysFromSessionKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::CreateNewUsageEntry() {
|
void Session::CreateNewUsageEntry(OEMCryptoResult* status) {
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
OEMCryptoResult result =
|
||||||
OEMCrypto_CreateNewUsageEntry(session_id(), &usage_entry_number_));
|
OEMCrypto_CreateNewUsageEntry(session_id(), &usage_entry_number_);
|
||||||
|
if (status) {
|
||||||
|
*status = result;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT_EQ(OEMCrypto_SUCCESS, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::UpdateUsageEntry(std::vector<uint8_t>* header_buffer) {
|
void Session::UpdateUsageEntry(std::vector<uint8_t>* header_buffer) {
|
||||||
|
|||||||
@@ -290,7 +290,9 @@ class Session {
|
|||||||
// GenerateDerivedKeysFromSessionKey to install known encryption and mac keys.
|
// GenerateDerivedKeysFromSessionKey to install known encryption and mac keys.
|
||||||
void InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key);
|
void InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key);
|
||||||
// Creates a new usage entry, and keeps track of the index.
|
// Creates a new usage entry, and keeps track of the index.
|
||||||
void CreateNewUsageEntry();
|
// If status is null, we expect success, otherwise status is set to the
|
||||||
|
// return value.
|
||||||
|
void CreateNewUsageEntry(OEMCryptoResult *status = NULL);
|
||||||
// Copy encrypted usage entry from other session, and then load it.
|
// Copy encrypted usage entry from other session, and then load it.
|
||||||
// This session must already be open.
|
// This session must already be open.
|
||||||
void LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer);
|
void LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -4684,66 +4685,6 @@ TEST_F(UsageTableTest, OnlineMissingEntry) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UsageTableTest, TwoHundredEntries) {
|
|
||||||
Session s1;
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.open());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1));
|
|
||||||
std::string pst1 = "pst saved";
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.FillSimpleMessage(
|
|
||||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired,
|
|
||||||
s1.get_nonce(), pst1));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.EncryptAndSign());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.CreateNewUsageEntry());
|
|
||||||
ASSERT_EQ(0u, s1.usage_entry_number());
|
|
||||||
time_t start = time(NULL);
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.close());
|
|
||||||
|
|
||||||
// API says should hold at least 200 entries. Subtract one for s1's entry.
|
|
||||||
const size_t ENTRY_COUNT = 200 - 1;
|
|
||||||
vector<Session> sessions(ENTRY_COUNT);
|
|
||||||
for (size_t i = 0; i < ENTRY_COUNT; i++) {
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].open());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i]));
|
|
||||||
std::string pst = "pst ";
|
|
||||||
char c1 = 'A' + (i/26);
|
|
||||||
char c2 = 'A' + (i%26);
|
|
||||||
pst = pst + c1 + c2;
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage(
|
|
||||||
0, wvoec_mock::kControlNonceOrEntry, sessions[i].get_nonce(), pst));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].CreateNewUsageEntry());
|
|
||||||
ASSERT_EQ(sessions[i].usage_entry_number(), i + 1);
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
|
||||||
sessions[i].UpdateUsageEntry(&encrypted_usage_header_));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].close());
|
|
||||||
}
|
|
||||||
sleep(kShortSleep);
|
|
||||||
for (size_t i = 0; i < ENTRY_COUNT; i++) {
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].open());
|
|
||||||
std::string pst = "pst ";
|
|
||||||
char c1 = 'A' + (i/26);
|
|
||||||
char c2 = 'A' + (i%26);
|
|
||||||
pst = pst + c1 + c2;
|
|
||||||
// Reuse license message created above.
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i]));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_))
|
|
||||||
<< "Failed to reload license " << i << " with pst = " << pst;
|
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
|
||||||
sessions[i].UpdateUsageEntry(&encrypted_usage_header_));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(sessions[i].close());
|
|
||||||
}
|
|
||||||
// Make sure s1's entry is still in the table.
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.open());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.GenerateVerifyReport(pst1, kUnused, start));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.close());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) {
|
TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) {
|
||||||
std::string pst = "A PST";
|
std::string pst = "A PST";
|
||||||
uint32_t nonce = session_.get_nonce();
|
uint32_t nonce = session_.get_nonce();
|
||||||
@@ -5418,6 +5359,95 @@ TEST_F(UsageTableDefragTest, ReloadUsageEntryBadData) {
|
|||||||
&data[0], data.size()));
|
&data[0], data.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string MakePST(size_t n) {
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << "pst-" << n;
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UsageTableDefragTest, TwoHundredEntries) {
|
||||||
|
// OEMCrypto is required to store at least 200 entries in the usage table
|
||||||
|
// header, but it is allowed to store more. This test verifies that if we keep
|
||||||
|
// adding entries, the error indicates a resource limit. It then verifies
|
||||||
|
// that all of the successful entries are still valid after we throw out the
|
||||||
|
// last invalid entry.
|
||||||
|
const size_t ENTRY_COUNT = 2000;
|
||||||
|
vector<Session> sessions(ENTRY_COUNT);
|
||||||
|
size_t successful_count = 0;
|
||||||
|
for (size_t i = 0; i < ENTRY_COUNT; i++) {
|
||||||
|
if (i % 50 == 0) LOGD("Creating license %zd", i);
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].open());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i]));
|
||||||
|
std::string pst = MakePST(i);
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage(
|
||||||
|
0, wvoec_mock::kControlNonceOrEntry, sessions[i].get_nonce(), pst));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign());
|
||||||
|
// We attempt to create a new usage table entry for this session.
|
||||||
|
OEMCryptoResult status;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].CreateNewUsageEntry(&status));
|
||||||
|
if (status == OEMCrypto_SUCCESS) {
|
||||||
|
ASSERT_EQ(sessions[i].usage_entry_number(), i);
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
sessions[i].UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
|
successful_count++;
|
||||||
|
} else {
|
||||||
|
// If we failed to create this many entries because of limited resources,
|
||||||
|
// then the error returned should be insufficient resources.
|
||||||
|
EXPECT_EQ(OEMCrypto_ERROR_INSUFFICIENT_RESOURCES, status)
|
||||||
|
<< "Failed to create license " << i << " with pst = " << pst;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].close());
|
||||||
|
}
|
||||||
|
LOGD("successful_count = %d", successful_count);
|
||||||
|
EXPECT_GE(successful_count, 200u);
|
||||||
|
sleep(kShortSleep);
|
||||||
|
// Now we will loop through each valid entry, and verify that we can still
|
||||||
|
// reload the license and perform a decrypt.
|
||||||
|
for (size_t i = 0; i < successful_count; i++) {
|
||||||
|
if (i % 50 == 0) LOGD("Reloading license %zd", i);
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].open());
|
||||||
|
std::string pst = MakePST(i);
|
||||||
|
// Reuse license message created above.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i]));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_))
|
||||||
|
<< "Failed to reload license " << i << " with pst = " << pst;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
sessions[i].UpdateUsageEntry(&encrypted_usage_header_))
|
||||||
|
<< "Failed to update license " << i << " with pst = " << pst;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].TestDecryptCTR())
|
||||||
|
<< "Failed to use license " << i << " with pst = " << pst;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
sessions[i].UpdateUsageEntry(&encrypted_usage_header_))
|
||||||
|
<< "Failed to update license " << i << " with pst = " << pst;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].close());
|
||||||
|
}
|
||||||
|
// We also need to verify that a full table can be shrunk, and the remaining
|
||||||
|
// licenses still work.
|
||||||
|
size_t smaller_size = 10u; // 10 is smaller than 200.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(ShrinkHeader(smaller_size));
|
||||||
|
for (size_t i = 0; i < smaller_size; i++) {
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].open());
|
||||||
|
std::string pst = MakePST(i);
|
||||||
|
// Reuse license message created above.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i]));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_))
|
||||||
|
<< "Failed to reload license " << i << " with pst = " << pst;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
sessions[i].UpdateUsageEntry(&encrypted_usage_header_))
|
||||||
|
<< "Failed to update license " << i << " with pst = " << pst;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].TestDecryptCTR())
|
||||||
|
<< "Failed to use license " << i << " with pst = " << pst;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
sessions[i].UpdateUsageEntry(&encrypted_usage_header_))
|
||||||
|
<< "Failed to update license " << i << " with pst = " << pst;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(sessions[i].close());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(UsageTableTest, CopyOldEntries) {
|
TEST_F(UsageTableTest, CopyOldEntries) {
|
||||||
// First create three old entries. We open sessions first to force creation
|
// First create three old entries. We open sessions first to force creation
|
||||||
// of the mac keys.
|
// of the mac keys.
|
||||||
|
|||||||
Reference in New Issue
Block a user