Entitlement rotation support
Updates also include: - Add APIs to query current Simulcrypt channel & stream status; - EMM format change (used only to carry fingerprinting and service blocking info); - Key fetcher example to use curl key fetcher.
This commit is contained in:
@@ -206,6 +206,7 @@ cc_library(
|
||||
"//base",
|
||||
"@abseil_repo//absl/memory",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/types:span",
|
||||
"//common:status",
|
||||
"//media_cas_packager_sdk/internal:ecmg_client_handler",
|
||||
],
|
||||
@@ -242,6 +243,7 @@ cc_test(
|
||||
":wv_cas_types",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/memory",
|
||||
"@abseil_repo//absl/types:span",
|
||||
"//common:status",
|
||||
"//media_cas_packager_sdk/internal:ecmg_client_handler",
|
||||
],
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
@@ -51,16 +52,11 @@ constexpr uint32_t kReservedBit = 0x0007;
|
||||
// The range of valid PIDs, from section 2.4.3.3, and table 2-3.
|
||||
constexpr uint32_t kMinValidPID = 0x0010;
|
||||
constexpr uint32_t kMaxValidPID = 0x1FFE;
|
||||
|
||||
// Maximum number of entitlement key ids shown in private data.
|
||||
constexpr uint32_t kMaxNumOfEntitlementKeyIds = 2;
|
||||
// Entitlement key id length is fixed to 16 bytes.
|
||||
constexpr uint16_t kEntitlementKeyIdLength = 16;
|
||||
} // namespace
|
||||
|
||||
Status WvCasCaDescriptor::GenerateCaDescriptor(
|
||||
uint16_t ca_pid, const std::string& provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids,
|
||||
const std::vector<std::string>& group_ids,
|
||||
std::string* serialized_ca_desc) const {
|
||||
if (serialized_ca_desc == nullptr) {
|
||||
return {error::INVALID_ARGUMENT,
|
||||
@@ -69,25 +65,11 @@ Status WvCasCaDescriptor::GenerateCaDescriptor(
|
||||
if (ca_pid < kMinValidPID || ca_pid > kMaxValidPID) {
|
||||
return {error::INVALID_ARGUMENT, "PID value is out of the valid range."};
|
||||
}
|
||||
if (entitlement_key_ids.size() > kMaxNumOfEntitlementKeyIds) {
|
||||
return {error::INVALID_ARGUMENT,
|
||||
absl::StrCat("Number of entitlement key ids shouldn't exceed ",
|
||||
kMaxNumOfEntitlementKeyIds)};
|
||||
}
|
||||
for (const auto& entitlement_key_id : entitlement_key_ids) {
|
||||
if (entitlement_key_id.size() != kEntitlementKeyIdLength) {
|
||||
return {error::INVALID_ARGUMENT,
|
||||
absl::StrCat("Entitlement key id length must be ",
|
||||
kEntitlementKeyIdLength,
|
||||
". The offending key id is ", entitlement_key_id)};
|
||||
}
|
||||
}
|
||||
|
||||
std::string private_data = "";
|
||||
std::string private_data;
|
||||
// Field of Entitlement_key_ids could be empty.
|
||||
if (!provider.empty() && !content_id.empty()) {
|
||||
private_data =
|
||||
GeneratePrivateData(provider, content_id, entitlement_key_ids);
|
||||
private_data = GeneratePrivateData(provider, content_id, group_ids);
|
||||
}
|
||||
|
||||
const size_t descriptor_length =
|
||||
@@ -130,12 +112,12 @@ size_t WvCasCaDescriptor::CaDescriptorBaseSize() const {
|
||||
|
||||
std::string WvCasCaDescriptor::GeneratePrivateData(
|
||||
const std::string& provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids) const {
|
||||
const std::vector<std::string>& group_ids) const {
|
||||
CaDescriptorPrivateData private_data;
|
||||
private_data.set_provider(provider);
|
||||
private_data.set_content_id(content_id);
|
||||
for (const auto& entitlement_key_id : entitlement_key_ids) {
|
||||
private_data.add_entitlement_key_ids(entitlement_key_id);
|
||||
for (const auto& group_id : group_ids) {
|
||||
private_data.add_group_ids(group_id);
|
||||
}
|
||||
return private_data.SerializeAsString();
|
||||
}
|
||||
|
||||
@@ -50,9 +50,7 @@ class WvCasCaDescriptor {
|
||||
// |ca_pid| the 13-bit PID of the ECMs
|
||||
// |provider| provider name, put in private data for client to construct pssh
|
||||
// |content_id| content ID, put in private data for client to construct pssh
|
||||
// |entitlement_key_ids| entitlement key ids, put in private data for client
|
||||
// to select entitlement keys from single fat license. This field is only used
|
||||
// when client uses single fat license.
|
||||
// |group_ids| the groups ids this channel belongs to. Optional.
|
||||
// |serialized_ca_desc| a std::string object to receive the encoded descriptor.
|
||||
//
|
||||
// Notes:
|
||||
@@ -60,11 +58,11 @@ class WvCasCaDescriptor {
|
||||
// section (for an EMM stream) or into a TS Program Map Table section (for an
|
||||
// ECM stream). The descriptor will be 6 bytes plus any bytes added as
|
||||
// (user-defined) private data.
|
||||
virtual Status GenerateCaDescriptor(
|
||||
uint16_t ca_pid, const std::string& provider,
|
||||
const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids,
|
||||
std::string* serialized_ca_desc) const;
|
||||
virtual Status GenerateCaDescriptor(uint16_t ca_pid,
|
||||
const std::string& provider,
|
||||
const std::string& content_id,
|
||||
const std::vector<std::string>& group_ids,
|
||||
std::string* serialized_ca_desc) const;
|
||||
|
||||
// Return the base size (before private data is added) of the CA
|
||||
// descriptor. The user can call this to plan the layout of the Table section
|
||||
@@ -74,7 +72,7 @@ class WvCasCaDescriptor {
|
||||
// Return private data in the CA descriptor.
|
||||
virtual std::string GeneratePrivateData(
|
||||
const std::string& provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids) const;
|
||||
const std::vector<std::string>& group_ids) const;
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "media_cas_packager_sdk/public/wv_cas_ca_descriptor.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
@@ -20,14 +22,10 @@ namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
namespace {
|
||||
|
||||
// Random value for PID
|
||||
constexpr int kTestPid = 50;
|
||||
constexpr char kProvider[] = "widevine_test";
|
||||
constexpr char kContentId[] = "1234";
|
||||
const std::vector<std::string>* const kEntitlementKeyIds =
|
||||
new std::vector<std::string>({"fakekey1fakekey1", "fakekey2fakekey2"});
|
||||
|
||||
} // namespace
|
||||
|
||||
class WvCasCaDescriptorTest : public Test {
|
||||
@@ -35,7 +33,6 @@ class WvCasCaDescriptorTest : public Test {
|
||||
WvCasCaDescriptorTest() {}
|
||||
WvCasCaDescriptor ca_descriptor_;
|
||||
std::string actual_ca_descriptor_;
|
||||
std::vector<std::string> entitlement_key_ids_;
|
||||
};
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, BaseSize) {
|
||||
@@ -44,7 +41,7 @@ TEST_F(WvCasCaDescriptorTest, BaseSize) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, BasicGoodGen) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
kTestPid, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
kTestPid, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xE0\x32", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -55,7 +52,7 @@ TEST_F(WvCasCaDescriptorTest, NoReturnStringFail) {
|
||||
ca_descriptor_
|
||||
.GenerateCaDescriptor(
|
||||
kTestPid, /*provider=*/"", /*content_id=*/"",
|
||||
/*entitlement_key_ids=*/{}, /*serialized_ca_desc=*/nullptr)
|
||||
/*group_ids=*/{}, /*serialized_ca_desc=*/nullptr)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
@@ -65,14 +62,14 @@ TEST_F(WvCasCaDescriptorTest, PidTooLowFail) {
|
||||
error::INVALID_ARGUMENT,
|
||||
ca_descriptor_
|
||||
.GenerateCaDescriptor(bad_pid, /*provider=*/"", /*content_id=*/"",
|
||||
entitlement_key_ids_, &actual_ca_descriptor_)
|
||||
/*group_ids=*/{}, &actual_ca_descriptor_)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidMinOK) {
|
||||
const uint32_t min_pid = 0x10;
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
min_pid, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
min_pid, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xE0\x10", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -81,7 +78,7 @@ TEST_F(WvCasCaDescriptorTest, PidMinOK) {
|
||||
TEST_F(WvCasCaDescriptorTest, PidMaxOK) {
|
||||
const uint32_t max_pid = 0x1FFE;
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
max_pid, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
max_pid, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xff\xfe");
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -89,17 +86,17 @@ TEST_F(WvCasCaDescriptorTest, PidMaxOK) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidTooHighFail) {
|
||||
const uint32_t bad_pid = 0x1FFF;
|
||||
EXPECT_EQ(error::INVALID_ARGUMENT,
|
||||
ca_descriptor_
|
||||
.GenerateCaDescriptor(
|
||||
bad_pid, /*provider=*/"", /*content_id=*/"",
|
||||
/*entitlement_key_ids=*/{}, &actual_ca_descriptor_)
|
||||
.error_code());
|
||||
EXPECT_EQ(
|
||||
error::INVALID_ARGUMENT,
|
||||
ca_descriptor_
|
||||
.GenerateCaDescriptor(bad_pid, /*provider=*/"", /*content_id=*/"",
|
||||
/*group_ids=*/{}, &actual_ca_descriptor_)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidOneByte) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
255, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
255, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe0\xff", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -107,7 +104,7 @@ TEST_F(WvCasCaDescriptorTest, PidOneByte) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidSecondByte) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x1F00, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
0x1F00, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xff\x00", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -115,7 +112,7 @@ TEST_F(WvCasCaDescriptorTest, PidSecondByte) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidTwelveBits) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0xFFF, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
0xFFF, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xef\xff");
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -123,7 +120,7 @@ TEST_F(WvCasCaDescriptorTest, PidTwelveBits) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidThirteenthBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x1000, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
0x1000, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xf0\x00", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -131,7 +128,7 @@ TEST_F(WvCasCaDescriptorTest, PidThirteenthBit) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidTwelthBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x800, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
0x800, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe8\x00", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -139,7 +136,7 @@ TEST_F(WvCasCaDescriptorTest, PidTwelthBit) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidElevenththBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x400, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
0x400, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe4\x00", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -147,7 +144,7 @@ TEST_F(WvCasCaDescriptorTest, PidElevenththBit) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidTenthBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x200, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
0x200, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe2\x00", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -155,7 +152,7 @@ TEST_F(WvCasCaDescriptorTest, PidTenthBit) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidNinthBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x100, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
0x100, /*provider=*/"", /*content_id=*/"", /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe1\x00", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
@@ -163,14 +160,14 @@ TEST_F(WvCasCaDescriptorTest, PidNinthBit) {
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PrivateDataWithNoContentIdIgnored) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, "", entitlement_key_ids_, &actual_ca_descriptor_));
|
||||
kTestPid, kProvider, "", /*group_ids=*/{}, &actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe0\x32", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PrivateDataWithNoProviderIgnored) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
kTestPid, "", kContentId, entitlement_key_ids_, &actual_ca_descriptor_));
|
||||
kTestPid, "", kContentId, /*group_ids=*/{}, &actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xe0\x32", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
}
|
||||
@@ -186,39 +183,16 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataWithNoEntitlementKeyIds) {
|
||||
actual_ca_descriptor_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest,
|
||||
PrivateDataFailedWhenNumberOfEntitlementKeyIdsExceedLimit) {
|
||||
const std::vector<std::string> entitlement_key_ids = {
|
||||
"fakekey1fakekey1", "fakekey2fakekey2", "fakekey3fakekey3"};
|
||||
Status status = {error::INVALID_ARGUMENT,
|
||||
"Number of entitlement key ids shouldn't exceed 2"};
|
||||
EXPECT_EQ(status, ca_descriptor_.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, kContentId, entitlement_key_ids,
|
||||
&actual_ca_descriptor_));
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest,
|
||||
PrivateDataFailedWhenEntitlementKeyIdLengthExceedLimit) {
|
||||
const std::vector<std::string> entitlement_key_ids = {
|
||||
"fakekey1fakekey1", "fakekey2fakekey2fakekey2"};
|
||||
Status status = {error::INVALID_ARGUMENT,
|
||||
"Entitlement key id length must be 16. The offending key id "
|
||||
"is fakekey2fakekey2fakekey2"};
|
||||
EXPECT_EQ(status, ca_descriptor_.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, kContentId, entitlement_key_ids,
|
||||
&actual_ca_descriptor_));
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PrivateData) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, kProvider, kContentId,
|
||||
*kEntitlementKeyIds,
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x3d\x4a\xd4\xe0\x32", 6);
|
||||
std::vector<std::string> group_ids = {"group1", "group2"};
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, kContentId, group_ids, &actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x29\x4a\xd4\xe0\x32", 6);
|
||||
CaDescriptorPrivateData private_data;
|
||||
private_data.set_provider(kProvider);
|
||||
private_data.set_content_id(kContentId);
|
||||
for (const auto& entitlementKeyId : *kEntitlementKeyIds) {
|
||||
private_data.add_entitlement_key_ids(entitlementKeyId);
|
||||
for (const auto& group_id : group_ids) {
|
||||
private_data.add_group_ids(group_id);
|
||||
}
|
||||
EXPECT_EQ(expected_ca_descriptor + private_data.SerializeAsString(),
|
||||
actual_ca_descriptor_);
|
||||
@@ -232,7 +206,7 @@ class FakePrivateDataCaDescriptor : public WvCasCaDescriptor {
|
||||
|
||||
std::string GeneratePrivateData(
|
||||
const std::string& provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids) const override {
|
||||
const std::vector<std::string>& group_ids) const override {
|
||||
return private_data_;
|
||||
}
|
||||
|
||||
@@ -243,9 +217,9 @@ class FakePrivateDataCaDescriptor : public WvCasCaDescriptor {
|
||||
TEST_F(WvCasCaDescriptorTest, PrivateDataOneByte) {
|
||||
FakePrivateDataCaDescriptor fake_descriptor;
|
||||
fake_descriptor.set_private_data("X");
|
||||
EXPECT_OK(fake_descriptor.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, kContentId, *kEntitlementKeyIds,
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(fake_descriptor.GenerateCaDescriptor(kTestPid, kProvider,
|
||||
kContentId, /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x05\x4a\xd4\xe0\x32X", 7);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
}
|
||||
@@ -254,9 +228,9 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataMultipleBytes) {
|
||||
const std::string private_data_bytes("X1234abcde");
|
||||
FakePrivateDataCaDescriptor fake_descriptor;
|
||||
fake_descriptor.set_private_data(private_data_bytes);
|
||||
EXPECT_OK(fake_descriptor.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, kContentId, *kEntitlementKeyIds,
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(fake_descriptor.GenerateCaDescriptor(kTestPid, kProvider,
|
||||
kContentId, /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x0e\x4a\xd4\xe0\x32", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor + private_data_bytes, actual_ca_descriptor_);
|
||||
}
|
||||
@@ -265,9 +239,9 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataMaxNumberBytes) {
|
||||
const std::string private_data_bytes(251, 'x');
|
||||
FakePrivateDataCaDescriptor fake_descriptor;
|
||||
fake_descriptor.set_private_data(private_data_bytes);
|
||||
EXPECT_OK(fake_descriptor.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, kContentId, *kEntitlementKeyIds,
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(fake_descriptor.GenerateCaDescriptor(kTestPid, kProvider,
|
||||
kContentId, /*group_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\xff\x4a\xd4\xe0\x32", 6);
|
||||
EXPECT_EQ(expected_ca_descriptor + private_data_bytes, actual_ca_descriptor_);
|
||||
}
|
||||
@@ -276,12 +250,11 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataTooManyBytesFail) {
|
||||
const std::string private_data_bytes(252, 'X');
|
||||
FakePrivateDataCaDescriptor fake_descriptor;
|
||||
fake_descriptor.set_private_data(private_data_bytes);
|
||||
EXPECT_EQ(
|
||||
error::INVALID_ARGUMENT,
|
||||
fake_descriptor
|
||||
.GenerateCaDescriptor(kTestPid, kProvider, kContentId,
|
||||
*kEntitlementKeyIds, &actual_ca_descriptor_)
|
||||
.error_code());
|
||||
EXPECT_EQ(error::INVALID_ARGUMENT,
|
||||
fake_descriptor
|
||||
.GenerateCaDescriptor(kTestPid, kProvider, kContentId,
|
||||
/*group_ids=*/{}, &actual_ca_descriptor_)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
|
||||
@@ -41,6 +41,7 @@ EcmInitParameters ConvertToEcmInitParameters(
|
||||
init_params.cas_id = ecm_parameters.cas_id;
|
||||
init_params.ecm_version = ecm_parameters.ecm_version;
|
||||
init_params.ecc_private_signing_key = ecm_parameters.ecc_private_signing_key;
|
||||
init_params.entitlement_rotation = ecm_parameters.entitlement_rotation;
|
||||
|
||||
return init_params;
|
||||
}
|
||||
@@ -67,6 +68,16 @@ void WvCasEcm::SetServiceBlocking(
|
||||
ecm_->SetServiceBlocking(service_blocking);
|
||||
}
|
||||
|
||||
Status WvCasEcm::SetEntitlementRotationWindowLeft(
|
||||
uint32_t entitlement_rotation_window_left) {
|
||||
return ecm_->SetEntitlementRotationWindowLeft(
|
||||
entitlement_rotation_window_left);
|
||||
}
|
||||
|
||||
uint32_t WvCasEcm::GetEntitlementRotationWindowLeft() const {
|
||||
return ecm_->GetEntitlementRotationWindowLeft();
|
||||
}
|
||||
|
||||
Status WvCasEcm::GenerateEcm(const WvCasContentKeyInfo& even_key,
|
||||
const WvCasContentKeyInfo& odd_key,
|
||||
const std::string& track_type,
|
||||
|
||||
@@ -61,6 +61,7 @@ struct WvCasEcmParameters {
|
||||
uint16_t cas_id = 0x4AD4;
|
||||
EcmVersion ecm_version = EcmVersion::kV2;
|
||||
std::string ecc_private_signing_key;
|
||||
EntitlementKeyRotationInfo entitlement_rotation;
|
||||
};
|
||||
|
||||
// Class for generating Widevine CAS ECMs.
|
||||
@@ -94,6 +95,20 @@ class WvCasEcm {
|
||||
virtual void SetServiceBlocking(
|
||||
const EcmServiceBlockingParams* service_blocking);
|
||||
|
||||
// Sets the current value of the entitlement key rotation window left to
|
||||
// |entitlement_rotation_window_left|. The value will be used in
|
||||
// GenerateEcm/GenerateSingleKeyEcm if entitlement rotation is enabled as
|
||||
// specified at initializing, and is automatically decreased by 1 on each call
|
||||
// to GenerateEcm/GenerateSingleKeyEcm, until it reaches 1.
|
||||
virtual Status SetEntitlementRotationWindowLeft(
|
||||
uint32_t entitlement_rotation_window_left);
|
||||
|
||||
// Gets the current value of the entitlement key rotation window left. The
|
||||
// value is used in GenerateEcm/GenerateSingleKeyEcm, and is automatically
|
||||
// decreased by 1 on each call to GenerateEcm/GenerateSingleKeyEcm, until it
|
||||
// reaches 1.
|
||||
virtual uint32_t GetEntitlementRotationWindowLeft() const;
|
||||
|
||||
// Constructs a Widevine ECM using the provided key info.
|
||||
// Args:
|
||||
// |even_key| information for even key to be encoded into ECM.
|
||||
@@ -106,6 +121,9 @@ class WvCasEcm {
|
||||
// consistent with the initialized settings.
|
||||
// The even_key and odd_key will be wrapped using the appropriate
|
||||
// entitlement key.
|
||||
// If entitlement rotation is enabled as specified at initializing, the
|
||||
// entitlement key rotation window left value will be automatically decreased
|
||||
// by 1, until it reaches 1.
|
||||
virtual Status GenerateEcm(const WvCasContentKeyInfo& even_key,
|
||||
const WvCasContentKeyInfo& odd_key,
|
||||
const std::string& track_type,
|
||||
@@ -122,6 +140,9 @@ class WvCasEcm {
|
||||
// |serialized_ecm| caller-supplied std::string pointer to receive the ECM.
|
||||
// The |key| contents (specifically IV sizes) must be consistent
|
||||
// with the initialized settings.
|
||||
// If entitlement rotation is enabled as specified at initializing, the
|
||||
// entitlement key rotation window left value will be automatically decreased
|
||||
// by 1, until it reaches 1.
|
||||
virtual Status GenerateSingleKeyEcm(const WvCasContentKeyInfo& key,
|
||||
const std::string& track_type,
|
||||
const std::vector<std::string>& group_ids,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "glog/logging.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
|
||||
|
||||
@@ -81,7 +82,8 @@ Status WvCasEcmgClientHandler::HandleRequest(size_t request_buffer_size,
|
||||
return {error::INVALID_ARGUMENT, error_message};
|
||||
}
|
||||
processed_request_length = inner_handler_->HandleRequest(
|
||||
request_buffer, response_buffer, &response_length);
|
||||
request_buffer, absl::MakeSpan(response_buffer, response_buffer_size),
|
||||
&response_length);
|
||||
if (processed_request_length == 0) {
|
||||
return {error::INTERNAL, "request not processed."};
|
||||
}
|
||||
@@ -89,5 +91,14 @@ Status WvCasEcmgClientHandler::HandleRequest(size_t request_buffer_size,
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
WvEcmgChannelStatus WvCasEcmgClientHandler::GetChannelStatus() const {
|
||||
return inner_handler_->GetChannelStatus();
|
||||
}
|
||||
|
||||
std::vector<WvEcmgStreamStatus> WvCasEcmgClientHandler::GetStreamStatus()
|
||||
const {
|
||||
return inner_handler_->GetStreamStatus();
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
@@ -74,6 +74,12 @@ class WvCasEcmgClientHandler {
|
||||
size_t& response_length,
|
||||
size_t& processed_request_length);
|
||||
|
||||
// Retrieves the current channel status;
|
||||
WvEcmgChannelStatus GetChannelStatus() const;
|
||||
|
||||
// Retrieves the status of all open streams;
|
||||
std::vector<WvEcmgStreamStatus> GetStreamStatus() const;
|
||||
|
||||
protected:
|
||||
// For unit test only.
|
||||
explicit WvCasEcmgClientHandler(
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
@@ -25,7 +26,7 @@ class MockEcmgClientHandler : public EcmgClientHandler {
|
||||
public:
|
||||
MockEcmgClientHandler() : EcmgClientHandler(&config_) {}
|
||||
MOCK_METHOD(size_t, HandleRequest,
|
||||
(const char* const request, char* response,
|
||||
(const char* const request, const absl::Span<char> response,
|
||||
size_t* response_length),
|
||||
(override));
|
||||
|
||||
@@ -50,7 +51,9 @@ TEST(WvCasEcmgClientHandlerTest, HandleRequestSuccess) {
|
||||
size_t processed_length = 0;
|
||||
auto mock_internal_handler = absl::make_unique<MockEcmgClientHandler>();
|
||||
EXPECT_CALL(*mock_internal_handler,
|
||||
HandleRequest(request_buffer, response_buffer, &response_length))
|
||||
HandleRequest(request_buffer,
|
||||
absl::MakeSpan(response_buffer, kBufferSize),
|
||||
&response_length))
|
||||
.WillOnce(testing::DoAll(testing::Return(100)));
|
||||
|
||||
auto handler = absl::make_unique<TestWvCasEcmgClientHandler>(
|
||||
|
||||
@@ -26,7 +26,6 @@ using ::testing::NotNull;
|
||||
using ::testing::Pointwise;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::SetArrayArgument;
|
||||
using ::testing::WithArg;
|
||||
|
||||
namespace widevine {
|
||||
@@ -62,12 +61,11 @@ class MockEmm : public Emm {
|
||||
Status, SetServiceBlocking,
|
||||
(const std::vector<ServiceBlockingInitParameters>& service_blockings),
|
||||
(override));
|
||||
MOCK_METHOD(Status, GenerateEmm, (std::string * serialized_emm),
|
||||
(const, override));
|
||||
MOCK_METHOD(Status, GenerateEmm, (std::string * serialized_emm), (override));
|
||||
MOCK_METHOD(Status, GenerateEmmTsPackets,
|
||||
(uint16_t pid, uint8_t* continuity_counter,
|
||||
const absl::Span<uint8_t> packet, ssize_t* bytes_modified),
|
||||
(const, override));
|
||||
(override));
|
||||
};
|
||||
|
||||
class MockWvCasEmm : public WvCasEmm {
|
||||
|
||||
@@ -39,8 +39,9 @@ WvCasKeyFetcher::WvCasKeyFetcher(const std::string& signing_provider,
|
||||
Status WvCasKeyFetcher::CreateEntitlementRequest(
|
||||
const EntitlementRequestParams& request_params,
|
||||
std::string* signed_request_json) const {
|
||||
if (request_params.content_id.empty()) {
|
||||
return {error::INVALID_ARGUMENT, "Content ID is empty."};
|
||||
if (request_params.content_id.empty() && request_params.group_id.empty()) {
|
||||
return {error::INVALID_ARGUMENT,
|
||||
"Either content ID or group ID must be specified."};
|
||||
}
|
||||
if (request_params.content_provider.empty()) {
|
||||
return {error::INVALID_ARGUMENT, "Content Provider is empty."};
|
||||
@@ -58,16 +59,21 @@ Status WvCasKeyFetcher::CreateEntitlementRequest(
|
||||
}
|
||||
|
||||
CasEncryptionRequest request;
|
||||
request.set_content_id(request_params.content_id);
|
||||
request.set_provider(request_params.content_provider);
|
||||
request.set_key_rotation(request_params.key_rotation);
|
||||
if (!request_params.group_id.empty()) {
|
||||
request.set_group_id(request_params.group_id);
|
||||
} else {
|
||||
request.set_content_id(request_params.content_id);
|
||||
}
|
||||
request.set_provider(request_params.content_provider);
|
||||
request.set_key_rotation(request_params.key_rotation);
|
||||
// Add labels for tracks.
|
||||
for (const auto& track_type : request_params.track_types) {
|
||||
request.add_track_types(track_type);
|
||||
}
|
||||
if (request_params.entitlement_rotation_enabled) {
|
||||
request.set_entitlement_period_index(
|
||||
request_params.entitlement_period_index);
|
||||
}
|
||||
|
||||
std::string request_json;
|
||||
JsonPrintOptions print_options;
|
||||
@@ -162,6 +168,9 @@ Status WvCasKeyFetcher::ParseEntitlementResponse(
|
||||
entitlement->key_id = key.key_id();
|
||||
entitlement->key_value = key.key();
|
||||
entitlement->track_type = key.track_type();
|
||||
if (!response.group_id().empty()) {
|
||||
entitlement->group_id = response.group_id();
|
||||
}
|
||||
// EVEN or SINGLE are both marked as is_even_key.
|
||||
entitlement->is_even_key =
|
||||
key.key_slot() != CasEncryptionResponse::KeyInfo::ODD;
|
||||
|
||||
@@ -29,12 +29,17 @@ namespace cas {
|
||||
// |group_id| optional field indicates if this is a key used for a group of
|
||||
// contents. If this field is not empty, entitlement key would be generated
|
||||
// for the group instead of the single content.
|
||||
// |entitlement_rotation_enabled| Whether entitlement rotation is enabled.
|
||||
// |entitlement_period_index| The requested entitlement period index if
|
||||
// entitlement rotation is enabled.
|
||||
struct EntitlementRequestParams {
|
||||
std::string content_id;
|
||||
std::string content_provider;
|
||||
std::vector<std::string> track_types;
|
||||
bool key_rotation;
|
||||
std::string group_id;
|
||||
bool entitlement_rotation_enabled = false;
|
||||
uint32_t entitlement_period_index;
|
||||
};
|
||||
|
||||
// WV CAS KeyFetcher. Performs the communication with the Widevine License
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "google/protobuf/util/json_util.h"
|
||||
#include "testing/gmock.h"
|
||||
@@ -38,12 +40,12 @@ const char kSignedCasEncryptionRequest[] =
|
||||
"RyYWNrX3R5cGVzIjpbIlNEIl0sImtleV9yb3RhdGlvbiI6ZmFsc2V9\",\"signature\":"
|
||||
"\"JyTnKEy1w98HRP1lL78+OIEiqtIXyfCN8iudvNXIWhw=\",\"signer\":\"widevine_"
|
||||
"test\"}";
|
||||
absl::string_view kSignedCasEncryptionRequestForGroupKey =
|
||||
constexpr absl::string_view kSignedCasEncryptionRequestForGroupKey =
|
||||
"{\"request\":"
|
||||
"\"eyJjb250ZW50X2lkIjoiTWpFeE5EQTRORFE9IiwicHJvdmlkZXIiOiJ3aWRldmluZSIsInRy"
|
||||
"YWNrX3R5cGVzIjpbIlNEIl0sImtleV9yb3RhdGlvbiI6ZmFsc2UsImdyb3VwX2lkIjoiWjNKdm"
|
||||
"RYQXgifQ==\",\"signature\":\"iDaAr74ldEV6X1X9kwyLoZ/"
|
||||
"hfP5RJOkXEzucq6IXyfQ=\",\"signer\":\"widevine_test\"}";
|
||||
"\"eyJwcm92aWRlciI6IndpZGV2aW5lIiwidHJhY2tfdHlwZXMiOlsiU0QiXSwia2V5X3JvdG"
|
||||
"F0aW9uIjpmYWxzZSwiZ3JvdXBfaWQiOiJaM0p2ZFhBeCJ9\",\"signature\":"
|
||||
"\"vRZ2qt65UC171S9pVEBib2KyTAbTGO+AjsCgV4d+4a0=\",\"signer\":\"widevine_"
|
||||
"test\"}";
|
||||
const char kHttpResponse[] =
|
||||
"{\"response\":"
|
||||
"\"eyJzdGF0dXMiOiJPSyIsImNvbnRlbnRfaWQiOiJNakV4TkRBNE5EUT0iLCJlbnRpdGxlbWVu"
|
||||
@@ -57,6 +59,8 @@ const char kContentId[] = "21140844";
|
||||
const char kGroupId[] = "group1";
|
||||
const char kEntitlementKeyId[] = "fake_key_id.....";
|
||||
const char kEntitlementKey[] = "fakefakefakefakefakefakefakefake";
|
||||
const uint32_t kEntitlementPeriod = 123;
|
||||
const char kEntitlementKeyIdForEntitlementPeriod123[] = "1234567812345678";
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
@@ -100,10 +104,11 @@ class MockWvCasKeyFetcher : public WvCasKeyFetcher {
|
||||
return Status(error::INTERNAL);
|
||||
}
|
||||
// Validate the parameters shown in the request.
|
||||
EXPECT_EQ(request.content_id(), kContentId);
|
||||
EXPECT_EQ(request.provider(), kProvider);
|
||||
if (request.has_group_id()) {
|
||||
EXPECT_EQ(request.group_id(), kGroupId);
|
||||
} else {
|
||||
EXPECT_EQ(request.content_id(), kContentId);
|
||||
}
|
||||
|
||||
CasEncryptionResponse response;
|
||||
@@ -119,13 +124,19 @@ class MockWvCasKeyFetcher : public WvCasKeyFetcher {
|
||||
if (request.key_rotation()) {
|
||||
// Add the Even key.
|
||||
auto key = response.add_entitlement_keys();
|
||||
key->set_key_id(kEntitlementKeyId);
|
||||
key->set_key_id(request.entitlement_period_index() ==
|
||||
kEntitlementPeriod
|
||||
? kEntitlementKeyIdForEntitlementPeriod123
|
||||
: kEntitlementKeyId);
|
||||
key->set_key(kEntitlementKey);
|
||||
key->set_track_type(track_type);
|
||||
key->set_key_slot(CasEncryptionResponse::KeyInfo::EVEN);
|
||||
// Add the Odd key.
|
||||
key = response.add_entitlement_keys();
|
||||
key->set_key_id(kEntitlementKeyId);
|
||||
key->set_key_id(request.entitlement_period_index() ==
|
||||
kEntitlementPeriod
|
||||
? kEntitlementKeyIdForEntitlementPeriod123
|
||||
: kEntitlementKeyId);
|
||||
key->set_key(kEntitlementKey);
|
||||
key->set_track_type(track_type);
|
||||
key->set_key_slot(CasEncryptionResponse::KeyInfo::ODD);
|
||||
@@ -212,6 +223,7 @@ TEST_F(WvCasKeyFetcherTest, TestRequestEntitlementKey) {
|
||||
CHECK(absl::Base64Unescape("hgRrrgjyH8642r67Twt8ruqNLPcLFkJqdUIDNvnFd0M=",
|
||||
&expected_key_value));
|
||||
EXPECT_EQ(entitlement.key_value, expected_key_value);
|
||||
EXPECT_TRUE(entitlement.group_id.empty());
|
||||
}
|
||||
|
||||
TEST_F(WvCasKeyFetcherTest, TestRequestWithGroupId) {
|
||||
@@ -230,7 +242,6 @@ TEST_F(WvCasKeyFetcherTest, TestRequestWithGroupId) {
|
||||
CasEncryptionRequest request;
|
||||
ASSERT_OK(
|
||||
google::protobuf::util::JsonStringToMessage(signed_request.request(), &request));
|
||||
EXPECT_EQ(request.content_id(), kContentId);
|
||||
EXPECT_EQ(request.provider(), kProvider);
|
||||
EXPECT_EQ(request.group_id(), kGroupId);
|
||||
EXPECT_EQ(request.track_types_size(), 1);
|
||||
@@ -247,7 +258,6 @@ TEST_F(WvCasKeyFetcherTest, TestRequestWithGroupId) {
|
||||
CasEncryptionResponse response;
|
||||
ASSERT_OK(
|
||||
google::protobuf::util::JsonStringToMessage(signed_response.response(), &response));
|
||||
EXPECT_EQ(response.content_id(), kContentId);
|
||||
EXPECT_EQ(response.status(), CasEncryptionResponse::OK);
|
||||
EXPECT_EQ(response.group_id(), kGroupId);
|
||||
|
||||
@@ -261,6 +271,7 @@ TEST_F(WvCasKeyFetcherTest, TestRequestWithGroupId) {
|
||||
EXPECT_EQ(entitlement.is_even_key, true);
|
||||
EXPECT_EQ(entitlement.key_id, kEntitlementKeyId);
|
||||
EXPECT_EQ(entitlement.key_value, kEntitlementKey);
|
||||
EXPECT_EQ(entitlement.group_id, kGroupId);
|
||||
}
|
||||
|
||||
TEST_F(WvCasKeyFetcherTest, OneKeyOK) {
|
||||
@@ -348,5 +359,27 @@ TEST_F(WvCasKeyFetcherTest, BadResponseFail) {
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasKeyFetcherTest, EntitlementRotationSuccess) {
|
||||
MockWvCasKeyFetcher mock_key_fetcher(kSigningProvider, kSingingKey,
|
||||
kSingingIv);
|
||||
std::string request;
|
||||
EntitlementRequestParams request_params =
|
||||
CreateRequestParamsStruct(kContentId, kProvider, /*group_id=*/"",
|
||||
{kTrackTypeSD}, /*key_rotation=*/true);
|
||||
request_params.entitlement_rotation_enabled = true;
|
||||
request_params.entitlement_period_index = kEntitlementPeriod;
|
||||
|
||||
ASSERT_OK(
|
||||
mock_key_fetcher.CreateEntitlementRequest(request_params, &request));
|
||||
std::string response;
|
||||
ASSERT_OK(mock_key_fetcher.MakeHttpRequest(request, &response));
|
||||
std::vector<EntitlementKeyInfo> entitlements;
|
||||
ASSERT_OK(mock_key_fetcher.ParseEntitlementResponse(response, &entitlements));
|
||||
|
||||
ASSERT_EQ(entitlements.size(), 2);
|
||||
EXPECT_EQ(entitlements[0].key_id, kEntitlementKeyIdForEntitlementPeriod123);
|
||||
EXPECT_EQ(entitlements[1].key_id, kEntitlementKeyIdForEntitlementPeriod123);
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
@@ -86,6 +86,23 @@ struct EntitlementKeyInfo {
|
||||
|
||||
enum class EcmVersion : int { kV2 = 0, kV3 = 1 };
|
||||
|
||||
// Used for generating ECMs if entitlement key rotation is enabled.
|
||||
struct EntitlementKeyRotationInfo {
|
||||
// Indicates if entitlement key rotation feature is used. If enabled,
|
||||
// |period_index| and |rotation_window_left| must also be specified and will
|
||||
// be carried in generated ECMs.
|
||||
bool rotation_enabled = false;
|
||||
// The current entitlement key period index. Only used if |rotation_enabled|
|
||||
// is set to true.
|
||||
uint32_t period_index = 0;
|
||||
// It tells the client how many crypto periods (unique ECMs) left (including
|
||||
// this ECM) that a new license request must be made. Must be a positive
|
||||
// value. Only used if |rotation_enabled| is set to true.
|
||||
// The value will automatically decrease by 1 on each call to GenerateEcm or
|
||||
// GenerateSingleKeyEcm, until the value reaches 1.
|
||||
uint32_t rotation_window_left = 0;
|
||||
};
|
||||
|
||||
// A struct that captures the Simulcrypt ECMG configurations. Most fields are
|
||||
// Simulcrypt standard fields (See ETSI TS 103 197 V1.5.1 (2008-10)
|
||||
// Section 5.3).
|
||||
@@ -177,6 +194,8 @@ struct EcmgCustomParameters {
|
||||
// when the ECM is received, and stops util the device is no longer in
|
||||
// |device_groups|.
|
||||
std::vector<std::string> service_blocking_groups;
|
||||
// Used if entitlement key rotation is enabled.
|
||||
EntitlementKeyRotationInfo entitlement_rotation;
|
||||
};
|
||||
|
||||
// A custom access control processing function used by ECMG to get information
|
||||
@@ -233,6 +252,33 @@ typedef std::function<EcmServiceBlockingParams(uint16_t channel_id,
|
||||
uint16_t stream_id)>
|
||||
ServiceBlockingSettingFunc;
|
||||
|
||||
struct WvEcmgChannelStatus {
|
||||
// If the channel has been set up or not.
|
||||
bool has_setup;
|
||||
uint16_t channel_id;
|
||||
uint16_t ca_system_id;
|
||||
int num_of_open_streams;
|
||||
EcmVersion ecm_version;
|
||||
// Below are Simulcrypt parameters values (see ETSI TS 103 197 V1.5.1
|
||||
// (2008-10) Section 5.3).
|
||||
uint8_t section_tspkt_flag;
|
||||
uint16_t delay_start;
|
||||
uint16_t delay_stop;
|
||||
uint16_t ecm_pep_period;
|
||||
uint16_t max_streams;
|
||||
uint16_t min_cp_duration;
|
||||
uint8_t lead_cw;
|
||||
uint8_t cw_per_message;
|
||||
uint16_t max_comp_time;
|
||||
};
|
||||
|
||||
struct WvEcmgStreamStatus {
|
||||
uint16_t stream_id;
|
||||
uint16_t ecm_id;
|
||||
CryptoMode crypto_mode;
|
||||
int age_restriction;
|
||||
};
|
||||
|
||||
struct WvCasEncryptionRequest {
|
||||
std::string content_id;
|
||||
std::string provider;
|
||||
|
||||
@@ -26,10 +26,6 @@
|
||||
#include "media_cas_packager_sdk/internal/emmg.h"
|
||||
|
||||
namespace {
|
||||
// Maximum number of entitlement key ids shown in private data.
|
||||
constexpr uint16_t kMaxNumOfEntitlementKeyIds = 2;
|
||||
// Entitlement key id length is fixed to 16 bytes.
|
||||
constexpr uint16_t kEntitlementKeyIdLengthBytes = 16;
|
||||
constexpr int32_t kEmmDataType = 0;
|
||||
constexpr int32_t kPrivateDataType = 1;
|
||||
} // namespace
|
||||
@@ -58,8 +54,8 @@ ABSL_FLAG(std::string, content_provider, "",
|
||||
"Content provider to put into priavte data.");
|
||||
ABSL_FLAG(std::string, content_id, "", "Content id to put into priavte data");
|
||||
ABSL_FLAG(
|
||||
std::vector<std::string>, entitlement_key_ids, {},
|
||||
"Comma-separated list of entitlement_key_ids to put into private data");
|
||||
std::vector<std::string>, group_ids, {},
|
||||
"Comma-separated list of group_ids to put into private data. Optional.");
|
||||
ABSL_FLAG(int32_t, bandwidth, 100, "Requested bandwidth in kbps");
|
||||
ABSL_FLAG(int32_t, max_num_message, 100,
|
||||
"Maximum number of messages that can be sent");
|
||||
@@ -93,8 +89,8 @@ void CheckEmmgUsage(const widevine::cas::EmmgConfig& config) {
|
||||
"data).";
|
||||
LOG_IF(WARNING, !config.content_id.empty())
|
||||
<< "content_id is set but ignored as data_type is set to 0 (EMM data).";
|
||||
LOG_IF(WARNING, !config.entitlement_key_ids.empty())
|
||||
<< "entitlement_key_ids is set but ignored as data_type is set to 0 "
|
||||
LOG_IF(WARNING, !config.group_ids.empty())
|
||||
<< "group_ids is set but ignored as data_type is set to 0 "
|
||||
"(EMM data).";
|
||||
}
|
||||
|
||||
@@ -130,17 +126,7 @@ void BuildEmmgConfig(widevine::cas::EmmgConfig* config) {
|
||||
// Below are private data specific configurations.
|
||||
config->content_provider = absl::GetFlag(FLAGS_content_provider);
|
||||
config->content_id = absl::GetFlag(FLAGS_content_id);
|
||||
// Check and set entitlement_key_ids.
|
||||
config->entitlement_key_ids = absl::GetFlag(FLAGS_entitlement_key_ids);
|
||||
CHECK_LE(config->entitlement_key_ids.size(), kMaxNumOfEntitlementKeyIds)
|
||||
<< absl::StrCat("Number of entitlement key ids shouldn't exceed ",
|
||||
kMaxNumOfEntitlementKeyIds);
|
||||
for (const auto& entitlement_key_id : config->entitlement_key_ids) {
|
||||
CHECK_EQ(entitlement_key_id.size(), kEntitlementKeyIdLengthBytes)
|
||||
<< absl::StrCat("Entitlement key id length must be ",
|
||||
kEntitlementKeyIdLengthBytes,
|
||||
". The offending key id is ", entitlement_key_id);
|
||||
}
|
||||
config->group_ids = absl::GetFlag(FLAGS_group_ids);
|
||||
|
||||
// Below are EMM data specific configurations.
|
||||
config->ecc_signing_key = absl::GetFlag(FLAGS_ecc_signing_key);
|
||||
|
||||
Reference in New Issue
Block a user