From c78ce178d4c12386ddf3d526b94c0cea468bdc0c Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 5 Feb 2018 23:26:19 -0800 Subject: [PATCH] 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;