Reject Embedded Keys Under 16 Bytes

(This is a merge of http://go/wvgerrit/60620)

The license code handles keys larger than 16 bytes correctly, but it
does not properly reject keys smaller than 16 bytes.

This patch adds unit tests not only for the new error case but also
the existing success cases which were not previously being tested. As
part of this, license_unittest was changed to use a Test Peer instead
of making the test fixture a friend class.

Bug: 111069024
Test: CE CDM unit tests
Test: Android unit tests
Change-Id: Idb2deb6fbe0aeb19b530f9818bebff480541f5c8
This commit is contained in:
John W. Bruce
2018-10-08 10:05:34 -07:00
parent 44fe62b0a6
commit 3d603eb12d
14 changed files with 158 additions and 32 deletions

View File

@@ -357,7 +357,7 @@ TEST_F(WvCdmEngineTest, LoadKey) {
holder.OpenSession(config_.key_system());
holder.GenerateKeyRequest(binary_key_id(), ISO_BMFF_VIDEO_MIME_TYPE);
holder.CreateDefaultLicense();
std::vector<uint8_t> key_data(KEY_SIZE, '1');
std::vector<uint8_t> key_data(CONTENT_KEY_SIZE, '1');
wvoec::KeyControlBlock block = {};
holder.AddKey("key_one", key_data, block);
holder.SignAndLoadLicense();

View File

@@ -43,10 +43,10 @@ class WvGenericOperationsTest : public WvCdmTestBase {
holder_.CreateDefaultLicense();
ency_id_ = "ency";
StripeBuffer(&ency_key_, KEY_SIZE, 'e');
StripeBuffer(&ency_key_, CONTENT_KEY_SIZE, 'e');
AddOneKey(ency_id_, ency_key_, wvoec::kControlAllowEncrypt);
dency_id_ = "dency";
StripeBuffer(&dency_key_, KEY_SIZE, 'd');
StripeBuffer(&dency_key_, CONTENT_KEY_SIZE, 'd');
AddOneKey(dency_id_, dency_key_, wvoec::kControlAllowDecrypt);
siggy_id_ = "siggy";
StripeBuffer(&siggy_key_, MAC_KEY_SIZE, 's');
@@ -55,10 +55,10 @@ class WvGenericOperationsTest : public WvCdmTestBase {
StripeBuffer(&vou_key_, MAC_KEY_SIZE, 'v');
AddOneKey(vou_id_, vou_key_, wvoec::kControlAllowVerify);
StripeBuffer(&in_vector_, KEY_SIZE * 15, '1');
StripeBuffer(&in_vector_, CONTENT_KEY_SIZE * 15, '1');
in_buffer_ = std::string(in_vector_.begin(), in_vector_.end());
StripeBuffer(&iv_vector_, KEY_SIZE, 'a');
StripeBuffer(&iv_vector_, KEY_IV_SIZE, 'a');
iv_ = std::string(iv_vector_.begin(), iv_vector_.end());
}
@@ -255,7 +255,7 @@ TEST_F(WvGenericOperationsTest, GenericEncryptDecrypt) {
KeyId key_id = "enc and dec";
std::vector<uint8_t> key_data;
StripeBuffer(&key_data, KEY_SIZE, '3');
StripeBuffer(&key_data, CONTENT_KEY_SIZE, '3');
AddOneKey(key_id, key_data,
wvoec::kControlAllowEncrypt | wvoec::kControlAllowDecrypt);

View File

@@ -8,6 +8,7 @@
#include <memory>
#include "clock.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "initialization_data.h"
#include "license.h"
@@ -144,6 +145,20 @@ const CryptoSession::SupportedCertificateTypes kDefaultSupportedCertTypes = {
true
};
const std::string kFakeEntitlementKeyId =
a2bs_hex("2a538231c616c67143032a645f9c545d");
const std::string kFakeEntitledKeyId =
a2bs_hex("f93c7a81e62d4e9a988ff20bca60f52d");
const std::string kFakeUnpaddedKey =
a2bs_hex("b2047e7fab08b3a4dac76b7b82e8cd4d");
const std::string kFakePaddedKey =
a2bs_hex("42f804e9ce0fa693692e1c4ffaeb0e14"
"10101010101010101010101010101010");
const std::string kFakeKeyTooLong =
a2bs_hex("d4bc8605d662878a46adb2adb6bf3c0b30a54a0c2f");
const std::string kFakeKeyTooShort = a2bs_hex("06e247e7f924208011");
const std::string kFakeIv = a2bs_hex("3d515a3ee0be1687080ac59da9e0d69a");
class MockCryptoSession : public TestCryptoSession {
public:
MockCryptoSession(metrics::CryptoMetrics* crypto_metrics)
@@ -159,12 +174,16 @@ class MockCryptoSession : public TestCryptoSession {
MOCK_METHOD3(GenerateSubSessionNonce,
bool(const std::string& sub_session_key_id, bool* exists,
uint32_t* nonce));
MOCK_METHOD1(LoadEntitledContentKeys,
CdmResponseType(const std::vector<CryptoKey>& key_array));
};
class MockPolicyEngine : public PolicyEngine {
public:
MockPolicyEngine(CryptoSession* crypto)
: PolicyEngine("mock_session_id", NULL, crypto) {}
MOCK_METHOD1(SetEntitledLicenseKeys,
void(const std::vector<video_widevine::WidevinePsshData_EntitledKey>&));
};
class MockClock : public Clock {
@@ -185,16 +204,33 @@ class MockInitializationData : public InitializationData {
// Protobuf generated classes
using video_widevine::LicenseRequest_ContentIdentification;
using video_widevine::ClientIdentification;
using video_widevine::License;
using video_widevine::License_KeyContainer;
using video_widevine::LicenseRequest;
using video_widevine::SignedMessage;
using video_widevine::WidevinePsshData_EntitledKey;
// gmock methods
using ::testing::_;
using ::testing::Eq;
using ::testing::NotNull;
using ::testing::PrintToStringParamName;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::UnorderedElementsAre;
using ::testing::Values;
class CdmLicenseTestPeer : public CdmLicense {
public:
CdmLicenseTestPeer(const CdmSessionId& session_id, Clock* clock)
: CdmLicense(session_id, clock) {}
using CdmLicense::HandleNewEntitledKeys;
void set_entitlement_keys(License license) {
entitlement_keys_.CopyFrom(license.key());
}
};
class CdmLicenseTest : public WvCdmTestBase {
protected:
@@ -221,11 +257,11 @@ class CdmLicenseTest : public WvCdmTestBase {
}
virtual void CreateCdmLicense() {
cdm_license_ = new CdmLicense(kCdmSessionId, clock_);
cdm_license_ = new CdmLicenseTestPeer(kCdmSessionId, clock_);
clock_ = NULL;
}
CdmLicense* cdm_license_;
CdmLicenseTestPeer* cdm_license_;
MockClock* clock_;
metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_;
@@ -416,6 +452,85 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
EXPECT_EQ(kNonce, license_request.key_control_nonce());
}
struct EntitledKeyVariant {
EntitledKeyVariant(const char* name, const std::string& key,
bool should_succeed)
: name(name),
key(key),
should_succeed(should_succeed) {}
const std::string name;
const std::string key;
const bool should_succeed;
friend void PrintTo(const EntitledKeyVariant& self, std::ostream* os) {
*os << self.name;
}
};
class CdmLicenseEntitledKeyTest
: public CdmLicenseTest,
public ::testing::WithParamInterface<EntitledKeyVariant> {};
TEST_P(CdmLicenseEntitledKeyTest, LoadsEntitledKeys) {
EntitledKeyVariant variant = GetParam();
// Set up a known, fake entitlement key
License entitlement_license;
License_KeyContainer* entitlement_key = entitlement_license.add_key();
entitlement_key->set_type(
video_widevine::License_KeyContainer_KeyType_ENTITLEMENT);
entitlement_key->set_id(kFakeEntitlementKeyId);
// Set up a fake entitled key that matches the entitlement key
std::vector<WidevinePsshData_EntitledKey> entitled_keys(1);
WidevinePsshData_EntitledKey& padded_key = entitled_keys[0];
padded_key.set_entitlement_key_id(kFakeEntitlementKeyId);
padded_key.set_key_id(kFakeEntitledKeyId);
padded_key.set_key(variant.key);
padded_key.set_iv(kFakeIv);
// Set the expected downstream calls
EXPECT_CALL(*crypto_session_, IsOpen())
.WillOnce(Return(true));
if (variant.should_succeed) {
EXPECT_CALL(*crypto_session_, LoadEntitledContentKeys(_))
.WillOnce(Return(KEY_ADDED));
EXPECT_CALL(*policy_engine_, SetEntitledLicenseKeys(_))
.Times(1);
} else {
EXPECT_CALL(*crypto_session_, LoadEntitledContentKeys(_))
.Times(0);
EXPECT_CALL(*policy_engine_, SetEntitledLicenseKeys(_))
.Times(0);
}
// Set up the CdmLicense with the mocks and fake entitlement key
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(
kToken, kClientTokenDrmCert, kEmptyString, true,
kDefaultServiceCertificate, crypto_session_, policy_engine_));
cdm_license_->set_entitlement_keys(entitlement_license);
// Call the function under test and check its return value
CdmResponseType ret = cdm_license_->HandleNewEntitledKeys(entitled_keys);
if (variant.should_succeed) {
EXPECT_EQ(KEY_ADDED, ret);
} else {
EXPECT_NE(KEY_ADDED, ret);
}
}
INSTANTIATE_TEST_CASE_P(
EntitledKeyTests, CdmLicenseEntitledKeyTest,
Values(
EntitledKeyVariant("UnpaddedKey", kFakeUnpaddedKey, true),
EntitledKeyVariant("PaddedKey", kFakePaddedKey, true),
EntitledKeyVariant("KeyTooLong", kFakeKeyTooLong, true),
EntitledKeyVariant("KeyTooShort", kFakeKeyTooShort, false)),
PrintToStringParamName());
// TODO(jfore): The pssh has changed in ways that are not compatible with
//sublicenses. Restructure or remove sublicense support including this test.
TEST_F(SubLicenseTest, DISABLED_VerifySubSessionData) {

View File

@@ -446,8 +446,8 @@ TestLicenseHolder::TestLicenseHolder(CdmEngine* cdm_engine)
derived_mac_key_client_(MAC_KEY_SIZE, 'b'),
mac_key_server_(MAC_KEY_SIZE, 'c'),
mac_key_client_(MAC_KEY_SIZE, 'd'),
enc_key_(KEY_SIZE, 'e'),
session_key_(KEY_SIZE, 'f') {}
enc_key_(CONTENT_KEY_SIZE, 'e'),
session_key_(CONTENT_KEY_SIZE, 'f') {}
TestLicenseHolder::~TestLicenseHolder() {
CloseSession();
@@ -526,7 +526,7 @@ void TestLicenseHolder::CreateDefaultLicense() {
void TestLicenseHolder::AddMacKey() {
video_widevine::License_KeyContainer* key_container = license()->add_key();
std::vector<uint8_t> iv(KEY_SIZE, 'v');
std::vector<uint8_t> iv(KEY_IV_SIZE, 'v');
std::string iv_s(iv.begin(), iv.end());
key_container->set_iv(iv_s);
key_container->set_type(video_widevine::License_KeyContainer_KeyType_SIGNING);
@@ -556,20 +556,20 @@ video_widevine::License_KeyContainer* TestLicenseHolder::AddKey(
key_container->set_level(
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO);
std::vector<uint8_t> iv(KEY_SIZE, 'v');
std::vector<uint8_t> iv(KEY_IV_SIZE, 'v');
std::string iv_s(iv.begin(), iv.end());
key_container->set_iv(iv_s);
std::string encrypted_key_data =
WvCdmTestBase::Aes128CbcEncrypt(enc_key_, key_data, iv);
// TODO(b/111069024): remove this!
std::string padding(KEY_SIZE, '-');
std::string padding(CONTENT_KEY_SIZE, '-');
key_container->set_key(encrypted_key_data + padding);
std::vector<uint8_t> block_v(
reinterpret_cast<const uint8_t*>(&block),
reinterpret_cast<const uint8_t*>(&block) + sizeof(block));
std::vector<uint8_t> block_iv(KEY_SIZE, 'w');
std::vector<uint8_t> block_iv(KEY_IV_SIZE, 'w');
std::string block_iv_s(block_iv.begin(), block_iv.end());
std::string encrypted_block =
WvCdmTestBase::Aes128CbcEncrypt(key_data, block_v, block_iv);

View File

@@ -184,7 +184,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case KEY_REQUEST_ERROR_1: *os << "KEY_REQUEST_ERROR_1";
break;
case KEY_SIZE_ERROR: *os << "KEY_SIZE_ERROR";
case KEY_SIZE_ERROR_1: *os << "KEY_SIZE_ERROR_1";
break;
case KEY_SIZE_ERROR_2: *os << "KEY_SIZE_ERROR_2";
break;
case KEYSET_ID_NOT_FOUND_1: *os << "KEYSET_ID_NOT_FOUND_1";
break;