Detect when unable to meet policy requirements

[ Merge of http://go/wvgerrit/25781 ]

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.

Test: Verified by WV unit+integration tests.
      Verified by WidevineDashPolicyTests
      Verified by WidevineDashPolicyTests#testL3SoftwareSecureDecoderRequired,
      testL3HardwareSecureCryptoRequired, testL3HardwareSecureDecodeRequired,
      testL3SecureVideoPathRequired.

b/31913737
b/31913439

Change-Id: Ibfc7f3dd6fc7264e8cf9b0d33f6f8d619eed6c00
This commit is contained in:
Rahul Frias
2017-04-13 02:37:05 -07:00
parent ce62e1d7e7
commit 1223330ccc
11 changed files with 172 additions and 18 deletions

View File

@@ -34,6 +34,10 @@ class PolicyEngine {
// status is not calculated to avoid overhead in the decryption path. // status is not calculated to avoid overhead in the decryption path.
virtual bool CanDecryptContent(const KeyId& key_id); 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 // OnTimerEvent is called when a timer fires. It notifies the Policy Engine
// that the timer has fired and dispatches the relevant events through // that the timer has fired and dispatches the relevant events through
// |event_listener_|. // |event_listener_|.

View File

@@ -295,6 +295,7 @@ enum CdmResponseType {
RELEASE_USAGE_INFO_FAILED, RELEASE_USAGE_INFO_FAILED,
INCORRECT_USAGE_SUPPORT_TYPE_1, INCORRECT_USAGE_SUPPORT_TYPE_1,
INCORRECT_USAGE_SUPPORT_TYPE_2, /* 255 */ INCORRECT_USAGE_SUPPORT_TYPE_2, /* 255 */
KEY_PROHIBITED_FOR_SECURITY_LEVEL,
}; };
enum CdmKeyStatus { enum CdmKeyStatus {
@@ -392,6 +393,16 @@ struct CdmUsageEntryInfo {
std::string usage_info_file_name; std::string usage_info_file_name;
}; };
enum CdmKeySecurityLevel {
kKeySecurityLevelUnset,
kSoftwareSecureCrypto,
kSoftwareSecureDecode,
kHardwareSecureCrypto,
kHardwareSecureDecode,
kHardwareSecureAll,
kKeySecurityLevelUnknown,
};
class CdmKeyAllowedUsage { class CdmKeyAllowedUsage {
public: public:
CdmKeyAllowedUsage() { CdmKeyAllowedUsage() {
@@ -408,6 +419,7 @@ class CdmKeyAllowedUsage {
generic_decrypt = false; generic_decrypt = false;
generic_sign = false; generic_sign = false;
generic_verify = false; generic_verify = false;
key_security_level_ = kKeySecurityLevelUnset;
valid_ = false; valid_ = false;
} }
@@ -418,7 +430,8 @@ class CdmKeyAllowedUsage {
generic_encrypt != other.generic_encrypt || generic_encrypt != other.generic_encrypt ||
generic_decrypt != other.generic_decrypt || generic_decrypt != other.generic_decrypt ||
generic_sign != other.generic_sign || 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 false;
} }
return true; return true;
@@ -430,6 +443,7 @@ class CdmKeyAllowedUsage {
bool generic_decrypt; bool generic_decrypt;
bool generic_sign; bool generic_sign;
bool generic_verify; bool generic_verify;
CdmKeySecurityLevel key_security_level_;
private: private:
bool valid_; bool valid_;

View File

@@ -553,6 +553,9 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY; 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); CdmResponseType status = crypto_session_->Decrypt(params);
if (status == NO_ERROR) { if (status == NO_ERROR) {

View File

@@ -169,6 +169,31 @@ void LicenseKeyStatus::ParseContentKey(const KeyContainer& key) {
allowed_usage_.decrypt_to_clear_buffer = true; allowed_usage_.decrypt_to_clear_buffer = true;
allowed_usage_.decrypt_to_secure_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(); allowed_usage_.SetValid();
if (key.video_resolution_constraints_size() > 0) { if (key.video_resolution_constraints_size() > 0) {

View File

@@ -283,6 +283,32 @@ CdmResponseType PolicyEngine::QueryKeyAllowedUsage(
return KEY_NOT_FOUND_1; 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) { bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
if (playback_start_time_ == 0) return false; if (playback_start_time_ == 0) return false;

View File

@@ -157,9 +157,11 @@ class LicenseKeysTest : public ::testing::Test {
} }
virtual void ExpectAllowedUsageContent( 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_secure_buffer, secure == kKeyFlagTrue);
EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == 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_encrypt);
EXPECT_FALSE(key_usage.generic_decrypt); EXPECT_FALSE(key_usage.generic_decrypt);
EXPECT_FALSE(key_usage.generic_sign); EXPECT_FALSE(key_usage.generic_sign);
@@ -420,36 +422,41 @@ TEST_F(LicenseKeysTest, AllowedUsageNull) {
CdmKeyAllowedUsage usage_2; CdmKeyAllowedUsage usage_2;
EXPECT_TRUE(license_keys_.GetAllowedUsage(c_key, &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; CdmKeyAllowedUsage usage_3;
EXPECT_TRUE(license_keys_.GetAllowedUsage(os_key, &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) { TEST_F(LicenseKeysTest, AllowedUsageContent) {
StageContentKeys(); StageContentKeys();
CdmKeyAllowedUsage u_sw_crypto; CdmKeyAllowedUsage u_sw_crypto;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_crypto, &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; CdmKeyAllowedUsage u_sw_decode;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_decode, &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; CdmKeyAllowedUsage u_hw_crypto;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_crypto, &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; CdmKeyAllowedUsage u_hw_decode;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_decode, &u_hw_decode)); EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_decode, &u_hw_decode));
ExpectAllowedUsageContent(u_hw_decode, kContentSecureTrue, ExpectAllowedUsageContent(u_hw_decode, kContentSecureTrue,
kContentClearFalse); kContentClearFalse, kHardwareSecureDecode);
CdmKeyAllowedUsage u_hw_secure; CdmKeyAllowedUsage u_hw_secure;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_secure, &u_hw_secure)); EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_secure, &u_hw_secure));
ExpectAllowedUsageContent(u_hw_secure, kContentSecureTrue, ExpectAllowedUsageContent(u_hw_secure, kContentSecureTrue,
kContentClearFalse); kContentClearFalse, kHardwareSecureAll);
} }
TEST_F(LicenseKeysTest, AllowedUsageOperatorSession) { TEST_F(LicenseKeysTest, AllowedUsageOperatorSession) {

View File

@@ -1259,6 +1259,63 @@ TEST_F(PolicyEngineTest, PlaybackOk_CanStoreGracePeriod) {
EXPECT_EQ(kPlaybackStartTime, policy_engine_->GetGracePeriodEndTime()); 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<KeySecurityLevelParams*> {};
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 { class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest {
protected: protected:
enum KeyFlag { enum KeyFlag {
@@ -1287,9 +1344,11 @@ class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest {
void ExpectAllowedContentKeySettings(const CdmKeyAllowedUsage& key_usage, 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_secure_buffer, secure == kKeyFlagTrue);
EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == 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_encrypt);
EXPECT_FALSE(key_usage.generic_decrypt); EXPECT_FALSE(key_usage.generic_decrypt);
EXPECT_FALSE(key_usage.generic_sign); EXPECT_FALSE(key_usage.generic_sign);
@@ -1307,22 +1366,24 @@ class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest {
EXPECT_EQ(key_usage.generic_verify, verify == kKeyFlagTrue); 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; CdmKeyAllowedUsage key_usage;
EXPECT_EQ(NO_ERROR, EXPECT_EQ(NO_ERROR,
policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage)); policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage));
ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue, 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; CdmKeyAllowedUsage key_usage;
EXPECT_EQ(NO_ERROR, EXPECT_EQ(NO_ERROR,
policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage)); policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage));
ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue, ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue,
kContentSecureTrue); kContentSecureTrue, key_security_level);
} }
void ExpectOperatorSessionKey(const KeyId& key_id, KeyFlag encrypt, void ExpectOperatorSessionKey(const KeyId& key_id, KeyFlag encrypt,
@@ -1388,8 +1449,8 @@ TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageBasic) {
policy_engine_->SetLicense(license_); policy_engine_->SetLicense(license_);
ExpectSecureContentKey(kKeyId); ExpectSecureContentKey(kKeyId, kHardwareSecureAll);
ExpectLessSecureContentKey(kAnotherKeyId); ExpectLessSecureContentKey(kAnotherKeyId, kKeySecurityLevelUnset);
ExpectOperatorSessionKey(kGenericKeyId, kEncryptNull, kDecryptNull, ExpectOperatorSessionKey(kGenericKeyId, kEncryptNull, kDecryptNull,
kSignTrue, kVerifyNull); kSignTrue, kVerifyNull);
@@ -1443,8 +1504,8 @@ TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageGeneric) {
policy_engine_->SetLicense(license_); policy_engine_->SetLicense(license_);
ExpectSecureContentKey(kKeyId); ExpectSecureContentKey(kKeyId, kHardwareSecureDecode);
ExpectLessSecureContentKey(kAnotherKeyId); ExpectLessSecureContentKey(kAnotherKeyId, kHardwareSecureCrypto);
ExpectOperatorSessionKey(kGenericEncryptKeyId, kEncryptTrue, kDecryptFalse, ExpectOperatorSessionKey(kGenericEncryptKeyId, kEncryptTrue, kDecryptFalse,
kSignFalse, kVerifyFalse); kSignFalse, kVerifyFalse);
ExpectOperatorSessionKey(kGenericDecryptKeyId, kEncryptFalse, kDecryptTrue, ExpectOperatorSessionKey(kGenericDecryptKeyId, kEncryptFalse, kDecryptTrue,

View File

@@ -568,6 +568,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case INCORRECT_USAGE_SUPPORT_TYPE_2: case INCORRECT_USAGE_SUPPORT_TYPE_2:
*os << "INCORRECT_USAGE_SUPPORT_TYPE_2"; *os << "INCORRECT_USAGE_SUPPORT_TYPE_2";
break; break;
case KEY_PROHIBITED_FOR_SECURITY_LEVEL:
*os << "KEY_PROHIBITED_FOR_SECURITY_LEVEL";
break;
default: default:
*os << "Unknown CdmResponseType"; *os << "Unknown CdmResponseType";

View File

@@ -377,6 +377,7 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
return android::ERROR_DRM_CANNOT_HANDLE; return android::ERROR_DRM_CANNOT_HANDLE;
case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION: case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION:
case wvcdm::ANALOG_OUTPUT_ERROR: case wvcdm::ANALOG_OUTPUT_ERROR:
case wvcdm::KEY_PROHIBITED_FOR_SECURITY_LEVEL:
return android::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION; return android::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION;
case wvcdm::SESSION_NOT_FOUND_12: case wvcdm::SESSION_NOT_FOUND_12:
return kSessionNotFound12; return kSessionNotFound12;

View File

@@ -322,9 +322,14 @@ status_t WVCryptoPlugin::attemptDecrypt(const CdmDecryptionParameters& params,
"Error decrypting data: unspecified error"); "Error decrypting data: unspecified error");
break; break;
case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION: case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION:
case wvcdm::ANALOG_OUTPUT_ERROR:
errorDetailMsg->setTo( errorDetailMsg->setTo(
"Error decrypting data: insufficient output protection"); "Error decrypting data: insufficient output protection");
break; break;
case wvcdm::KEY_PROHIBITED_FOR_SECURITY_LEVEL:
errorDetailMsg->setTo(
"Error decrypting data: key prohibited for security level");
break;
default: default:
actionableError = false; actionableError = false;
break; break;

View File

@@ -354,9 +354,14 @@ status_t WVCryptoPlugin::attemptDecrypt(const CdmDecryptionParameters& params,
"Error decrypting data: unspecified error"); "Error decrypting data: unspecified error");
break; break;
case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION: case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION:
case wvcdm::ANALOG_OUTPUT_ERROR:
errorDetailMsg->assign( errorDetailMsg->assign(
"Error decrypting data: insufficient output protection"); "Error decrypting data: insufficient output protection");
break; break;
case wvcdm::KEY_PROHIBITED_FOR_SECURITY_LEVEL:
errorDetailMsg->assign(
"Error decrypting data: key prohibited for security level");
break;
default: default:
actionableError = false; actionableError = false;
break; break;