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:
@@ -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,13 +344,38 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::CheckNonceOrPST(KeyControlBlock& key_control_block,
|
||||
const std::vector<uint8_t>& pst) {
|
||||
// TODO(fredgc): look at replay control bits, too.
|
||||
if ((key_control_block.control_bits() & kControlNonceEnabled) &&
|
||||
(!CheckNonce(key_control_block.nonce()))) {
|
||||
LOGE("KCB: BAD Nonce");
|
||||
return false;
|
||||
bool SessionContext::CheckNonceOrEntry(const KeyControlBlock& key_control_block,
|
||||
const std::vector<uint8_t>& pst) {
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user