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.content_id = FLAGS_content_id_bytes;
widevine.policy = FLAGS_policy; widevine.policy = FLAGS_policy;
widevine.group_id = FLAGS_group_id_bytes; widevine.group_id = FLAGS_group_id_bytes;
widevine.enable_entitlement_license = FLAGS_enable_entitlement_license;
if (!GetWidevineSigner(&widevine.signer)) if (!GetWidevineSigner(&widevine.signer))
return base::nullopt; return base::nullopt;
break; 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_signer(std::move(request_signer));
} }
widevine_key_source->set_group_id(widevine.group_id); widevine_key_source->set_group_id(widevine.group_id);
widevine_key_source->set_enable_entitlement_license(
widevine.enable_entitlement_license);
Status status = Status status =
widevine_key_source->FetchKeys(widevine.content_id, widevine.policy); 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 " "Crypto period duration in seconds. If it is non-zero, key "
"rotation is enabled."); "rotation is enabled.");
DEFINE_hex_bytes(group_id, "", "Identifier for a group of licenses (hex)."); 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 shaka {
namespace { namespace {

View File

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

View File

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

View File

@ -13,52 +13,86 @@ namespace shaka {
namespace media { namespace media {
namespace { namespace {
const uint8_t kV0BoxArray[] = { const uint8_t kSystemId1V0BoxArray[] = {
0x00, 0x00, 0x00, 0x21, 'p', 's', 's', 'h', // Header 0x00, 0x00, 0x00, 0x21, 'p', 's', 's', 'h', // Header
0x00, 0x00, 0x00, 0x00, // Version = 0, flags = 0 0x00, 0x00, 0x00, 0x00, // Version = 0, flags = 0
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x00, 0x00, 0x01, // Data size(1) 0x00, 0x00, 0x00, 0x01, // Data size(1)
0xFF 0xFF,
}; };
const uint8_t kV1BoxArray[] = { const uint8_t kSystemId1V1BoxArray[] = {
0x00, 0x00, 0x00, 0x35, 'p', 's', 's', 'h', // Header 0x00, 0x00, 0x00, 0x35, 'p', 's', 's', 'h', // Header
0x01, 0x00, 0x00, 0x00, // Version = 1, flags = 0 0x01, 0x00, 0x00, 0x00, // Version = 1, flags = 0
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0x00, 0x00, 0x00, 0x01, // KID_count(1) 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, // First KID
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, // Data size(1) 0x00, 0x00, 0x00, 0x01, // Data size(1)
0xFF 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[] = { const uint8_t kTestSystemId1Array[] = {
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, // System ID 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 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[] = { 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}; 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 } // namespace
class PsshTest : public ::testing::Test { class PsshTest : public ::testing::Test {
public: public:
PsshTest() PsshTest()
: v0_box_(kV0BoxArray, kV0BoxArray + arraysize(kV0BoxArray)), : system_id1_v0_box_(std::begin(kSystemId1V0BoxArray),
v1_box_(kV1BoxArray, kV1BoxArray + arraysize(kV1BoxArray)), std::end(kSystemId1V0BoxArray)),
test_system_id_(kTestSystemIdArray, system_id1_v1_box_(std::begin(kSystemId1V1BoxArray),
kTestSystemIdArray + arraysize(kTestSystemIdArray)), std::end(kSystemId1V1BoxArray)),
test_key_id_(kTestKeyIdArray, system_id2_v0_box_(std::begin(kSystemId2V0BoxArray),
kTestKeyIdArray + arraysize(kTestKeyIdArray)), std::end(kSystemId2V0BoxArray)),
test_pssh_data_(kTestPsshDataArray, test_system_id1_(std::begin(kTestSystemId1Array),
kTestPsshDataArray + arraysize(kTestPsshDataArray)) {} 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> system_id1_v0_box_;
const std::vector<uint8_t> v1_box_; const std::vector<uint8_t> system_id1_v1_box_;
const std::vector<uint8_t> test_system_id_; 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_key_id_;
const std::vector<uint8_t> test_pssh_data_; const std::vector<uint8_t> test_pssh_data_;
}; };
@ -66,17 +100,17 @@ class PsshTest : public ::testing::Test {
TEST_F(PsshTest, ParseBoxes_SupportsV0) { TEST_F(PsshTest, ParseBoxes_SupportsV0) {
std::vector<ProtectionSystemSpecificInfo> info; std::vector<ProtectionSystemSpecificInfo> info;
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes( 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()); 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 = std::unique_ptr<PsshBoxBuilder> pssh_builder =
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size()); PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
ASSERT_TRUE(pssh_builder); ASSERT_TRUE(pssh_builder);
ASSERT_EQ(0u, pssh_builder->key_ids().size()); 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(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(0, pssh_builder->pssh_box_version()); EXPECT_EQ(0, pssh_builder->pssh_box_version());
} }
@ -84,80 +118,74 @@ TEST_F(PsshTest, ParseBoxes_SupportsV0) {
TEST_F(PsshTest, ParseBoxes_SupportsV1) { TEST_F(PsshTest, ParseBoxes_SupportsV1) {
std::vector<ProtectionSystemSpecificInfo> info; std::vector<ProtectionSystemSpecificInfo> info;
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes( 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()); 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 = std::unique_ptr<PsshBoxBuilder> pssh_builder =
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size()); PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
ASSERT_TRUE(pssh_builder); ASSERT_TRUE(pssh_builder);
ASSERT_EQ(1u, pssh_builder->key_ids().size()); 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_key_id_, pssh_builder->key_ids()[0]);
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data()); EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(1, pssh_builder->pssh_box_version()); EXPECT_EQ(1, pssh_builder->pssh_box_version());
} }
TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) { TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) {
std::vector<uint8_t> data; std::vector<uint8_t> data =
data.insert(data.end(), v1_box_.begin(), v1_box_.end()); ConcatVectors(system_id1_v0_box_, system_id2_v0_box_, system_id1_v1_box_);
data.insert(data.end(), v0_box_.begin(), v0_box_.end());
data.insert(data.end(), v1_box_.begin(), v1_box_.end());
std::vector<ProtectionSystemSpecificInfo> info; std::vector<ProtectionSystemSpecificInfo> info;
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(data.data(), data.size(), ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(data.data(), data.size(),
&info)); &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 = std::unique_ptr<PsshBoxBuilder> pssh_builder =
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size()); PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
ASSERT_TRUE(pssh_builder); ASSERT_TRUE(pssh_builder);
ASSERT_EQ(1u, pssh_builder->key_ids().size()); 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_key_id_, pssh_builder->key_ids()[0]);
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data()); 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 = pssh_builder =
PsshBoxBuilder::ParseFromBox(info[1].psshs.data(), info[1].psshs.size()); PsshBoxBuilder::ParseFromBox(info[1].psshs.data(), info[1].psshs.size());
ASSERT_TRUE(pssh_builder); ASSERT_TRUE(pssh_builder);
ASSERT_EQ(0u, pssh_builder->key_ids().size()); 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(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(0, pssh_builder->pssh_box_version()); 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) { TEST_F(PsshTest, CreateBox_MakesV0Boxes) {
PsshBoxBuilder pssh_builder; 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_data(test_pssh_data_);
pssh_builder.set_pssh_box_version(0); 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) { TEST_F(PsshTest, CreateBox_MakesV1Boxes) {
PsshBoxBuilder pssh_builder; PsshBoxBuilder pssh_builder;
pssh_builder.add_key_id(test_key_id_); 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_data(test_pssh_data_);
pssh_builder.set_pssh_box_version(1); 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 } // 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 } // namespace
WidevineKeySource::WidevineKeySource(const std::string& server_url, WidevineKeySource::WidevineKeySource(const std::string& server_url,
@ -73,7 +91,8 @@ WidevineKeySource::WidevineKeySource(const std::string& server_url,
key_production_started_(false), key_production_started_(false),
start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC, start_key_production_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED), base::WaitableEvent::InitialState::NOT_SIGNALED),
first_crypto_period_index_(0) { first_crypto_period_index_(0),
enable_entitlement_license_(false) {
key_production_thread_.Start(); 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_policy(policy);
common_encryption_request_->set_protection_scheme( common_encryption_request_->set_protection_scheme(
ToCommonEncryptionProtectionScheme(protection_scheme_)); ToCommonEncryptionProtectionScheme(protection_scheme_));
if (enable_entitlement_license_)
common_encryption_request_->set_enable_entitlement_license(true);
return FetchKeysInternal(!kEnableKeyRotation, 0, false); return FetchKeysInternal(!kEnableKeyRotation, 0, false);
} }
@ -218,10 +239,6 @@ void WidevineKeySource::set_key_fetcher(
key_fetcher_ = std::move(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, Status WidevineKeySource::GetKeyInternal(uint32_t crypto_period_index,
const std::string& stream_label, const std::string& stream_label,
EncryptionKey* key) { EncryptionKey* key) {
@ -430,19 +447,8 @@ bool WidevineKeySource::ExtractEncryptionKey(
<< track.pssh_size(); << track.pssh_size();
return false; 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( 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); 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. /// @param key_fetcher points to the @b KeyFetcher object to be injected.
void set_key_fetcher(std::unique_ptr<KeyFetcher> key_fetcher); void set_key_fetcher(std::unique_ptr<KeyFetcher> key_fetcher);
// Set the group id for the key source void set_group_id(const std::vector<uint8_t>& group_id) {
// @param group_id group identifier group_id_ = group_id;
void set_group_id(const std::vector<uint8_t>& group_id); }
void set_enable_entitlement_license(bool enable_entitlement_license) {
enable_entitlement_license_ = enable_entitlement_license;
}
private: private:
typedef ProducerConsumerQueue<std::shared_ptr<EncryptionKeyMap>> typedef ProducerConsumerQueue<std::shared_ptr<EncryptionKeyMap>>
@ -125,6 +128,7 @@ class WidevineKeySource : public KeySource {
base::WaitableEvent start_key_production_; base::WaitableEvent start_key_production_;
uint32_t first_crypto_period_index_; uint32_t first_crypto_period_index_;
std::vector<uint8_t> group_id_; std::vector<uint8_t> group_id_;
bool enable_entitlement_license_;
std::unique_ptr<EncryptionKeyQueue> key_pool_; std::unique_ptr<EncryptionKeyQueue> key_pool_;
EncryptionKeyMap encryption_key_map_; // For non key rotation request. EncryptionKeyMap encryption_key_map_; // For non key rotation request.
Status common_encryption_request_status_; Status common_encryption_request_status_;

View File

@ -56,6 +56,12 @@ const char kExpectedRequestMessageFormat[] =
R"("tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)" R"("tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)"
R"({"type":"UHD2"},{"type":"AUDIO"}],)" R"({"type":"UHD2"},{"type":"AUDIO"}],)"
R"("drm_types":["WIDEVINE"],"protection_scheme":"%s"})"; 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[] = const char kExpectedRequestMessageWithAssetIdFormat[] =
R"({"tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)" R"({"tracks":[{"type":"SD"},{"type":"HD"},{"type":"UHD1"},)"
R"({"type":"UHD2"},{"type":"AUDIO"}],)" R"({"type":"UHD2"},{"type":"AUDIO"}],)"
@ -68,6 +74,9 @@ const char kExpectedSignedMessageFormat[] =
R"({"request":"%s","signature":"%s","signer":"%s"})"; R"({"request":"%s","signature":"%s","signer":"%s"})";
const char kTrackFormat[] = R"({"type":"%s","key_id":"%s","key":"%s",)" const char kTrackFormat[] = R"({"type":"%s","key_id":"%s","key":"%s",)"
R"("pssh":[{"drm_type":"WIDEVINE","data":"%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 kClassicTrackFormat[] = R"({"type":"%s","key":"%s"})";
const char kLicenseResponseFormat[] = R"({"status":"%s","tracks":[%s]})"; const char kLicenseResponseFormat[] = R"({"status":"%s","tracks":[%s]})";
const char kHttpResponseFormat[] = R"({"response":"%s"})"; const char kHttpResponseFormat[] = R"({"response":"%s"})";
@ -115,6 +124,21 @@ std::string GetMockPsshData() {
return kRequestPsshData; 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() { std::string GenerateMockLicenseResponse() {
const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"}; const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"};
std::string tracks; std::string tracks;
@ -323,6 +347,26 @@ TEST_F(WidevineKeySourceTest, NoRetryOnUnknownError) {
widevine_key_source_->FetchKeys(content_id_, kPolicy).error_code()); 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 class WidevineKeySourceParameterizedTest
: public WidevineKeySourceTest, : public WidevineKeySourceTest,
public WithParamInterface<std::tr1::tuple<bool, bool, FourCC>> { public WithParamInterface<std::tr1::tuple<bool, bool, FourCC>> {
@ -453,6 +497,21 @@ TEST_P(WidevineKeySourceParameterizedTest, LicenseStatusClassicOK) {
VerifyKeys(true); 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 { namespace {
const char kCryptoPeriodRequestMessageFormat[] = const char kCryptoPeriodRequestMessageFormat[] =

View File

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