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:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(¤t_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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user