OEMCrypto v16.1

Merge of http://go/wvgerrit/93404

This CL updates the Widevine CDM to support OEMCrypto v16.1

Test: Tested in 16.2 CL
Bug: 141247171
Change-Id: I69bd993500f6fb63bf6010c8b0250dc7acc3d71b
This commit is contained in:
Fred Gylys-Colwell
2020-01-18 10:11:24 -08:00
parent 7e2619e379
commit 7665614b2e
132 changed files with 12331 additions and 9341 deletions

View File

@@ -11,8 +11,6 @@ LOCAL_SRC_FILES:= \
src/oemcrypto_keybox_ref.cpp \
src/oemcrypto_keybox_testkey.cpp \
src/oemcrypto_ref.cpp \
src/oemcrypto_nonce_table.cpp \
src/oemcrypto_old_usage_table_ref.cpp \
src/oemcrypto_rsa_key_shared.cpp \
src/oemcrypto_session.cpp \
src/oemcrypto_session_key_table.cpp \

View File

@@ -53,7 +53,7 @@ class Prov30CryptoEngine : public CryptoEngine {
if (kOEMPublicCertSize == 0) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (public_cert_length == NULL) {
if (public_cert_length == nullptr) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (*public_cert_length < kOEMPublicCertSize) {
@@ -61,7 +61,7 @@ class Prov30CryptoEngine : public CryptoEngine {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*public_cert_length = kOEMPublicCertSize;
if (public_cert == NULL) {
if (public_cert == nullptr) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(public_cert, kOEMPublicCert, kOEMPublicCertSize);

View File

@@ -7,7 +7,6 @@
#include "oemcrypto_engine_ref.h"
#include <assert.h>
#include <chrono>
#include <string.h>
#include <algorithm>
#include <iostream>
@@ -17,6 +16,7 @@
#include <openssl/aes.h>
#include <openssl/err.h>
#include "clock.h"
#include "keys.h"
#include "log.h"
#include "oemcrypto_key_ref.h"
@@ -88,23 +88,26 @@ SessionContext* CryptoEngine::FindSession(SessionId sid) {
if (it != sessions_.end()) {
return it->second;
}
return NULL;
return nullptr;
}
time_t CryptoEngine::OnlineTime() {
int64_t CryptoEngine::OnlineTime() {
// Use the monotonic clock for times that don't have to be stable across
// device boots.
std::chrono::steady_clock clock;
return clock.now().time_since_epoch() / std::chrono::seconds(1);
int64_t now = wvcdm::Clock().GetCurrentTime();
static int64_t then = now;
if (now < then) now = then;
then = now;
return now;
}
time_t CryptoEngine::RollbackCorrectedOfflineTime() {
int64_t CryptoEngine::RollbackCorrectedOfflineTime() {
struct TimeInfo {
// The max time recorded through this function call.
time_t previous_time;
int64_t previous_time;
// If the wall time is rollbacked to before the previous_time, this member
// is updated to reflect the offset.
time_t rollback_offset;
int64_t rollback_offset;
// Pad the struct so that TimeInfo is a multiple of 16.
uint8_t padding[16 - (2 * sizeof(time_t)) % 16];
};
@@ -135,7 +138,7 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() {
if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
filename.c_str());
return time(NULL);
return OnlineTime();
}
file->Read(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
// Decrypt the encrypted TimeInfo buffer.
@@ -147,9 +150,9 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() {
memcpy(&time_info, &clear_buffer[0], sizeof(TimeInfo));
}
time_t current_time;
int64_t current_time;
// Add any time offsets in the past to the current time.
current_time = time(NULL) + time_info.rollback_offset;
current_time = OnlineTime() + time_info.rollback_offset;
if (time_info.previous_time > current_time) {
// Time has been rolled back.
// Update the rollback offset.
@@ -174,7 +177,7 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() {
if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
filename.c_str());
return time(NULL);
return OnlineTime();
}
file->Write(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
@@ -183,9 +186,9 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() {
}
bool CryptoEngine::NonceCollision(uint32_t nonce) {
for (const auto & session_pair : sessions_) {
for (const auto& session_pair : sessions_) {
const SessionContext* session = session_pair.second;
if (session->NonceCollision(nonce)) return true;
if (nonce == session->nonce()) return true;
}
return false;
}
@@ -199,45 +202,45 @@ OEMCrypto_HDCP_Capability CryptoEngine::config_maximum_hdcp_capability() {
}
OEMCryptoResult CryptoEngine::SetDestination(
OEMCrypto_DestBufferDesc* out_description, size_t data_length,
const OEMCrypto_DestBufferDesc& out_description, size_t data_length,
uint8_t subsample_flags) {
size_t max_length = 0;
switch (out_description->type) {
switch (out_description.type) {
case OEMCrypto_BufferType_Clear:
destination_ = out_description->buffer.clear.address;
max_length = out_description->buffer.clear.max_length;
destination_ = out_description.buffer.clear.address;
max_length = out_description.buffer.clear.address_length;
break;
case OEMCrypto_BufferType_Secure:
destination_ =
reinterpret_cast<uint8_t*>(out_description->buffer.secure.handle) +
out_description->buffer.secure.offset;
max_length = out_description->buffer.secure.max_length -
out_description->buffer.secure.offset;
reinterpret_cast<uint8_t*>(out_description.buffer.secure.handle) +
out_description.buffer.secure.offset;
max_length = out_description.buffer.secure.handle_length -
out_description.buffer.secure.offset;
break;
case OEMCrypto_BufferType_Direct:
// Direct buffer type is only used on some specialized devices where
// oemcrypto has a direct connection to the screen buffer. It is not,
// for example, supported on Android.
destination_ = NULL;
destination_ = nullptr;
break;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
size_t max_allowed = max_output_size();
const size_t max_allowed = max_sample_size();
if (max_allowed > 0 &&
(max_allowed < max_length || max_allowed < data_length)) {
LOGE("Output too large (or buffer too small).");
return OEMCrypto_ERROR_OUTPUT_TOO_LARGE;
}
if (out_description->type != OEMCrypto_BufferType_Direct &&
if (out_description.type != OEMCrypto_BufferType_Direct &&
max_length < data_length) {
LOGE("[SetDestination(): OEMCrypto_ERROR_SHORT_BUFFER]");
return OEMCrypto_ERROR_SHORT_BUFFER;
}
adjust_destination(out_description, data_length, subsample_flags);
if ((out_description->type != OEMCrypto_BufferType_Direct) &&
(destination_ == NULL)) {
if ((out_description.type != OEMCrypto_BufferType_Direct) &&
(destination_ == nullptr)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return OEMCrypto_SUCCESS;

View File

@@ -31,6 +31,9 @@ typedef std::map<SessionId, SessionContext*> ActiveSessions;
class CryptoEngine {
public:
static const uint32_t kApiVersion = 16;
static const uint32_t kMinorApiVersion = 0;
// This is like a factory method, except we choose which version to use at
// compile time. It is defined in several source files. The build system
// should choose which one to use by only linking in the correct one.
@@ -87,12 +90,11 @@ class CryptoEngine {
return kMaxSupportedOEMCryptoSessions;
}
time_t OnlineTime();
// The OEMCrypto system time. Prevents time rollback.
// TODO(b/145836634): Combine RollbackCorrectedOfflineTime with OnlineTime().
int64_t SystemTime() { return RollbackCorrectedOfflineTime(); }
time_t RollbackCorrectedOfflineTime();
// Verify that this nonce does not collide with another nonce in any session's
// nonce table.
// Verify that this nonce does not collide with another nonce in any session.
virtual bool NonceCollision(uint32_t nonce);
// Returns the HDCP version currently in use.
@@ -133,12 +135,15 @@ class CryptoEngine {
return OEMCrypto_Keybox;
}
virtual OEMCryptoResult get_oem_certificate(SessionContext* session,
uint8_t* public_cert,
virtual OEMCryptoResult get_oem_certificate(uint8_t* public_cert,
size_t* public_cert_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual OEMCryptoResult load_oem_private_key(SessionContext* session) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
// Used for OEMCrypto_IsAntiRollbackHwPresent.
virtual bool config_is_anti_rollback_hw_present() { return false; }
@@ -150,14 +155,14 @@ class CryptoEngine {
// been applied to the device that fixes a security bug.
virtual uint8_t config_security_patch_level() { return 0; }
// If 0 no restriction, otherwise it's the max buffer for DecryptCENC.
// This is the same as the max subsample size, not the sample or frame size.
virtual size_t max_buffer_size() { return 1024 * 100; } // 100 KiB.
// If 0 no restriction, otherwise it's the max subsample size for
// DecryptCENC. This is not the same as the max sample or buffer size.
virtual size_t max_subsample_size() { return 1024 * 100; } // 100 KiB
// If 0 no restriction, otherwise it's the max output buffer for DecryptCENC
// and CopyBuffer. This is the same as the max frame or sample size, not the
// subsample size.
virtual size_t max_output_size() { return 0; }
// If 0 no restriction, otherwise it's the max sample size for DecryptCENC.
// This is the same as the max input and output buffer size for DecryptCENC
// and CopyBuffer. It is not the same as the max subsample size.
virtual size_t max_sample_size() { return 1024 * 1024; } // 1 MiB
virtual bool srm_update_supported() { return false; }
@@ -176,8 +181,8 @@ class CryptoEngine {
virtual bool srm_blacklisted_device_attached() { return false; }
// Rate limit for nonce generation. Default to 20 nonce/second.
virtual int nonce_flood_count() { return 20; }
// Rate limit for nonce generation. Default to 200 nonce/second.
virtual int nonce_flood_count() { return 200; }
// Limit for size of usage table. If this is zero, then the
// size is unlimited -- or limited only by memory size.
@@ -186,24 +191,32 @@ class CryptoEngine {
virtual uint32_t resource_rating() { return 1; }
// Set destination pointer based on the output destination description.
OEMCryptoResult SetDestination(OEMCrypto_DestBufferDesc* out_description,
size_t data_length, uint8_t subsample_flags);
OEMCryptoResult SetDestination(
const OEMCrypto_DestBufferDesc& out_description, size_t data_length,
uint8_t subsample_flags);
// The current destination.
uint8_t* destination() { return destination_; }
// Subclasses can adjust the destination -- for use in testing.
virtual void adjust_destination(OEMCrypto_DestBufferDesc* out_description,
size_t data_length, uint8_t subsample_flags) {
}
virtual void adjust_destination(
const OEMCrypto_DestBufferDesc& out_description, size_t data_length,
uint8_t subsample_flags) {}
// Push destination buffer to output -- used by subclasses for testing.
virtual OEMCryptoResult PushDestination(
OEMCrypto_DestBufferDesc* out_description, uint8_t subsample_flags) {
const OEMCrypto_DestBufferDesc& out_description,
uint8_t subsample_flags) {
return OEMCrypto_SUCCESS;
}
protected:
// System clock, measuring time in seconds.
int64_t OnlineTime();
// System clock with antirollback protection, measuring time in seconds.
int64_t RollbackCorrectedOfflineTime();
explicit CryptoEngine(std::unique_ptr<wvcdm::FileSystem>&& file_system);
virtual SessionContext* MakeSession(SessionId sid);
virtual UsageTable* MakeUsageTable();

View File

@@ -22,7 +22,8 @@ bool KeyControlBlock::Validate() {
memcmp(verification_, "kc12", 4) && // add in version 12 api
memcmp(verification_, "kc13", 4) && // add in version 13 api
memcmp(verification_, "kc14", 4) && // add in version 14 api
memcmp(verification_, "kc15", 4)) { // add in version 15 api
memcmp(verification_, "kc15", 4) && // add in version 15 api
memcmp(verification_, "kc16", 4)) { // add in version 16 api
LOGE("KCB: BAD verification string: %4.4s", verification_);
valid_ = false;
} else {

View File

@@ -18,7 +18,10 @@
namespace wvoec_ref {
WvKeybox::WvKeybox() : loaded_(false) {}
WvKeybox::WvKeybox() : loaded_(false) {
static std::string fake_device_id = "device_with_no_keybox";
device_id_.assign(fake_device_id.begin(), fake_device_id.end());
}
KeyboxError WvKeybox::Validate() {
if (!loaded_) {

View File

@@ -1,76 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_nonce_table.h"
namespace wvoec_ref {
void NonceTable::AddNonce(uint32_t nonce) {
int new_slot = -1;
int oldest_slot = -1;
// Flush any nonces that have been checked but not flushed.
// After flush, nonces will be either valid or invalid.
Flush();
for (int i = 0; i < kTableSize; ++i) {
// Increase age of all valid nonces.
if (kNTStateValid == state_[i]) {
++age_[i];
if (-1 == oldest_slot) {
oldest_slot = i;
} else {
if (age_[i] > age_[oldest_slot]) {
oldest_slot = i;
}
}
} else {
if (-1 == new_slot) {
age_[i] = 0;
nonces_[i] = nonce;
state_[i] = kNTStateValid;
new_slot = i;
}
}
}
if (-1 == new_slot) {
// reuse oldest
// assert (oldest_slot != -1)
int i = oldest_slot;
age_[i] = 0;
nonces_[i] = nonce;
state_[i] = kNTStateValid;
}
}
bool NonceTable::CheckNonce(uint32_t nonce) {
for (int i = 0; i < kTableSize; ++i) {
if (kNTStateInvalid != state_[i]) {
if (nonce == nonces_[i]) {
state_[i] = kNTStateFlushPending;
return true;
}
}
}
return false;
}
bool NonceTable::NonceCollision(uint32_t nonce) const {
for (int i = 0; i < kTableSize; ++i) {
if (nonce == nonces_[i]) return true;
}
return false;
}
void NonceTable::Flush() {
for (int i = 0; i < kTableSize; ++i) {
if (kNTStateFlushPending == state_[i]) {
state_[i] = kNTStateInvalid;
}
}
}
} // namespace wvoec_ref

View File

@@ -1,42 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef REF_OEMCRYPTO_NONCE_TABLE_H_
#define REF_OEMCRYPTO_NONCE_TABLE_H_
#include <stdint.h>
namespace wvoec_ref {
class NonceTable {
public:
static const int kTableSize = 4;
NonceTable() {
for (int i = 0; i < kTableSize; ++i) {
state_[i] = kNTStateInvalid;
}
}
~NonceTable() {}
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
// Verify that the nonce is not the same as any in this table.
bool NonceCollision(uint32_t nonce) const;
void Flush();
private:
enum NonceTableState {
kNTStateInvalid,
kNTStateValid,
kNTStateFlushPending
};
NonceTableState state_[kTableSize];
uint32_t age_[kTableSize];
uint32_t nonces_[kTableSize];
};
} // namespace wvoec_ref
#endif // REF_OEMCRYPTO_NONCE_TABLE_H_

View File

@@ -1,239 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference 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.
#include "oemcrypto_old_usage_table_ref.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_ref.h"
// TODO(fredgc): Setting the device files base bath is currently broken as
// wvcdm::Properties is no longer used by the reference code.
//#include "properties.h"
#include "pst_report.h"
#include "string_conversions.h"
namespace wvoec_ref {
OldUsageTableEntry::OldUsageTableEntry(OldUsageTable *old_usage_table,
const std::vector<uint8_t> &pst_hash)
: pst_hash_(pst_hash),
old_usage_table_(old_usage_table),
time_of_license_received_(
old_usage_table_->ce_->RollbackCorrectedOfflineTime()),
time_of_first_decrypt_(0),
time_of_last_decrypt_(0),
status_(kUnused) {}
OldUsageTableEntry::~OldUsageTableEntry() {}
OldUsageTableEntry::OldUsageTableEntry(OldUsageTable *old_usage_table,
const OldStoredUsageEntry *buffer)
: old_usage_table_(old_usage_table) {
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 + wvoec::MAC_KEY_SIZE);
mac_key_client_.assign(buffer->mac_key_client,
buffer->mac_key_client + wvoec::MAC_KEY_SIZE);
}
OldUsageTable::OldUsageTable(CryptoEngine *ce) {
ce_ = ce;
generation_ = 0;
table_.clear();
// Load saved table.
wvcdm::FileSystem *file_system = ce->file_system();
std::unique_ptr<wvcdm::File> file;
std::string path;
// Note: this path is OK for a real implementation, but using security level 1
// would be better.
// TODO(fredgc, jfore): Address how this property is presented to the ref.
// For now, the path is empty.
/*if (!Properties::GetDeviceFilesBasePath(kSecurityLevelL3, &path)) {
LOGE("OldUsageTable: Unable to get base path");
return;
}*/
std::string filename = path + "UsageTable.dat";
if (!file_system->Exists(filename)) {
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);
// 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 std::vector<uint8_t> &key = ce_->DeviceRootKey();
if (key.empty()) {
LOGE("OldUsageTable: DeviceRootKey is unexpectedly empty.");
table_.clear();
return;
}
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[wvoec::KEY_IV_SIZE];
memcpy(iv_buffer, encrypted_table->iv, wvoec::KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_decrypt_key(&key[0], 128, &aes_key);
AES_cbc_encrypt(&encrypted_buffer[SHA256_DIGEST_LENGTH + wvoec::KEY_IV_SIZE],
&buffer[SHA256_DIGEST_LENGTH + wvoec::KEY_IV_SIZE],
file_size - SHA256_DIGEST_LENGTH - wvoec::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));
if ((stored_table->generation > generation_ + 1) ||
(stored_table->generation < generation_ - 1)) {
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(this, &stored_table->entries[i].entry);
table_[entry->pst_hash()] = entry;
}
}
OldUsageTableEntry *OldUsageTable::FindEntry(const std::vector<uint8_t> &pst) {
std::unique_lock<std::mutex> 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(this, pst_hash);
std::unique_lock<std::mutex> lock(lock_);
table_[pst_hash] = entry;
return entry;
}
void OldUsageTable::Clear() {
std::unique_lock<std::mutex> 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.
// TODO(jfore): Address how this property is presented to the ref. For now,
// the path is empty.
/*if (!Properties::GetDeviceFilesBasePath(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_ref

View File

@@ -1,101 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference 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_REF_H_
#define OEMCRYPTO_OLD_USAGE_TABLE_REF_H_
#include <stdint.h>
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include "OEMCryptoCENC.h"
#include "oemcrypto_types.h"
#include "openssl/sha.h"
namespace wvoec_ref {
class CryptoEngine;
class OldUsageTable;
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[wvoec::MAC_KEY_SIZE];
uint8_t mac_key_client[wvoec::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[wvoec::KEY_IV_SIZE];
int64_t generation;
uint64_t count;
AlignedOldStoredUsageEntry entries[];
};
class OldUsageTableEntry {
public:
OldUsageTableEntry(OldUsageTable *old_usage_table,
const std::vector<uint8_t> &pst_hash);
OldUsageTableEntry(OldUsageTable *old_usage_table,
const OldStoredUsageEntry *buffer);
~OldUsageTableEntry();
const std::vector<uint8_t> &pst_hash() const { return pst_hash_; }
private:
std::vector<uint8_t> pst_hash_;
const OldUsageTable *old_usage_table_;
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_;
std::mutex lock_;
int64_t generation_;
CryptoEngine *ce_;
friend class OldUsageTableEntry;
};
} // namespace wvoec_ref
#endif // OEMCRYPTO_OLD_USAGE_TABLE_REF_H_

File diff suppressed because it is too large Load Diff

View File

@@ -34,50 +34,50 @@ void RSA_shared_ptr::reset() {
RSA_free(rsa_key_);
}
key_owned_ = false;
rsa_key_ = NULL;
rsa_key_ = nullptr;
}
bool RSA_shared_ptr::LoadPkcs8RsaKey(const uint8_t* buffer, size_t length) {
assert(buffer != NULL);
assert(buffer != nullptr);
reset();
uint8_t* pkcs8_rsa_key = const_cast<uint8_t*>(buffer);
BIO* bio = BIO_new_mem_buf(pkcs8_rsa_key, length);
if (bio == NULL) {
if (bio == nullptr) {
LOGE("[LoadPkcs8RsaKey(): Could not allocate bio buffer]");
return false;
}
bool success = true;
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
if (pkcs8_pki == NULL) {
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
if (pkcs8_pki == nullptr) {
BIO_reset(bio);
pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
if (pkcs8_pki == NULL) {
LOGE("[LoadPkcs8RsaKey(): d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL]");
pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
if (pkcs8_pki == nullptr) {
LOGE("[LoadPkcs8RsaKey(): d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr]");
dump_boringssl_error();
success = false;
}
}
EVP_PKEY* evp = NULL;
EVP_PKEY* evp = nullptr;
if (success) {
evp = EVP_PKCS82PKEY(pkcs8_pki);
if (evp == NULL) {
LOGE("[LoadPkcs8RsaKey(): EVP_PKCS82PKEY returned NULL]");
if (evp == nullptr) {
LOGE("[LoadPkcs8RsaKey(): EVP_PKCS82PKEY returned nullptr]");
dump_boringssl_error();
success = false;
}
}
if (success) {
rsa_key_ = EVP_PKEY_get1_RSA(evp);
if (rsa_key_ == NULL) {
if (rsa_key_ == nullptr) {
LOGE("[LoadPkcs8RsaKey(): PrivateKeyInfo did not contain an RSA key]");
success = false;
}
key_owned_ = true;
}
if (evp != NULL) {
if (evp != nullptr) {
EVP_PKEY_free(evp);
}
if (pkcs8_pki != NULL) {
if (pkcs8_pki != nullptr) {
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
}
BIO_free(bio);

View File

@@ -18,7 +18,7 @@ namespace wvoec_ref {
// counting.
class RSA_shared_ptr {
public:
RSA_shared_ptr() : rsa_key_(NULL), key_owned_(false) {}
RSA_shared_ptr() : rsa_key_(nullptr), key_owned_(false) {}
~RSA_shared_ptr() { reset(); };
// Explicitly allow copy as share.
explicit RSA_shared_ptr(const RSA_shared_ptr& other) :

File diff suppressed because it is too large Load Diff

View File

@@ -15,9 +15,9 @@
#include <openssl/rsa.h>
#include "OEMCryptoCENC.h"
#include "odk_structs.h"
#include "oemcrypto_auth_ref.h"
#include "oemcrypto_key_ref.h"
#include "oemcrypto_nonce_table.h"
#include "oemcrypto_rsa_key_shared.h"
#include "oemcrypto_session_key_table.h"
#include "oemcrypto_types.h"
@@ -37,6 +37,7 @@ class SessionContextKeys {
virtual size_t size() = 0;
virtual bool Insert(const KeyId& key_id, const Key& key_data) = 0;
virtual Key* Find(const KeyId& key_id) = 0;
virtual Key* FirstKey() = 0;
virtual void Remove(const KeyId& key_id) = 0;
virtual void UpdateDuration(const KeyControlBlock& control) = 0;
@@ -58,25 +59,11 @@ class SessionContextKeys {
};
class SessionContext {
private:
SessionContext() {}
public:
SessionContext(CryptoEngine* ce, SessionId sid, const RSA_shared_ptr& rsa_key)
: valid_(true),
ce_(ce),
id_(sid),
current_content_key_(NULL),
session_keys_(NULL),
rsa_key_(rsa_key),
allowed_schemes_(kSign_RSASSA_PSS),
usage_entry_(NULL),
srm_requirements_status_(NoSRMVersion),
usage_entry_status_(kNoUsageEntry),
compute_hash_(false),
current_hash_(0),
bad_frame_number_(0),
hash_error_(OEMCrypto_SUCCESS) {}
SessionContext(CryptoEngine* ce, SessionId sid,
const RSA_shared_ptr& rsa_key);
SessionContext() = delete;
virtual ~SessionContext();
bool isValid() { return valid_; }
@@ -87,8 +74,21 @@ class SessionContext {
virtual bool RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
const std::vector<uint8_t>& mac_context,
const std::vector<uint8_t>& enc_context);
virtual bool GenerateSignature(const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length);
virtual OEMCryptoResult PrepAndSignLicenseRequest(uint8_t* message,
size_t message_length,
size_t* core_message_length,
uint8_t* signature,
size_t* signature_length);
virtual OEMCryptoResult PrepAndSignRenewalRequest(uint8_t* message,
size_t message_length,
size_t* core_message_length,
uint8_t* signature,
size_t* signature_length);
virtual OEMCryptoResult PrepAndSignProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint8_t* signature, size_t* signature_length);
// The size of an RSA signature. This is used when signing as a CAST
// receiver.
size_t RSASignatureSize();
virtual OEMCryptoResult GenerateRSASignature(
const uint8_t* message, size_t message_length, uint8_t* signature,
@@ -96,13 +96,9 @@ class SessionContext {
virtual bool ValidateMessage(const uint8_t* message, size_t message_length,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult DecryptCENC(const uint8_t* iv, size_t block_offset,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
const uint8_t* cipher_data,
size_t cipher_data_length, bool is_encrypted,
uint8_t* clear_data,
OEMCryptoBufferType buffer_type,
uint8_t subsample_flags);
OEMCryptoResult DecryptSamples(
const OEMCrypto_SampleDescription* samples, size_t samples_length,
const OEMCrypto_CENCEncryptPatternDesc* pattern);
OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer,
size_t buffer_length, const uint8_t* iv,
@@ -119,8 +115,11 @@ class SessionContext {
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length);
void StartTimer();
uint32_t CurrentTimer(); // (seconds).
virtual OEMCryptoResult LoadLicense(const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
virtual OEMCryptoResult LoadKeys(
const uint8_t* message, size_t message_length, const uint8_t* signature,
size_t signature_length, OEMCrypto_Substring enc_mac_keys_iv,
@@ -128,8 +127,14 @@ class SessionContext {
const OEMCrypto_KeyObject* key_array, OEMCrypto_Substring pst,
OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
virtual OEMCryptoResult LoadKeysNoSignature(
const uint8_t* message, size_t message_length,
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
virtual OEMCryptoResult LoadEntitledContentKeys(
const uint8_t* message, size_t message_length, size_t num_keys,
const uint8_t* message, size_t message_length, size_t key_array_length,
const OEMCrypto_EntitledContentKeyObject* key_array);
virtual OEMCryptoResult InstallKey(
const KeyId& key_id, const std::vector<uint8_t>& key_data,
@@ -143,6 +148,11 @@ class SessionContext {
bool EncryptRSAKey(const uint8_t* pkcs8_rsa_key, size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv, uint8_t* enc_rsa_key);
bool LoadRSAKey(const uint8_t* pkcs8_rsa_key, size_t rsa_key_length);
virtual OEMCryptoResult LoadRenewal(const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
virtual OEMCryptoResult RefreshKey(
const KeyId& key_id, const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv);
@@ -171,13 +181,14 @@ class SessionContext {
const std::vector<uint8_t>& encryption_key() { return encryption_key_; }
uint32_t allowed_schemes() const { return allowed_schemes_; }
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
// Verify that the nonce does not match any in this session's nonce table.
bool NonceCollision(uint32_t nonce) const {
return nonce_table_.NonceCollision(nonce);
}
void FlushNonces();
// Return true if nonce was set.
bool set_nonce(uint32_t nonce);
uint32_t nonce() const { return nonce_values_.nonce; }
ODK_NonceValues& nonce_values() { return nonce_values_; }
bool CheckNonce(uint32_t nonce) const {
return nonce != 0 && nonce == nonce_values_.nonce;
};
virtual OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number);
virtual OEMCryptoResult LoadUsageEntry(uint32_t index,
@@ -190,9 +201,21 @@ class SessionContext {
virtual OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst,
uint8_t* buffer, size_t* buffer_length);
OEMCryptoResult MoveEntry(uint32_t new_index);
OEMCryptoResult CopyOldUsageEntry(const std::vector<uint8_t>& pst);
bool usage_entry_present() const { return usage_entry_ != nullptr; }
protected:
// Signature size of the currently loaded private key.
size_t CertSignatureSize();
// Signature size when using a keybox or OEM Cert's private key.
size_t ROTSignatureSize();
virtual OEMCryptoResult GenerateCertSignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
virtual OEMCryptoResult GenerateSignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
bool DeriveKey(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& context, int counter,
std::vector<uint8_t>* out);
@@ -211,18 +234,19 @@ class SessionContext {
OEMCryptoResult CheckStatusOnline(uint32_t nonce, uint32_t control);
// Check that the usage entry status is valid for offline use.
OEMCryptoResult CheckStatusOffline(uint32_t nonce, uint32_t control);
OEMCryptoResult DecryptSubsample(
const OEMCrypto_SubSampleDescription& subsample,
const uint8_t* cipher_data, uint8_t* clear_data,
OEMCryptoBufferType buffer_type, const uint8_t (&iv)[wvoec::KEY_IV_SIZE],
const OEMCrypto_CENCEncryptPatternDesc* pattern);
OEMCryptoResult ChooseDecrypt(const uint8_t* iv, size_t block_offset,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
const uint8_t* cipher_data,
size_t cipher_data_length, bool is_encrypted,
uint8_t* clear_data,
size_t cipher_data_length, uint8_t* clear_data,
OEMCryptoBufferType buffer_type);
OEMCryptoResult DecryptCBC(const uint8_t* key, const uint8_t* iv,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
const uint8_t* cipher_data,
size_t cipher_data_length, uint8_t* clear_data);
OEMCryptoResult PatternDecryptCTR(
const uint8_t* key, const uint8_t* iv, size_t block_offset,
OEMCryptoResult PatternDecryptCBC(
const uint8_t* key, const uint8_t* iv,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
const uint8_t* cipher_data, size_t cipher_data_length,
uint8_t* clear_data);
@@ -244,10 +268,13 @@ class SessionContext {
std::vector<uint8_t> session_key_;
const Key* current_content_key_;
SessionContextKeys* session_keys_;
NonceTable nonce_table_;
ODK_NonceValues nonce_values_;
uint8_t license_request_hash_[ODK_SHA256_HASH_SIZE];
RSA_shared_ptr rsa_key_;
uint32_t allowed_schemes_; // for RSA signatures.
time_t timer_start_;
bool decrypt_started_; // If the license has been used in this session.
ODK_TimerLimits timer_limits_;
ODK_ClockValues clock_values_;
UsageTableEntry* usage_entry_;
SRMVersionStatus srm_requirements_status_;
enum UsageEntryStatus {
@@ -265,6 +292,12 @@ class SessionContext {
uint32_t bad_frame_number_; // Frame number with bad hash.
OEMCryptoResult hash_error_; // Error code for first bad frame.
// The bare minimum state machine is to only call each of these function
// categories at most once.
bool state_nonce_created_;
bool state_request_signed_;
bool state_response_loaded_;
CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext);
};

View File

@@ -13,7 +13,7 @@ namespace wvoec_ref {
SessionKeyTable::~SessionKeyTable() {
for (KeyMap::iterator i = keys_.begin(); i != keys_.end(); ++i) {
if (NULL != i->second) {
if (nullptr != i->second) {
delete i->second;
}
}
@@ -27,7 +27,7 @@ bool SessionKeyTable::Insert(const KeyId key_id, const Key& key_data) {
Key* SessionKeyTable::Find(const KeyId key_id) {
if (keys_.find(key_id) == keys_.end()) {
return NULL;
return nullptr;
}
return keys_[key_id];
}
@@ -59,11 +59,11 @@ Key* EntitlementKeyTable::Find(const KeyId key_id) {
ContentIdToEntitlementIdMap::iterator it =
contentid_to_entitlementid_.find(key_id);
if (it == contentid_to_entitlementid_.end()) {
return NULL;
return nullptr;
}
if (keys_.find(it->second) == keys_.end()) {
return NULL;
return nullptr;
}
return keys_[it->second];
}

View File

@@ -34,6 +34,7 @@ class SessionKeyTable {
bool Insert(const KeyId key_id, const Key& key_data);
Key* Find(const KeyId key_id);
Key* FirstKey() { return keys_.begin()->second; }
void Remove(const KeyId key_id);
void UpdateDuration(const KeyControlBlock& control);
size_t size() const { return keys_.size(); }
@@ -52,6 +53,7 @@ class EntitlementKeyTable {
~EntitlementKeyTable() {}
bool Insert(const KeyId key_id, const Key& key_data);
Key* Find(const KeyId key_id);
Key* FirstKey() { return keys_.begin()->second; }
void Remove(const KeyId key_id);
void UpdateDuration(const KeyControlBlock& control);
size_t size() const { return contentid_to_entitlementid_.size(); }

View File

@@ -19,8 +19,8 @@
#include "file_store.h"
#include "log.h"
#include "odk.h"
#include "oemcrypto_engine_ref.h"
#include "oemcrypto_old_usage_table_ref.h"
// TODO(fredgc): Setting the device files base bath is currently broken as
// wvcdm::Properties is no longer used by the reference code.
//#include "properties.h"
@@ -71,8 +71,7 @@ OEMCryptoResult UsageTableEntry::SetPST(const uint8_t* pst, size_t pst_length) {
data_.pst_length = pst_length;
if (!pst || !pst_length) return OEMCrypto_ERROR_INVALID_CONTEXT;
memcpy(data_.pst, pst, pst_length);
data_.time_of_license_received =
usage_table_->ce_->RollbackCorrectedOfflineTime();
data_.time_of_license_received = usage_table_->ce_->SystemTime();
return OEMCrypto_SUCCESS;
}
@@ -101,25 +100,7 @@ bool UsageTableEntry::SetMacKeys(const std::vector<uint8_t>& server,
return true;
}
bool UsageTableEntry::CheckForUse() {
if (Inactive()) return false;
recent_decrypt_ = true;
if (data_.status == kUnused) {
data_.status = kActive;
data_.time_of_first_decrypt =
usage_table_->ce_->RollbackCorrectedOfflineTime();
data_.generation_number++;
usage_table_->IncrementGeneration();
}
return true;
}
void UsageTableEntry::Deactivate(const std::vector<uint8_t>& pst) {
if (data_.status == kUnused) {
data_.status = kInactiveUnused;
} else if (data_.status == kActive) {
data_.status = kInactiveUsed;
}
void UsageTableEntry::ForbidReport() {
forbid_report_ = true;
data_.generation_number++;
usage_table_->IncrementGeneration();
@@ -151,7 +132,7 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector<uint8_t>& pst,
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
wvcdm::Unpacked_PST_Report pst_report(buffer);
int64_t now = usage_table_->ce_->RollbackCorrectedOfflineTime();
int64_t now = usage_table_->ce_->SystemTime();
pst_report.set_seconds_since_license_received(now -
data_.time_of_license_received);
pst_report.set_seconds_since_first_decrypt(now - data_.time_of_first_decrypt);
@@ -170,12 +151,29 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector<uint8_t>& pst,
return OEMCrypto_SUCCESS;
}
void UsageTableEntry::UpdateAndIncrement() {
void UsageTableEntry::UpdateAndIncrement(ODK_ClockValues* clock_values) {
if (recent_decrypt_) {
data_.time_of_last_decrypt =
usage_table_->ce_->RollbackCorrectedOfflineTime();
data_.time_of_last_decrypt = usage_table_->ce_->SystemTime();
recent_decrypt_ = false;
}
data_.time_of_license_received = clock_values->time_of_license_signed;
data_.time_of_first_decrypt = clock_values->time_of_first_decrypt;
// Use the most recent time_of_last_decrypt.
if (static_cast<uint64_t>(data_.time_of_last_decrypt) <
clock_values->time_of_last_decrypt) {
// For the reference implementation, we update the clock_values on every
// decrypt.
data_.time_of_last_decrypt = clock_values->time_of_last_decrypt;
} else {
// For this reference implementation of OEMCrypto, we regularly update
// clock_values->time_of_last_decrypt and we could just update
// data_.time_of_last_decrypt here. However, I'm including the line below to
// make it clear that you could do it the other way around. When this
// function is called, the two values should be synced so that the usage
// entry can be saved with the correct value.
clock_values->time_of_last_decrypt = data_.time_of_last_decrypt;
}
data_.status = clock_values->status;
data_.generation_number++;
usage_table_->IncrementGeneration();
forbid_report_ = false;
@@ -194,7 +192,7 @@ OEMCryptoResult UsageTableEntry::SaveData(CryptoEngine* ce,
reinterpret_cast<SignedEntryBlock*>(&clear_buffer[0]);
SignedEntryBlock* encrypted =
reinterpret_cast<SignedEntryBlock*>(signed_buffer);
clear->data = this->data_; // Copy the current data.
clear->data = data_; // Copy the current data.
memcpy(clear->verification, kEntryVerification, kMagicLength);
// This should be encrypted and signed with a device specific key.
@@ -228,7 +226,8 @@ OEMCryptoResult UsageTableEntry::SaveData(CryptoEngine* ce,
}
OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index,
const std::vector<uint8_t>& buffer) {
const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values) {
if (buffer.size() < SignedEntrySize()) return OEMCrypto_ERROR_SHORT_BUFFER;
if (buffer.size() > SignedEntrySize())
LOGW("LoadUsageTableEntry: buffer is large. %d > %d", buffer.size(),
@@ -295,39 +294,10 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index,
LOGE("LoadUsageEntry: entry has bad status %d", clear->data.status);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
this->data_ = clear->data;
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() != wvoec::MAC_KEY_SIZE) {
LOGE("CopyOldEntry: Old entry has bad server mac key.");
} else {
memcpy(data_.mac_key_server, &(old_entry->mac_key_server_[0]),
wvoec::MAC_KEY_SIZE);
}
if (old_entry->mac_key_client_.size() != wvoec::MAC_KEY_SIZE) {
LOGE("CopyOldEntry: Old entry has bad client mac key.");
} else {
memcpy(data_.mac_key_client, &(old_entry->mac_key_client_[0]),
wvoec::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.data(), data_.pst_length);
data_.pst[data_.pst_length] = '\0';
return OEMCrypto_SUCCESS;
data_ = clear->data;
return ODK_ReloadClockValues(
clock_values, data_.time_of_license_received, data_.time_of_first_decrypt,
data_.time_of_last_decrypt, data_.status, ce->SystemTime());
}
size_t UsageTableEntry::SignedEntrySize() {
@@ -337,12 +307,7 @@ size_t UsageTableEntry::SignedEntrySize() {
return blocks * wvoec::KEY_IV_SIZE;
}
UsageTable::~UsageTable() {
if (old_table_) {
delete old_table_;
old_table_ = NULL;
}
}
UsageTable::~UsageTable() {}
size_t UsageTable::SignedHeaderSize(size_t count) {
size_t base = sizeof(SignedHeaderBlock) + count * sizeof(int64_t);
@@ -351,12 +316,10 @@ size_t UsageTable::SignedHeaderSize(size_t count) {
return blocks * wvoec::KEY_IV_SIZE;
}
OEMCryptoResult UsageTable::UpdateUsageEntry(SessionContext* session,
UsageTableEntry* entry,
uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length) {
OEMCryptoResult UsageTable::UpdateUsageEntry(
SessionContext* session, UsageTableEntry* entry, uint8_t* header_buffer,
size_t* header_buffer_length, uint8_t* entry_buffer,
size_t* entry_buffer_length, ODK_ClockValues* clock_values) {
size_t signed_header_size = SignedHeaderSize(generation_numbers_.size());
if (*entry_buffer_length < UsageTableEntry::SignedEntrySize() ||
*header_buffer_length < signed_header_size) {
@@ -368,7 +331,7 @@ OEMCryptoResult UsageTable::UpdateUsageEntry(SessionContext* session,
*header_buffer_length = signed_header_size;
if ((!header_buffer) || (!entry_buffer))
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
entry->UpdateAndIncrement();
entry->UpdateAndIncrement(clock_values);
generation_numbers_[entry->index()] = entry->generation_number();
OEMCryptoResult result =
entry->SaveData(ce_, session, entry_buffer, *entry_buffer_length);
@@ -408,7 +371,8 @@ OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session,
OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
UsageTableEntry** entry,
uint32_t index,
const std::vector<uint8_t>& buffer) {
const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values) {
if (!header_loaded_) {
LOGE("CreateNewUsageEntry: Header not loaded.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -427,7 +391,8 @@ OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
}
UsageTableEntry* new_entry = MakeEntry(index);
OEMCryptoResult status = new_entry->LoadData(ce_, index, buffer);
OEMCryptoResult status =
new_entry->LoadData(ce_, index, buffer, clock_values);
if (status != OEMCrypto_SUCCESS) {
delete new_entry;
return status;
@@ -722,7 +687,7 @@ OEMCryptoResult UsageTable::CreateUsageTableHeader(
if (!LoadGenerationNumber(true)) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
// Make sure there are no entries that are currently tied to an open session.
for (size_t i = 0; i < sessions_.size(); ++i) {
if (sessions_[i] != NULL) {
if (sessions_[i] != nullptr) {
LOGE("CreateUsageTableHeader: index %d used by session.", i);
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -733,41 +698,4 @@ OEMCryptoResult UsageTable::CreateUsageTableHeader(
return SaveUsageTableHeader(header_buffer, *header_buffer_length);
}
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() {
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 = ce_->RollbackCorrectedOfflineTime();
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 + wvoec::MAC_KEY_SIZE);
old_entry->mac_key_client_.assign(client_mac_key,
client_mac_key + wvoec::MAC_KEY_SIZE);
return OEMCrypto_SUCCESS;
}
} // namespace wvoec_ref

View File

@@ -13,16 +13,15 @@
#include <vector>
#include "OEMCryptoCENC.h"
#include "openssl/sha.h"
#include "odk_structs.h"
#include "oemcrypto_types.h"
#include "openssl/sha.h"
namespace wvoec_ref {
class SessionContext;
class CryptoEngine;
class UsageTable;
class OldUsageTable;
class OldUsageTableEntry;
const size_t kMaxPSTLength = 255;
// This is the data we store offline.
@@ -44,29 +43,34 @@ class UsageTableEntry {
UsageTableEntry(UsageTable* table, uint32_t index, int64_t generation);
virtual ~UsageTableEntry(); // Free memory, remove reference in header.
bool Inactive() { return data_.status >= kInactive; }
// Mark this entry as modified and forbid a usage report until the data has
// been saved. This is done on important events like first decrypt and
// deactivation.
void ForbidReport();
OEMCryptoResult SetPST(const uint8_t* pst, size_t pst_length);
bool VerifyPST(const uint8_t* pst, size_t pst_length);
bool VerifyMacKeys(const std::vector<uint8_t>& server,
const std::vector<uint8_t>& client);
bool SetMacKeys(const std::vector<uint8_t>& server,
const std::vector<uint8_t>& client);
// Returns false if the entry is inactive. Otherwise, returns true.
// If the status was unused, it is updated, and decrypt times are flaged
// for update.
bool CheckForUse();
void Deactivate(const std::vector<uint8_t>& pst);
virtual OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst,
uint8_t* buffer, size_t* buffer_length);
virtual void UpdateAndIncrement();
virtual void UpdateAndIncrement(ODK_ClockValues* clock_values);
// Save all data to the give buffer. This should be called after updating the
// data.
OEMCryptoResult SaveData(CryptoEngine* ce, SessionContext* session,
uint8_t* signed_buffer, size_t buffer_size);
// Load all data from the buffer, and then update clock_values.
OEMCryptoResult LoadData(CryptoEngine* ce, uint32_t index,
const std::vector<uint8_t>& buffer);
virtual OEMCryptoResult CopyOldUsageEntry(const std::vector<uint8_t>& pst);
const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values);
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; }
uint32_t index() { return data_.index; }
void set_recent_decrypt(bool recent_decrypt) {
recent_decrypt_ = recent_decrypt;
}
static size_t SignedEntrySize();
const uint8_t* mac_key_server() { return data_.mac_key_server; }
const uint8_t* mac_key_client() { return data_.mac_key_client; }
@@ -80,8 +84,7 @@ class UsageTableEntry {
class UsageTable {
public:
explicit UsageTable(CryptoEngine* ce)
: ce_(ce), header_loaded_(false), old_table_(NULL) {};
explicit UsageTable(CryptoEngine* ce) : ce_(ce), header_loaded_(false){};
virtual ~UsageTable();
OEMCryptoResult CreateNewUsageEntry(SessionContext* session,
@@ -89,13 +92,12 @@ class UsageTable {
uint32_t* usage_entry_number);
OEMCryptoResult LoadUsageEntry(SessionContext* session,
UsageTableEntry** entry, uint32_t index,
const std::vector<uint8_t>& buffer);
OEMCryptoResult UpdateUsageEntry(SessionContext* session,
UsageTableEntry* entry,
uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length);
const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values);
OEMCryptoResult UpdateUsageEntry(
SessionContext* session, UsageTableEntry* entry, uint8_t* header_buffer,
size_t* header_buffer_length, uint8_t* entry_buffer,
size_t* entry_buffer_length, ODK_ClockValues* clock_values);
OEMCryptoResult MoveEntry(UsageTableEntry* entry, uint32_t new_index);
OEMCryptoResult CreateUsageTableHeader(uint8_t* header_buffer,
size_t* header_buffer_length);
@@ -106,15 +108,6 @@ class UsageTable {
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);
protected:
virtual UsageTableEntry* MakeEntry(uint32_t index);
@@ -128,7 +121,6 @@ class UsageTable {
int64_t master_generation_number_;
std::vector<int64_t> generation_numbers_;
std::vector<SessionContext*> sessions_;
OldUsageTable* old_table_;
friend class UsageTableEntry;
};