diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index 04570fca..bce85556 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -697,6 +697,63 @@ bool SessionContext::LoadRSAKey(const uint8_t* pkcs8_rsa_key, return rsa_key_.LoadPkcs8RsaKey(pkcs8_rsa_key, rsa_key_length); } +OEMCryptoResult SessionContext::AllowKeyUse(const std::string& log_string, + uint32_t use_type, + OEMCryptoBufferType buffer_type) { + const KeyControlBlock& control = current_content_key()->control(); + if (use_type && (!(control.control_bits() & use_type))) { + LOGE("[%s(): control bit says not allowed.", log_string.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (control.control_bits() & kControlDataPathSecure) { + if (!ce_->config_closed_platform() && + buffer_type == OEMCrypto_BufferType_Clear) { + LOGE("[%s(): Secure key with insecure buffer]", log_string.c_str()); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + } + if (control.control_bits() & kControlReplayMask) { + if (!IsUsageEntryValid()) { + LOGE("[%s(): usage entry not valid]", log_string.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + if (control.duration() > 0) { + if (control.duration() < CurrentTimer()) { + LOGE("[%s(): key expired.", log_string.c_str()); + return OEMCrypto_ERROR_KEY_EXPIRED; + } + } + if (!ce_->config_local_display_only()) { + // Only look at HDCP and Analog restrictions if the display is non-local. + if (control.control_bits() & kControlHDCPRequired) { + uint8_t required_hdcp = + (control.control_bits() & kControlHDCPVersionMask) >> + kControlHDCPVersionShift; + // For reference implementation, we pretend we can handle the current + // HDCP version. + if (required_hdcp > ce_->config_current_hdcp_capability() || + ce_->config_current_hdcp_capability() == 0) { + 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) { + if (control.control_bits() & kControlDisableAnalogOutput) { + LOGE("[%s(): control bit says disable analog.", + log_string.c_str()); + return OEMCrypto_ERROR_ANALOG_OUTPUT; + } + } + return OEMCrypto_SUCCESS; +} + OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, size_t buffer_length, const uint8_t* iv, @@ -708,28 +765,14 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, return OEMCrypto_ERROR_NO_CONTENT_KEY; } const std::vector& key = current_content_key()->value(); - const KeyControlBlock& control = current_content_key()->control(); // Set the AES key. if (static_cast(key.size()) != AES_BLOCK_SIZE) { LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (!(control.control_bits() & kControlAllowEncrypt)) { - LOGE("[Generic_Encrypt(): control bit says not allowed."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (control.duration() > 0) { - if (control.duration() < CurrentTimer()) { - LOGE("[Generic_Encrypt(): key expired."); - return OEMCrypto_ERROR_KEY_EXPIRED; - } - } - if (control.control_bits() & kControlReplayMask) { - if (!IsUsageEntryValid()) { - LOGE("[Generic_Encrypt(): usage entry not valid]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } + OEMCryptoResult result = AllowKeyUse("Generic_Encrypt", kControlAllowEncrypt, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { LOGE("[Generic_Encrypt(): algorithm bad."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -762,34 +805,15 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, return OEMCrypto_ERROR_NO_CONTENT_KEY; } const std::vector& key = current_content_key()->value(); - const KeyControlBlock& control = current_content_key()->control(); // Set the AES key. if (static_cast(key.size()) != AES_BLOCK_SIZE) { LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (!(control.control_bits() & kControlAllowDecrypt)) { - LOGE("[Generic_Decrypt(): control bit says not allowed."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (control.control_bits() & kControlDataPathSecure) { - if (!ce_->config_closed_platform()) { - LOGE("[Generic_Decrypt(): control bit says secure path only."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } - if (control.duration() > 0) { - if (control.duration() < CurrentTimer()) { - LOGE("[Generic_Decrypt(): key expired."); - return OEMCrypto_ERROR_KEY_EXPIRED; - } - } - if (control.control_bits() & kControlReplayMask) { - if (!IsUsageEntryValid()) { - LOGE("[Generic_Decrypt(): usage entry not valid]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } + OEMCryptoResult result = AllowKeyUse("Generic_Decrypt", kControlAllowDecrypt, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; + if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { LOGE("[Generic_Decrypt(): bad algorithm."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -827,27 +851,13 @@ OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const std::vector& key = current_content_key()->value(); - const KeyControlBlock& control = current_content_key()->control(); if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { LOGE("[Generic_Sign(): CONTENT_KEY has wrong size; %d", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (!(control.control_bits() & kControlAllowSign)) { - LOGE("[Generic_Sign(): control bit says not allowed."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (control.duration() > 0) { - if (control.duration() < CurrentTimer()) { - LOGE("[Generic_Sign(): key expired."); - return OEMCrypto_ERROR_KEY_EXPIRED; - } - } - if (control.control_bits() & kControlReplayMask) { - if (!IsUsageEntryValid()) { - LOGE("[Generic_Sign(): usage entry not valid]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } + OEMCryptoResult result = AllowKeyUse("Generic_Sign", kControlAllowSign, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_HMAC_SHA256) { LOGE("[Generic_Sign(): bad algorithm."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -877,27 +887,13 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const std::vector& key = current_content_key()->value(); - const KeyControlBlock& control = current_content_key()->control(); if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %d", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (!(control.control_bits() & kControlAllowVerify)) { - LOGE("[Generic_Verify(): control bit says not allowed."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (control.duration() > 0) { - if (control.duration() < CurrentTimer()) { - LOGE("[Generic_Verify(): key expired."); - return OEMCrypto_ERROR_KEY_EXPIRED; - } - } - if (control.control_bits() & kControlReplayMask) { - if (!IsUsageEntryValid()) { - LOGE("[Generic_Verify(): usage entry not valid]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } + OEMCryptoResult result = AllowKeyUse("Generic_Verify", kControlAllowVerify, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_HMAC_SHA256) { LOGE("[Generic_Verify(): bad algorithm."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -1083,40 +1079,10 @@ OEMCryptoResult SessionContext::DecryptCENC( LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); return OEMCrypto_ERROR_DECRYPT_FAILED; } - const KeyControlBlock& control = current_content_key()->control(); - if (control.control_bits() & kControlDataPathSecure) { - if (!ce_->config_closed_platform() && - buffer_type == OEMCrypto_BufferType_Clear) { - LOGE("[DecryptCTR(): Secure key with insecure buffer]"); - return OEMCrypto_ERROR_DECRYPT_FAILED; - } - } - if (control.duration() > 0) { - if (control.duration() < CurrentTimer()) { - LOGE("[DecryptCTR(): KEY_EXPIRED]"); - 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_->config_local_display_only()) { - // Only look at HDCP if the display is non-local. - if (control.control_bits() & kControlHDCPRequired) { - uint8_t required_hdcp = - (control.control_bits() & kControlHDCPVersionMask) >> - kControlHDCPVersionShift; - // For reference implementation, we pretend we can handle the current - // HDCP version. - if (required_hdcp > ce_->config_current_hdcp_capability() || - ce_->config_current_hdcp_capability() == 0) { - return OEMCrypto_ERROR_INSUFFICIENT_HDCP; - } - } - } + + OEMCryptoResult result = AllowKeyUse("DecryptCENC", 0, buffer_type); + if (result != OEMCrypto_SUCCESS) return result; + const std::vector& content_key = current_content_key()->value(); // Set the AES key. diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index 8cee1b72..ffcf31da 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -212,7 +212,8 @@ class SessionContext { OEMCryptoResult DecryptCTR(const uint8_t* key_u8, const uint8_t* iv, size_t block_offset, const uint8_t* cipher_data, size_t cipher_data_length, uint8_t* clear_data); - + OEMCryptoResult AllowKeyUse(const std::string& log_string, uint32_t use_type, + OEMCryptoBufferType buffer_type); RSA* rsa_key() { return rsa_key_.get(); } bool valid_; diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp index af0c949e..afba52c7 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp @@ -59,6 +59,13 @@ KeyControlBlock::KeyControlBlock( LOGD(" nonce: %08X", nonce()); LOGD(" magic: %08X", verification()); LOGD(" bits: %08X", control_bits()); + LOGD(" bit kControlSRMVersionRequired %s.", + (control_bits() & kControlSRMVersionRequired) ? "set" : "unset"); + LOGD(" bit kControlDisableAnalogOutput %s.", + (control_bits() & kControlDisableAnalogOutput) ? "set" : "unset"); + LOGD(" bits kControlSecurityPatchLevel 0x%02x.", + (control_bits() & kControlSecurityPatchLevelMask) + >> kControlSecurityPatchLevelShift); switch (control_bits() & kControlReplayMask) { case kControlNonceRequired: LOGD(" bits kControlReplay kControlNonceRequired."); @@ -70,12 +77,9 @@ KeyControlBlock::KeyControlBlock( LOGD(" bits kControlReplay unset."); break; } - LOGD(" bits kControlKDCPVersion 0x%02x.", + LOGD(" bits kControlHDCPVersion 0x%02x.", (control_bits() & kControlHDCPVersionMask) >> kControlHDCPVersionShift); - LOGD(" bits kControlSecurityPatchLevel 0x%02x.", - (control_bits() & kControlSecurityPatchLevelMask) - >> kControlSecurityPatchLevelShift); LOGD(" bit kControlAllowEncrypt %s.", (control_bits() & kControlAllowEncrypt) ? "set" : "unset"); LOGD(" bit kControlAllowDecrypt %s.", diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h index ec26e02e..aac0423d 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h @@ -15,6 +15,8 @@ const uint32_t kControlObserveDataPath = (1<<31); const uint32_t kControlObserveHDCP = (1<<30); const uint32_t kControlObserveCGMS = (1<<29); const uint32_t kControlRequireAntiRollbackHardware = (1<<28); +const uint32_t kControlSRMVersionRequired = (1<<22); +const uint32_t kControlDisableAnalogOutput = (1<<21); const uint32_t kControlSecurityPatchLevelShift = 15; const uint32_t kControlSecurityPatchLevelMask = (0x3F< bad_srm(42); + RAND_pseudo_bytes(&bad_srm[0], bad_srm.size()); + EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadSRM(&bad_srm[0], bad_srm.size())); +} + TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { size_t sessions_count; ASSERT_EQ(OEMCrypto_SUCCESS, @@ -1153,7 +1171,8 @@ TEST_P(SessionTestAlternateVerification, LoadKeys) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } else { // Otherwise, LoadKeys should succeed. - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(OEMCrypto_SUCCESS, sts) + << "LoadKeys failed for key control block kc" << target_api_; } } @@ -1161,7 +1180,7 @@ TEST_P(SessionTestAlternateVerification, LoadKeys) { // the current API + 2. We use +2 because we want to test at least 1 // future API, and the ::testing::Range is not inclusive. INSTANTIATE_TEST_CASE_P(TestAll, SessionTestAlternateVerification, - Range(8, 12 + 2)); + Range(8, 13 + 2)); TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) { Session s; @@ -2104,6 +2123,18 @@ TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { s.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } +TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClear) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, wvoec_mock::kControlDisableAnalogOutput, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT)); +} + TEST_F(OEMCryptoSessionTests, KeyDuration) { Session s; ASSERT_NO_FATAL_FAILURE(s.open());