diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 25ce8d3a..d7fada08 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -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. diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_device_properties_mod.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_device_properties_mod.cpp index 63f588ef..fe2be840 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_device_properties_mod.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_device_properties_mod.cpp @@ -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(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(&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; diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index 3452f2c0..4cc07964 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -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); diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp index 8bfafb96..18f0eeef 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp @@ -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 diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h index 925fa517..1e76543c 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h @@ -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& str, int idx); diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index 3f956950..794db8f5 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -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( diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp index 3a73836e..80a304fb 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp @@ -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(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) { diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h index b5af29ee..e5cc6b7d 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h @@ -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& key_data, const std::vector& 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. diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 34161992..73634539 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -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); }