From 2afe783ce0900bf6ffd6bd51c68c4466acebe4ed Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Mon, 23 Jan 2017 20:34:13 -0800 Subject: [PATCH] Unit Test Updates for v13 Merge from widevine of http://go/wvgerrit/23042 This updates unit tests to account for key control block changes for OEMCrypto v13. There are two new bits, restricting SRM version and restricting analog output. The verification string is also updated. Part of this is to include some simple unit tests for the SRM functions. b/33815454 b/28955520 Change-Id: I7cc2ce508688fded2b67fc2a4379c7a8d59d8d22 --- .../mock/src/oemcrypto_engine_mock.cpp | 182 +++++++----------- .../mock/src/oemcrypto_engine_mock.h | 3 +- .../oemcrypto/mock/src/oemcrypto_key_mock.cpp | 12 +- .../oemcrypto/mock/src/oemcrypto_key_mock.h | 2 + .../oemcrypto/test/oec_device_features.cpp | 1 + .../oemcrypto/test/oec_session_util.cpp | 16 +- .../oemcrypto/test/oemcrypto_test.cpp | 35 +++- 7 files changed, 133 insertions(+), 118 deletions(-) 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());