diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index 6cd9aad1..5305505b 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -34,6 +34,10 @@ class PolicyEngine { // status is not calculated to avoid overhead in the decryption path. virtual bool CanDecryptContent(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 6b4d6e50..64ba2618 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -295,6 +295,7 @@ enum CdmResponseType { RELEASE_USAGE_INFO_FAILED, INCORRECT_USAGE_SUPPORT_TYPE_1, INCORRECT_USAGE_SUPPORT_TYPE_2, /* 255 */ + KEY_PROHIBITED_FOR_SECURITY_LEVEL, }; enum CdmKeyStatus { @@ -392,6 +393,16 @@ struct CdmUsageEntryInfo { std::string usage_info_file_name; }; +enum CdmKeySecurityLevel { + kKeySecurityLevelUnset, + kSoftwareSecureCrypto, + kSoftwareSecureDecode, + kHardwareSecureCrypto, + kHardwareSecureDecode, + kHardwareSecureAll, + kKeySecurityLevelUnknown, +}; + class CdmKeyAllowedUsage { public: CdmKeyAllowedUsage() { @@ -408,6 +419,7 @@ class CdmKeyAllowedUsage { generic_decrypt = false; generic_sign = false; generic_verify = false; + key_security_level_ = kKeySecurityLevelUnset; valid_ = false; } @@ -418,7 +430,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; @@ -430,6 +443,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 8f63cc5e..7d3dfefe 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -553,6 +553,9 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : 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 0fe19363..a0edb7a3 100644 --- a/libwvdrmengine/cdm/core/src/license_key_status.cpp +++ b/libwvdrmengine/cdm/core/src/license_key_status.cpp @@ -169,6 +169,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 6caae554..f46fe19e 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -283,6 +283,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: + return security_level == kSecurityLevelL2 || + security_level == kSecurityLevelL3; + case kSoftwareSecureDecode: + 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 23b7f3d4..1d445d28 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -1259,6 +1259,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_L_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, false, 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_L_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 { @@ -1287,9 +1344,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); @@ -1307,22 +1366,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, @@ -1388,8 +1449,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); @@ -1443,8 +1504,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/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index b33389d9..afd426ff 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -568,6 +568,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case INCORRECT_USAGE_SUPPORT_TYPE_2: *os << "INCORRECT_USAGE_SUPPORT_TYPE_2"; break; + case KEY_PROHIBITED_FOR_SECURITY_LEVEL: + *os << "KEY_PROHIBITED_FOR_SECURITY_LEVEL"; + break; default: *os << "Unknown CdmResponseType"; diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index b1b33275..b143aae4 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -377,6 +377,7 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) { return android::ERROR_DRM_CANNOT_HANDLE; case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION: case wvcdm::ANALOG_OUTPUT_ERROR: + case wvcdm::KEY_PROHIBITED_FOR_SECURITY_LEVEL: return android::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION; case wvcdm::SESSION_NOT_FOUND_12: return kSessionNotFound12; diff --git a/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp index f813c9d4..b01994d9 100644 --- a/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp +++ b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp @@ -322,9 +322,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 570e54a7..ec6900e2 100644 --- a/libwvdrmengine/mediacrypto/src_hidl/WVCryptoPlugin.cpp +++ b/libwvdrmengine/mediacrypto/src_hidl/WVCryptoPlugin.cpp @@ -354,9 +354,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->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;