Merge "OEMCrypto Backwards Compatible Usage Table"
This commit is contained in:
committed by
Android (Google) Code Review
commit
49a61f6270
@@ -48,7 +48,11 @@ OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(SecurityLevel level,
|
||||
uint32_t new_table_size,
|
||||
uint8_t* header_buffer,
|
||||
size_t* header_buffer_length);
|
||||
|
||||
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(SecurityLevel level,
|
||||
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
|
||||
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
|
||||
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
|
||||
size_t pst_length);
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
|
||||
@@ -242,6 +242,11 @@ typedef OEMCryptoResult (*L1_MoveEntry_t)(OEMCrypto_SESSION session,
|
||||
typedef OEMCryptoResult (*L1_CopyOldUsageEntry_t)(OEMCrypto_SESSION session,
|
||||
const uint8_t*pst,
|
||||
size_t pst_length);
|
||||
typedef OEMCryptoResult (*L1_CreateOldUsageEntry_t)(
|
||||
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
|
||||
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
|
||||
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
|
||||
size_t pst_length);
|
||||
|
||||
struct FunctionPointers {
|
||||
uint32_t version;
|
||||
@@ -305,6 +310,7 @@ struct FunctionPointers {
|
||||
L1_ShrinkUsageTableHeader_t ShrinkUsageTableHeader;
|
||||
L1_MoveEntry_t MoveEntry;
|
||||
L1_CopyOldUsageEntry_t CopyOldUsageEntry;
|
||||
L1_CreateOldUsageEntry_t CreateOldUsageEntry;
|
||||
|
||||
L1_LoadKeys_V8_t LoadKeys_V8;
|
||||
L1_GenerateRSASignature_V8_t GenerateRSASignature_V8;
|
||||
@@ -590,6 +596,7 @@ class Adapter {
|
||||
LOOKUP(ShrinkUsageTableHeader, OEMCrypto_ShrinkUsageTableHeader);
|
||||
LOOKUP(MoveEntry, OEMCrypto_MoveEntry);
|
||||
LOOKUP(CopyOldUsageEntry, OEMCrypto_CopyOldUsageEntry);
|
||||
LOOKUP(CreateOldUsageEntry, OEMCrypto_CreateOldUsageEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -750,6 +757,8 @@ class Adapter {
|
||||
level3_.ShrinkUsageTableHeader = Level3_ShrinkUsageTableHeader;
|
||||
level3_.MoveEntry = Level3_MoveEntry;
|
||||
level3_.CopyOldUsageEntry = Level3_CopyOldUsageEntry;
|
||||
// TODO(fredgc): add stub.
|
||||
// level3_.CreateOldUsageEntry = Level3_CreateOldUsageEntry;
|
||||
|
||||
level3_.version = Level3_APIVersion();
|
||||
}
|
||||
@@ -1018,10 +1027,26 @@ OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(SecurityLevel level,
|
||||
const FunctionPointers* fcn = kAdapter->get(level);
|
||||
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
if (fcn->version < 13) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (!fcn->ShrinkUsageTableHeader) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
return fcn->ShrinkUsageTableHeader(new_table_size, header_buffer,
|
||||
header_buffer_length);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
|
||||
SecurityLevel level, uint64_t time_since_license_received,
|
||||
uint64_t time_since_first_decrypt, uint64_t time_since_last_decrypt,
|
||||
OEMCrypto_Usage_Entry_Status status, uint8_t* server_mac_key,
|
||||
uint8_t* client_mac_key, const uint8_t* pst, size_t pst_length) {
|
||||
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
const FunctionPointers* fcn = kAdapter->get(level);
|
||||
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
if (fcn->version < 13) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (!fcn->CreateOldUsageEntry) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
return fcn->CreateOldUsageEntry(
|
||||
time_since_license_received, time_since_first_decrypt,
|
||||
time_since_last_decrypt, status, server_mac_key, client_mac_key, pst,
|
||||
pst_length);
|
||||
}
|
||||
} // namespace wvcdm
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_Initialize(void) {
|
||||
@@ -1088,6 +1113,7 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys,
|
||||
const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
|
||||
size_t pst_length, const uint8_t* srm_requirement) {
|
||||
|
||||
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
LevelSession pair = kAdapter->get(session);
|
||||
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
@@ -1557,6 +1583,7 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(
|
||||
const uint8_t* message, size_t message_length, const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
LOGE("TODO(fredgc): remove DeleteUsageEntry.");
|
||||
|
||||
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
LevelSession pair = kAdapter->get(session);
|
||||
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
@@ -1572,6 +1599,7 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(
|
||||
extern "C" OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(
|
||||
const uint8_t* pst, size_t pst_length) {
|
||||
LOGE("TODO(fredgc): remove ForceDeleteUsageEntry.");
|
||||
|
||||
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
const FunctionPointers* fcn1 = kAdapter->get(kLevelDefault);
|
||||
const FunctionPointers* fcn3 = kAdapter->get(kLevel3);
|
||||
@@ -1589,6 +1617,7 @@ extern "C" OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_DeleteOldUsageTable() {
|
||||
|
||||
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
const FunctionPointers* fcn1 = kAdapter->get(kLevelDefault);
|
||||
const FunctionPointers* fcn3 = kAdapter->get(kLevel3);
|
||||
@@ -1718,15 +1747,13 @@ OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
|
||||
return pair.fcn->CopyOldUsageEntry(pair.session, pst, pst_length);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult Level3_CreateOldUsageEntry(uint64_t time_since_license_received,
|
||||
uint64_t time_since_first_decrypt,
|
||||
uint64_t time_since_last_decrypt,
|
||||
OEMCrypto_Usage_Entry_Status status,
|
||||
uint8_t *server_mac_key,
|
||||
uint8_t *client_mac_key,
|
||||
const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
// TODO(fredgc): add this.
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
extern "C" OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
|
||||
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
|
||||
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
|
||||
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
return OEMCrypto_CreateOldUsageEntry(
|
||||
kLevelDefault, time_since_license_received, time_since_first_decrypt,
|
||||
time_since_last_decrypt, status, server_mac_key, client_mac_key, pst,
|
||||
pst_length);
|
||||
}
|
||||
|
||||
@@ -251,8 +251,8 @@ OEMCryptoResult Level3_SetDecryptHash(OEMCrypto_SESSION session,
|
||||
size_t hash_length);
|
||||
OEMCryptoResult Level3_VerifyDecryptHash(OEMCrypto_SESSION session,
|
||||
uint64_t* failure_data);
|
||||
// TODO(fredgc): add stub for level3.
|
||||
OEMCryptoResult Level3_CreateUsageTableHeader();
|
||||
OEMCryptoResult Level3_CreateUsageTableHeader(uint8_t* header_buffer,
|
||||
size_t* header_buffer_length);
|
||||
OEMCryptoResult Level3_LoadUsageTableHeader(const uint8_t* buffer,
|
||||
size_t buffer_length);
|
||||
OEMCryptoResult Level3_CreateNewUsageEntry(OEMCrypto_SESSION session,
|
||||
|
||||
@@ -13,6 +13,7 @@ LOCAL_SRC_FILES:= \
|
||||
src/oemcrypto_logging.cpp \
|
||||
src/oemcrypto_mock.cpp \
|
||||
src/oemcrypto_nonce_table.cpp \
|
||||
src/oemcrypto_old_usage_table_mock.cpp \
|
||||
src/oemcrypto_rsa_key_shared.cpp \
|
||||
src/oemcrypto_session.cpp \
|
||||
src/oemcrypto_session_key_table.cpp \
|
||||
|
||||
@@ -1417,15 +1417,6 @@ extern "C" uint32_t OEMCrypto_SupportedCertificates() {
|
||||
}
|
||||
return OEMCrypto_Supports_RSA_2048bit | OEMCrypto_Supports_RSA_3072bit |
|
||||
OEMCrypto_Supports_RSA_CAST;
|
||||
if (!crypto_engine) {
|
||||
LOGE("OEMCrypto_GetProvisioningMethod: OEMCrypto Not Initialized.");
|
||||
return 0;
|
||||
}
|
||||
if (crypto_engine->config_provisioning_method() == OEMCrypto_DrmCertificate) {
|
||||
return 0;
|
||||
}
|
||||
return OEMCrypto_Supports_RSA_2048bit | OEMCrypto_Supports_RSA_3072bit |
|
||||
OEMCrypto_Supports_RSA_CAST;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_Generic_Encrypt(
|
||||
@@ -1827,10 +1818,6 @@ extern "C" OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(
|
||||
if (!crypto_engine->config_supports_usage_table()) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
if (!header_buffer) {
|
||||
LOGE("OEMCrypto_ShrinkUsageTableHeader: buffer null.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return crypto_engine->usage_table().ShrinkUsageTableHeader(
|
||||
new_table_size, header_buffer, header_buffer_length);
|
||||
}
|
||||
@@ -1876,4 +1863,29 @@ extern "C" OEMCryptoResult OEMCrypto_CopyOldUsageEntry(
|
||||
return session_ctx->CopyOldUsageEntry(pstv);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(uint64_t time_since_license_received,
|
||||
uint64_t time_since_first_decrypt,
|
||||
uint64_t time_since_last_decrypt,
|
||||
OEMCrypto_Usage_Entry_Status status,
|
||||
uint8_t *server_mac_key,
|
||||
uint8_t *client_mac_key,
|
||||
const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_CreateOldUsageEntry()\n");
|
||||
}
|
||||
if (!crypto_engine) {
|
||||
LOGE("OEMCrypto_CreateOldUsageEntry: OEMCrypto Not Initialized.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (!crypto_engine->config_supports_usage_table()) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
return crypto_engine->usage_table().CreateOldUsageEntry(
|
||||
time_since_license_received, time_since_first_decrypt,
|
||||
time_since_last_decrypt, status, server_mac_key, client_mac_key, pst,
|
||||
pst_length);
|
||||
}
|
||||
|
||||
} // namespace wvoec_mock
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#include "oemcrypto_old_usage_table_mock.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "file_store.h"
|
||||
#include "log.h"
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
#include "oemcrypto_logging.h"
|
||||
#include "properties.h"
|
||||
#include "pst_report.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
OldUsageTableEntry::OldUsageTableEntry(const std::vector<uint8_t> &pst_hash)
|
||||
: pst_hash_(pst_hash),
|
||||
time_of_license_received_(time(NULL)),
|
||||
time_of_first_decrypt_(0),
|
||||
time_of_last_decrypt_(0),
|
||||
status_(kUnused) {}
|
||||
|
||||
OldUsageTableEntry::~OldUsageTableEntry() {}
|
||||
|
||||
OldUsageTableEntry::OldUsageTableEntry(const OldStoredUsageEntry *buffer) {
|
||||
pst_hash_.assign(buffer->pst_hash, buffer->pst_hash + SHA256_DIGEST_LENGTH);
|
||||
time_of_license_received_ = buffer->time_of_license_received;
|
||||
time_of_first_decrypt_ = buffer->time_of_first_decrypt;
|
||||
time_of_last_decrypt_ = buffer->time_of_last_decrypt;
|
||||
status_ = buffer->status;
|
||||
mac_key_server_.assign(buffer->mac_key_server,
|
||||
buffer->mac_key_server + wvcdm::MAC_KEY_SIZE);
|
||||
mac_key_client_.assign(buffer->mac_key_client,
|
||||
buffer->mac_key_client + wvcdm::MAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
OldUsageTable::OldUsageTable(CryptoEngine *ce) {
|
||||
ce_ = ce;
|
||||
generation_ = 0;
|
||||
table_.clear();
|
||||
|
||||
// Load saved table.
|
||||
wvcdm::FileSystem *file_system = ce->file_system();
|
||||
wvcdm::File *file;
|
||||
std::string path;
|
||||
// Note: this path is OK for a real implementation, but using security level 1
|
||||
// would be better.
|
||||
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
|
||||
&path)) {
|
||||
LOGE("OldUsageTable: Unable to get base path");
|
||||
return;
|
||||
}
|
||||
std::string filename = path + "UsageTable.dat";
|
||||
if (!file_system->Exists(filename)) {
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
LOGI("OldUsageTable: No saved usage table. Creating new table.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
size_t file_size = file_system->FileSize(filename);
|
||||
std::vector<uint8_t> encrypted_buffer(file_size);
|
||||
std::vector<uint8_t> buffer(file_size);
|
||||
OldStoredUsageTable *stored_table =
|
||||
reinterpret_cast<OldStoredUsageTable *>(&buffer[0]);
|
||||
OldStoredUsageTable *encrypted_table =
|
||||
reinterpret_cast<OldStoredUsageTable *>(&encrypted_buffer[0]);
|
||||
|
||||
file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly);
|
||||
if (!file) {
|
||||
LOGE("OldUsageTable: File open failed: %s", path.c_str());
|
||||
return;
|
||||
}
|
||||
file->Read(reinterpret_cast<char *>(&encrypted_buffer[0]), file_size);
|
||||
file->Close();
|
||||
|
||||
// Verify the signature of the usage table file.
|
||||
|
||||
// This should be encrypted and signed with a device specific key.
|
||||
// For the reference implementation, I'm just going to use the keybox key.
|
||||
const bool override_to_real = true;
|
||||
const std::vector<uint8_t> &key = ce_->DeviceRootKey(override_to_real);
|
||||
|
||||
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
|
||||
unsigned int sig_length = sizeof(computed_signature);
|
||||
if (!HMAC(EVP_sha256(), &key[0], key.size(),
|
||||
&encrypted_buffer[SHA256_DIGEST_LENGTH],
|
||||
file_size - SHA256_DIGEST_LENGTH, computed_signature,
|
||||
&sig_length)) {
|
||||
LOGE("OldUsageTable: Could not recreate signature.");
|
||||
table_.clear();
|
||||
return;
|
||||
}
|
||||
if (memcmp(encrypted_table->signature, computed_signature, sig_length)) {
|
||||
LOGE("OldUsageTable: Invalid signature given: %s",
|
||||
wvcdm::HexEncode(&encrypted_buffer[0], sig_length).c_str());
|
||||
LOGE("OldUsageTable: Invalid signature computed: %s",
|
||||
wvcdm::HexEncode(computed_signature, sig_length).c_str());
|
||||
table_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Next, decrypt the table.
|
||||
uint8_t iv_buffer[wvcdm::KEY_IV_SIZE];
|
||||
memcpy(iv_buffer, encrypted_table->iv, wvcdm::KEY_IV_SIZE);
|
||||
AES_KEY aes_key;
|
||||
AES_set_decrypt_key(&key[0], 128, &aes_key);
|
||||
AES_cbc_encrypt(&encrypted_buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE],
|
||||
&buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE],
|
||||
file_size - SHA256_DIGEST_LENGTH - wvcdm::KEY_IV_SIZE,
|
||||
&aes_key, iv_buffer, AES_DECRYPT);
|
||||
|
||||
// Next, read the generation number from a different location.
|
||||
// On a real implementation, you should NOT put the generation number in
|
||||
// a file in user space. It should be stored in secure memory. For the
|
||||
// reference implementation, we'll just pretend this is secure.
|
||||
std::string filename2 = path + "GenerationNumber.dat";
|
||||
file = file_system->Open(filename2, wvcdm::FileSystem::kReadOnly);
|
||||
if (!file) {
|
||||
LOGE("OldUsageTable: File open failed: %s (clearing table)", path.c_str());
|
||||
generation_ = 0;
|
||||
table_.clear();
|
||||
return;
|
||||
}
|
||||
file->Read(reinterpret_cast<char *>(&generation_), sizeof(int64_t));
|
||||
file->Close();
|
||||
if (stored_table->generation == generation_ + 1) {
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
LOGW("OldUsageTable: File is one generation old. Acceptable rollback.");
|
||||
}
|
||||
} else if (stored_table->generation == generation_ - 1) {
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
LOGW("OldUsageTable: File is one generation new. Acceptable rollback.");
|
||||
}
|
||||
// This might happen if the generation number was rolled back?
|
||||
} else if (stored_table->generation != generation_) {
|
||||
LOGE("OldUsageTable: Rollback detected. Clearing Usage Table. %lx -> %lx",
|
||||
generation_, stored_table->generation);
|
||||
table_.clear();
|
||||
generation_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point, the stored table looks valid. We can load in all the
|
||||
// entries.
|
||||
for (uint64_t i = 0; i < stored_table->count; i++) {
|
||||
OldUsageTableEntry *entry =
|
||||
new OldUsageTableEntry(&stored_table->entries[i].entry);
|
||||
table_[entry->pst_hash()] = entry;
|
||||
}
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
LOGI("OldUsageTable: loaded %d entries.", stored_table->count);
|
||||
}
|
||||
}
|
||||
|
||||
OldUsageTableEntry *OldUsageTable::FindEntry(const std::vector<uint8_t> &pst) {
|
||||
wvcdm::AutoLock lock(lock_);
|
||||
return FindEntryLocked(pst);
|
||||
}
|
||||
|
||||
OldUsageTableEntry *OldUsageTable::FindEntryLocked(const std::vector<uint8_t> &pst) {
|
||||
std::vector<uint8_t> pst_hash;
|
||||
if (!ComputeHash(pst, pst_hash)) {
|
||||
LOGE("OldUsageTable: Could not compute hash of pst.");
|
||||
return NULL;
|
||||
}
|
||||
EntryMap::iterator it = table_.find(pst_hash);
|
||||
if (it == table_.end()) {
|
||||
return NULL;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
OldUsageTableEntry *OldUsageTable::CreateEntry(const std::vector<uint8_t> &pst) {
|
||||
std::vector<uint8_t> pst_hash;
|
||||
if (!ComputeHash(pst, pst_hash)) {
|
||||
LOGE("OldUsageTable: Could not compute hash of pst.");
|
||||
return NULL;
|
||||
}
|
||||
OldUsageTableEntry *entry = new OldUsageTableEntry(pst_hash);
|
||||
wvcdm::AutoLock lock(lock_);
|
||||
table_[pst_hash] = entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
void OldUsageTable::Clear() {
|
||||
wvcdm::AutoLock lock(lock_);
|
||||
for (EntryMap::iterator i = table_.begin(); i != table_.end(); ++i) {
|
||||
if (i->second) delete i->second;
|
||||
}
|
||||
table_.clear();
|
||||
}
|
||||
|
||||
void OldUsageTable::DeleteFile(CryptoEngine *ce) {
|
||||
wvcdm::FileSystem *file_system = ce->file_system();
|
||||
std::string path;
|
||||
// Note: this path is OK for a real implementation, but using security level 1
|
||||
// would be better.
|
||||
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
|
||||
&path)) {
|
||||
LOGE("OldUsageTable: Unable to get base path");
|
||||
return;
|
||||
}
|
||||
std::string filename = path + "UsageTable.dat";
|
||||
if (file_system->Exists(filename)) {
|
||||
if (!file_system->Remove(filename)) {
|
||||
LOGE("DeleteOldUsageTable: error removing file.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OldUsageTable::ComputeHash(const std::vector<uint8_t> &pst,
|
||||
std::vector<uint8_t> &pst_hash) {
|
||||
// The PST is not fixed size, and we have no promises that it is reasonbly
|
||||
// sized, so we compute a hash of it, and store that instead.
|
||||
pst_hash.resize(SHA256_DIGEST_LENGTH);
|
||||
SHA256_CTX context;
|
||||
if (!SHA256_Init(&context)) return false;
|
||||
if (!SHA256_Update(&context, &pst[0], pst.size())) return false;
|
||||
if (!SHA256_Final(&pst_hash[0], &context)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvoec_mock
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
// This is from the v12 version of oemcrypto usage tables. It is used for
|
||||
// devices that upgrade from v12 to v13 in the field, and need to convert from
|
||||
// the old type of usage table to the new.
|
||||
#ifndef OEMCRYPTO_OLD_USAGE_TABLE_MOCK_H_
|
||||
#define OEMCRYPTO_OLD_USAGE_TABLE_MOCK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "lock.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
class CryptoEngine;
|
||||
class UsagetTableEntry;
|
||||
|
||||
struct OldStoredUsageEntry {
|
||||
// To save disk space, we only store a hash of the pst.
|
||||
uint8_t pst_hash[SHA256_DIGEST_LENGTH];
|
||||
int64_t time_of_license_received;
|
||||
int64_t time_of_first_decrypt;
|
||||
int64_t time_of_last_decrypt;
|
||||
enum OEMCrypto_Usage_Entry_Status status;
|
||||
uint8_t mac_key_server[wvcdm::MAC_KEY_SIZE];
|
||||
uint8_t mac_key_client[wvcdm::MAC_KEY_SIZE];
|
||||
};
|
||||
|
||||
typedef union {
|
||||
struct OldStoredUsageEntry entry;
|
||||
uint8_t padding[128]; // multiple of block size and bigger than entry size.
|
||||
} AlignedOldStoredUsageEntry;
|
||||
|
||||
struct OldStoredUsageTable {
|
||||
uint8_t signature[SHA256_DIGEST_LENGTH];
|
||||
uint8_t iv[wvcdm::KEY_IV_SIZE];
|
||||
int64_t generation;
|
||||
uint64_t count;
|
||||
AlignedOldStoredUsageEntry entries[];
|
||||
};
|
||||
|
||||
class OldUsageTableEntry {
|
||||
public:
|
||||
OldUsageTableEntry(const std::vector<uint8_t> &pst_hash);
|
||||
OldUsageTableEntry(const OldStoredUsageEntry *buffer);
|
||||
~OldUsageTableEntry();
|
||||
const std::vector<uint8_t> &pst_hash() const { return pst_hash_; }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> pst_hash_;
|
||||
int64_t time_of_license_received_;
|
||||
int64_t time_of_first_decrypt_;
|
||||
int64_t time_of_last_decrypt_;
|
||||
enum OEMCrypto_Usage_Entry_Status status_;
|
||||
std::vector<uint8_t> mac_key_server_;
|
||||
std::vector<uint8_t> mac_key_client_;
|
||||
|
||||
friend class UsageTableEntry;
|
||||
friend class UsageTable;
|
||||
};
|
||||
|
||||
class OldUsageTable {
|
||||
public:
|
||||
OldUsageTable(CryptoEngine *ce);
|
||||
~OldUsageTable() { Clear(); }
|
||||
OldUsageTableEntry *FindEntry(const std::vector<uint8_t> &pst);
|
||||
OldUsageTableEntry *CreateEntry(const std::vector<uint8_t> &pst);
|
||||
void Clear();
|
||||
static void DeleteFile(CryptoEngine *ce);
|
||||
|
||||
private:
|
||||
OldUsageTableEntry *FindEntryLocked(const std::vector<uint8_t> &pst);
|
||||
bool ComputeHash(const std::vector<uint8_t> &pst,
|
||||
std::vector<uint8_t> &pst_hash);
|
||||
|
||||
typedef std::map<std::vector<uint8_t>, OldUsageTableEntry *> EntryMap;
|
||||
EntryMap table_;
|
||||
wvcdm::Lock lock_;
|
||||
int64_t generation_;
|
||||
CryptoEngine *ce_;
|
||||
};
|
||||
|
||||
} // namespace wvoec_mock
|
||||
|
||||
#endif // OEMCRYPTO_OLD_USAGE_TABLE_MOCK_H_
|
||||
@@ -1015,7 +1015,10 @@ OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer,
|
||||
size_t* header_buffer_length,
|
||||
uint8_t* entry_buffer,
|
||||
size_t* entry_buffer_length) {
|
||||
if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
if (!usage_entry_) {
|
||||
LOGE("UpdateUsageEntry: Session has no entry.");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
return ce_->usage_table().UpdateUsageEntry(this, usage_entry_, header_buffer,
|
||||
header_buffer_length, entry_buffer,
|
||||
entry_buffer_length);
|
||||
@@ -1043,7 +1046,7 @@ OEMCryptoResult SessionContext::MoveEntry(uint32_t new_index) {
|
||||
OEMCryptoResult SessionContext::CopyOldUsageEntry(
|
||||
const std::vector<uint8_t>& pst) {
|
||||
if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
return ce_->usage_table().CopyOldUsageEntry(usage_entry_, pst);
|
||||
return usage_entry_->CopyOldUsageEntry(pst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,11 +19,13 @@
|
||||
#include "log.h"
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
#include "oemcrypto_logging.h"
|
||||
#include "oemcrypto_old_usage_table_mock.h"
|
||||
#include "properties.h"
|
||||
#include "pst_report.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
namespace {
|
||||
const size_t kMagicLength = 8;
|
||||
const char* kEntryVerification = "USEENTRY";
|
||||
@@ -31,13 +33,15 @@ const char* kHeaderVerification = "USEHEADR";
|
||||
// Offset into a signed block where we start encrypting. We need to
|
||||
// skip the signature and the iv.
|
||||
const size_t kEncryptionOffset = SHA256_DIGEST_LENGTH + SHA256_DIGEST_LENGTH;
|
||||
|
||||
// A structure that holds an usage entry and its signature.
|
||||
struct SignedEntryBlock {
|
||||
uint8_t signature[SHA256_DIGEST_LENGTH];
|
||||
uint8_t iv[SHA256_DIGEST_LENGTH];
|
||||
uint8_t verification[kMagicLength];
|
||||
wvoec_mock::StoredUsageEntry data;
|
||||
StoredUsageEntry data;
|
||||
};
|
||||
|
||||
// This has the data in the header of constant size. There is also an array
|
||||
// of generation numbers.
|
||||
struct SignedHeaderBlock {
|
||||
@@ -50,8 +54,6 @@ struct SignedHeaderBlock {
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
UsageTableEntry::UsageTableEntry(UsageTable* table, uint32_t index,
|
||||
int64_t generation)
|
||||
: usage_table_(table), recent_decrypt_(false), forbid_report_(true) {
|
||||
@@ -286,6 +288,37 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index,
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult UsageTableEntry::CopyOldUsageEntry(
|
||||
const std::vector<uint8_t>& pst) {
|
||||
OldUsageTableEntry* old_entry = usage_table_->FindOldUsageEntry(pst);
|
||||
if (!old_entry) return OEMCrypto_ERROR_WRONG_PST;
|
||||
data_.time_of_license_received = old_entry->time_of_license_received_;
|
||||
data_.time_of_first_decrypt = old_entry->time_of_first_decrypt_;
|
||||
data_.time_of_last_decrypt = old_entry->time_of_last_decrypt_;
|
||||
data_.status = old_entry->status_;
|
||||
if (old_entry->mac_key_server_.size() != wvcdm::MAC_KEY_SIZE) {
|
||||
LOGE("CopyOldEntry: Old entry has bad server mac key.");
|
||||
} else {
|
||||
memcpy(data_.mac_key_server, &(old_entry->mac_key_server_[0]),
|
||||
wvcdm::MAC_KEY_SIZE);
|
||||
}
|
||||
if (old_entry->mac_key_client_.size() != wvcdm::MAC_KEY_SIZE) {
|
||||
LOGE("CopyOldEntry: Old entry has bad client mac key.");
|
||||
} else {
|
||||
memcpy(data_.mac_key_client, &(old_entry->mac_key_client_[0]),
|
||||
wvcdm::MAC_KEY_SIZE);
|
||||
}
|
||||
if (pst.size() > kMaxPSTLength) {
|
||||
LOGE("CopyOldEntry: PST Length was too large. Truncating.");
|
||||
data_.pst_length = kMaxPSTLength;
|
||||
} else {
|
||||
data_.pst_length = pst.size();
|
||||
}
|
||||
memcpy(data_.pst, &pst[0], wvcdm::MAC_KEY_SIZE);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
size_t UsageTableEntry::SignedEntrySize() {
|
||||
size_t base = sizeof(SignedEntryBlock);
|
||||
// round up to make even number of blocks:
|
||||
@@ -293,6 +326,13 @@ size_t UsageTableEntry::SignedEntrySize() {
|
||||
return blocks * wvcdm::KEY_IV_SIZE;
|
||||
}
|
||||
|
||||
UsageTable::~UsageTable() {
|
||||
if (old_table_) {
|
||||
delete old_table_;
|
||||
old_table_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
size_t UsageTable::SignedHeaderSize(size_t count) {
|
||||
size_t base = sizeof(SignedHeaderBlock) + count * sizeof(int64_t);
|
||||
// round up to make even number of blocks:
|
||||
@@ -329,7 +369,10 @@ OEMCryptoResult UsageTable::UpdateUsageEntry(SessionContext* session,
|
||||
OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session,
|
||||
UsageTableEntry** entry,
|
||||
uint32_t* usage_entry_number) {
|
||||
if (!header_loaded_) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (!header_loaded_) {
|
||||
LOGE("CreateNewUsageEntry: Header not loaded.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (!usage_entry_number) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
uint32_t index = generation_numbers_.size();
|
||||
@@ -347,7 +390,10 @@ OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
|
||||
UsageTableEntry** entry,
|
||||
uint32_t index,
|
||||
const std::vector<uint8_t>& buffer) {
|
||||
if (!header_loaded_) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (!header_loaded_) {
|
||||
LOGE("CreateNewUsageEntry: Header not loaded.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (index >= generation_numbers_.size())
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -389,6 +435,10 @@ OEMCryptoResult UsageTable::ShrinkUsageTableHeader(
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
*header_buffer_length = signed_header_size;
|
||||
if (!header_buffer) {
|
||||
LOGE("OEMCrypto_ShrinkUsageTableHeader: buffer null.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
for (size_t i = new_table_size; i < sessions_.size(); i++) {
|
||||
if (sessions_[i]) {
|
||||
LOGE("ShrinkUsageTableHeader: session open for %d", i);
|
||||
@@ -544,7 +594,7 @@ OEMCryptoResult UsageTable::MoveEntry(UsageTableEntry* entry,
|
||||
}
|
||||
if (sessions_[new_index]) {
|
||||
LOGE("MoveEntry: session open for %d", new_index);
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
if (!entry) {
|
||||
LOGE("MoveEntry: null entry");
|
||||
@@ -636,15 +686,41 @@ OEMCryptoResult UsageTable::CreateUsageTableHeader(
|
||||
return SaveUsageTableHeader(header_buffer, *header_buffer_length);
|
||||
}
|
||||
|
||||
OEMCryptoResult UsageTable::CopyOldUsageEntry(UsageTableEntry* entry,
|
||||
const std::vector<uint8_t>& pst) {
|
||||
// TODO(fredgc): add this.
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
OldUsageTableEntry* UsageTable::FindOldUsageEntry(
|
||||
const std::vector<uint8_t>& pst) {
|
||||
if (!old_table_) old_table_ = new OldUsageTable(ce_);
|
||||
return old_table_->FindEntry(pst);
|
||||
}
|
||||
|
||||
OEMCryptoResult UsageTable::DeleteOldUsageTable() {
|
||||
// TODO(fredgc): add this.
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (old_table_) {
|
||||
old_table_->Clear();
|
||||
delete old_table_;
|
||||
old_table_ = NULL;
|
||||
}
|
||||
OldUsageTable::DeleteFile(ce_);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult UsageTable::CreateOldUsageEntry(
|
||||
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
|
||||
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
|
||||
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
if (!old_table_) old_table_ = new OldUsageTable(ce_);
|
||||
std::vector<uint8_t> pstv(pst, pst+pst_length);
|
||||
OldUsageTableEntry *old_entry = old_table_->CreateEntry(pstv);
|
||||
|
||||
int64_t now = time(NULL);
|
||||
old_entry->time_of_license_received_ = now - time_since_license_received;
|
||||
old_entry->time_of_first_decrypt_ = now - time_since_first_decrypt;
|
||||
old_entry->time_of_last_decrypt_ = now - time_since_last_decrypt;
|
||||
old_entry->status_ = status;
|
||||
old_entry->mac_key_server_.assign(server_mac_key,
|
||||
server_mac_key + wvcdm::MAC_KEY_SIZE);
|
||||
old_entry->mac_key_client_.assign(client_mac_key,
|
||||
client_mac_key + wvcdm::MAC_KEY_SIZE);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace wvoec_mock
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace wvoec_mock {
|
||||
class SessionContext;
|
||||
class CryptoEngine;
|
||||
class UsageTable;
|
||||
class OldUsageTable;
|
||||
class OldUsageTableEntry;
|
||||
|
||||
const size_t kMaxPSTLength = 255;
|
||||
// This is the data we store offline.
|
||||
@@ -61,6 +63,7 @@ class UsageTableEntry {
|
||||
uint8_t* signed_buffer, size_t buffer_size);
|
||||
OEMCryptoResult LoadData(CryptoEngine* ce, uint32_t index,
|
||||
const std::vector<uint8_t>& buffer);
|
||||
OEMCryptoResult CopyOldUsageEntry(const std::vector<uint8_t>& pst);
|
||||
int64_t generation_number() { return data_.generation_number; }
|
||||
void set_generation_number(int64_t value) { data_.generation_number = value; }
|
||||
void set_index(int32_t index) { data_.index = index; }
|
||||
@@ -77,7 +80,9 @@ class UsageTableEntry {
|
||||
class UsageTable {
|
||||
public:
|
||||
UsageTable(CryptoEngine* ce, wvcdm::FileSystem* file_system)
|
||||
: ce_(ce), file_system_(file_system), header_loaded_(false){};
|
||||
: ce_(ce), file_system_(file_system), header_loaded_(false),
|
||||
old_table_(NULL){};
|
||||
~UsageTable();
|
||||
|
||||
OEMCryptoResult CreateNewUsageEntry(SessionContext* session,
|
||||
UsageTableEntry** entry,
|
||||
@@ -92,19 +97,25 @@ class UsageTable {
|
||||
uint8_t* entry_buffer,
|
||||
size_t* entry_buffer_length);
|
||||
OEMCryptoResult MoveEntry(UsageTableEntry* entry, uint32_t new_index);
|
||||
OEMCryptoResult CopyOldUsageEntry(UsageTableEntry* entry,
|
||||
const std::vector<uint8_t>& pst);
|
||||
OEMCryptoResult DeleteOldUsageTable();
|
||||
OEMCryptoResult CreateUsageTableHeader(uint8_t* header_buffer,
|
||||
size_t* header_buffer_length);
|
||||
OEMCryptoResult LoadUsageTableHeader(const std::vector<uint8_t>& buffer);
|
||||
OEMCryptoResult ShrinkUsageTableHeader(uint32_t new_table_size,
|
||||
uint8_t* header_buffer,
|
||||
size_t* header_buffer_length);
|
||||
// Diassociates this entry with it's session.
|
||||
void ReleaseEntry(uint32_t index) { sessions_[index] = 0; }
|
||||
void IncrementGeneration();
|
||||
static size_t SignedHeaderSize(size_t count);
|
||||
OldUsageTableEntry* FindOldUsageEntry(const std::vector<uint8_t>& pst);
|
||||
OEMCryptoResult DeleteOldUsageTable();
|
||||
OEMCryptoResult CreateOldUsageEntry(uint64_t time_since_license_received,
|
||||
uint64_t time_since_first_decrypt,
|
||||
uint64_t time_since_last_decrypt,
|
||||
OEMCrypto_Usage_Entry_Status status,
|
||||
uint8_t *server_mac_key,
|
||||
uint8_t *client_mac_key,
|
||||
const uint8_t* pst,
|
||||
size_t pst_length);
|
||||
|
||||
private:
|
||||
OEMCryptoResult SaveUsageTableHeader(uint8_t* signed_buffer,
|
||||
@@ -118,6 +129,7 @@ class UsageTable {
|
||||
int64_t master_generation_number_;
|
||||
std::vector<int64_t> generation_numbers_;
|
||||
std::vector<SessionContext*> sessions_;
|
||||
OldUsageTable *old_table_;
|
||||
};
|
||||
|
||||
} // namespace wvoec_mock
|
||||
|
||||
@@ -332,6 +332,7 @@ void Session::FillSimpleMessage(uint32_t duration, uint32_t control,
|
||||
license_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR;
|
||||
}
|
||||
memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length()));
|
||||
pst_ = pst;
|
||||
}
|
||||
|
||||
void Session::FillRefreshMessage(size_t key_count, uint32_t control_bits,
|
||||
@@ -857,6 +858,20 @@ void Session::LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer) {
|
||||
OEMCrypto_LoadUsageEntry(session_id(), index, &buffer[0], buffer.size()));
|
||||
}
|
||||
|
||||
void Session::MoveUsageEntry(uint32_t new_index,
|
||||
std::vector<uint8_t>* header_buffer,
|
||||
OEMCryptoResult expect_result) {
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(open());
|
||||
ASSERT_NO_FATAL_FAILURE(ReloadUsageEntry());
|
||||
ASSERT_EQ(expect_result, OEMCrypto_MoveEntry(session_id(), new_index));
|
||||
if (expect_result == OEMCrypto_SUCCESS) {
|
||||
usage_entry_number_ = new_index;
|
||||
ASSERT_NO_FATAL_FAILURE(UpdateUsageEntry(header_buffer));
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(close());
|
||||
}
|
||||
|
||||
void Session::GenerateReport(const std::string& pst, bool expect_success,
|
||||
Session* other) {
|
||||
ASSERT_TRUE(open_);
|
||||
@@ -897,6 +912,84 @@ void Session::GenerateReport(const std::string& pst, bool expect_success,
|
||||
EXPECT_EQ(0, memcmp(pst.c_str(), pst_report().pst(), pst.length()));
|
||||
}
|
||||
|
||||
void Session::VerifyPST(const Test_PST_Report& expected) {
|
||||
wvcdm::Unpacked_PST_Report computed = pst_report();
|
||||
EXPECT_EQ(expected.status, computed.status());
|
||||
char* pst_ptr = reinterpret_cast<char *>(computed.pst());
|
||||
std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length());
|
||||
ASSERT_EQ(expected.pst, computed_pst);
|
||||
EXPECT_NEAR(expected.seconds_since_license_received,
|
||||
computed.seconds_since_license_received(),
|
||||
kLicenseReceivedTimeTolerance);
|
||||
// Decrypt times only valid on licenses that have been active.
|
||||
if (expected.status == kActive || expected.status == kInactiveUsed) {
|
||||
EXPECT_NEAR(expected.seconds_since_first_decrypt,
|
||||
computed.seconds_since_first_decrypt(),
|
||||
kUsageTableTimeTolerance);
|
||||
EXPECT_NEAR(expected.seconds_since_last_decrypt,
|
||||
computed.seconds_since_last_decrypt(),
|
||||
kUsageTableTimeTolerance);
|
||||
}
|
||||
std::vector<uint8_t> signature(SHA_DIGEST_LENGTH);
|
||||
unsigned int md_len = SHA_DIGEST_LENGTH;
|
||||
if (!HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(),
|
||||
&pst_report_buffer_[0] + SHA_DIGEST_LENGTH,
|
||||
pst_report_buffer_.size() - SHA_DIGEST_LENGTH,
|
||||
&signature[0], &md_len)) {
|
||||
cout << "Error computing HMAC.\n";
|
||||
dump_openssl_error();
|
||||
}
|
||||
EXPECT_EQ(0, memcmp(computed.signature(), &signature[0],
|
||||
SHA_DIGEST_LENGTH));
|
||||
}
|
||||
|
||||
static int64_t MaybeAdjustTime(int64_t t, time_t now) {
|
||||
int64_t k10Minutes = 60 * 10; // in seconds.
|
||||
if (t > k10Minutes) return now - t;
|
||||
return t;
|
||||
}
|
||||
|
||||
void Session::GenerateVerifyReport(const std::string& pst,
|
||||
OEMCrypto_Usage_Entry_Status status,
|
||||
int64_t time_license_received,
|
||||
int64_t time_first_decrypt,
|
||||
int64_t time_last_decrypt) {
|
||||
ASSERT_NO_FATAL_FAILURE(GenerateReport(pst));
|
||||
Test_PST_Report expected(pst, status);
|
||||
time_t now = time(NULL);
|
||||
expected.seconds_since_license_received =
|
||||
MaybeAdjustTime(time_license_received, now);
|
||||
expected.seconds_since_first_decrypt =
|
||||
MaybeAdjustTime(time_first_decrypt, now);
|
||||
expected.seconds_since_last_decrypt = MaybeAdjustTime(time_last_decrypt, now);
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyPST(expected));
|
||||
}
|
||||
|
||||
void Session::CreateOldEntry(const Test_PST_Report& report) {
|
||||
OEMCryptoResult result = OEMCrypto_CreateOldUsageEntry(
|
||||
report.seconds_since_license_received,
|
||||
report.seconds_since_first_decrypt,
|
||||
report.seconds_since_last_decrypt,
|
||||
report.status, &mac_key_server_[0],
|
||||
&mac_key_client_[0],
|
||||
reinterpret_cast<const uint8_t*>(report.pst.c_str()),
|
||||
report.pst.length());
|
||||
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) return;
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, result);
|
||||
}
|
||||
|
||||
void Session::CopyAndVerifyOldEntry(const Test_PST_Report& report,
|
||||
std::vector<uint8_t>* header_buffer) {
|
||||
ASSERT_NO_FATAL_FAILURE(CreateNewUsageEntry());
|
||||
OEMCryptoResult result = OEMCrypto_CopyOldUsageEntry(
|
||||
session_id(), reinterpret_cast<const uint8_t*>(report.pst.c_str()),
|
||||
report.pst.length());
|
||||
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) return;
|
||||
ASSERT_NO_FATAL_FAILURE(UpdateUsageEntry(header_buffer));
|
||||
ASSERT_NO_FATAL_FAILURE(GenerateReport(report.pst));
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyPST(report));
|
||||
}
|
||||
|
||||
const uint8_t* Session::message_ptr() {
|
||||
return reinterpret_cast<const uint8_t*>(&encrypted_license());
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ const int kLongSleep = 2 * kSpeedMultiplier;
|
||||
const uint32_t kDuration = 2 * kSpeedMultiplier;
|
||||
const uint32_t kLongDuration = 5 * kSpeedMultiplier;
|
||||
const int32_t kTimeTolerance = 3 * kSpeedMultiplier;
|
||||
const int kLicenseReceivedTimeTolerance = kSpeedMultiplier;
|
||||
const time_t kUsageTableTimeTolerance = 10 * kSpeedMultiplier;
|
||||
} // namespace
|
||||
|
||||
typedef struct {
|
||||
@@ -102,6 +104,18 @@ struct RSAPrivateKeyMessage {
|
||||
uint32_t nonce;
|
||||
};
|
||||
|
||||
struct Test_PST_Report {
|
||||
Test_PST_Report(const std::string& pst_in,
|
||||
OEMCrypto_Usage_Entry_Status status_in)
|
||||
: pst(pst_in), status(status_in) {}
|
||||
|
||||
OEMCrypto_Usage_Entry_Status status;
|
||||
int64_t seconds_since_license_received;
|
||||
int64_t seconds_since_first_decrypt;
|
||||
int64_t seconds_since_last_decrypt;
|
||||
std::string pst;
|
||||
};
|
||||
|
||||
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
||||
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
|
||||
// is different from the OpenSSL implementation, so we implement the CTR loop
|
||||
@@ -267,6 +281,7 @@ class Session {
|
||||
OEMCryptoResult expect_result = OEMCrypto_SUCCESS);
|
||||
// The usage entry number for this session's usage entry.
|
||||
uint32_t usage_entry_number() const { return usage_entry_number_; }
|
||||
void set_usage_entry_number(uint32_t v) { usage_entry_number_ = v; }
|
||||
// The encrypted buffer holding the recently updated and saved usage entry.
|
||||
const vector<uint8_t>& encrypted_usage_entry() const {
|
||||
return encrypted_usage_entry_;
|
||||
@@ -277,11 +292,32 @@ class Session {
|
||||
// order to verify signatures.
|
||||
void GenerateReport(const std::string& pst, bool expect_success = true,
|
||||
Session* other = 0);
|
||||
// Move this usage entry to a new index.
|
||||
void MoveUsageEntry(uint32_t new_index, std::vector<uint8_t>* header_buffer,
|
||||
OEMCryptoResult expect_result = OEMCrypto_SUCCESS);
|
||||
// PST used in FillSimpleMesage.
|
||||
string pst() const { return pst_; }
|
||||
// Returns a pointer-like thing to the usage report generated by the previous
|
||||
// call to GenerateReport.
|
||||
wvcdm::Unpacked_PST_Report pst_report() {
|
||||
return wvcdm::Unpacked_PST_Report(&pst_report_buffer_[0]);
|
||||
}
|
||||
// Verify the PST report.
|
||||
void VerifyPST(const Test_PST_Report& report);
|
||||
// Generate and Verify the Usage Report. If any time is greater than 10
|
||||
// minutes, it is assumed to be an absolute time, and time_since will be
|
||||
// computed relative to now.
|
||||
void GenerateVerifyReport(const std::string& pst,
|
||||
OEMCrypto_Usage_Entry_Status status,
|
||||
int64_t time_license_received = 0,
|
||||
int64_t time_first_decrypt = 0,
|
||||
int64_t time_last_decrypt = 0);
|
||||
// Create an entry in the old usage table based on the given report.
|
||||
void CreateOldEntry(const Test_PST_Report &report);
|
||||
// Create a new entry and copy the old entry into it. Then very the report
|
||||
// is right.
|
||||
void CopyAndVerifyOldEntry(const Test_PST_Report &report,
|
||||
std::vector<uint8_t>* header_buffer);
|
||||
|
||||
// The unencrypted license response or license renewal response.
|
||||
MessageData& license() { return license_; }
|
||||
@@ -329,6 +365,7 @@ class Session {
|
||||
int num_keys_;
|
||||
vector<uint8_t> encrypted_usage_entry_;
|
||||
uint32_t usage_entry_number_;
|
||||
string pst_;
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -130,4 +130,12 @@ TEST_F(OEMCryptoAndroidNYCTest, MinVersionNumber11) {
|
||||
ASSERT_GE(version, 11u);
|
||||
}
|
||||
|
||||
// These tests are required for O Android devices.
|
||||
class OEMCryptoAndroidOCTest : public OEMCryptoAndroidNYCTest {};
|
||||
|
||||
TEST_F(OEMCryptoAndroidOCTest, MinVersionNumber13) {
|
||||
uint32_t version = OEMCrypto_APIVersion();
|
||||
ASSERT_GE(version, 13u);
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
Reference in New Issue
Block a user