From 92eaccb3c7d069f476aaa664cc400ca3a44e08d6 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 5 Feb 2018 12:05:18 -0800 Subject: [PATCH 1/4] Add tests for Cenc 3.0 cipher and sample modes [ Merge of http://go/wvgerrit/42444 ] This covers cenc, cens, cbc1 and cbcs. This also covers HLS v2 format. b/70684636 Test: Verified using WV unit/integration tests Change-Id: I3e85b496d29b91c514f0bb806712cdf0cee12903 --- .../cdm/test/request_license_test.cpp | 158 +++++++++++++++++- 1 file changed, 154 insertions(+), 4 deletions(-) diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 8cf33cc7..3e4429f8 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -881,6 +881,17 @@ const std::string kFullCencPssh = wvcdm::a2bs_hex( "74657374220a323031355f7465617273" "2a024844"); +const std::string kFullCensPssh = wvcdm::a2bs_hex( + "00000053" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000033" // pssh data size + "12103030303030303030303030303030" // pssh data + "30321a0d7769646576696e655f746573" + "74220a323031355f746561727348f3dc" + "959b06"); + const std::string kFullCbc1Pssh = wvcdm::a2bs_hex( "00000053" // blob size "70737368" // "pssh" @@ -892,6 +903,18 @@ const std::string kFullCbc1Pssh = wvcdm::a2bs_hex( "74220a323031355f746561727348b1c6" "899b06"); + +const std::string kFullCbcsPssh = wvcdm::a2bs_hex( + "00000053" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000033" // pssh data size + "12103030303030303030303030303030" + "30321a0d7769646576696e655f746573" + "74220a323031355f746561727348f3c6" + "899b06"); + struct Cenc30SampleInfo { bool is_encrypted; wvcdm::KeyId key_id; @@ -952,6 +975,31 @@ Cenc30SampleInfo kCenc30CencKey32Sample = { wvcdm::kCipherModeCtr, }; +Cenc30SampleInfo kCenc30CensKey33Sample = { + true, wvcdm::a2bs_hex("30303030303030303030303030303033"), + wvcdm::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), + wvcdm::a2bs_hex( + "011E88387D58EBB8E5CDCC38D431EEF4B6094B9201F200932F8EB5E1A94FB0B977" + "FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203" + "4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056" + "E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827" + "CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC868701CB8BE038" + "15BBFFB95FD3A86F142127720A35234070799173B37219127141922CBA8CB2DC48" + "EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B" + "7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"), + wvcdm::a2bs_hex( + "E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977" + "FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203" + "4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056" + "E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827" + "CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD" + "A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48" + "EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B" + "7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"), + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample, + wvcdm::kCipherModeCtr, +}; + Cenc30SampleInfo kCenc30Cbc1Key33Sample = { true, wvcdm::a2bs_hex("30303030303030303030303030303033"), wvcdm::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1002,12 +1050,49 @@ Cenc30SampleInfo kCenc30Cbc1Key32Sample = { wvcdm::kCipherModeCbc, }; -struct Cenc30SwitchCipherInfo { +Cenc30SampleInfo kCenc30CbcsKey33Sample = { + true, wvcdm::a2bs_hex("30303030303030303030303030303033"), + wvcdm::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), + wvcdm::a2bs_hex( + "4392E38BAE263267ED15394DE349AD1577F37B7D906C3A61536EE5A288F66F22F2" + "F5098964B7F2860A848C3C4FD30E538B3BCD2E700DC3FBC1657A6E9EAE44DE97C4" + "6F27C82A49198EE185D92931F093C3342FDBF6CF8203E18CCDC4B88E79C95EC052" + "3FD10F9409945349169FAA8F6A37179D2BEDC04A158A09BCBF742DA05245428788" + "E972B9B465FED5849AEDDB74B8919673C0C8829B5B062A38B3146CB8D497F03A4D" + "5C0A1D463504C1F116A811EF32503695B8FF78D9E93CDF7B2F7493E8043D4DE110" + "FE1D342D1C0175BF1466A544FC0D02DD0E314098256DD65B48098323C3AED9B7E0" + "CF260DBC5A0F09A46E39AE5E26A66ABFA52CBA26FBA83975E4"), + wvcdm::a2bs_hex( + "E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977" + "FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203" + "4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056" + "E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827" + "CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD" + "A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48" + "EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B" + "7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"), + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample, + wvcdm::kCipherModeCbc, +}; + +struct SingleSampleDecryptionInfo { + const std::string pssh; + Cenc30SampleInfo sample_info; +}; + +SingleSampleDecryptionInfo kCenc30DecryptionData[4] = { + { kFullCencPssh, kCenc30CencKey33Sample }, + { kFullCensPssh, kCenc30CensKey33Sample }, + { kFullCbc1Pssh, kCenc30Cbc1Key33Sample }, + { kFullCbcsPssh, kCenc30CbcsKey33Sample }, +}; + +struct FourSampleDecryptionInfo { const std::string pssh; Cenc30SampleInfo sample_info[4]; }; -Cenc30SwitchCipherInfo kCenc30SwitchCipherData[8] = { +FourSampleDecryptionInfo kCenc30SwitchCipherData[8] = { // Switch between cipher modes { kFullCencPssh, { kCenc30CencKey33Sample, kCenc30Cbc1Key33Sample, kCenc30CencKey33Sample, kCenc30Cbc1Key33Sample, } }, @@ -3860,9 +3945,74 @@ INSTANTIATE_TEST_CASE_P( ::testing::Range(&kHlsFourCCBackwardCompatibilityTestVectors[0], &kHlsFourCCBackwardCompatibilityTestVectors[4])); +class WvCenc30Test + : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCenc30Test, DecryptionTest) { + Provision(kLevel3); + TestWvCdmClientPropertySet client_property_set; + client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); + + std::string value; + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_.QueryStatus( + kLevel3, wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, &value)); + std::istringstream ss(value); + uint32_t api_version; + ss >> api_version; + ASSERT_FALSE(ss.fail()); + EXPECT_TRUE(ss.eof()); + + // Ability to switch between cipher modes without re-requesting a license + // was introduced in OEMCrypto v14 + if (api_version < 14u) + return; + + SingleSampleDecryptionInfo* info = GetParam(); + + TestWvCdmHlsEventListener listener; + decryptor_.OpenSession(g_key_system, &client_property_set, + kDefaultCdmIdentifier, &listener, &session_id_); + CdmAppParameterMap app_parameters; + GenerateKeyRequest(info->pssh, app_parameters, + kLicenseTypeStreaming, NULL); + VerifyKeyRequestResponse(g_license_server, g_client_auth); + CdmKeyStatusMap key_status_map = listener.GetKeyStatusMap(); + EXPECT_EQ(8u, key_status_map.size()); + + Cenc30SampleInfo* data = &info->sample_info; + std::vector output_buffer(data->encrypted_data.size(), 0); + std::vector iv(data->iv.begin(), data->iv.end()); + CdmDecryptionParameters decryption_parameters( + &data->key_id, + reinterpret_cast(data->encrypted_data.c_str()), + data->encrypted_data.size(), &iv, 0, &output_buffer[0]); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = false; + decryption_parameters.cipher_mode = data->cipher_mode; + if (data->cipher_mode == kCipherModeCtr) { + decryption_parameters.pattern_descriptor.encrypt_blocks = 1; + decryption_parameters.pattern_descriptor.skip_blocks = 9; + } + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, false, + decryption_parameters)); + EXPECT_EQ(data->clear_data, + std::string(reinterpret_cast(&output_buffer[0]), + output_buffer.size())); + + decryptor_.CloseSession(session_id_); +} + +INSTANTIATE_TEST_CASE_P( + Cdm, WvCenc30Test, + ::testing::Range(&kCenc30DecryptionData[0], + &kCenc30DecryptionData[4])); + class WvCenc30SwitchCipherModeTest : public WvCdmRequestLicenseTest, - public ::testing::WithParamInterface {}; + public ::testing::WithParamInterface {}; TEST_P(WvCenc30SwitchCipherModeTest, DecryptionTest) { Provision(kLevel3); @@ -3884,7 +4034,7 @@ TEST_P(WvCenc30SwitchCipherModeTest, DecryptionTest) { if (api_version < 14) return; - Cenc30SwitchCipherInfo* info = GetParam(); + FourSampleDecryptionInfo* info = GetParam(); TestWvCdmHlsEventListener listener; decryptor_.OpenSession(g_key_system, &client_property_set, From c78ce178d4c12386ddf3d526b94c0cea468bdc0c Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 5 Feb 2018 23:26:19 -0800 Subject: [PATCH 2/4] Detect when unable to meet policy requirements [ Merged of http://go/wvgerrit/39766 ] The security level (software/hardware, decryption/decode) in the policy that specified how the key was to be used was not being respected for L3. Playback would either continue or a vendor specific error would be thrown. If the device cannot use the key as permitted by the policy CryptoException#ERROR_INSUFFICIENT_OUTPUT_PROTECTION will be thrown. Bug: 31913737 Bug: 31913439 Test: WV unit/integration tests Test: Playback using playmovies and netflix. Cast playback using playmovies. Change-Id: If25735ab0f789108431115623cb236687c5ef818 --- .../cdm/core/include/policy_engine.h | 4 + .../cdm/core/include/wv_cdm_types.h | 15 +++- libwvdrmengine/cdm/core/src/cdm_session.cpp | 3 + .../cdm/core/src/license_key_status.cpp | 25 ++++++ libwvdrmengine/cdm/core/src/policy_engine.cpp | 26 ++++++ .../cdm/core/test/license_keys_unittest.cpp | 23 ++++-- .../cdm/core/test/policy_engine_unittest.cpp | 79 ++++++++++++++++--- libwvdrmengine/cdm/src/ami_adapter.cpp | 2 - .../mediacrypto/src/WVCryptoPlugin.cpp | 5 ++ .../mediacrypto/src_hidl/WVCryptoPlugin.cpp | 5 ++ 10 files changed, 167 insertions(+), 20 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index 113253af..3ca846b7 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -39,6 +39,10 @@ class PolicyEngine { // out why a key is not usable. virtual CdmKeyStatus GetKeyStatus(const KeyId& key_id); + // Verifies whether the policy allows use of the specified key of + // a given security level for content decryption. + virtual bool CanUseKey(const KeyId& key_id, CdmSecurityLevel security_level); + // OnTimerEvent is called when a timer fires. It notifies the Policy Engine // that the timer has fired and dispatches the relevant events through // |event_listener_|. diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 1ebce62c..361500c4 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -428,6 +428,16 @@ struct CdmUsageEntryInfo { } }; +enum CdmKeySecurityLevel { + kKeySecurityLevelUnset, + kSoftwareSecureCrypto, + kSoftwareSecureDecode, + kHardwareSecureCrypto, + kHardwareSecureDecode, + kHardwareSecureAll, + kKeySecurityLevelUnknown, +}; + class CdmKeyAllowedUsage { public: CdmKeyAllowedUsage() { @@ -444,6 +454,7 @@ class CdmKeyAllowedUsage { generic_decrypt = false; generic_sign = false; generic_verify = false; + key_security_level_ = kKeySecurityLevelUnset; valid_ = false; } @@ -454,7 +465,8 @@ class CdmKeyAllowedUsage { generic_encrypt != other.generic_encrypt || generic_decrypt != other.generic_decrypt || generic_sign != other.generic_sign || - generic_verify != other.generic_verify) { + generic_verify != other.generic_verify || + key_security_level_ != other.key_security_level_) { return false; } return true; @@ -466,6 +478,7 @@ class CdmKeyAllowedUsage { bool generic_decrypt; bool generic_sign; bool generic_verify; + CdmKeySecurityLevel key_security_level_; private: bool valid_; diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 7a8813e1..1c8833f8 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -557,6 +557,9 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { return NEED_KEY; } + if (!policy_engine_->CanUseKey(*params.key_id, security_level_)) + return KEY_PROHIBITED_FOR_SECURITY_LEVEL; + CdmResponseType status = crypto_session_->Decrypt(params); if (status == NO_ERROR) { diff --git a/libwvdrmengine/cdm/core/src/license_key_status.cpp b/libwvdrmengine/cdm/core/src/license_key_status.cpp index 29a847f1..98a2e36b 100644 --- a/libwvdrmengine/cdm/core/src/license_key_status.cpp +++ b/libwvdrmengine/cdm/core/src/license_key_status.cpp @@ -178,6 +178,31 @@ void LicenseKeyStatus::ParseContentKey(const KeyContainer& key) { allowed_usage_.decrypt_to_clear_buffer = true; allowed_usage_.decrypt_to_secure_buffer = true; } + + if (key.has_level()) { + switch (key.level()) { + case KeyContainer::SW_SECURE_CRYPTO: + allowed_usage_.key_security_level_ = kSoftwareSecureCrypto; + break; + case KeyContainer::SW_SECURE_DECODE: + allowed_usage_.key_security_level_ = kSoftwareSecureDecode; + break; + case KeyContainer::HW_SECURE_CRYPTO: + allowed_usage_.key_security_level_ = kHardwareSecureCrypto; + break; + case KeyContainer::HW_SECURE_DECODE: + allowed_usage_.key_security_level_ = kHardwareSecureDecode; + break; + case KeyContainer::HW_SECURE_ALL: + allowed_usage_.key_security_level_ = kHardwareSecureAll; + break; + default: + allowed_usage_.key_security_level_ = kKeySecurityLevelUnknown; + break; + } + } else { + allowed_usage_.key_security_level_ = kKeySecurityLevelUnset; + } allowed_usage_.SetValid(); if (key.video_resolution_constraints_size() > 0) { diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index f77e3a69..6eb9c36b 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -309,6 +309,32 @@ CdmResponseType PolicyEngine::QueryKeyAllowedUsage( return KEY_NOT_FOUND_1; } +bool PolicyEngine::CanUseKey( + const KeyId& key_id, + CdmSecurityLevel security_level) { + + if (security_level == kSecurityLevelL1) return true; + + CdmKeyAllowedUsage key_usage; + CdmResponseType status = QueryKeyAllowedUsage(key_id, &key_usage); + + if (status != NO_ERROR) return false; + + // L1 has already been addressed so verify that L2/3 are allowed + switch (key_usage.key_security_level_) { + case kKeySecurityLevelUnset: + return true; + case kSoftwareSecureCrypto: + case kSoftwareSecureDecode: + return security_level == kSecurityLevelL2 || + security_level == kSecurityLevelL3; + case kHardwareSecureCrypto: + return security_level == kSecurityLevelL2; + default: + return false; + } +} + bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) { if (playback_start_time_ == 0) return false; diff --git a/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp b/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp index dc645883..7ae7186d 100644 --- a/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp @@ -157,9 +157,11 @@ class LicenseKeysTest : public ::testing::Test { } virtual void ExpectAllowedUsageContent( - const CdmKeyAllowedUsage& key_usage, KeyFlag secure, KeyFlag clear) { + const CdmKeyAllowedUsage& key_usage, KeyFlag secure, KeyFlag clear, + CdmKeySecurityLevel key_security_level) { EXPECT_EQ(key_usage.decrypt_to_secure_buffer, secure == kKeyFlagTrue); EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == kKeyFlagTrue); + EXPECT_EQ(key_usage.key_security_level_, key_security_level); EXPECT_FALSE(key_usage.generic_encrypt); EXPECT_FALSE(key_usage.generic_decrypt); EXPECT_FALSE(key_usage.generic_sign); @@ -420,36 +422,41 @@ TEST_F(LicenseKeysTest, AllowedUsageNull) { CdmKeyAllowedUsage usage_2; EXPECT_TRUE(license_keys_.GetAllowedUsage(c_key, &usage_2)); - ExpectAllowedUsageContent(usage_2, kContentClearTrue, kContentSecureTrue); + ExpectAllowedUsageContent(usage_2, kContentClearTrue, kContentSecureTrue, + kKeySecurityLevelUnset); CdmKeyAllowedUsage usage_3; EXPECT_TRUE(license_keys_.GetAllowedUsage(os_key, &usage_3)); - ExpectAllowedUsageContent(usage_3, kContentClearFalse, kContentSecureFalse); + ExpectAllowedUsageContent(usage_3, kContentClearFalse, kContentSecureFalse, + kKeySecurityLevelUnset); } TEST_F(LicenseKeysTest, AllowedUsageContent) { StageContentKeys(); CdmKeyAllowedUsage u_sw_crypto; EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_crypto, &u_sw_crypto)); - ExpectAllowedUsageContent(u_sw_crypto, kContentSecureTrue, kContentClearTrue); + ExpectAllowedUsageContent(u_sw_crypto, kContentSecureTrue, kContentClearTrue, + kSoftwareSecureCrypto); CdmKeyAllowedUsage u_sw_decode; EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_decode, &u_sw_decode)); - ExpectAllowedUsageContent(u_sw_decode, kContentSecureTrue, kContentClearTrue); + ExpectAllowedUsageContent(u_sw_decode, kContentSecureTrue, kContentClearTrue, + kSoftwareSecureDecode); CdmKeyAllowedUsage u_hw_crypto; EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_crypto, &u_hw_crypto)); - ExpectAllowedUsageContent(u_hw_crypto, kContentSecureTrue, kContentClearTrue); + ExpectAllowedUsageContent(u_hw_crypto, kContentSecureTrue, kContentClearTrue, + kHardwareSecureCrypto); CdmKeyAllowedUsage u_hw_decode; EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_decode, &u_hw_decode)); ExpectAllowedUsageContent(u_hw_decode, kContentSecureTrue, - kContentClearFalse); + kContentClearFalse, kHardwareSecureDecode); CdmKeyAllowedUsage u_hw_secure; EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_secure, &u_hw_secure)); ExpectAllowedUsageContent(u_hw_secure, kContentSecureTrue, - kContentClearFalse); + kContentClearFalse, kHardwareSecureAll); } TEST_F(LicenseKeysTest, AllowedUsageOperatorSession) { diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index 49d80b02..e1ba626c 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -1568,6 +1568,63 @@ TEST_F(PolicyEngineTest, PlaybackOk_CanStoreGracePeriod) { EXPECT_EQ(kPlaybackStartTime, policy_engine_->GetGracePeriodEndTime()); } +struct KeySecurityLevelParams { + bool is_security_level_set; + License::KeyContainer::SecurityLevel security_level; + bool expect_can_L1_use_key; + bool expect_can_L2_use_key; + bool expect_can_L3_use_key; + bool expect_can_level_unknown_use_key; +}; + +KeySecurityLevelParams key_security_test_vectors[] = { + { false, License::KeyContainer::HW_SECURE_ALL, true, true, true, true }, + { true, License::KeyContainer::SW_SECURE_CRYPTO, true, true, true, false }, + { true, License::KeyContainer::SW_SECURE_DECODE, true, true, true, false }, + { true, License::KeyContainer::HW_SECURE_CRYPTO, true, true, false, false }, + { true, License::KeyContainer::HW_SECURE_DECODE, true, false, false, false }, + { true, License::KeyContainer::HW_SECURE_ALL, true, false, false, false }, +}; + +class PolicyEngineKeySecurityLevelTest + : public PolicyEngineTest, + public ::testing::WithParamInterface {}; + +TEST_P(PolicyEngineKeySecurityLevelTest, CanUseKey) { + + KeySecurityLevelParams* param = GetParam(); + + license_.clear_key(); + + License::KeyContainer* content_key = license_.add_key(); + content_key->set_type(License::KeyContainer::CONTENT); + content_key->set_id(kKeyId); + if (param->is_security_level_set) + content_key->set_level(param->security_level); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _)); + + policy_engine_->SetLicense(license_); + + EXPECT_EQ(param->expect_can_L1_use_key, + policy_engine_->CanUseKey(kKeyId, kSecurityLevelL1)); + EXPECT_EQ(param->expect_can_L2_use_key, + policy_engine_->CanUseKey(kKeyId, kSecurityLevelL2)); + EXPECT_EQ(param->expect_can_L3_use_key, + policy_engine_->CanUseKey(kKeyId, kSecurityLevelL3)); + EXPECT_EQ(param->expect_can_level_unknown_use_key, + policy_engine_->CanUseKey(kKeyId, kSecurityLevelUnknown)); +} + +INSTANTIATE_TEST_CASE_P( + PolicyEngine, PolicyEngineKeySecurityLevelTest, + ::testing::Range(&key_security_test_vectors[0], + &key_security_test_vectors[5])); + class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest { protected: enum KeyFlag { @@ -1596,9 +1653,11 @@ class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest { void ExpectAllowedContentKeySettings(const CdmKeyAllowedUsage& key_usage, - KeyFlag secure, KeyFlag clear) { + KeyFlag secure, KeyFlag clear, + CdmKeySecurityLevel key_security_level) { EXPECT_EQ(key_usage.decrypt_to_secure_buffer, secure == kKeyFlagTrue); EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == kKeyFlagTrue); + EXPECT_EQ(key_usage.key_security_level_, key_security_level); EXPECT_FALSE(key_usage.generic_encrypt); EXPECT_FALSE(key_usage.generic_decrypt); EXPECT_FALSE(key_usage.generic_sign); @@ -1616,22 +1675,24 @@ class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest { EXPECT_EQ(key_usage.generic_verify, verify == kKeyFlagTrue); } - void ExpectSecureContentKey(const KeyId& key_id) { + void ExpectSecureContentKey(const KeyId& key_id, + CdmKeySecurityLevel key_security_level) { CdmKeyAllowedUsage key_usage; EXPECT_EQ(NO_ERROR, policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage)); ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue, - kContentSecureFalse); + kContentSecureFalse, key_security_level); } - void ExpectLessSecureContentKey(const KeyId& key_id) { + void ExpectLessSecureContentKey(const KeyId& key_id, + CdmKeySecurityLevel key_security_level) { CdmKeyAllowedUsage key_usage; EXPECT_EQ(NO_ERROR, policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage)); ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue, - kContentSecureTrue); + kContentSecureTrue, key_security_level); } void ExpectOperatorSessionKey(const KeyId& key_id, KeyFlag encrypt, @@ -1702,8 +1763,8 @@ TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageBasic) { policy_engine_->SetLicense(license_); - ExpectSecureContentKey(kKeyId); - ExpectLessSecureContentKey(kAnotherKeyId); + ExpectSecureContentKey(kKeyId, kHardwareSecureAll); + ExpectLessSecureContentKey(kAnotherKeyId, kKeySecurityLevelUnset); ExpectOperatorSessionKey(kGenericKeyId, kEncryptNull, kDecryptNull, kSignTrue, kVerifyNull); @@ -1762,8 +1823,8 @@ TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageGeneric) { policy_engine_->SetLicense(license_); - ExpectSecureContentKey(kKeyId); - ExpectLessSecureContentKey(kAnotherKeyId); + ExpectSecureContentKey(kKeyId, kHardwareSecureDecode); + ExpectLessSecureContentKey(kAnotherKeyId, kHardwareSecureCrypto); ExpectOperatorSessionKey(kGenericEncryptKeyId, kEncryptTrue, kDecryptFalse, kSignFalse, kVerifyFalse); ExpectOperatorSessionKey(kGenericDecryptKeyId, kEncryptFalse, kDecryptTrue, diff --git a/libwvdrmengine/cdm/src/ami_adapter.cpp b/libwvdrmengine/cdm/src/ami_adapter.cpp index d56e84f7..59ba48b5 100644 --- a/libwvdrmengine/cdm/src/ami_adapter.cpp +++ b/libwvdrmengine/cdm/src/ami_adapter.cpp @@ -62,5 +62,3 @@ void AmiAdapter::UpdateDouble(const std::string& metric_id, } } // namespace wvcdm - - diff --git a/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp index cbe68546..83027295 100644 --- a/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp +++ b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp @@ -323,9 +323,14 @@ status_t WVCryptoPlugin::attemptDecrypt(const CdmDecryptionParameters& params, "Error decrypting data: unspecified error"); break; case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION: + case wvcdm::ANALOG_OUTPUT_ERROR: errorDetailMsg->setTo( "Error decrypting data: insufficient output protection"); break; + case wvcdm::KEY_PROHIBITED_FOR_SECURITY_LEVEL: + errorDetailMsg->setTo( + "Error decrypting data: key prohibited for security level"); + break; default: actionableError = false; break; diff --git a/libwvdrmengine/mediacrypto/src_hidl/WVCryptoPlugin.cpp b/libwvdrmengine/mediacrypto/src_hidl/WVCryptoPlugin.cpp index 075f6d88..0ab8795e 100644 --- a/libwvdrmengine/mediacrypto/src_hidl/WVCryptoPlugin.cpp +++ b/libwvdrmengine/mediacrypto/src_hidl/WVCryptoPlugin.cpp @@ -365,9 +365,14 @@ Status WVCryptoPlugin::attemptDecrypt(const CdmDecryptionParameters& params, "Error decrypting data: unspecified error"); break; case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION: + case wvcdm::ANALOG_OUTPUT_ERROR: errorDetailMsg->assign( "Error decrypting data: insufficient output protection"); break; + case wvcdm::KEY_PROHIBITED_FOR_SECURITY_LEVEL: + errorDetailMsg->assign( + "Error decrypting data: key prohibited for security level"); + break; default: actionableError = false; break; From 3bbd0584a82f14094a862912807e0b030b89b9ba Mon Sep 17 00:00:00 2001 From: Srujan Gaddam Date: Thu, 8 Feb 2018 14:55:17 -0800 Subject: [PATCH 3/4] Fix cipher mode assignment in adapter b/73127061 Merge of http://go/wvgerrit/42923 Test: Unit tests/playback --- libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index d74124e5..9bffd76f 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -1368,7 +1368,7 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys( key_array_v13[i].key_data_length = key_array[i].key_data_length; key_array_v13[i].key_control_iv = key_array[i].key_control_iv; key_array_v13[i].key_control = key_array[i].key_control; - key_array_v13[i].cipher_mode == OEMCrypto_CipherMode_CTR; + key_array_v13[i].cipher_mode = OEMCrypto_CipherMode_CTR; } OEMCrypto_KeyObject_V13* key_array_v13_ptr = NULL; if (num_keys > 0) key_array_v13_ptr = &key_array_v13[0]; From dc25029fc49ef8eda71b8f0fcae94cf08cab87e7 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Wed, 7 Feb 2018 23:16:43 -0800 Subject: [PATCH 4/4] Changes to support new MediaDrm methods [ Merge from http://go/wvgerrit/42602 ] Renamed HDCP related query names to better reflect their purpose. Bug: 69674645 Test: wv unit/integration tests Change-Id: If4da45ff676da0e812852bf34f209d99e59c059b --- .../cdm/core/include/wv_cdm_constants.h | 6 ++++-- libwvdrmengine/cdm/core/src/cdm_engine.cpp | 7 ++++--- .../cdm/test/request_license_test.cpp | 20 +++++++++++-------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index ae3bd2dd..45587142 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -78,12 +78,14 @@ static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2"; static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3"; static const std::string QUERY_VALUE_SECURITY_LEVEL_UNKNOWN = "Unknown"; static const std::string QUERY_VALUE_SECURITY_LEVEL_DEFAULT = "Default"; -static const std::string QUERY_VALUE_DISCONNECTED = "Disconnected"; -static const std::string QUERY_VALUE_UNPROTECTED = "Unprotected"; +static const std::string QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT = + "HDCP-NoDigitalOutput"; +static const std::string QUERY_VALUE_HDCP_NONE = "HDCP-None"; static const std::string QUERY_VALUE_HDCP_V1 = "HDCP-1.x"; static const std::string QUERY_VALUE_HDCP_V2_0 = "HDCP-2.0"; static const std::string QUERY_VALUE_HDCP_V2_1 = "HDCP-2.1"; static const std::string QUERY_VALUE_HDCP_V2_2 = "HDCP-2.2"; +static const std::string QUERY_VALUE_HDCP_LEVEL_UNKNOWN = "HDCP-LevelUnknown"; static const std::string ISO_BMFF_VIDEO_MIME_TYPE = "video/mp4"; static const std::string ISO_BMFF_AUDIO_MIME_TYPE = "audio/mp4"; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index a806dff6..ad4640d3 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -1621,7 +1621,7 @@ std::string CdmEngine::MapHdcpVersion( CryptoSession::HdcpCapability version) { switch (version) { case HDCP_NONE: - return QUERY_VALUE_UNPROTECTED; + return QUERY_VALUE_HDCP_NONE; case HDCP_V1: return QUERY_VALUE_HDCP_V1; case HDCP_V2: @@ -1631,9 +1631,10 @@ std::string CdmEngine::MapHdcpVersion( case HDCP_V2_2: return QUERY_VALUE_HDCP_V2_2; case HDCP_NO_DIGITAL_OUTPUT: - return QUERY_VALUE_DISCONNECTED; + return QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT; + default: + return QUERY_VALUE_HDCP_LEVEL_UNKNOWN; } - return ""; } void CdmEngine::CloseExpiredReleaseSessions() { diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 3e4429f8..c0476af1 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -3152,17 +3152,19 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) { decryptor_.QueryStatus( kLevelDefault, wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, &value)); EXPECT_TRUE( - value == QUERY_VALUE_UNPROTECTED || value == QUERY_VALUE_HDCP_V1 || + value == QUERY_VALUE_HDCP_NONE || value == QUERY_VALUE_HDCP_V1 || value == QUERY_VALUE_HDCP_V2_0 || value == QUERY_VALUE_HDCP_V2_1 || - value == QUERY_VALUE_HDCP_V2_2 || value == QUERY_VALUE_DISCONNECTED); + value == QUERY_VALUE_HDCP_V2_2 || + value == QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_MAX_HDCP_LEVEL, &value)); EXPECT_TRUE( - value == QUERY_VALUE_UNPROTECTED || value == QUERY_VALUE_HDCP_V1 || + value == QUERY_VALUE_HDCP_NONE || value == QUERY_VALUE_HDCP_V1 || value == QUERY_VALUE_HDCP_V2_0 || value == QUERY_VALUE_HDCP_V2_1 || - value == QUERY_VALUE_HDCP_V2_2 || value == QUERY_VALUE_DISCONNECTED); + value == QUERY_VALUE_HDCP_V2_2 || + value == QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryStatus(kLevelDefault, @@ -3252,17 +3254,19 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatusL3) { decryptor_.QueryStatus(kLevel3, wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, &value)); EXPECT_TRUE( - value == QUERY_VALUE_UNPROTECTED || value == QUERY_VALUE_HDCP_V1 || + value == QUERY_VALUE_HDCP_NONE || value == QUERY_VALUE_HDCP_V1 || value == QUERY_VALUE_HDCP_V2_0 || value == QUERY_VALUE_HDCP_V2_1 || - value == QUERY_VALUE_HDCP_V2_2 || value == QUERY_VALUE_DISCONNECTED); + value == QUERY_VALUE_HDCP_V2_2 || + value == QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT); EXPECT_EQ( wvcdm::NO_ERROR, decryptor_.QueryStatus(kLevel3, wvcdm::QUERY_KEY_MAX_HDCP_LEVEL, &value)); EXPECT_TRUE( - value == QUERY_VALUE_UNPROTECTED || value == QUERY_VALUE_HDCP_V1 || + value == QUERY_VALUE_HDCP_NONE || value == QUERY_VALUE_HDCP_V1 || value == QUERY_VALUE_HDCP_V2_0 || value == QUERY_VALUE_HDCP_V2_1 || - value == QUERY_VALUE_HDCP_V2_2 || value == QUERY_VALUE_DISCONNECTED); + value == QUERY_VALUE_HDCP_V2_2 || + value == QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT); EXPECT_EQ( wvcdm::NO_ERROR,