Support entitlement license with Widevine key server

- Added a new --enable_entitlement_license flag, which sets
  'enable_entitlement_license' in Widevine CommonEncryptionRequest;
- Support 'boxes' in Widevine CommonEncryptionResponse.

b/78171767

Change-Id: Id399fc7fcb2948c571e12c8af7687cfcfcef41fe
This commit is contained in:
KongQun Yang 2018-05-10 17:19:28 -07:00
parent 7f7843d241
commit 59941a101a
10 changed files with 205 additions and 92 deletions

View File

@ -323,6 +323,7 @@ base::Optional<PackagingParams> GetPackagingParams() {
widevine.content_id = FLAGS_content_id_bytes;
widevine.policy = FLAGS_policy;
widevine.group_id = FLAGS_group_id_bytes;
widevine.enable_entitlement_license = FLAGS_enable_entitlement_license;
if (!GetWidevineSigner(&widevine.signer))
return base::nullopt;
break;

View File

@ -81,6 +81,8 @@ std::unique_ptr<KeySource> CreateEncryptionKeySource(
widevine_key_source->set_signer(std::move(request_signer));
}
widevine_key_source->set_group_id(widevine.group_id);
widevine_key_source->set_enable_entitlement_license(
widevine.enable_entitlement_license);
Status status =
widevine_key_source->FetchKeys(widevine.content_id, widevine.policy);

View File

@ -59,6 +59,9 @@ DEFINE_int32(crypto_period_duration,
"Crypto period duration in seconds. If it is non-zero, key "
"rotation is enabled.");
DEFINE_hex_bytes(group_id, "", "Identifier for a group of licenses (hex).");
DEFINE_bool(enable_entitlement_license,
false,
"Enable entitlement license when using Widevine key server.");
namespace shaka {
namespace {

View File

@ -27,6 +27,7 @@ DECLARE_hex_bytes(aes_signing_iv);
DECLARE_string(rsa_signing_key_path);
DECLARE_int32(crypto_period_duration);
DECLARE_hex_bytes(group_id);
DECLARE_bool(enable_entitlement_license);
namespace shaka {

View File

@ -6,6 +6,8 @@
#include "packager/media/base/protection_system_specific_info.h"
#include <map>
#include "packager/media/base/buffer_reader.h"
#include "packager/media/base/buffer_writer.h"
#include "packager/media/base/fourccs.h"
@ -33,7 +35,9 @@ bool ProtectionSystemSpecificInfo::ParseBoxes(
const uint8_t* data,
size_t data_size,
std::vector<ProtectionSystemSpecificInfo>* pssh_infos) {
std::map<std::vector<uint8_t>, size_t> info_map;
pssh_infos->clear();
BufferReader reader(data, data_size);
while (reader.HasBytes(1)) {
uint32_t size;
@ -41,10 +45,17 @@ bool ProtectionSystemSpecificInfo::ParseBoxes(
RCHECK(reader.SkipBytes(size - 4));
RCHECK(size > kPsshBoxHeaderSize + kSystemIdSize);
pssh_infos->push_back(
{std::vector<uint8_t>(data + kPsshBoxHeaderSize,
data + kPsshBoxHeaderSize + kSystemIdSize),
std::vector<uint8_t>(data, data + size)});
const std::vector<uint8_t> system_id(
data + kPsshBoxHeaderSize, data + kPsshBoxHeaderSize + kSystemIdSize);
auto iter = info_map.find(system_id);
if (iter != info_map.end()) {
ProtectionSystemSpecificInfo& info = (*pssh_infos)[iter->second];
info.psshs.insert(info.psshs.end(), data, data + size);
} else {
pssh_infos->push_back(
{system_id, std::vector<uint8_t>(data, data + size)});
info_map[system_id] = pssh_infos->size() - 1;
}
data += size;
}
@ -63,7 +74,6 @@ std::unique_ptr<PsshBoxBuilder> PsshBoxBuilder::ParseFromBox(
uint32_t version_and_flags;
RETURN_NULL_IF_FALSE(reader.Read4(&size));
RETURN_NULL_IF_FALSE(reader.Read4(&box_type));
RETURN_NULL_IF_FALSE(size == data_size);
RETURN_NULL_IF_FALSE(box_type == FOURCC_pssh);
RETURN_NULL_IF_FALSE(reader.Read4(&version_and_flags));
@ -90,10 +100,7 @@ std::unique_ptr<PsshBoxBuilder> PsshBoxBuilder::ParseFromBox(
RETURN_NULL_IF_FALSE(
reader.ReadToVector(&pssh_builder->pssh_data_, pssh_data_size));
// We should be at the end of the data. The reader should be initialized to
// the data and size according to the size field of the box; therefore it
// is an error if there are bytes remaining.
RETURN_NULL_IF_FALSE(!reader.HasBytes(1));
// Ignore extra data if there is any.
return pssh_builder;
}

View File

@ -13,52 +13,86 @@ namespace shaka {
namespace media {
namespace {
const uint8_t kV0BoxArray[] = {
0x00, 0x00, 0x00, 0x21, 'p', 's', 's', 'h', // Header
0x00, 0x00, 0x00, 0x00, // Version = 0, flags = 0
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x00, 0x00, 0x01, // Data size(1)
0xFF
const uint8_t kSystemId1V0BoxArray[] = {
0x00, 0x00, 0x00, 0x21, 'p', 's', 's', 'h', // Header
0x00, 0x00, 0x00, 0x00, // Version = 0, flags = 0
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x00, 0x00, 0x01, // Data size(1)
0xFF,
};
const uint8_t kV1BoxArray[] = {
0x00, 0x00, 0x00, 0x35, 'p', 's', 's', 'h', // Header
0x01, 0x00, 0x00, 0x00, // Version = 1, flags = 0
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x00, 0x00, 0x01, // KID_count(1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // First KID
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, // Data size(1)
0xFF
const uint8_t kSystemId1V1BoxArray[] = {
0x00, 0x00, 0x00, 0x35, 'p', 's', 's', 'h', // Header
0x01, 0x00, 0x00, 0x00, // Version = 1, flags = 0
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x00, 0x00, 0x01, // KID_count(1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // First KID
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, // Data size(1)
0xFF,
};
const uint8_t kSystemId2V0BoxArray[] = {
0x00, 0x00, 0x00, 0x21, 'p', 's', 's', 'h', // Header
0x00, 0x00, 0x00, 0x00, // Version = 0, flags = 0
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, // System ID
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
0x00, 0x00, 0x00, 0x01, // Data size(1)
0xFF,
};
const uint8_t kTestSystemIdArray[] = {
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
const uint8_t kTestSystemId1Array[] = {
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
};
const uint8_t kTestSystemId2Array[] = {
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
};
const uint8_t kTestKeyIdArray[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // First KID
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const uint8_t kTestPsshDataArray[] = {0xFF};
std::vector<uint8_t> ConcatVectors(const std::vector<uint8_t>& a,
const std::vector<uint8_t>& b) {
std::vector<uint8_t> out;
out.insert(out.end(), a.begin(), a.end());
out.insert(out.end(), b.begin(), b.end());
return out;
}
std::vector<uint8_t> ConcatVectors(const std::vector<uint8_t>& a,
const std::vector<uint8_t>& b,
const std::vector<uint8_t>& c) {
return ConcatVectors(ConcatVectors(a, b), c);
}
} // namespace
class PsshTest : public ::testing::Test {
public:
PsshTest()
: v0_box_(kV0BoxArray, kV0BoxArray + arraysize(kV0BoxArray)),
v1_box_(kV1BoxArray, kV1BoxArray + arraysize(kV1BoxArray)),
test_system_id_(kTestSystemIdArray,
kTestSystemIdArray + arraysize(kTestSystemIdArray)),
test_key_id_(kTestKeyIdArray,
kTestKeyIdArray + arraysize(kTestKeyIdArray)),
test_pssh_data_(kTestPsshDataArray,
kTestPsshDataArray + arraysize(kTestPsshDataArray)) {}
: system_id1_v0_box_(std::begin(kSystemId1V0BoxArray),
std::end(kSystemId1V0BoxArray)),
system_id1_v1_box_(std::begin(kSystemId1V1BoxArray),
std::end(kSystemId1V1BoxArray)),
system_id2_v0_box_(std::begin(kSystemId2V0BoxArray),
std::end(kSystemId2V0BoxArray)),
test_system_id1_(std::begin(kTestSystemId1Array),
std::end(kTestSystemId1Array)),
test_system_id2_(std::begin(kTestSystemId2Array),
std::end(kTestSystemId2Array)),
test_key_id_(std::begin(kTestKeyIdArray), std::end(kTestKeyIdArray)),
test_pssh_data_(std::begin(kTestPsshDataArray),
std::end(kTestPsshDataArray)) {}
const std::vector<uint8_t> v0_box_;
const std::vector<uint8_t> v1_box_;
const std::vector<uint8_t> test_system_id_;
const std::vector<uint8_t> system_id1_v0_box_;
const std::vector<uint8_t> system_id1_v1_box_;
const std::vector<uint8_t> system_id2_v0_box_;
const std::vector<uint8_t> test_system_id1_;
const std::vector<uint8_t> test_system_id2_;
const std::vector<uint8_t> test_key_id_;
const std::vector<uint8_t> test_pssh_data_;
};
@ -66,17 +100,17 @@ class PsshTest : public ::testing::Test {
TEST_F(PsshTest, ParseBoxes_SupportsV0) {
std::vector<ProtectionSystemSpecificInfo> info;
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(
v0_box_.data(), v0_box_.size(), &info));
system_id1_v0_box_.data(), system_id1_v0_box_.size(), &info));
ASSERT_EQ(1u, info.size());
EXPECT_EQ(test_system_id_, info[0].system_id);
EXPECT_EQ(test_system_id1_, info[0].system_id);
std::unique_ptr<PsshBoxBuilder> pssh_builder =
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
ASSERT_TRUE(pssh_builder);
ASSERT_EQ(0u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_system_id1_, pssh_builder->system_id());
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(0, pssh_builder->pssh_box_version());
}
@ -84,80 +118,74 @@ TEST_F(PsshTest, ParseBoxes_SupportsV0) {
TEST_F(PsshTest, ParseBoxes_SupportsV1) {
std::vector<ProtectionSystemSpecificInfo> info;
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(
v1_box_.data(), v1_box_.size(), &info));
system_id1_v1_box_.data(), system_id1_v1_box_.size(), &info));
ASSERT_EQ(1u, info.size());
EXPECT_EQ(test_system_id_, info[0].system_id);
EXPECT_EQ(test_system_id1_, info[0].system_id);
std::unique_ptr<PsshBoxBuilder> pssh_builder =
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
ASSERT_TRUE(pssh_builder);
ASSERT_EQ(1u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_system_id1_, pssh_builder->system_id());
EXPECT_EQ(test_key_id_, pssh_builder->key_ids()[0]);
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(1, pssh_builder->pssh_box_version());
}
TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) {
std::vector<uint8_t> data;
data.insert(data.end(), v1_box_.begin(), v1_box_.end());
data.insert(data.end(), v0_box_.begin(), v0_box_.end());
data.insert(data.end(), v1_box_.begin(), v1_box_.end());
std::vector<uint8_t> data =
ConcatVectors(system_id1_v0_box_, system_id2_v0_box_, system_id1_v1_box_);
std::vector<ProtectionSystemSpecificInfo> info;
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(data.data(), data.size(),
&info));
ASSERT_EQ(3u, info.size());
// The PSSHs are grouped by system id. Since there are only two system ids,
// there are two ProtectionSystemSpecificInfo.
ASSERT_EQ(2u, info.size());
EXPECT_EQ(ConcatVectors(system_id1_v0_box_, system_id1_v1_box_),
info[0].psshs);
EXPECT_EQ(system_id2_v0_box_, info[1].psshs);
std::unique_ptr<PsshBoxBuilder> pssh_builder =
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
ASSERT_TRUE(pssh_builder);
ASSERT_EQ(1u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_key_id_, pssh_builder->key_ids()[0]);
ASSERT_EQ(0u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id1_, pssh_builder->system_id());
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(1, pssh_builder->pssh_box_version());
EXPECT_EQ(0, pssh_builder->pssh_box_version());
pssh_builder =
PsshBoxBuilder::ParseFromBox(info[1].psshs.data(), info[1].psshs.size());
ASSERT_TRUE(pssh_builder);
ASSERT_EQ(0u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_system_id2_, pssh_builder->system_id());
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(0, pssh_builder->pssh_box_version());
pssh_builder =
PsshBoxBuilder::ParseFromBox(info[2].psshs.data(), info[2].psshs.size());
ASSERT_TRUE(pssh_builder);
ASSERT_EQ(1u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_key_id_, pssh_builder->key_ids()[0]);
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(1, pssh_builder->pssh_box_version());
}
TEST_F(PsshTest, CreateBox_MakesV0Boxes) {
PsshBoxBuilder pssh_builder;
pssh_builder.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray));
pssh_builder.set_system_id(kTestSystemId1Array,
arraysize(kTestSystemId1Array));
pssh_builder.set_pssh_data(test_pssh_data_);
pssh_builder.set_pssh_box_version(0);
EXPECT_EQ(v0_box_, pssh_builder.CreateBox());
EXPECT_EQ(system_id1_v0_box_, pssh_builder.CreateBox());
}
TEST_F(PsshTest, CreateBox_MakesV1Boxes) {
PsshBoxBuilder pssh_builder;
pssh_builder.add_key_id(test_key_id_);
pssh_builder.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray));
pssh_builder.set_system_id(kTestSystemId1Array,
arraysize(kTestSystemId1Array));
pssh_builder.set_pssh_data(test_pssh_data_);
pssh_builder.set_pssh_box_version(1);
EXPECT_EQ(v1_box_, pssh_builder.CreateBox());
EXPECT_EQ(system_id1_v1_box_, pssh_builder.CreateBox());
}
} // namespace media

View File

@ -57,6 +57,24 @@ CommonEncryptionRequest::ProtectionScheme ToCommonEncryptionProtectionScheme(
}
}
ProtectionSystemSpecificInfo ProtectionSystemInfoFromPsshProto(
const CommonEncryptionResponse::Track::Pssh& pssh_proto) {
PsshBoxBuilder pssh_builder;
pssh_builder.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
if (pssh_proto.has_boxes()) {
return {pssh_builder.system_id(),
std::vector<uint8_t>(pssh_proto.boxes().begin(),
pssh_proto.boxes().end())};
} else {
pssh_builder.set_pssh_box_version(0);
const std::vector<uint8_t> pssh_data(pssh_proto.data().begin(),
pssh_proto.data().end());
pssh_builder.set_pssh_data(pssh_data);
return {pssh_builder.system_id(), pssh_builder.CreateBox()};
}
}
} // namespace
WidevineKeySource::WidevineKeySource(const std::string& server_url,
@ -73,7 +91,8 @@ WidevineKeySource::WidevineKeySource(const std::string& server_url,
key_production_started_(false),
start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED),
first_crypto_period_index_(0) {
first_crypto_period_index_(0),
enable_entitlement_license_(false) {
key_production_thread_.Start();
}
@ -97,6 +116,8 @@ Status WidevineKeySource::FetchKeys(const std::vector<uint8_t>& content_id,
common_encryption_request_->set_policy(policy);
common_encryption_request_->set_protection_scheme(
ToCommonEncryptionProtectionScheme(protection_scheme_));
if (enable_entitlement_license_)
common_encryption_request_->set_enable_entitlement_license(true);
return FetchKeysInternal(!kEnableKeyRotation, 0, false);
}
@ -218,10 +239,6 @@ void WidevineKeySource::set_key_fetcher(
key_fetcher_ = std::move(key_fetcher);
}
void WidevineKeySource::set_group_id(const std::vector<uint8_t>& group_id) {
group_id_ = group_id;
}
Status WidevineKeySource::GetKeyInternal(uint32_t crypto_period_index,
const std::string& stream_label,
EncryptionKey* key) {
@ -430,19 +447,8 @@ bool WidevineKeySource::ExtractEncryptionKey(
<< track.pssh_size();
return false;
}
const auto& pssh_proto = track.pssh(0);
const std::vector<uint8_t> pssh_data(pssh_proto.data().begin(),
pssh_proto.data().end());
PsshBoxBuilder pssh_builder;
pssh_builder.add_key_id(encryption_key->key_id);
pssh_builder.set_system_id(kWidevineSystemId,
arraysize(kWidevineSystemId));
pssh_builder.set_pssh_box_version(0);
pssh_builder.set_pssh_data(pssh_data);
encryption_key->key_system_info.push_back(
{pssh_builder.system_id(), pssh_builder.CreateBox()});
ProtectionSystemInfoFromPsshProto(track.pssh(0)));
}
encryption_key_map[stream_label] = std::move(encryption_key);
}

