OEMCrypto Mock SRM functionality

Merge from Widevine repo of http://go/wvgerrit/24730

This CL adds SRM functionality to the modable version of oemcrypto
mock.  This can be used for end-to-end testing.

b/28955873
b/37353534

Change-Id: I2c6f513495ccfd42f7a3d7a3449db6f810563c04
This commit is contained in:
Fred Gylys-Colwell
2017-04-14 13:51:57 -07:00
parent 5c768c896e
commit 426e53bbbf
9 changed files with 191 additions and 15 deletions

View File

@@ -13,6 +13,7 @@ static const size_t KEY_PAD_SIZE = 16;
static const size_t KEY_SIZE = 16;
static const size_t MAC_KEY_SIZE = 32;
static const size_t KEYBOX_KEY_DATA_SIZE = 72;
static const size_t SRM_REQUIREMENT_SIZE = 12;
// Initial estimate of certificate size. Code that
// uses this estimate should be able to adapt to a larger or smaller size.

View File

@@ -35,7 +35,8 @@
// srm_load_version: If this is set, then it will be used as the
// new srm version after loading an SRM -- ignoring the contents of the SRM.
// srm_blacklisted_device_attached: If set to "1", then a
// oemcrypto will act as if a blacklisted device is attached.
// oemcrypto will act as if a blacklisted device is attached -- i.e.
// playback will be restricted to the local display only.
// security_patch_level: This is the value returned by
// OEMCrypto_Security_Patch_Level. If the key control block requires a
// higher level, then OEMCrypto_LoadKeys will fail.
@@ -336,6 +337,91 @@ class AndroidModifiableCryptoEngine : public CryptoEngine {
return max; // If 0, no restriction. If something else, use that restriction.
}
bool srm_update_supported() {
int supported = GetOption("srm_update_supported", 0);
LOGI("OEMCrypto mock %s supporting SRM update.",
supported ? "is" : "is not");
return supported != 0;
}
OEMCryptoResult current_srm_version(uint16_t *version) {
if (srm_loaded_) {
*version = srm_version_;
return OEMCrypto_SUCCESS;
}
int value = GetOption("srm_initial_version", -1);
if (value > 0) {
*version = srm_version_;
return OEMCrypto_SUCCESS;
} else {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
}
OEMCryptoResult load_srm(const uint8_t *buffer, size_t buffer_length) {
if (!srm_update_supported()) {
LOGE("OEMCrypto mock update not supported, but load_srm called.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
int result = GetOption("srm_load_fail", 0);
if (result > 0) {
LOGE("OEMCrypto mock load_srm returning error %d.", result);
return static_cast<OEMCryptoResult>(result);
}
int new_version = GetOption("srm_load_version", -1);
if (new_version >= 0) {
srm_version_ = new_version;
LOGI("OEMCrypto mock told to change SRM version to %d.", srm_version_);
srm_loaded_ = true;
return OEMCrypto_SUCCESS;
}
if (buffer_length < 4) {
LOGE("OEMCrypto mock bad buffer size: %d.", buffer_length);
return OEMCrypto_ERROR_SHORT_BUFFER;
}
uint8_t srm_id = buffer[0];
uint8_t first_nibble = srm_id >> 4;
uint8_t second_nibble = srm_id & 0x0F;
uint8_t reserved = buffer[1];
uint16_t version = htons(*reinterpret_cast<const uint16_t *>(&buffer[2]));
if (reserved)
LOGE("OEMCrypto mock. SRM's second byte nonzero: %02X.", reserved);
if (first_nibble == 8 && second_nibble == 0) {
LOGI("OEMCrypto mock loading HDCP1 SRM. version = %d.", version);
} else if (first_nibble == 9 && second_nibble == 1) {
LOGI("OEMCrypto mock loading HDCP2 SRM. version = %d.", version);
} else {
LOGE("OEMCrypto mock bad buffer start: %02X%02X%02X%02X...", buffer[0],
buffer[1], buffer[2], buffer[3]);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// Note: we ignore the signature. Use system property srm_load_fail to
// simulate a bad signature.
srm_loaded_ = true;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult remove_srm() {
if (!srm_update_supported()) {
LOGE("OEMCrypto mock update not supported, bug load_srm called.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
srm_version_ = 0;
srm_loaded_ = false;
return OEMCrypto_SUCCESS;
}
bool srm_blacklisted_device_attached() {
static int blacklisted = 0;
int new_value = GetOption("srm_blacklisted_device_attached", 0);
if (new_value != blacklisted) {
LOGI("SRM blacklisted device changed from %d to %d", blacklisted,
new_value);
blacklisted = new_value;
}
return blacklisted > 0;
}
virtual void adjust_destination(OEMCrypto_DestBufferDesc *out_description,
size_t data_length, uint8_t subsample_flags) {
if (out_description->type != OEMCrypto_BufferType_Secure) return;

View File

@@ -123,6 +123,23 @@ class CryptoEngine {
// If 0 no restriction, otherwise it's the max buffer for DecryptCENC.
virtual size_t max_buffer_size() { return 1024 * 100; } // 100 KiB.
virtual bool srm_update_supported() { return false; }
virtual OEMCryptoResult current_srm_version(uint16_t* version) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual OEMCryptoResult load_srm(const uint8_t* buffer,
size_t buffer_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual OEMCryptoResult remove_srm() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual bool srm_blacklisted_device_attached() { return false; }
// Set destination pointer based on the output destination description.
OEMCryptoResult SetDestination(OEMCrypto_DestBufferDesc* out_description,
size_t data_length, uint8_t subsample_flags);

View File

@@ -113,4 +113,9 @@ void Key::UpdateDuration(const KeyControlBlock& control) {
control_.set_duration(control.duration());
}
void KeyControlBlock::RequireLocalDisplay() {
// Set all bits to require HDCP Local Display Only.
control_bits_ |= kControlHDCPVersionMask;
}
} // namespace wvoec_mock

View File

@@ -52,6 +52,7 @@ class KeyControlBlock {
uint32_t nonce() const { return nonce_; }
const char* verification() const { return verification_; }
uint32_t control_bits() const { return control_bits_; }
void RequireLocalDisplay();
private:
uint32_t ExtractField(const std::vector<uint8_t>& str, int idx);

View File

@@ -297,6 +297,7 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
dump_hex("enc_mac_key_iv", enc_mac_key_iv, wvcdm::KEY_IV_SIZE);
dump_hex("enc_mac_keys", enc_mac_keys, 2 * wvcdm::MAC_KEY_SIZE);
dump_hex("pst", pst, pst_length);
dump_hex("srm_requirement", srm_requirement, wvcdm::KEY_CONTROL_SIZE);
for (size_t i = 0; i < num_keys; i++) {
LOGV("key_array[%zu].key_id_length=%zu;\n", i,
key_array[i].key_id_length);
@@ -346,7 +347,9 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
2 * wvcdm::MAC_KEY_SIZE, true) ||
!RangeCheck(message, message_length, enc_mac_key_iv, wvcdm::KEY_IV_SIZE,
true) ||
!RangeCheck(message, message_length, pst, pst_length, true)) {
!RangeCheck(message, message_length, pst, pst_length, true) ||
!RangeCheck(message, message_length, srm_requirement,
wvcdm::SRM_REQUIREMENT_SIZE, true)) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - range "
"check.]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
@@ -370,7 +373,8 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
}
return session_ctx->LoadKeys(message, message_length, signature,
signature_length, enc_mac_key_iv, enc_mac_keys,
num_keys, key_array, pst, pst_length);
num_keys, key_array, pst, pst_length,
srm_requirement);
}
extern "C" OEMCryptoResult OEMCrypto_RefreshKeys(
@@ -1625,19 +1629,40 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteOldUsageTable() {
return crypto_engine->usage_table().DeleteOldUsageTable();
}
extern "C" bool OEMCrypto_IsSRMUpdateSupported() { return false; }
extern "C" bool OEMCrypto_IsSRMUpdateSupported() {
if (!crypto_engine) {
LOGE("OEMCrypto_IsSRMUpdateSupported: OEMCrypto Not Initialized.");
return false;
}
return crypto_engine->srm_update_supported();
}
extern "C" OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (!crypto_engine) {
LOGE("OEMCrypto_GetCurrentSRMVersion: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (crypto_engine->config_local_display_only()) {
return OEMCrypto_LOCAL_DISPLAY_ONLY;
}
return crypto_engine->current_srm_version(version);
}
extern "C" OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer,
size_t buffer_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (!crypto_engine) {
LOGE("OEMCrypto_LoadSRM: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return crypto_engine->load_srm(buffer, buffer_length);
}
extern "C" OEMCryptoResult OEMCrypto_RemoveSRM() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (!crypto_engine) {
LOGE("OEMCrypto_RemoveSRM: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return crypto_engine->remove_srm();
}
extern "C" OEMCryptoResult OEMCrypto_CreateUsageTableHeader(

View File

@@ -391,7 +391,7 @@ OEMCryptoResult SessionContext::LoadKeys(
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) {
size_t pst_length, const uint8_t* srm_requirement) {
// Validate message signature
if (!ValidateMessage(message, message_length, signature, signature_length)) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
@@ -399,6 +399,31 @@ OEMCryptoResult SessionContext::LoadKeys(
StartTimer();
if (srm_requirement) {
const std::string kSRMVerificationString = "HDCPDATA";
if (memcmp(srm_requirement, kSRMVerificationString.c_str(),
kSRMVerificationString.size())) {
LOGE("SRM Requirement Data has bad verification string: %8s",
srm_requirement);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint32_t minimum_version =
htonl(*reinterpret_cast<const uint32_t*>(srm_requirement + 8));
uint16_t current_version = 0;
if (OEMCrypto_SUCCESS == ce_->current_srm_version(&current_version) &&
current_version >= minimum_version) {
srm_requirements_status_ = ValidSRMVersion;
if (ce_->srm_blacklisted_device_attached()) {
LOGW("[LoadKeys: SRM blacklisted device attached]");
srm_requirements_status_ = InvalidSRMVersion;
}
} else {
LOGW("[LoadKeys: SRM Version too small %d, required: %d",
current_version, minimum_version);
srm_requirements_status_ = InvalidSRMVersion;
}
}
// If there are already keys installed in this session, then we can load
// a shared license.
bool second_license = (session_keys_.size() > 0);
@@ -557,6 +582,16 @@ OEMCryptoResult SessionContext::InstallKey(
return OEMCrypto_ERROR_MISSING_MASTER;
}
}
if (key_control_block.control_bits() & kControlSRMVersionRequired) {
if (srm_requirements_status_ == NoSRMVersion) {
LOGE("[LoadKeys: control bit says SRM version required]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (srm_requirements_status_ == InvalidSRMVersion) {
// If the SRM version is too small, treat this key as local display only.
key_control_block.RequireLocalDisplay();
}
}
Key key(content_key, key_control_block, ctr_mode);
session_keys_.Insert(key_id, key);
@@ -733,11 +768,15 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string,
}
}
if (!ce_->config_local_display_only()) {
// Only look at HDCP and Analog restrictions if the display is non-local.
// Only look at HDCP and Analog restrictions if the display can be
// non-local.
if (control.control_bits() & kControlHDCPRequired) {
uint8_t required_hdcp =
(control.control_bits() & kControlHDCPVersionMask) >>
kControlHDCPVersionShift;
if (ce_->srm_blacklisted_device_attached()) {
required_hdcp = HDCP_NO_DIGITAL_OUTPUT;
}
// For reference implementation, we pretend we can handle the current
// HDCP version.
if (required_hdcp > ce_->config_current_hdcp_capability() ||
@@ -745,10 +784,6 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string,
return OEMCrypto_ERROR_INSUFFICIENT_HDCP;
}
}
if (control.control_bits() & kControlSRMVersionRequired) {
LOGE("[%s(): control bit says SRM version required.", log_string.c_str());
return OEMCrypto_ERROR_INSUFFICIENT_HDCP;
}
}
if (!ce_->config_local_display_only() ||
buffer_type == OEMCrypto_BufferType_Clear) {

View File

@@ -28,6 +28,8 @@ namespace wvoec_mock {
class CryptoEngine;
typedef uint32_t SessionId;
enum SRMVersionStatus { NoSRMVersion, ValidSRMVersion, InvalidSRMVersion };
class SessionContext {
private:
SessionContext() {}
@@ -41,6 +43,7 @@ class SessionContext {
rsa_key_(rsa_key),
allowed_schemes_(kSign_RSASSA_PSS),
usage_entry_(NULL),
srm_requirements_status_(NoSRMVersion),
usage_entry_status_(kNoUsageEntry) {}
~SessionContext();
@@ -91,7 +94,8 @@ class SessionContext {
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);
const uint8_t* pst, size_t pst_length,
const uint8_t* srm_requirement);
OEMCryptoResult InstallKey(const KeyId& key_id,
const std::vector<uint8_t>& key_data,
const std::vector<uint8_t>& key_data_iv,
@@ -195,7 +199,7 @@ class SessionContext {
uint32_t allowed_schemes_; // for RSA signatures.
time_t timer_start_;
UsageTableEntry* usage_entry_;
SRMVersionStatus srm_requirements_status_;
enum UsageEntryStatus {
kNoUsageEntry, // No entry loaded for this session.
kUsageEntryNew, // After entry was created.

View File

@@ -157,6 +157,8 @@ TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) {
if (current_result == OEMCrypto_SUCCESS) {
printf(" Current SRM Version: %d.\n", version);
EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_GetCurrentSRMVersion(NULL));
} else if (current_result == OEMCrypto_LOCAL_DISPLAY_ONLY) {
printf(" Current SRM Status: Local Display Only.\n");
} else {
EXPECT_EQ(OEMCrypto_ERROR_NOT_IMPLEMENTED, current_result);
}