OEMCrypto Usage Tables
This CL adds usage tables to the OEMCrypto reference implementation (mock) and unit tests. There is also a new parameter called oem_crypto_require_usage_tables that determines if the usage tables are required or not. This is set to true for Android and false for all other platforms. This CL is most of OEMCrypto version 9 updates. This CL is a copy of https://widevine-internal-review.googlesource.com/#/c/9720 https://widevine-internal-review.googlesource.com/#/c/9874 https://widevine-internal-review.googlesource.com/#/c/9873 Change-Id: I78c4f7651306f9f79ba2260c3e04fb1eca7e20e3
This commit is contained in:
@@ -39,6 +39,9 @@ class Properties {
|
||||
static inline bool oem_crypto_use_userspace_buffers() {
|
||||
return oem_crypto_use_userspace_buffers_;
|
||||
}
|
||||
static inline bool oem_crypto_require_usage_tables() {
|
||||
return oem_crypto_require_usage_tables_;
|
||||
}
|
||||
static inline bool use_certificates_as_identification() {
|
||||
return use_certificates_as_identification_;
|
||||
}
|
||||
@@ -87,6 +90,9 @@ class Properties {
|
||||
static void set_oem_crypto_use_userspace_buffers(bool flag) {
|
||||
oem_crypto_use_userspace_buffers_ = flag;
|
||||
}
|
||||
static void set_oem_crypto_require_usage_tables(bool flag) {
|
||||
oem_crypto_require_usage_tables_ = flag;
|
||||
}
|
||||
static void set_use_certificates_as_identification(bool flag) {
|
||||
use_certificates_as_identification_ = flag;
|
||||
}
|
||||
@@ -104,6 +110,7 @@ class Properties {
|
||||
static bool oem_crypto_use_secure_buffers_;
|
||||
static bool oem_crypto_use_fifo_;
|
||||
static bool oem_crypto_use_userspace_buffers_;
|
||||
static bool oem_crypto_require_usage_tables_;
|
||||
static bool use_certificates_as_identification_;
|
||||
static bool decrypt_with_empty_session_support_;
|
||||
static bool security_level_path_backward_compatibility_support_;
|
||||
|
||||
@@ -21,6 +21,7 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& bin_input);
|
||||
std::string HexEncode(const uint8_t* bytes, unsigned size);
|
||||
std::string IntToString(int value);
|
||||
std::string UintToString(unsigned int value);
|
||||
int64_t htonll64(int64_t x);
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ bool Properties::require_explicit_renew_request_;
|
||||
bool Properties::oem_crypto_use_secure_buffers_;
|
||||
bool Properties::oem_crypto_use_fifo_;
|
||||
bool Properties::oem_crypto_use_userspace_buffers_;
|
||||
bool Properties::oem_crypto_require_usage_tables_;
|
||||
bool Properties::use_certificates_as_identification_;
|
||||
bool Properties::decrypt_with_empty_session_support_;
|
||||
bool Properties::security_level_path_backward_compatibility_support_;
|
||||
@@ -25,6 +26,7 @@ void Properties::Init() {
|
||||
oem_crypto_use_secure_buffers_ = kPropertyOemCryptoUseSecureBuffers;
|
||||
oem_crypto_use_fifo_ = kPropertyOemCryptoUseFifo;
|
||||
oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers;
|
||||
oem_crypto_require_usage_tables_ = kPropertyOemCryptoRequireUsageTable;
|
||||
use_certificates_as_identification_ =
|
||||
kPropertyUseCertificatesAsIdentification;
|
||||
decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "string_conversions.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
@@ -167,4 +168,21 @@ std::string UintToString(unsigned int value) {
|
||||
return out_string;
|
||||
}
|
||||
|
||||
int64_t htonll64(int64_t x) { // Convert to big endian (network-byte-order)
|
||||
union {
|
||||
uint32_t array[2];
|
||||
int64_t number;
|
||||
} mixed;
|
||||
mixed.number = 1;
|
||||
if (mixed.array[0] == 1) {
|
||||
mixed.number = x; // Little Endian.
|
||||
uint32_t temp = mixed.array[0];
|
||||
mixed.array[0] = htonl(mixed.array[1]);
|
||||
mixed.array[1] = htonl(temp);
|
||||
return mixed.number;
|
||||
} else {
|
||||
return x; // Big Endian.
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
@@ -70,4 +70,18 @@ TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) {
|
||||
INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64EncodeDecodeTest,
|
||||
::testing::ValuesIn(kBase64TestVectors));
|
||||
|
||||
class HtoNLL64Test : public ::testing::Test {};
|
||||
|
||||
TEST_F(HtoNLL64Test, PositiveNumber) {
|
||||
uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
int64_t *network_byte_order = reinterpret_cast<int64_t *>(data);
|
||||
int64_t host_byte_order = htonll64(*network_byte_order);
|
||||
EXPECT_EQ(0x0102030405060708, host_byte_order);
|
||||
}
|
||||
TEST_F(HtoNLL64Test, NegativeNumber) {
|
||||
uint8_t data[8] = {0xfe, 2, 3, 4, 5, 6, 7, 8};
|
||||
int64_t *network_byte_order = reinterpret_cast<int64_t *>(data);
|
||||
int64_t host_byte_order = htonll64(*network_byte_order);
|
||||
EXPECT_EQ(-0x01FdFcFbFaF9F8F8, host_byte_order);
|
||||
}
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -22,6 +22,9 @@ const bool kPropertyOemCryptoUseSecureBuffers = true;
|
||||
const bool kPropertyOemCryptoUseFifo = false;
|
||||
const bool kPropertyOemCryptoUseUserSpaceBuffers = false;
|
||||
|
||||
// If true, the unit tests require OEMCrypto to support usage tables.
|
||||
const bool kPropertyOemCryptoRequireUsageTable = true;
|
||||
|
||||
// If false, keyboxes will be used as client identification
|
||||
// and passed as the token in the license request
|
||||
const bool kPropertyUseCertificatesAsIdentification = true;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
/*********************************************************************
|
||||
* OEMCryptoCENC.h
|
||||
*
|
||||
* (c) Copyright 2013 Google, Inc.
|
||||
*
|
||||
* Reference APIs needed to support Widevine's crypto algorithms.
|
||||
*********************************************************************/
|
||||
|
||||
@@ -101,7 +101,7 @@ typedef enum OEMCryptoBufferType {
|
||||
OEMCrypto_BufferType_Clear,
|
||||
OEMCrypto_BufferType_Secure,
|
||||
OEMCrypto_BufferType_Direct
|
||||
} OEMCrytoBufferType;
|
||||
} OEMCryptoBufferType;
|
||||
|
||||
typedef struct {
|
||||
OEMCryptoBufferType type;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
/*********************************************************************
|
||||
* level3.h
|
||||
*
|
||||
* (c) Copyright 2013 Google, Inc.
|
||||
*
|
||||
* Reference APIs needed to support Widevine's crypto algorithms.
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \
|
||||
src/oemcrypto_engine_mock.cpp \
|
||||
src/oemcrypto_key_mock.cpp \
|
||||
src/oemcrypto_keybox_mock.cpp \
|
||||
src/oemcrypto_usage_table_mock.cpp \
|
||||
src/oemcrypto_mock.cpp \
|
||||
src/wvcrc.cpp \
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@ LIB_OBJECTS = \
|
||||
oemcrypto_mock.o \
|
||||
oemcrypto_engine_mock.o \
|
||||
oemcrypto_key_mock.o \
|
||||
oemcrypto_keybox_mock.o
|
||||
oemcrypto_keybox_mock.o \
|
||||
oemcrypto_usage_table_mock.o
|
||||
|
||||
INCLUDES = \
|
||||
-I$(CDM_INCLUDE_PATH)
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
@@ -15,6 +11,7 @@
|
||||
|
||||
#include "log.h"
|
||||
#include "oemcrypto_key_mock.h"
|
||||
#include "oemcrypto_usage_table_mock.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/cmac.h"
|
||||
@@ -28,7 +25,6 @@
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
|
||||
static const int kPssSaltLength = 20;
|
||||
|
||||
namespace {
|
||||
@@ -86,6 +82,7 @@ void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) {
|
||||
}
|
||||
|
||||
SessionContext::~SessionContext() {
|
||||
if (usage_entry_) usage_entry_->set_session(NULL);
|
||||
}
|
||||
|
||||
// Internal utility function to derive key using CMAC-128
|
||||
@@ -142,7 +139,8 @@ bool SessionContext::DeriveKeys(const std::vector<uint8_t>& master_key,
|
||||
if (!DeriveKey(master_key, mac_key_context, 2, &mac_key_part2)) {
|
||||
return false;
|
||||
}
|
||||
mac_key_server.insert(mac_key_server.end(), mac_key_part2.begin(), mac_key_part2.end());
|
||||
mac_key_server.insert(mac_key_server.end(), mac_key_part2.begin(),
|
||||
mac_key_part2.end());
|
||||
|
||||
if (!DeriveKey(master_key, mac_key_context, 3, &mac_key_client)) {
|
||||
return false;
|
||||
@@ -150,7 +148,8 @@ bool SessionContext::DeriveKeys(const std::vector<uint8_t>& master_key,
|
||||
if (!DeriveKey(master_key, mac_key_context, 4, &mac_key_part2)) {
|
||||
return false;
|
||||
}
|
||||
mac_key_client.insert(mac_key_client.end(), mac_key_part2.begin(), mac_key_part2.end());
|
||||
mac_key_client.insert(mac_key_client.end(), mac_key_part2.begin(),
|
||||
mac_key_part2.end());
|
||||
|
||||
// Generate derived key for encryption key
|
||||
std::vector<uint8_t> enc_key;
|
||||
@@ -159,10 +158,14 @@ bool SessionContext::DeriveKeys(const std::vector<uint8_t>& master_key,
|
||||
}
|
||||
|
||||
#if 0 // Print Derived Keys to stdout.
|
||||
std::cout << " mac_key_context = " << wvcdm::b2a_hex(mac_key_context) << std::endl;
|
||||
std::cout << " enc_key_context = " << wvcdm::b2a_hex(enc_key_context) << std::endl;
|
||||
std::cout << " mac_key_server = " << wvcdm::b2a_hex(mac_key_server) << std::endl;
|
||||
std::cout << " mac_key_client = " << wvcdm::b2a_hex(mac_key_client) << std::endl;
|
||||
std::cout << " mac_key_context = " << wvcdm::b2a_hex(mac_key_context)
|
||||
<< std::endl;
|
||||
std::cout << " enc_key_context = " << wvcdm::b2a_hex(enc_key_context)
|
||||
<< std::endl;
|
||||
std::cout << " mac_key_server = " << wvcdm::b2a_hex(mac_key_server)
|
||||
<< std::endl;
|
||||
std::cout << " mac_key_client = " << wvcdm::b2a_hex(mac_key_client)
|
||||
<< std::endl;
|
||||
std::cout << " enc_key = " << wvcdm::b2a_hex(enc_key) << std::endl;
|
||||
#endif
|
||||
|
||||
@@ -218,7 +221,8 @@ bool SessionContext::GenerateSignature(const uint8_t* message,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mac_key_client_.empty() || mac_key_client_.size() != wvcdm::MAC_KEY_SIZE) {
|
||||
if (mac_key_client_.empty() ||
|
||||
mac_key_client_.size() != wvcdm::MAC_KEY_SIZE) {
|
||||
LOGE("[GenerateSignature(): No MAC Key]");
|
||||
return false;
|
||||
}
|
||||
@@ -340,14 +344,39 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::CheckNonceOrPST(KeyControlBlock& key_control_block,
|
||||
bool SessionContext::CheckNonceOrEntry(const KeyControlBlock& key_control_block,
|
||||
const std::vector<uint8_t>& pst) {
|
||||
// TODO(fredgc): look at replay control bits, too.
|
||||
switch (key_control_block.control_bits() & kControlReplayMask) {
|
||||
case kControlNonceRequired: // Online license. Nonce always required.
|
||||
if (!CheckNonce(key_control_block.nonce())) return false;
|
||||
if (!usage_entry_) {
|
||||
if (ce_->usage_table()->FindEntry(pst)) {
|
||||
LOGE("KCB: Cannot create duplicate entries in usage table.");
|
||||
return false;
|
||||
}
|
||||
usage_entry_ = ce_->usage_table()->CreateEntry(pst, this);
|
||||
}
|
||||
break; // Offline license. Nonce required on first use.
|
||||
case kControlNonceOrEntry:
|
||||
if (!usage_entry_) {
|
||||
usage_entry_ = ce_->usage_table()->FindEntry(pst);
|
||||
if (usage_entry_) {
|
||||
if (usage_entry_->status() == kInactive) return false;
|
||||
} else {
|
||||
if (!CheckNonce(key_control_block.nonce())) return false;
|
||||
usage_entry_ = ce_->usage_table()->CreateEntry(pst, this);
|
||||
}
|
||||
} else {
|
||||
if (usage_entry_->status() == kInactive) return false;
|
||||
}
|
||||
break; // Usage table not required. Look at nonce enabled bit.
|
||||
default:
|
||||
if ((key_control_block.control_bits() & kControlNonceEnabled) &&
|
||||
(!CheckNonce(key_control_block.nonce()))) {
|
||||
LOGE("KCB: BAD Nonce");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -361,9 +390,9 @@ uint32_t SessionContext::CurrentTimer() {
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::LoadKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_keys, size_t num_keys,
|
||||
const uint8_t* message, size_t message_length, const uint8_t* signature,
|
||||
size_t signature_length, const uint8_t* enc_mac_key_iv,
|
||||
const uint8_t* enc_mac_keys, size_t num_keys,
|
||||
const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
// Validate message signature
|
||||
@@ -419,6 +448,11 @@ OEMCryptoResult SessionContext::LoadKeys(
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if (usage_entry_) {
|
||||
if (!usage_entry_->VerifyOrSetMacKeys(mac_key_server_, mac_key_client_)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -466,8 +500,8 @@ bool SessionContext::InstallKey(const KeyId& key_id,
|
||||
LOGE("Error parsing key control.");
|
||||
return false;
|
||||
}
|
||||
if (!CheckNonceOrPST(key_control_block, pst)) {
|
||||
LOGE("Failed Nonce or PST check.");
|
||||
if (!CheckNonceOrEntry(key_control_block, pst)) {
|
||||
LOGE("Failed Nonce/PST check.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -671,6 +705,12 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer,
|
||||
return OEMCrypto_ERROR_KEY_EXPIRED;
|
||||
}
|
||||
}
|
||||
if (control.control_bits() & kControlReplayMask) {
|
||||
if (!IsUsageEntryValid()) {
|
||||
LOGE("[Generic_Encrypt(): usage entry not valid]");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
|
||||
LOGE("[Generic_Encrypt(): algorithm bad.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -723,6 +763,12 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer,
|
||||
return OEMCrypto_ERROR_KEY_EXPIRED;
|
||||
}
|
||||
}
|
||||
if (control.control_bits() & kControlReplayMask) {
|
||||
if (!IsUsageEntryValid()) {
|
||||
LOGE("[Generic_Decrypt(): usage entry not valid]");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
|
||||
LOGE("[Generic_Decrypt(): bad algorithm.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -775,6 +821,12 @@ OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer,
|
||||
return OEMCrypto_ERROR_KEY_EXPIRED;
|
||||
}
|
||||
}
|
||||
if (control.control_bits() & kControlReplayMask) {
|
||||
if (!IsUsageEntryValid()) {
|
||||
LOGE("[Generic_Sign(): usage entry not valid]");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_HMAC_SHA256 ) {
|
||||
LOGE("[Generic_Sign(): bad algorithm.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -819,6 +871,12 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer,
|
||||
return OEMCrypto_ERROR_KEY_EXPIRED;
|
||||
}
|
||||
}
|
||||
if (control.control_bits() & kControlReplayMask) {
|
||||
if (!IsUsageEntryValid()) {
|
||||
LOGE("[Generic_Verify(): usage entry not valid]");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_HMAC_SHA256 ) {
|
||||
LOGE("[Generic_Verify(): bad algorithm.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -881,6 +939,13 @@ void SessionContext::FlushNonces() {
|
||||
nonce_table_.Flush();
|
||||
}
|
||||
|
||||
bool SessionContext::IsUsageEntryValid() {
|
||||
if (!usage_entry_) return false;
|
||||
return usage_entry_->UpdateTime();
|
||||
}
|
||||
|
||||
void SessionContext::ReleaseUsageEntry() { usage_entry_ = NULL; }
|
||||
|
||||
CryptoEngine::CryptoEngine() :
|
||||
ce_state_(CE_INITIALIZED), current_session_(NULL) {
|
||||
valid_ = true;
|
||||
@@ -891,11 +956,13 @@ CryptoEngine::CryptoEngine() :
|
||||
// If local_display_ is true, we pretend we are using a built-in display,
|
||||
// instead of HDMI or WiFi output.
|
||||
local_display_ = false;
|
||||
usage_table_ = new UsageTable(this);
|
||||
}
|
||||
|
||||
CryptoEngine::~CryptoEngine() {
|
||||
current_session_ = NULL;
|
||||
sessions_.clear();
|
||||
if (usage_table_) delete usage_table_;
|
||||
}
|
||||
|
||||
void CryptoEngine::Terminate() {
|
||||
@@ -981,6 +1048,12 @@ OEMCryptoResult SessionContext::DecryptCTR(
|
||||
return OEMCrypto_ERROR_KEY_EXPIRED;
|
||||
}
|
||||
}
|
||||
if (control.control_bits() & kControlReplayMask) {
|
||||
if (!IsUsageEntryValid()) {
|
||||
LOGE("[DecryptCTR(): usage entry not valid]");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if (!ce_->local_display()) { // Only look at HDCP if the display is not
|
||||
// local.
|
||||
if (control.control_bits() & kControlHDCPRequired) {
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef OEMCRYPTO_ENGINE_MOCK_H_
|
||||
#define OEMCRYPTO_ENGINE_MOCK_H_
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#ifndef WVOEC_MOCK_OEMCRYPTO_ENGINE_MOCK_H_
|
||||
#define WVOEC_MOCK_OEMCRYPTO_ENGINE_MOCK_H_
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
#include <stdint.h>
|
||||
@@ -20,9 +16,7 @@
|
||||
#include "oemcrypto_keybox_mock.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
// TODO(fredgc,gmorgan): Revisit the need to keep interface separate.
|
||||
// For now, we need to include the enum OEMCrypto_Algorithm.
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "OEMCryptoCENC.h" // Needed for enum OEMCrypto_Algorithm.
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
@@ -34,6 +28,8 @@ enum BufferType {
|
||||
|
||||
class SessionContext;
|
||||
class CryptoEngine;
|
||||
class UsageTable;
|
||||
class UsageTableEntry;
|
||||
|
||||
typedef uint32_t SessionId;
|
||||
typedef std::map<SessionId, SessionContext*> ActiveSessions;
|
||||
@@ -92,7 +88,8 @@ class SessionContext {
|
||||
id_(sid),
|
||||
current_content_key_(NULL),
|
||||
rsa_key_(NULL),
|
||||
allowed_schemes_(kSign_RSASSA_PSS) {}
|
||||
allowed_schemes_(kSign_RSASSA_PSS),
|
||||
usage_entry_(NULL) {}
|
||||
~SessionContext();
|
||||
|
||||
bool isValid() { return valid_; }
|
||||
@@ -139,17 +136,12 @@ class SessionContext {
|
||||
size_t signature_length);
|
||||
void StartTimer();
|
||||
uint32_t CurrentTimer(); // (seconds).
|
||||
OEMCryptoResult LoadKeys(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
OEMCryptoResult LoadKeys(const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
const uint8_t* enc_mac_key_iv,
|
||||
const uint8_t* enc_mac_keys,
|
||||
size_t num_keys,
|
||||
const uint8_t* enc_mac_keys, size_t num_keys,
|
||||
const OEMCrypto_KeyObject* key_array,
|
||||
const uint8_t* pst,
|
||||
size_t pst_length);
|
||||
const uint8_t* pst, size_t pst_length);
|
||||
bool InstallKey(const KeyId& key_id,
|
||||
const std::vector<uint8_t>& key_data,
|
||||
const std::vector<uint8_t>& key_data_iv,
|
||||
@@ -195,17 +187,19 @@ class SessionContext {
|
||||
void AddNonce(uint32_t nonce);
|
||||
bool CheckNonce(uint32_t nonce);
|
||||
void FlushNonces();
|
||||
void ReleaseUsageEntry();
|
||||
|
||||
private:
|
||||
|
||||
bool DeriveKey(const std::vector<uint8_t>& key, const std::vector<uint8_t>& context,
|
||||
bool DeriveKey(const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& context,
|
||||
int counter, std::vector<uint8_t>* out);
|
||||
bool DecryptMessage(const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<uint8_t>& message,
|
||||
std::vector<uint8_t>* decrypted);
|
||||
bool CheckNonceOrPST(KeyControlBlock& key_control_block,
|
||||
bool CheckNonceOrEntry(const KeyControlBlock& key_control_block,
|
||||
const std::vector<uint8_t>& pst);
|
||||
bool IsUsageEntryValid();
|
||||
|
||||
bool valid_;
|
||||
CryptoEngine* ce_;
|
||||
@@ -220,6 +214,7 @@ class SessionContext {
|
||||
RSA* rsa_key_;
|
||||
uint32_t allowed_schemes_; // for RSA signatures.
|
||||
time_t timer_start_;
|
||||
UsageTableEntry* usage_entry_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext);
|
||||
};
|
||||
@@ -227,7 +222,6 @@ class SessionContext {
|
||||
class CryptoEngine {
|
||||
|
||||
private:
|
||||
|
||||
enum CryptoEngineState {
|
||||
CE_ILLEGAL,
|
||||
CE_INITIALIZED,
|
||||
@@ -237,7 +231,6 @@ class CryptoEngine {
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
CryptoEngine();
|
||||
~CryptoEngine();
|
||||
|
||||
@@ -268,10 +261,10 @@ class CryptoEngine {
|
||||
return maximum_hdcp_capability_;
|
||||
}
|
||||
|
||||
UsageTable* usage_table() { return usage_table_; }
|
||||
bool local_display() { return local_display_; }
|
||||
|
||||
private:
|
||||
|
||||
bool valid_;
|
||||
CryptoEngineState ce_state_;
|
||||
SessionContext* current_session_;
|
||||
@@ -281,9 +274,11 @@ class CryptoEngine {
|
||||
OEMCrypto_HDCP_Capability current_hdcp_capability_;
|
||||
OEMCrypto_HDCP_Capability maximum_hdcp_capability_;
|
||||
bool local_display_;
|
||||
UsageTable* usage_table_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine);
|
||||
};
|
||||
|
||||
}; // namespace wvoec_eng
|
||||
#endif
|
||||
|
||||
#endif // WVOEC_MOCK_OEMCRYPTO_ENGINE_MOCK_H_
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#include "oemcrypto_key_mock.h"
|
||||
|
||||
#include <cstring>
|
||||
@@ -31,7 +27,8 @@ bool KeyControlBlock::Validate() {
|
||||
|
||||
// This extracts 4 bytes in network byte order to a 32 bit integer in
|
||||
// host byte order.
|
||||
uint32_t KeyControlBlock::ExtractField(const std::vector<uint8_t>& str, int idx) {
|
||||
uint32_t KeyControlBlock::ExtractField(const std::vector<uint8_t>& str,
|
||||
int idx) {
|
||||
int bidx = idx * 4;
|
||||
uint32_t t = static_cast<unsigned char>(str[bidx]) << 24;
|
||||
t |= static_cast<unsigned char>(str[bidx + 1]) << 16;
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#ifndef OEMCRYPTO_KEY_MOCK_H_
|
||||
#define OEMCRYPTO_KEY_MOCK_H_
|
||||
|
||||
@@ -51,7 +47,6 @@ class KeyControlBlock {
|
||||
uint32_t control_bits() const { return control_bits_; }
|
||||
|
||||
private:
|
||||
|
||||
uint32_t ExtractField(const std::vector<uint8_t>& str, int idx);
|
||||
|
||||
bool valid_;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#include "oemcrypto_keybox_mock.h"
|
||||
#include <arpa/inet.h> // TODO(fredgc): Add ntoh to wv_cdm_utilities.h
|
||||
|
||||
#include <arpa/inet.h> // needed for ntoh()
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <sys/types.h>
|
||||
@@ -79,7 +76,7 @@ KeyboxError WvKeybox::Validate() {
|
||||
memcpy(keybox.magic_, magic_, sizeof(keybox.magic_));
|
||||
|
||||
crc_computed = ntohl(wvcrc32(reinterpret_cast<uint8_t*>(&keybox),
|
||||
sizeof(keybox) - 4)); // Don't include last 4 bytes.
|
||||
sizeof(keybox) - 4)); // Drop last 4 bytes.
|
||||
if (crc_computed != *crc_stored) {
|
||||
LOGE("[KEYBOX CRC problem: computed = %08x, stored = %08x]\n",
|
||||
crc_computed, *crc_stored);
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef OEMCRYPTO_KEYBOX_MOCK_H_
|
||||
#define OEMCRYPTO_KEYBOX_MOCK_H_
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#ifndef WVOEC_MOCK_OEMCRYPTO_KEYBOX_MOCK_H_
|
||||
#define WVOEC_MOCK_OEMCRYPTO_KEYBOX_MOCK_H_
|
||||
|
||||
#include "oemcrypto_key_mock.h"
|
||||
|
||||
@@ -35,7 +31,6 @@ class WvKeybox {
|
||||
bool InstallKeybox(const uint8_t* keybox, size_t keyBoxLength);
|
||||
|
||||
private:
|
||||
|
||||
bool Prepare();
|
||||
|
||||
bool valid_;
|
||||
@@ -47,4 +42,5 @@ class WvKeybox {
|
||||
};
|
||||
|
||||
}; // namespace wvoec_eng
|
||||
#endif
|
||||
|
||||
#endif // WVOEC_MOCK_OEMCRYPTO_KEYBOX_MOCK_H_
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Reference implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Reference implementation of OEMCrypto APIs
|
||||
//
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#include <iostream>
|
||||
@@ -15,6 +11,7 @@
|
||||
#include <string>
|
||||
#include "log.h"
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
#include "oemcrypto_usage_table_mock.h"
|
||||
#include "openssl/cmac.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/hmac.h"
|
||||
@@ -179,8 +176,10 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
|
||||
uint32_t enc_key_context_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GenerateDerivedKeys(\n");
|
||||
dump_hex("mac_key_context", mac_key_context, (size_t)mac_key_context_length);
|
||||
dump_hex("enc_key_context", enc_key_context, (size_t)enc_key_context_length);
|
||||
dump_hex("mac_key_context", mac_key_context,
|
||||
(size_t)mac_key_context_length);
|
||||
dump_hex("enc_key_context", enc_key_context,
|
||||
(size_t)enc_key_context_length);
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]");
|
||||
@@ -193,10 +192,10 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> mac_ctx_str(mac_key_context,
|
||||
mac_key_context + mac_key_context_length);
|
||||
const std::vector<uint8_t> enc_ctx_str(enc_key_context,
|
||||
enc_key_context + enc_key_context_length);
|
||||
const std::vector<uint8_t>
|
||||
mac_ctx_str(mac_key_context, mac_key_context + mac_key_context_length);
|
||||
const std::vector<uint8_t>
|
||||
enc_ctx_str(enc_key_context, enc_key_context + enc_key_context_length);
|
||||
|
||||
// Generate mac and encryption keys for current session context
|
||||
if (!session_ctx->DeriveKeys(crypto_engine->keybox().device_key(),
|
||||
@@ -291,7 +290,8 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
dump_hex("enc_mac_keys", enc_mac_keys, 2*wvcdm::MAC_KEY_SIZE);
|
||||
dump_hex("pst", pst, pst_length);
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
printf("key_array[%zu].key_id_length=%zu;\n", i, key_array[i].key_id_length);
|
||||
printf("key_array[%zu].key_id_length=%zu;\n", i,
|
||||
key_array[i].key_id_length);
|
||||
dump_array_part("key_array", i, "key_id",
|
||||
key_array[i].key_id, key_array[i].key_id_length);
|
||||
dump_array_part("key_array", i, "key_data_iv",
|
||||
@@ -326,8 +326,9 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
// Range check
|
||||
if (!RangeCheck(message, message_length, enc_mac_keys,
|
||||
2 * wvcdm::MAC_KEY_SIZE, true) ||
|
||||
!RangeCheck(message, message_length, enc_mac_key_iv,
|
||||
wvcdm::KEY_IV_SIZE, true)) {
|
||||
!RangeCheck(message, message_length, enc_mac_key_iv, wvcdm::KEY_IV_SIZE,
|
||||
true) ||
|
||||
!RangeCheck(message, message_length, pst, pst_length, true)) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]");
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
@@ -348,9 +349,9 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
}
|
||||
}
|
||||
|
||||
return session_ctx->LoadKeys(
|
||||
session, message, message_length, signature, signature_length,
|
||||
enc_mac_key_iv, enc_mac_keys, num_keys, key_array, pst, pst_length);
|
||||
return session_ctx->LoadKeys(message, message_length, signature,
|
||||
signature_length, enc_mac_key_iv, enc_mac_keys,
|
||||
num_keys, key_array, pst, pst_length);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@@ -362,7 +363,8 @@ OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
|
||||
size_t num_keys,
|
||||
const OEMCrypto_KeyRefreshObject* key_array) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%zu)\n", num_keys);
|
||||
printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%zu)\n",
|
||||
num_keys);
|
||||
}
|
||||
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
@@ -464,7 +466,8 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> key_id_str = std::vector<uint8_t>(key_id, key_id + key_id_length);
|
||||
const std::vector<uint8_t> key_id_str =
|
||||
std::vector<uint8_t>(key_id, key_id + key_id_length);
|
||||
if (!session_ctx->SelectContentKey(key_id_str)) {
|
||||
LOGE("[OEMCrypto_SelectKey(): FAIL]");
|
||||
return OEMCrypto_ERROR_NO_CONTENT_KEY;
|
||||
@@ -692,7 +695,8 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
}
|
||||
|
||||
// Range check
|
||||
if (!RangeCheck(message, message_length, reinterpret_cast<const uint8_t*>(nonce),
|
||||
if (!RangeCheck(message, message_length,
|
||||
reinterpret_cast<const uint8_t*>(nonce),
|
||||
sizeof(uint32_t), true) ||
|
||||
!RangeCheck(message, message_length, enc_rsa_key, enc_rsa_key_length,
|
||||
true) ||
|
||||
@@ -702,7 +706,6 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// Validate nonce
|
||||
if (!session_ctx->CheckNonce(*nonce)) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
@@ -745,8 +748,8 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
const std::vector<uint8_t> context(wrapped->context,
|
||||
wrapped->context + sizeof(wrapped->context));
|
||||
const std::vector<uint8_t>
|
||||
context(wrapped->context, wrapped->context + sizeof(wrapped->context));
|
||||
// Generate mac and encryption keys for encrypting the signature.
|
||||
if (result == OEMCrypto_SUCCESS) {
|
||||
if (!session_ctx->DeriveKeys(crypto_engine->keybox().device_key(), context,
|
||||
@@ -811,8 +814,8 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
const std::vector<uint8_t> context(wrapped->context,
|
||||
wrapped->context + sizeof(wrapped->context));
|
||||
const std::vector<uint8_t>
|
||||
context(wrapped->context, wrapped->context + sizeof(wrapped->context));
|
||||
// Generate mac and encryption keys for encrypting the signature.
|
||||
if (!session_ctx->DeriveKeys(crypto_engine->keybox().device_key(), context,
|
||||
context)) {
|
||||
@@ -837,11 +840,10 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
size_t rsa_key_length = enc_rsa_key_length - padding;
|
||||
// verify signature.
|
||||
if (result == OEMCrypto_SUCCESS) {
|
||||
if (!session_ctx->LoadRSAKey(pkcs8_rsa_key, rsa_key_length,
|
||||
wrapped->context,
|
||||
if (!session_ctx->LoadRSAKey(
|
||||
pkcs8_rsa_key, rsa_key_length, wrapped->context,
|
||||
wrapped_rsa_key_length - sizeof(wrapped->signature),
|
||||
wrapped->signature,
|
||||
sizeof(wrapped->signature))) {
|
||||
wrapped->signature, sizeof(wrapped->signature))) {
|
||||
result = OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
// return OEMCrypto_ERROR_INVALID_RSA_KEY;
|
||||
}
|
||||
@@ -914,8 +916,10 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n");
|
||||
dump_hex("enc_session_key", enc_session_key, enc_session_key_length);
|
||||
dump_hex("mac_key_context", mac_key_context, (size_t)mac_key_context_length);
|
||||
dump_hex("enc_key_context", enc_key_context, (size_t)enc_key_context_length);
|
||||
dump_hex("mac_key_context", mac_key_context,
|
||||
(size_t)mac_key_context_length);
|
||||
dump_hex("enc_key_context", enc_key_context,
|
||||
(size_t)enc_key_context_length);
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]");
|
||||
@@ -933,12 +937,12 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
return OEMCrypto_ERROR_INVALID_RSA_KEY;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> ssn_key_str(enc_session_key,
|
||||
enc_session_key + enc_session_key_length);
|
||||
const std::vector<uint8_t> mac_ctx_str(mac_key_context,
|
||||
mac_key_context + mac_key_context_length);
|
||||
const std::vector<uint8_t> enc_ctx_str(enc_key_context,
|
||||
enc_key_context + enc_key_context_length);
|
||||
const std::vector<uint8_t>
|
||||
ssn_key_str(enc_session_key, enc_session_key + enc_session_key_length);
|
||||
const std::vector<uint8_t>
|
||||
mac_ctx_str(mac_key_context, mac_key_context + mac_key_context_length);
|
||||
const std::vector<uint8_t>
|
||||
enc_ctx_str(enc_key_context, enc_key_context + enc_key_context_length);
|
||||
|
||||
// Generate mac and encryption keys for current session context
|
||||
if (!session_ctx->RSADeriveKeys(ssn_key_str, mac_ctx_str, enc_ctx_str)) {
|
||||
@@ -1125,17 +1129,28 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session,
|
||||
|
||||
extern "C"
|
||||
bool OEMCrypto_SupportsUsageTable() {
|
||||
return false;
|
||||
if (trace_all_calls) {
|
||||
printf("-- bool OEMCrypto_SupportsUsageTable(); // returns true.\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_UpdateUsageTable() {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_UpdateUsageTable();\n");
|
||||
}
|
||||
return crypto_engine->usage_table()->UpdateTable();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t *pst,
|
||||
size_t pst_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DeactivateUsageEntry(\n");
|
||||
dump_hex("pst", pst, pst_length);
|
||||
}
|
||||
std::vector<uint8_t> pstv(pst, pst + pst_length);
|
||||
return crypto_engine->usage_table()->DeactivateEntry(pstv);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@@ -1144,7 +1159,29 @@ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
|
||||
size_t pst_length,
|
||||
OEMCrypto_PST_Report *buffer,
|
||||
size_t *buffer_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_ReportUsage(\n");
|
||||
dump_hex("pst", pst, pst_length);
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_ReportUsage(): ERROR_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
std::vector<uint8_t> pstv(pst, pst + pst_length);
|
||||
UsageTableEntry* entry = crypto_engine->usage_table()->FindEntry(pstv);
|
||||
if (!entry) {
|
||||
LOGE("[OEMCrypto_ReportUsage(): Usage Table Entry not found]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
OEMCryptoResult sts =
|
||||
entry->ReportUsage(session_ctx, pstv, buffer, buffer_length);
|
||||
crypto_engine->usage_table()->UpdateTable();
|
||||
if (trace_all_calls) {
|
||||
dump_hex("usage buffer", reinterpret_cast<uint8_t*>(buffer),
|
||||
*buffer_length);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@@ -1155,12 +1192,48 @@ OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session,
|
||||
size_t message_length,
|
||||
const uint8_t *signature,
|
||||
size_t signature_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DeleteUsageEntry(\n");
|
||||
dump_hex("pst", pst, pst_length);
|
||||
dump_hex("message", message, message_length);
|
||||
dump_hex("signature", signature, signature_length);
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_DeleteUsageEntry(): ERROR_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
if (message == NULL || message_length == 0 || signature == NULL ||
|
||||
signature_length == 0 || pst == NULL || pst_length == 0) {
|
||||
LOGE("[OEMCrypto_DeleteUsageEntry(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!RangeCheck(message, message_length, pst, pst_length, false)) {
|
||||
LOGE("[OEMCrypto_DeleteUsageEntry(): range check.]");
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
// Validate message signature
|
||||
if (!session_ctx->ValidateMessage(message, message_length, signature,
|
||||
signature_length)) {
|
||||
LOGE("[OEMCrypto_DeleteUsageEntry(): OEMCrypto_ERROR_SIGNATURE_FAILURE.]");
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
std::vector<uint8_t> pstv(pst, pst + pst_length);
|
||||
if (crypto_engine->usage_table()->DeleteEntry(pstv)) {
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_DeleteUsageTable() {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DeleteUsageTable()\n");
|
||||
}
|
||||
if (crypto_engine->usage_table()->Clear()) {
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
}; // namespace wvoec_mock
|
||||
|
||||
415
libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp
Normal file
415
libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp
Normal file
@@ -0,0 +1,415 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#include "oemcrypto_usage_table_mock.h"
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "clock.h"
|
||||
#include "log.h"
|
||||
#include "file_store.h"
|
||||
#include "properties.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/rand.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "openssl/hmac.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
wvcdm::Clock UsageTableEntry::clock_;
|
||||
|
||||
UsageTableEntry::UsageTableEntry(const std::vector<uint8_t> &pst_hash,
|
||||
SessionContext *ctx)
|
||||
: pst_hash_(pst_hash),
|
||||
time_of_license_received_(clock_.GetCurrentTime()),
|
||||
time_of_first_decrypt_(0),
|
||||
time_of_last_decrypt_(0),
|
||||
status_(kUnused),
|
||||
session_(ctx) {}
|
||||
|
||||
UsageTableEntry::~UsageTableEntry() {
|
||||
if (session_) session_->ReleaseUsageEntry();
|
||||
}
|
||||
|
||||
UsageTableEntry::UsageTableEntry(const StoredUsageEntry *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);
|
||||
}
|
||||
|
||||
void UsageTableEntry::SaveToBuffer(StoredUsageEntry *buffer) {
|
||||
if (pst_hash_.size() != sizeof(buffer->pst_hash)) {
|
||||
LOGE("Coding Error: pst hash has wrong size.");
|
||||
return;
|
||||
}
|
||||
memcpy(buffer->pst_hash, &pst_hash_[0], pst_hash_.size());
|
||||
buffer->time_of_license_received = time_of_license_received_;
|
||||
buffer->time_of_first_decrypt = time_of_first_decrypt_;
|
||||
buffer->time_of_last_decrypt = time_of_last_decrypt_;
|
||||
buffer->status = status_;
|
||||
memcpy(buffer->mac_key_server, &mac_key_server_[0], wvcdm::MAC_KEY_SIZE);
|
||||
memcpy(buffer->mac_key_client, &mac_key_client_[0], wvcdm::MAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
void UsageTableEntry::Deactivate() {
|
||||
status_ = kInactive;
|
||||
if (session_) {
|
||||
session_->ReleaseUsageEntry();
|
||||
session_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool UsageTableEntry::UpdateTime() {
|
||||
int64_t now = clock_.GetCurrentTime();
|
||||
switch (status_) {
|
||||
case kUnused:
|
||||
status_ = kActive;
|
||||
time_of_first_decrypt_ = now;
|
||||
time_of_last_decrypt_ = now;
|
||||
return true;
|
||||
case kActive:
|
||||
time_of_last_decrypt_ = now;
|
||||
return true;
|
||||
case kInactive:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
OEMCryptoResult UsageTableEntry::ReportUsage(SessionContext *session,
|
||||
const std::vector<uint8_t> &pst,
|
||||
OEMCrypto_PST_Report *buffer,
|
||||
size_t *buffer_length) {
|
||||
size_t length_needed = sizeof(OEMCrypto_PST_Report) + pst.size();
|
||||
if (*buffer_length < length_needed) {
|
||||
*buffer_length = length_needed;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
if (!buffer) {
|
||||
LOGE("ReportUsage: buffer was null pointer.");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
int64_t now = clock_.GetCurrentTime();
|
||||
buffer->seconds_since_license_received =
|
||||
wvcdm::htonll64(now - time_of_license_received_);
|
||||
buffer->seconds_since_first_decrypt =
|
||||
wvcdm::htonll64(now - time_of_first_decrypt_);
|
||||
buffer->seconds_since_last_decrypt =
|
||||
wvcdm::htonll64(now - time_of_last_decrypt_);
|
||||
buffer->status = status_;
|
||||
buffer->clock_security_level = kSecureTimer;
|
||||
buffer->pst_length = static_cast<uint8_t>(pst.size());
|
||||
memcpy(buffer->pst, &pst[0], length_needed - sizeof(OEMCrypto_PST_Report));
|
||||
unsigned int md_len = sizeof(buffer->signature);
|
||||
if (!HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(),
|
||||
reinterpret_cast<uint8_t *>(buffer) + SHA_DIGEST_LENGTH,
|
||||
length_needed - SHA_DIGEST_LENGTH, buffer->signature, &md_len)) {
|
||||
LOGE("UsageTableEntry: could not compute signature.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
session->set_mac_key_server(mac_key_server_);
|
||||
session->set_mac_key_client(mac_key_client_);
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
bool UsageTableEntry::VerifyOrSetMacKeys(const std::vector<uint8_t> &server,
|
||||
const std::vector<uint8_t> &client) {
|
||||
if (mac_key_server_.size() == 0) { // Not set yet, so set it now.
|
||||
mac_key_server_ = server;
|
||||
mac_key_client_ = client;
|
||||
return true;
|
||||
} else {
|
||||
return (mac_key_server_ == server && mac_key_client_ == client);
|
||||
}
|
||||
}
|
||||
|
||||
UsageTable::UsageTable(CryptoEngine *ce) {
|
||||
ce_ = ce;
|
||||
generation_ = 0;
|
||||
table_.clear();
|
||||
|
||||
// Load saved table.
|
||||
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("UsageTable: Unable to get base path");
|
||||
return;
|
||||
}
|
||||
if (!file.IsDirectory(path)) {
|
||||
if (!file.CreateDirectory(path)) {
|
||||
LOGE("UsageTable: could not create directory: %s", path.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string filename = path + "UsageTable.dat";
|
||||
if (!file.Exists(filename)) {
|
||||
LOGI("UsageTable: No saved usage table. Creating new table.");
|
||||
return;
|
||||
}
|
||||
size_t file_size = file.FileSize(filename);
|
||||
uint8_t encrypted_buffer[file_size];
|
||||
uint8_t buffer[file_size];
|
||||
StoredUsageTable *stored_table = reinterpret_cast<StoredUsageTable *>(buffer);
|
||||
StoredUsageTable *encrypted_table =
|
||||
reinterpret_cast<StoredUsageTable *>(encrypted_buffer);
|
||||
|
||||
if (!file.Open(filename, wvcdm::File::kReadOnly | wvcdm::File::kBinary)) {
|
||||
LOGE("UsageTable: File open failed: %s", path.c_str());
|
||||
return;
|
||||
}
|
||||
file.Read(reinterpret_cast<char *>(encrypted_buffer), file_size);
|
||||
file.Close();
|
||||
|
||||
// First, verify the signature of the usage table file.
|
||||
std::vector<uint8_t> &key = ce_->keybox().device_key();
|
||||
unsigned int sig_length = sizeof(stored_table->signature);
|
||||
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
|
||||
if (!HMAC(EVP_sha256(), &key[0], key.size(),
|
||||
encrypted_buffer + SHA256_DIGEST_LENGTH,
|
||||
file_size - SHA256_DIGEST_LENGTH, computed_signature,
|
||||
&sig_length)) {
|
||||
LOGE("UsageTable: Could not recreate signature.");
|
||||
table_.clear();
|
||||
return;
|
||||
}
|
||||
if (memcmp(encrypted_table->signature, computed_signature, sig_length)) {
|
||||
LOGE("UsageTable: Invalid signature given: %s",
|
||||
wvcdm::HexEncode(encrypted_buffer, sig_length).c_str());
|
||||
LOGE("UsageTable: Invalid signature computed: %s",
|
||||
wvcdm::HexEncode(computed_signature, sig_length).c_str());
|
||||
table_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Next, decrypt the table.
|
||||
memset(buffer, 0, file_size);
|
||||
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";
|
||||
if (!file.Exists(filename2) ||
|
||||
!file.Open(filename2, wvcdm::File::kReadOnly | wvcdm::File::kBinary)) {
|
||||
LOGE("UsageTable: 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) {
|
||||
LOGW("UsageTable: File is one generation old. Acceptable rollback.");
|
||||
} else if (stored_table->generation == generation_ - 1) {
|
||||
LOGW("UsageTable: File is one generation new. Acceptable rollback.");
|
||||
// This might happen if the generation number was rolled back?
|
||||
} else if (stored_table->generation != generation_) {
|
||||
LOGE("UsageTable: 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 (size_t i = 0; i < stored_table->count; i++) {
|
||||
UsageTableEntry *entry =
|
||||
new UsageTableEntry(&stored_table->entries[i].entry);
|
||||
table_[entry->pst_hash()] = entry;
|
||||
}
|
||||
LOGI("UsageTable: loaded %d entryies.", stored_table->count);
|
||||
}
|
||||
|
||||
bool UsageTable::SaveToFile() {
|
||||
// This is always called by a locking function.
|
||||
// Update the generation number so we can detect rollback.
|
||||
generation_++;
|
||||
// Now save data to the file as seen in the constructor, above.
|
||||
size_t file_size = sizeof(StoredUsageTable) +
|
||||
table_.size() * sizeof(AlignedStoredUsageEntry);
|
||||
uint8_t buffer[file_size];
|
||||
uint8_t encrypted_buffer[file_size];
|
||||
StoredUsageTable *stored_table = reinterpret_cast<StoredUsageTable *>(buffer);
|
||||
StoredUsageTable *encrypted_table =
|
||||
reinterpret_cast<StoredUsageTable *>(encrypted_buffer);
|
||||
memset(buffer, 0, file_size);
|
||||
stored_table->generation = generation_;
|
||||
stored_table->count = 0;
|
||||
for (EntryMap::iterator i = table_.begin(); i != table_.end(); ++i) {
|
||||
UsageTableEntry *entry = i->second;
|
||||
entry->SaveToBuffer(&stored_table->entries[stored_table->count].entry);
|
||||
stored_table->count++;
|
||||
}
|
||||
|
||||
// This should be encrypted and signed with a device specific key.
|
||||
// For the reference implementation, I'm just going to use the keybox key.
|
||||
std::vector<uint8_t> &key = ce_->keybox().device_key();
|
||||
|
||||
// Encrypt the table.
|
||||
RAND_bytes(encrypted_table->iv, wvcdm::KEY_IV_SIZE);
|
||||
uint8_t iv_buffer[wvcdm::KEY_IV_SIZE];
|
||||
memcpy(iv_buffer, encrypted_table->iv, wvcdm::KEY_IV_SIZE);
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(&key[0], 128, &aes_key);
|
||||
AES_cbc_encrypt(buffer + SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE,
|
||||
encrypted_buffer + SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE,
|
||||
file_size - SHA256_DIGEST_LENGTH - wvcdm::KEY_IV_SIZE,
|
||||
&aes_key, iv_buffer, AES_ENCRYPT);
|
||||
|
||||
// Sign the table.
|
||||
unsigned int sig_length = sizeof(stored_table->signature);
|
||||
if (!HMAC(EVP_sha256(), &key[0], key.size(),
|
||||
encrypted_buffer + SHA256_DIGEST_LENGTH,
|
||||
file_size - SHA256_DIGEST_LENGTH, encrypted_table->signature,
|
||||
&sig_length)) {
|
||||
LOGE("UsageTable: Could not sign table.");
|
||||
return false;
|
||||
}
|
||||
|
||||
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("UsageTable: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
if (!file.IsDirectory(path)) {
|
||||
if (!file.CreateDirectory(path)) {
|
||||
LOGE("UsageTable: could not create directory: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string filename = path + "UsageTable.dat";
|
||||
if (!file.Exists(filename)) {
|
||||
LOGI("UsageTable: No saved usage table. Creating new table.");
|
||||
}
|
||||
|
||||
if (!file.Open(filename, wvcdm::File::kCreate | wvcdm::File::kTruncate |
|
||||
wvcdm::File::kBinary)) {
|
||||
LOGE("UsageTable: Could not save usage table: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
file.Write(reinterpret_cast<char *>(encrypted_buffer), file_size);
|
||||
file.Close();
|
||||
|
||||
// On a real implementation, you should NOT put the generation number in
|
||||
// a file in user space. It should be stored in secure memory.
|
||||
std::string filename2 = path + "GenerationNumber.dat";
|
||||
if (!file.Open(filename2, wvcdm::File::kCreate | wvcdm::File::kTruncate |
|
||||
wvcdm::File::kBinary)) {
|
||||
LOGE("UsageTable: File open failed: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
file.Write(reinterpret_cast<char *>(&generation_), sizeof(int64_t));
|
||||
file.Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
UsageTableEntry *UsageTable::FindEntry(const std::vector<uint8_t> &pst) {
|
||||
wvcdm::AutoLock lock(lock_);
|
||||
return FindEntryLocked(pst);
|
||||
}
|
||||
|
||||
UsageTableEntry *UsageTable::FindEntryLocked(const std::vector<uint8_t> &pst) {
|
||||
std::vector<uint8_t> pst_hash;
|
||||
if (!ComputeHash(pst, pst_hash)) {
|
||||
LOGE("UsageTable: Could not compute hash of pst.");
|
||||
return NULL;
|
||||
}
|
||||
EntryMap::iterator it = table_.find(pst_hash);
|
||||
if (it == table_.end()) {
|
||||
return NULL;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
UsageTableEntry *UsageTable::CreateEntry(const std::vector<uint8_t> &pst,
|
||||
SessionContext *ctx) {
|
||||
std::vector<uint8_t> pst_hash;
|
||||
if (!ComputeHash(pst, pst_hash)) {
|
||||
LOGE("UsageTable: Could not compute hash of pst.");
|
||||
return NULL;
|
||||
}
|
||||
UsageTableEntry *entry = new UsageTableEntry(pst_hash, ctx);
|
||||
wvcdm::AutoLock lock(lock_);
|
||||
table_[pst_hash] = entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
OEMCryptoResult UsageTable::UpdateTable() {
|
||||
wvcdm::AutoLock lock(lock_);
|
||||
if (SaveToFile()) return OEMCrypto_SUCCESS;
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
OEMCryptoResult UsageTable::DeactivateEntry(const std::vector<uint8_t> &pst) {
|
||||
wvcdm::AutoLock lock(lock_);
|
||||
UsageTableEntry *entry = FindEntryLocked(pst);
|
||||
if (!entry) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
entry->Deactivate();
|
||||
if (SaveToFile()) return OEMCrypto_SUCCESS;
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
bool UsageTable::DeleteEntry(const std::vector<uint8_t> &pst) {
|
||||
std::vector<uint8_t> pst_hash;
|
||||
if (!ComputeHash(pst, pst_hash)) {
|
||||
LOGE("UsageTable: Could not compute hash of pst.");
|
||||
return false;
|
||||
}
|
||||
wvcdm::AutoLock lock(lock_);
|
||||
EntryMap::iterator it = table_.find(pst_hash);
|
||||
if (it == table_.end()) return false;
|
||||
if (it->second) delete it->second;
|
||||
table_.erase(it);
|
||||
return SaveToFile();
|
||||
}
|
||||
|
||||
bool UsageTable::Clear() {
|
||||
wvcdm::AutoLock lock(lock_);
|
||||
for (EntryMap::iterator i = table_.begin(); i != table_.end(); ++i) {
|
||||
if (i->second) delete i->second;
|
||||
}
|
||||
table_.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UsageTable::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
|
||||
106
libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.h
Normal file
106
libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.h
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#ifndef WVOEC_MOCK_OEMCRYPTO_USAGE_TABLE_MOCK_H_
|
||||
#define WVOEC_MOCK_OEMCRYPTO_USAGE_TABLE_MOCK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "clock.h"
|
||||
#include "lock.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
class SessionContext;
|
||||
class CryptoEngine;
|
||||
|
||||
struct StoredUsageEntry {
|
||||
// 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 StoredUsageEntry entry;
|
||||
uint8_t padding[128]; // multiple of block size and bigger than entry size.
|
||||
} AlignedStoredUsageEntry;
|
||||
|
||||
struct StoredUsageTable {
|
||||
uint8_t signature[SHA256_DIGEST_LENGTH];
|
||||
uint8_t iv[wvcdm::KEY_IV_SIZE];
|
||||
int64_t generation;
|
||||
size_t count;
|
||||
AlignedStoredUsageEntry entries[];
|
||||
};
|
||||
|
||||
class UsageTableEntry {
|
||||
public:
|
||||
UsageTableEntry(const std::vector<uint8_t> &pst_hash, SessionContext *ctx);
|
||||
UsageTableEntry(const StoredUsageEntry *buffer);
|
||||
~UsageTableEntry();
|
||||
void SaveToBuffer(StoredUsageEntry *buffer);
|
||||
OEMCrypto_Usage_Entry_Status status() const { return status_; }
|
||||
void Deactivate();
|
||||
bool UpdateTime();
|
||||
OEMCryptoResult ReportUsage(SessionContext *session,
|
||||
const std::vector<uint8_t> &pst,
|
||||
OEMCrypto_PST_Report *buffer,
|
||||
size_t *buffer_length);
|
||||
// Set them if not set, verify if already set.
|
||||
bool VerifyOrSetMacKeys(const std::vector<uint8_t> &server,
|
||||
const std::vector<uint8_t> &client);
|
||||
const std::vector<uint8_t> &pst_hash() const { return pst_hash_; }
|
||||
void set_session(SessionContext *session) { session_ = session; }
|
||||
|
||||
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_;
|
||||
|
||||
SessionContext *session_;
|
||||
static wvcdm::Clock clock_;
|
||||
};
|
||||
|
||||
class UsageTable {
|
||||
public:
|
||||
UsageTable(CryptoEngine *ce);
|
||||
~UsageTable() { Clear(); }
|
||||
UsageTableEntry *FindEntry(const std::vector<uint8_t> &pst);
|
||||
UsageTableEntry *CreateEntry(const std::vector<uint8_t> &pst,
|
||||
SessionContext *ctx);
|
||||
OEMCryptoResult UpdateTable();
|
||||
OEMCryptoResult DeactivateEntry(const std::vector<uint8_t> &pst);
|
||||
bool DeleteEntry(const std::vector<uint8_t> &pst);
|
||||
bool Clear();
|
||||
|
||||
private:
|
||||
UsageTableEntry *FindEntryLocked(const std::vector<uint8_t> &pst);
|
||||
bool SaveToFile();
|
||||
bool ComputeHash(const std::vector<uint8_t> &pst,
|
||||
std::vector<uint8_t> &pst_hash);
|
||||
|
||||
typedef std::map<std::vector<uint8_t>, UsageTableEntry *> EntryMap;
|
||||
EntryMap table_;
|
||||
wvcdm::Lock lock_;
|
||||
int64_t generation_;
|
||||
CryptoEngine *ce_;
|
||||
};
|
||||
|
||||
}; // namespace wvoec_mock
|
||||
|
||||
#endif // WVOEC_MOCK_OEMCRYPTO_USAGE_TABLE_MOCK_H_
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WV_KEYBOX_H_
|
||||
#define WV_KEYBOX_H_
|
||||
#ifndef WVOEC_MOCK_WV_KEYBOX_H_
|
||||
#define WVOEC_MOCK_WV_KEYBOX_H_
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
@@ -21,4 +21,4 @@ typedef struct { // 128 bytes total.
|
||||
|
||||
}
|
||||
|
||||
#endif // WV_KEYBOX_H_
|
||||
#endif // WVOEC_MOCK_WV_KEYBOX_H_
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/*********************************************************************
|
||||
* wvcrc32.cpp
|
||||
*
|
||||
* (c) Copyright 2011-2012 Google, Inc.
|
||||
*
|
||||
* Compte CRC32 Checksum. Needed for verification of WV Keybox.
|
||||
*********************************************************************/
|
||||
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Compute CRC32 Checksum. Needed for verification of WV Keybox.
|
||||
//
|
||||
#include <arpa/inet.h>
|
||||
#include "wvcrc32.h"
|
||||
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
/*********************************************************************
|
||||
* wvcrc32.h
|
||||
*
|
||||
* (c) Copyright 2011-2012 Google, Inc.
|
||||
*
|
||||
* Compte CRC32 Checksum. Needed for verification of WV Keybox.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef WV_CRC_32_H_
|
||||
#define WV_CRC_32_H_
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Compute CRC32 Checksum. Needed for verification of WV Keybox.
|
||||
//
|
||||
#ifndef WVOEC_MOCK_WVCRC32_H_
|
||||
#define WVOEC_MOCK_WVCRC32_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t wvcrc32(const uint8_t* p_begin, int i_count);
|
||||
uint32_t wvcrc32n(const uint8_t* p_begin, int i_count); // Convert to network byte
|
||||
// order.
|
||||
|
||||
#endif // WV_CRC_32_H_
|
||||
// Convert to network byte order
|
||||
uint32_t wvcrc32n(const uint8_t* p_begin, int i_count);
|
||||
|
||||
#endif // WVOEC_MOCK_WVCRC32_H_
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user