View File

@ -68,9 +68,12 @@ class WidevineKeySource : public KeySource {
/// @param key_fetcher points to the @b KeyFetcher object to be injected.
void set_key_fetcher(std::unique_ptr<KeyFetcher> key_fetcher);
// Set the group id for the key source
// @param group_id group identifier
void set_group_id(const std::vector<uint8_t>& group_id);
void set_group_id(const std::vector<uint8_t>& group_id) {
group_id_ = group_id;
}
void set_enable_entitlement_license(bool enable_entitlement_license) {
enable_entitlement_license_ = enable_entitlement_license;
}
private:
typedef ProducerConsumerQueue<std::shared_ptr<EncryptionKeyMap>>
@ -125,6 +128,7 @@ class WidevineKeySource : public KeySource {
base::WaitableEvent start_key_production_;
uint32_t first_crypto_period_index_;
std::vector<uint8_t> group_id_;
bool enable_entitlement_license_;
std::unique_ptr<EncryptionKeyQueue> key_pool_;
EncryptionKeyMap encryption_key_map_; // For non key rotation request.
Status common_encryption_request_status_;

View File

@ -56,6 +56,12 @@ const char kExpectedRequestMessageFormat[] =
R"("tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)"
R"({"type":"UHD2"},{"type":"AUDIO"}],)"
R"("drm_types":["WIDEVINE"],"protection_scheme":"%s"})";
const char kExpectedRequestMessageFormatWithEntitlement[] =
R"({"content_id":"%s","policy":"%s",)"
R"("tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)"
R"({"type":"UHD2"},{"type":"AUDIO"}],)"
R"("drm_types":["WIDEVINE"],"protection_scheme":"%s",)"
R"("enable_entitlement_license":true})";
const char kExpectedRequestMessageWithAssetIdFormat[] =
R"({"tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)"
R"({"type":"UHD2"},{"type":"AUDIO"}],)"
@ -68,6 +74,9 @@ const char kExpectedSignedMessageFormat[] =
R"({"request":"%s","signature":"%s","signer":"%s"})";
const char kTrackFormat[] = R"({"type":"%s","key_id":"%s","key":"%s",)"
R"("pssh":[{"drm_type":"WIDEVINE","data":"%s"}]})";
const char kTrackFormatWithBoxes[] =
R"({"type":"%s","key_id":"%s","key":"%s",)"
R"("pssh":[{"drm_type":"WIDEVINE","data":"%s","boxes":"%s"}]})";
const char kClassicTrackFormat[] = R"({"type":"%s","key":"%s"})";
const char kLicenseResponseFormat[] = R"({"status":"%s","tracks":[%s]})";
const char kHttpResponseFormat[] = R"({"response":"%s"})";
@ -115,6 +124,21 @@ std::string GetMockPsshData() {
return kRequestPsshData;
}
std::string GenerateMockLicenseResponseWithBoxes(const std::string& boxes) {
const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
std::string tracks;
for (size_t i = 0; i < 5; ++i) {
if (!tracks.empty())
tracks += ",";
tracks += base::StringPrintf(
kTrackFormatWithBoxes, kTrackTypes[i].c_str(),
Base64Encode(GetMockKeyId(kTrackTypes[i])).c_str(),
Base64Encode(GetMockKey(kTrackTypes[i])).c_str(),
Base64Encode(GetMockPsshData()).c_str(), boxes.c_str());
}
return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str());
}
std::string GenerateMockLicenseResponse() {
const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
std::string tracks;
@ -323,6 +347,26 @@ TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) {
widevine_key_source_->FetchKeys(content_id_, kPolicy).error_code());
}
TEST_F(WidevineKeySourceTest, BoxesInResponse) {
const char kMockBoxes[] = "mock_pssh_boxes";
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponseWithBoxes(
Base64Encode(kMockBoxes)))
.c_str());
EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
CreateWidevineKeySource();
ASSERT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
const char kHdStreamLabel[] = "HD";
EncryptionKey encryption_key;
ASSERT_OK(widevine_key_source_->GetKey(kHdStreamLabel, &encryption_key));
ASSERT_EQ(1u, encryption_key.key_system_info.size());
ASSERT_EQ(kMockBoxes, ToString(encryption_key.key_system_info.front().psshs));
}
class WidevineKeySourceParameterizedTest
: public WidevineKeySourceTest,
public WithParamInterface<std::tr1::tuple<bool, bool, FourCC>> {
@ -453,6 +497,21 @@ TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusClassicOK) {
VerifyKeys(true);
}
TEST_P(WidevineKeySourceParameterizedTest, VerifyEntitlementLicenseRequest) {
const std::string expected_message =
base::StringPrintf(kExpectedRequestMessageFormatWithEntitlement,
Base64Encode(kContentId).c_str(), kPolicy,
GetExpectedProtectionScheme().c_str());
EXPECT_CALL(*mock_request_signer_,
GenerateSignature(StrEq(expected_message), _))
.WillOnce(Return(false));
CreateWidevineKeySource();
widevine_key_source_->set_enable_entitlement_license(true);
widevine_key_source_->set_signer(std::move(mock_request_signer_));
ASSERT_NOT_OK(widevine_key_source_->FetchKeys(content_id_, kPolicy));
}
namespace {
const char kCryptoPeriodRequestMessageFormat[] =

View File

@ -59,6 +59,8 @@ struct WidevineEncryptionParams {
WidevineSigner signer;
/// Group identifier, if present licenses will belong to this group.
std::vector<uint8_t> group_id;
/// Enables entitlement license when set to true.
bool enable_entitlement_license;
};
/// PlayReady encryption parameters.