Update Simulcrypt ECMg
This commit is contained in:
@@ -90,6 +90,7 @@ cc_library(
|
||||
"@abseil_repo//absl/container:node_hash_map",
|
||||
"@abseil_repo//absl/memory",
|
||||
"@abseil_repo//absl/strings",
|
||||
"@abseil_repo//absl/types:optional",
|
||||
"//common:crypto_util",
|
||||
"//common:random_util",
|
||||
"//common:status",
|
||||
@@ -242,3 +243,25 @@ cc_test(
|
||||
"//common:status",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "emm",
|
||||
srcs = ["emm.cc"],
|
||||
hdrs = ["emm.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//common:status",
|
||||
"//common:string_util",
|
||||
"//protos/public:media_cas_cc_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "emm_test",
|
||||
srcs = ["emm_test.cc"],
|
||||
deps = [
|
||||
":emm",
|
||||
"//testing:gunit_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -95,10 +95,10 @@ class MockEcm : public Ecm {
|
||||
MockEcm() = default;
|
||||
~MockEcm() override = default;
|
||||
|
||||
MOCK_CONST_METHOD0(age_restriction, uint8_t());
|
||||
MOCK_CONST_METHOD0(crypto_mode, CryptoMode());
|
||||
MOCK_CONST_METHOD0(paired_keys_required, bool());
|
||||
MOCK_CONST_METHOD0(content_iv_size, size_t());
|
||||
MOCK_METHOD(uint8_t, age_restriction, (), (const, override));
|
||||
MOCK_METHOD(CryptoMode, crypto_mode, (), (const, override));
|
||||
MOCK_METHOD(bool, paired_keys_required, (), (const, override));
|
||||
MOCK_METHOD(size_t, content_iv_size, (), (const, override));
|
||||
|
||||
std::string CallSerializeEcm(const std::vector<EntitledKeyInfo*>& keys) {
|
||||
return SerializeEcm(keys);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "media_cas_packager_sdk/internal/ecmg_client_handler.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include "glog/logging.h"
|
||||
@@ -873,32 +874,55 @@ Status EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params,
|
||||
EcmgStreamInfo* stream_info = streams_info_.at(params.ecm_stream_id).get();
|
||||
DCHECK(stream_info->ecm);
|
||||
|
||||
size_t key_count = params.cp_cw_combinations.size();
|
||||
// Number of keys can only be 1 or 2.
|
||||
if (key_count != ecmg_config_->number_of_content_keys || key_count < 1 ||
|
||||
key_count > 2) {
|
||||
return {error::INVALID_ARGUMENT, "Unexpected cp_cw_combinations size."};
|
||||
}
|
||||
|
||||
// Generate serialized ECM.
|
||||
CryptoMode crypto_mode = stream_info->crypto_mode == CryptoMode::kInvalid
|
||||
? ecmg_config_->crypto_mode
|
||||
: stream_info->crypto_mode;
|
||||
|
||||
// If two keys are present, the even key should be put first, followed by the
|
||||
// odd key.
|
||||
std::vector<EntitledKeyInfo> keys;
|
||||
keys.reserve(ecmg_config_->number_of_content_keys);
|
||||
for (size_t i = 0; i < ecmg_config_->number_of_content_keys; i++) {
|
||||
DCHECK(params.cp_cw_combinations[i].cp == params.cp_number + i);
|
||||
keys.emplace_back();
|
||||
keys[i].key_value = params.cp_cw_combinations[i].cw;
|
||||
keys.reserve(key_count);
|
||||
for (const auto& cp_cw : params.cp_cw_combinations) {
|
||||
auto key_info = cp_cw.cp % 2 == 0 ? keys.emplace(keys.begin())
|
||||
: keys.emplace(keys.end());
|
||||
key_info->key_value = cp_cw.cw;
|
||||
// Make content key to 16 bytes if crypto mode is Csa2.
|
||||
if (crypto_mode == CryptoMode::kDvbCsa2 && keys[i].key_value.size() == 8) {
|
||||
keys[i].key_value = keys[i].key_value + keys[i].key_value;
|
||||
if (crypto_mode == CryptoMode::kDvbCsa2 &&
|
||||
key_info->key_value.size() == 8) {
|
||||
key_info->key_value =
|
||||
absl::StrCat(key_info->key_value, key_info->key_value);
|
||||
}
|
||||
keys[i].key_id = crypto_util::DeriveKeyId(keys[i].key_value);
|
||||
keys[i].content_iv = stream_info->content_ivs.empty()
|
||||
? content_ivs_[i]
|
||||
: stream_info->content_ivs[i];
|
||||
if (!RandomBytes(kWrappedKeyIvSizeBytes, &keys[i].wrapped_key_iv)) {
|
||||
key_info->key_id = crypto_util::DeriveKeyId(key_info->key_value);
|
||||
auto generated_key_iv = GenerateRandomWrappedKeyIv();
|
||||
if (!generated_key_iv.has_value() ||
|
||||
generated_key_iv->size() != kWrappedKeyIvSizeBytes) {
|
||||
return {error::INTERNAL, "Unable to generate random wrapped key iv."};
|
||||
}
|
||||
key_info->wrapped_key_iv = generated_key_iv.value();
|
||||
}
|
||||
|
||||
if (content_ivs_.size() < key_count &&
|
||||
stream_info->content_ivs.size() < key_count) {
|
||||
return {error::INVALID_ARGUMENT, "Not enough content iv."};
|
||||
}
|
||||
// The first iv received is for even key and second is for odd key.
|
||||
for (size_t i = 0; i < key_count; ++i) {
|
||||
keys[i].content_iv = stream_info->content_ivs.size() >= key_count
|
||||
? stream_info->content_ivs[i]
|
||||
: content_ivs_[i];
|
||||
}
|
||||
|
||||
Status status;
|
||||
std::string serialized_ecm;
|
||||
if (ecmg_config_->number_of_content_keys > 1) {
|
||||
if (key_count > 1) {
|
||||
status = stream_info->ecm->GenerateEcm(
|
||||
&keys[0], &keys[1], stream_info->track_type, &serialized_ecm);
|
||||
} else {
|
||||
@@ -926,5 +950,14 @@ Status EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params,
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
absl::optional<std::string> EcmgClientHandler::GenerateRandomWrappedKeyIv()
|
||||
const {
|
||||
std::string output_iv;
|
||||
if (RandomBytes(kWrappedKeyIvSizeBytes, &output_iv)) {
|
||||
return output_iv;
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include "absl/container/node_hash_map.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/internal/ecm.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
@@ -126,6 +127,10 @@ class EcmgClientHandler {
|
||||
Status BuildEcmDatagram(const EcmgParameters& params,
|
||||
uint8_t* ecm_datagram) const;
|
||||
|
||||
// Generates a random wrapped key iv string. Returns true on success, false
|
||||
// otherwise. The main purpose for this function is easier testing.
|
||||
virtual absl::optional<std::string> GenerateRandomWrappedKeyIv() const;
|
||||
|
||||
EcmgConfig* ecmg_config_;
|
||||
// Per spec, "There is always one (and only one) channel per TCP connection".
|
||||
bool channel_id_set_;
|
||||
|
||||
@@ -12,12 +12,14 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "example/test_ecmg_messages.h"
|
||||
#include "media_cas_packager_sdk/internal/ecmg_constants.h"
|
||||
#include "media_cas_packager_sdk/internal/mpeg2ts.h"
|
||||
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
|
||||
#include "media_cas_packager_sdk/internal/util.h"
|
||||
|
||||
@@ -31,28 +33,36 @@ using simulcrypt_util::AddUint32Param;
|
||||
using simulcrypt_util::AddUint8Param;
|
||||
using simulcrypt_util::BuildMessageHeader;
|
||||
|
||||
static constexpr size_t kBufferSize = 1024;
|
||||
static constexpr size_t kSuperCasId = 0x4AD40000;
|
||||
static constexpr size_t kChannelId = 1;
|
||||
static constexpr size_t kStreamId = 1;
|
||||
static constexpr size_t kEcmId = 2;
|
||||
static constexpr size_t kNominalCpDuration = 0x64;
|
||||
static constexpr size_t kCpNumber = 0;
|
||||
static constexpr char kContentKeyEven[] = "0123456701234567";
|
||||
static constexpr char kContentKeyEven8Bytes[] = "01234567";
|
||||
static constexpr char kContentKeyOdd[] = "abcdefghabcdefgh";
|
||||
static constexpr char kContentKeyOdd8Bytes[] = "abcdefgh";
|
||||
static constexpr char kEntitlementKeyIdEven[] = "0123456701234567";
|
||||
static constexpr char kEntitlementKeyValueEven[] =
|
||||
"01234567012345670123456701234567";
|
||||
static constexpr char kEntitlementKeyIdOdd[] = "abcdefghabcdefgh";
|
||||
static constexpr char kEntitlementKeyValueOdd[] =
|
||||
"abcdefghabcdefghabcdefghabcdefgh";
|
||||
static constexpr size_t kAgeRestriction = 3;
|
||||
static constexpr char kCryptoMode[] = "AesScte";
|
||||
static constexpr char kCryptoModeCsa2[] = "DvbCsa2";
|
||||
static constexpr char kTrackTypesSD[] = "SD";
|
||||
static constexpr char kTrackTypesHD[] = "HD";
|
||||
constexpr size_t kBufferSize = 1024;
|
||||
constexpr size_t kSuperCasId = 0x4AD40000;
|
||||
constexpr size_t kChannelId = 1;
|
||||
constexpr size_t kStreamId = 1;
|
||||
constexpr size_t kEcmId = 2;
|
||||
constexpr size_t kNominalCpDuration = 0x64;
|
||||
constexpr size_t kCpNumber = 0;
|
||||
constexpr char kContentKeyEven[] = "0123456701234567";
|
||||
constexpr char kContentKeyEven8Bytes[] = "01234567";
|
||||
constexpr char kContentKeyOdd[] = "abcdefghabcdefgh";
|
||||
constexpr char kContentKeyOdd8Bytes[] = "abcdefgh";
|
||||
constexpr char kEntitlementKeyIdEven[] = "0123456701234567";
|
||||
constexpr char kEntitlementKeyValueEven[] = "01234567012345670123456701234567";
|
||||
constexpr char kEntitlementKeyIdOdd[] = "abcdefghabcdefgh";
|
||||
constexpr char kEntitlementKeyValueOdd[] = "abcdefghabcdefghabcdefghabcdefgh";
|
||||
constexpr size_t kAgeRestriction = 3;
|
||||
constexpr char kCryptoMode[] = "AesScte";
|
||||
constexpr char kCryptoModeCsa2[] = "DvbCsa2";
|
||||
constexpr char kTrackTypesSD[] = "SD";
|
||||
constexpr char kTrackTypesHD[] = "HD";
|
||||
constexpr absl::string_view kWrappedKeyIv = "0123456701234567";
|
||||
|
||||
class MockEcmgClientHandler : public EcmgClientHandler {
|
||||
public:
|
||||
explicit MockEcmgClientHandler(EcmgConfig* ecmg_config)
|
||||
: EcmgClientHandler(ecmg_config) {}
|
||||
absl::optional<std::string> GenerateRandomWrappedKeyIv() const override {
|
||||
return std::string(kWrappedKeyIv);
|
||||
}
|
||||
};
|
||||
|
||||
class EcmgClientHandlerTest : public ::testing::Test {
|
||||
protected:
|
||||
@@ -63,7 +73,7 @@ class EcmgClientHandlerTest : public ::testing::Test {
|
||||
config_.max_comp_time = 100;
|
||||
config_.access_criteria_transfer_mode = 1;
|
||||
config_.number_of_content_keys = 2;
|
||||
handler_ = absl::make_unique<EcmgClientHandler>(&config_);
|
||||
handler_ = absl::make_unique<MockEcmgClientHandler>(&config_);
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -564,6 +574,37 @@ TEST_F(EcmgClientHandlerTest, WrongMessageLength) {
|
||||
CheckChannelError(UNKNOWN_PARAMETER_TYPE_VALUE, response_, response_len_);
|
||||
}
|
||||
|
||||
TEST_F(EcmgClientHandlerTest, BuildEcmDatagramSequenceOfEvenOdd) {
|
||||
SetupValidChannelStream();
|
||||
|
||||
std::vector<EcmgCpCwCombination> cp_cw_combination = {
|
||||
{kCpNumber, kContentKeyEven}, {kCpNumber + 1, kContentKeyOdd}};
|
||||
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
|
||||
request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
EXPECT_EQ(response_len_, sizeof(kTestEcmgEcmResponse));
|
||||
std::string first_response(response_, response_len_);
|
||||
|
||||
// Change the sequence of cp_cw_combination.
|
||||
cp_cw_combination = {{kCpNumber + 1, kContentKeyOdd},
|
||||
{kCpNumber, kContentKeyEven}};
|
||||
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
|
||||
request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
// Sequence of cp_cw_combination does not matter as even/odd is based on cp
|
||||
// number.
|
||||
EXPECT_EQ(std::string(response_, response_len_), first_response);
|
||||
|
||||
// Swap the key value in cp_cw_combination.
|
||||
cp_cw_combination = {{kCpNumber, kContentKeyOdd},
|
||||
{kCpNumber + 1, kContentKeyEven}};
|
||||
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
|
||||
request_, &request_len_);
|
||||
handler_->HandleRequest(request_, response_, &response_len_);
|
||||
// Swapping key value changes generated ecm.
|
||||
EXPECT_NE(std::string(response_, response_len_), first_response);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
136
media_cas_packager_sdk/internal/emm.cc
Normal file
136
media_cas_packager_sdk/internal/emm.cc
Normal file
@@ -0,0 +1,136 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2020 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "media_cas_packager_sdk/internal/emm.h"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "common/status.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kNumBitsVersionField = 8;
|
||||
constexpr int kNumBitsHeaderLengthField = 8;
|
||||
constexpr int kNumBitsTimestampLengthField = 64;
|
||||
constexpr int kNumBitsPayloadLengthField = 16;
|
||||
|
||||
// Version - this should be incremented if there are changes to the EMM.
|
||||
constexpr uint8_t kEmmVersion = 1;
|
||||
} // namespace
|
||||
|
||||
Status Emm::SetFingerprinting(
|
||||
const std::vector<FingerprintingInitParameters>& fingerprintings) {
|
||||
// First clear all current fingerprinting payload.
|
||||
emm_payload_.clear_fingerprinting();
|
||||
|
||||
// TODO(b/161149665): validate passed in data.
|
||||
Status status;
|
||||
for (const auto& fingerprinting_param : fingerprintings) {
|
||||
Fingerprinting* fingerprinting_payload = emm_payload_.add_fingerprinting();
|
||||
for (const auto& channel : fingerprinting_param.channels) {
|
||||
fingerprinting_payload->add_channels(channel);
|
||||
}
|
||||
fingerprinting_payload->set_control(fingerprinting_param.control);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status Emm::SetServiceBlocking(
|
||||
const std::vector<ServiceBlockingInitParameters>& service_blockings) {
|
||||
// First clear all current service blocking payload.
|
||||
emm_payload_.clear_service_blocking();
|
||||
|
||||
// TODO(b/161149665): validate passed in data.
|
||||
Status status;
|
||||
for (const auto& service_blocking_param : service_blockings) {
|
||||
ServiceBlocking* service_blocking_payload =
|
||||
emm_payload_.add_service_blocking();
|
||||
for (const auto& channel : service_blocking_param.channels) {
|
||||
service_blocking_payload->add_channels(channel);
|
||||
}
|
||||
for (const auto& device_group : service_blocking_param.device_groups) {
|
||||
service_blocking_payload->add_device_groups(device_group);
|
||||
}
|
||||
if (service_blocking_param.start_time != 0) {
|
||||
service_blocking_payload->set_start_time_sec(
|
||||
service_blocking_param.start_time);
|
||||
}
|
||||
service_blocking_payload->set_end_time_sec(service_blocking_param.end_time);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status Emm::GenerateEmm(std::string* serialized_emm) const {
|
||||
if (serialized_emm == nullptr) {
|
||||
return {error::INVALID_ARGUMENT, "No return emm std::string pointer."};
|
||||
}
|
||||
|
||||
EmmSerializingParameters serializing_params;
|
||||
serializing_params.payload = emm_payload_.SerializeAsString();
|
||||
serializing_params.timestamp = GenerateTimestamp();
|
||||
|
||||
// Generate serialized emm (without signature yet).
|
||||
Status status =
|
||||
GenerateSerializedEmmNoSignature(serializing_params, serialized_emm);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Calculate and append signature.
|
||||
absl::StrAppend(serialized_emm, GenerateSignature(*serialized_emm));
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
Status Emm::GenerateSerializedEmmNoSignature(
|
||||
const EmmSerializingParameters& params, std::string* serialized_emm) const {
|
||||
if (serialized_emm == nullptr) {
|
||||
return {error::INVALID_ARGUMENT, "No return emm std::string pointer."};
|
||||
}
|
||||
|
||||
std::bitset<kNumBitsVersionField> version(kEmmVersion);
|
||||
std::bitset<kNumBitsHeaderLengthField> header_length(
|
||||
sizeof(params.timestamp));
|
||||
std::bitset<kNumBitsTimestampLengthField> timestamp(params.timestamp);
|
||||
std::bitset<kNumBitsPayloadLengthField> payload_length(
|
||||
params.payload.length());
|
||||
|
||||
std::string emm_bitset =
|
||||
absl::StrCat(version.to_string(), header_length.to_string(),
|
||||
timestamp.to_string(), payload_length.to_string());
|
||||
|
||||
Status status =
|
||||
string_util::BitsetStringToBinaryString(emm_bitset, serialized_emm);
|
||||
if (!status.ok() || serialized_emm->empty()) {
|
||||
LOG(ERROR) << "Failed to convert EMM bitset to std::string";
|
||||
return {error::INTERNAL, "Failed to convert EMM bitset to std::string"};
|
||||
}
|
||||
|
||||
// Appends payload.
|
||||
absl::StrAppend(serialized_emm, params.payload);
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
int64_t Emm::GenerateTimestamp() const {
|
||||
// TODO(b/161252065): Generate timestamp.
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string Emm::GenerateSignature(const std::string& content) const {
|
||||
// TODO(b/161252442): Calculate signature.
|
||||
std::string signature(32, 'x'); // A fake 32 bytes signature.
|
||||
return signature;
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
74
media_cas_packager_sdk/internal/emm.h
Normal file
74
media_cas_packager_sdk/internal/emm.h
Normal file
@@ -0,0 +1,74 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2020 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMM_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMM_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "common/status.h"
|
||||
#include "protos/public/media_cas.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
struct FingerprintingInitParameters {
|
||||
std::vector<std::string> channels;
|
||||
std::string control;
|
||||
};
|
||||
|
||||
struct ServiceBlockingInitParameters {
|
||||
std::vector<std::string> channels;
|
||||
std::vector<std::string> device_groups;
|
||||
// Value 0 in start_time means immediate.
|
||||
int64_t start_time = 0;
|
||||
int64_t end_time;
|
||||
};
|
||||
|
||||
// Generator for producing Widevine CAS-compliant EMMs. Used to construct the
|
||||
// Transport Stream packet payload of an EMM containing messages including
|
||||
// fingerprinting and service blocking.
|
||||
// Class Emm is not thread safe.
|
||||
class Emm {
|
||||
public:
|
||||
Emm() = default;
|
||||
Emm(const Emm&) = delete;
|
||||
Emm& operator=(const Emm&) = delete;
|
||||
virtual ~Emm() = default;
|
||||
|
||||
// Replaces current fingerprinting info with |fingerprintings|.
|
||||
Status SetFingerprinting(
|
||||
const std::vector<FingerprintingInitParameters>& fingerprintings);
|
||||
|
||||
// Replaces current service blocking info with |service_blockings|.
|
||||
Status SetServiceBlocking(
|
||||
const std::vector<ServiceBlockingInitParameters>& service_blockings);
|
||||
|
||||
// Generates serialized EMM to |serialized_emm|.
|
||||
Status GenerateEmm(std::string* serialized_emm) const;
|
||||
|
||||
private:
|
||||
struct EmmSerializingParameters {
|
||||
int64_t timestamp;
|
||||
std::string payload;
|
||||
};
|
||||
|
||||
Status GenerateSerializedEmmNoSignature(
|
||||
const EmmSerializingParameters& params,
|
||||
std::string* serialized_emm) const;
|
||||
int64_t GenerateTimestamp() const;
|
||||
std::string GenerateSignature(const std::string& content) const;
|
||||
|
||||
EmmPayload emm_payload_;
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMM_H_
|
||||
236
media_cas_packager_sdk/internal/emm_test.cc
Normal file
236
media_cas_packager_sdk/internal/emm_test.cc
Normal file
@@ -0,0 +1,236 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2020 Google LLC.
|
||||
//
|
||||
// This software is licensed under the terms defined in the Widevine Master
|
||||
// License Agreement. For a copy of this agreement, please contact
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "media_cas_packager_sdk/internal/emm.h"
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
namespace {
|
||||
|
||||
constexpr char kChannelOne[] = "CH1";
|
||||
constexpr char kChannelTwo[] = "CH2";
|
||||
constexpr char kChannelThree[] = "CH3";
|
||||
constexpr char kFingerprintingControl[] = "controls";
|
||||
constexpr char kDeviceGroupOne[] = "Group1";
|
||||
constexpr char kDeviceGroupTwo[] = "Group2";
|
||||
constexpr int64_t kServiceBockingStartTime = 100;
|
||||
constexpr int64_t kServiceBockingEndTime = 1000;
|
||||
|
||||
// Length in bytes when there is no payload.
|
||||
constexpr uint8_t kExpectedNoPayloadLengthBytes = 44;
|
||||
constexpr uint8_t kExpectedEmmVersion = 1;
|
||||
constexpr uint8_t kExpectedHeaderLength = 8;
|
||||
|
||||
constexpr uint8_t kVersionStartIndex = 0;
|
||||
constexpr uint8_t kHeaderLengthStartIndex = 1;
|
||||
constexpr uint8_t kPayloadLengthStartIndex = 10;
|
||||
constexpr uint8_t kPayloadStartIndex = 12;
|
||||
constexpr uint8_t kSignatureLength = 32;
|
||||
|
||||
FingerprintingInitParameters GetValidFingerprintingParams() {
|
||||
FingerprintingInitParameters fingerprinting_params;
|
||||
fingerprinting_params.channels = {kChannelOne, kChannelTwo};
|
||||
fingerprinting_params.control = kFingerprintingControl;
|
||||
return fingerprinting_params;
|
||||
}
|
||||
|
||||
void LoadExpectedFingerprintingProto(Fingerprinting* fingerprinting_payload) {
|
||||
fingerprinting_payload->add_channels(kChannelOne);
|
||||
fingerprinting_payload->add_channels(kChannelTwo);
|
||||
fingerprinting_payload->set_control(kFingerprintingControl);
|
||||
}
|
||||
|
||||
ServiceBlockingInitParameters GetValidServiceBlockingParams() {
|
||||
ServiceBlockingInitParameters service_blocking_params;
|
||||
service_blocking_params.channels = {kChannelOne, kChannelTwo};
|
||||
service_blocking_params.device_groups = {kDeviceGroupOne, kDeviceGroupTwo};
|
||||
service_blocking_params.start_time = kServiceBockingStartTime;
|
||||
service_blocking_params.end_time = kServiceBockingEndTime;
|
||||
return service_blocking_params;
|
||||
}
|
||||
|
||||
void LoadExpectedServiceBlockingProto(
|
||||
ServiceBlocking* service_blocking_payload) {
|
||||
service_blocking_payload->add_channels(kChannelOne);
|
||||
service_blocking_payload->add_channels(kChannelTwo);
|
||||
service_blocking_payload->add_device_groups(kDeviceGroupOne);
|
||||
service_blocking_payload->add_device_groups(kDeviceGroupTwo);
|
||||
service_blocking_payload->set_start_time_sec(kServiceBockingStartTime);
|
||||
service_blocking_payload->set_end_time_sec(kServiceBockingEndTime);
|
||||
}
|
||||
|
||||
TEST(EmmTest, GenerateEmmSinglePayloadSuccess) {
|
||||
Emm emm_gen;
|
||||
FingerprintingInitParameters fingerprinting = GetValidFingerprintingParams();
|
||||
ServiceBlockingInitParameters service_blocking =
|
||||
GetValidServiceBlockingParams();
|
||||
EXPECT_EQ(emm_gen.SetFingerprinting({fingerprinting}), OkStatus());
|
||||
EXPECT_EQ(emm_gen.SetServiceBlocking({service_blocking}), OkStatus());
|
||||
|
||||
std::string result;
|
||||
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
|
||||
EXPECT_GT(result.length(), kExpectedNoPayloadLengthBytes);
|
||||
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
|
||||
kExpectedEmmVersion);
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
|
||||
kExpectedHeaderLength);
|
||||
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
|
||||
<< 8 |
|
||||
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
|
||||
ASSERT_GT(payload_lengh, 0);
|
||||
ASSERT_EQ(result.length(),
|
||||
kPayloadStartIndex + payload_lengh + kSignatureLength);
|
||||
|
||||
std::string payload_section =
|
||||
result.substr(kPayloadStartIndex, payload_lengh);
|
||||
// Parse the payload and validate fields.
|
||||
EmmPayload emm_payload;
|
||||
ASSERT_TRUE(emm_payload.ParseFromString(payload_section));
|
||||
|
||||
EmmPayload expected_payload;
|
||||
LoadExpectedFingerprintingProto(expected_payload.add_fingerprinting());
|
||||
LoadExpectedServiceBlockingProto(expected_payload.add_service_blocking());
|
||||
std::string serialized_expected_payload;
|
||||
expected_payload.SerializeToString(&serialized_expected_payload);
|
||||
EXPECT_EQ(payload_section, serialized_expected_payload);
|
||||
}
|
||||
|
||||
TEST(EmmTest, GenerateEmmMultiplePayloadSuccess) {
|
||||
Emm emm_gen;
|
||||
FingerprintingInitParameters fingerprinting_params;
|
||||
fingerprinting_params.channels = {kChannelThree};
|
||||
fingerprinting_params.control = kFingerprintingControl;
|
||||
EXPECT_EQ(emm_gen.SetFingerprinting(
|
||||
{GetValidFingerprintingParams(), fingerprinting_params}),
|
||||
OkStatus());
|
||||
|
||||
ServiceBlockingInitParameters service_blocking_params;
|
||||
service_blocking_params.channels = {kChannelThree};
|
||||
service_blocking_params.device_groups = {kDeviceGroupOne, kDeviceGroupTwo};
|
||||
service_blocking_params.start_time = kServiceBockingStartTime;
|
||||
service_blocking_params.end_time = kServiceBockingEndTime;
|
||||
EXPECT_EQ(emm_gen.SetServiceBlocking(
|
||||
{GetValidServiceBlockingParams(), service_blocking_params}),
|
||||
OkStatus());
|
||||
|
||||
std::string result;
|
||||
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
|
||||
EXPECT_GT(result.length(), kExpectedNoPayloadLengthBytes);
|
||||
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
|
||||
kExpectedEmmVersion);
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
|
||||
kExpectedHeaderLength);
|
||||
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
|
||||
<< 8 |
|
||||
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
|
||||
ASSERT_GT(payload_lengh, 0);
|
||||
ASSERT_EQ(result.length(),
|
||||
kPayloadStartIndex + payload_lengh + kSignatureLength);
|
||||
|
||||
std::string payload_section =
|
||||
result.substr(kPayloadStartIndex, payload_lengh);
|
||||
// Parse the payload and validate fields.
|
||||
EmmPayload emm_payload;
|
||||
ASSERT_TRUE(emm_payload.ParseFromString(payload_section));
|
||||
EXPECT_EQ(emm_payload.fingerprinting_size(), 2);
|
||||
EXPECT_EQ(emm_payload.service_blocking_size(), 2);
|
||||
}
|
||||
|
||||
TEST(EmmTest, GenerateEmmFingerprintingOnlySuccess) {
|
||||
Emm emm_gen;
|
||||
FingerprintingInitParameters fingerprinting = GetValidFingerprintingParams();
|
||||
EXPECT_EQ(emm_gen.SetFingerprinting({fingerprinting}), OkStatus());
|
||||
// OK to be called again.
|
||||
EXPECT_EQ(emm_gen.SetFingerprinting({fingerprinting}), OkStatus());
|
||||
|
||||
std::string result;
|
||||
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
|
||||
EXPECT_GT(result.length(), kExpectedNoPayloadLengthBytes);
|
||||
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
|
||||
kExpectedEmmVersion);
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
|
||||
kExpectedHeaderLength);
|
||||
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
|
||||
<< 8 |
|
||||
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
|
||||
ASSERT_GT(payload_lengh, 0);
|
||||
ASSERT_EQ(result.length(),
|
||||
kPayloadStartIndex + payload_lengh + kSignatureLength);
|
||||
|
||||
std::string payload_section =
|
||||
result.substr(kPayloadStartIndex, payload_lengh);
|
||||
// Parse the payload and validate fields.
|
||||
EmmPayload emm_payload;
|
||||
ASSERT_TRUE(emm_payload.ParseFromString(payload_section));
|
||||
EmmPayload expected_payload;
|
||||
LoadExpectedFingerprintingProto(expected_payload.add_fingerprinting());
|
||||
std::string serialized_expected_payload;
|
||||
expected_payload.SerializeToString(&serialized_expected_payload);
|
||||
EXPECT_EQ(payload_section, serialized_expected_payload);
|
||||
}
|
||||
|
||||
TEST(EmmTest, GenerateEmmServiceBlockingOnlySuccess) {
|
||||
Emm emm_gen;
|
||||
ServiceBlockingInitParameters service_blocking =
|
||||
GetValidServiceBlockingParams();
|
||||
EXPECT_EQ(emm_gen.SetServiceBlocking({service_blocking}), OkStatus());
|
||||
|
||||
std::string result;
|
||||
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
|
||||
EXPECT_GT(result.length(), kExpectedNoPayloadLengthBytes);
|
||||
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
|
||||
kExpectedEmmVersion);
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
|
||||
kExpectedHeaderLength);
|
||||
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
|
||||
<< 8 |
|
||||
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
|
||||
ASSERT_GT(payload_lengh, 0);
|
||||
ASSERT_EQ(result.length(),
|
||||
kPayloadStartIndex + payload_lengh + kSignatureLength);
|
||||
|
||||
std::string payload_section =
|
||||
result.substr(kPayloadStartIndex, payload_lengh);
|
||||
// Parse the payload and validate fields.
|
||||
EmmPayload emm_payload;
|
||||
ASSERT_TRUE(emm_payload.ParseFromString(payload_section));
|
||||
EmmPayload expected_payload;
|
||||
LoadExpectedServiceBlockingProto(expected_payload.add_service_blocking());
|
||||
std::string serialized_expected_payload;
|
||||
expected_payload.SerializeToString(&serialized_expected_payload);
|
||||
EXPECT_EQ(payload_section, serialized_expected_payload);
|
||||
}
|
||||
|
||||
TEST(EmmTest, GenerateEmmNoPayloadSuccess) {
|
||||
Emm emm_gen;
|
||||
std::string result;
|
||||
EXPECT_EQ(emm_gen.GenerateEmm(&result), OkStatus());
|
||||
EXPECT_EQ(result.length(), kExpectedNoPayloadLengthBytes);
|
||||
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kVersionStartIndex]),
|
||||
kExpectedEmmVersion);
|
||||
EXPECT_EQ(static_cast<uint8_t>(result[kHeaderLengthStartIndex]),
|
||||
kExpectedHeaderLength);
|
||||
int payload_lengh = static_cast<uint16_t>(result[kPayloadLengthStartIndex])
|
||||
<< 8 |
|
||||
static_cast<uint8_t>(result[kPayloadLengthStartIndex + 1]);
|
||||
EXPECT_EQ(payload_lengh, 0);
|
||||
EXPECT_EQ(result.length(), kPayloadStartIndex + kSignatureLength);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
@@ -28,6 +28,10 @@
|
||||
|
||||
// Minimum sending interval in milliseconds.
|
||||
static constexpr uint16_t KMinSendIntervalMs = 2;
|
||||
// Maximum number of entitlement key ids shown in private data.
|
||||
static constexpr uint16_t kMaxNumOfEntitlementKeyIds = 2;
|
||||
// Entitlement key id length is fixed to 16 bytes.
|
||||
static constexpr uint16_t kEntitlementKeyIdLength = 16;
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
@@ -153,8 +157,8 @@ void Emmg::Start() {
|
||||
|
||||
for (size_t i = 0; i < emmg_config_->max_num_message; i++) {
|
||||
SendDataProvision();
|
||||
absl::SleepFor(
|
||||
absl::Milliseconds(std::max(KMinSendIntervalMs, send_interval_ms_)));
|
||||
absl::SleepFor(std::max(absl::Milliseconds(KMinSendIntervalMs),
|
||||
absl::Milliseconds(send_interval_ms_)));
|
||||
}
|
||||
|
||||
SendStreamCloseRequest();
|
||||
@@ -220,13 +224,33 @@ void Emmg::BuildStreamBwRequest() {
|
||||
Host16ToBigEndian(request_ + 3, &total_param_length);
|
||||
}
|
||||
|
||||
Status Emmg::GeneratePrivateData(const std::string& content_provider,
|
||||
const std::string& content_id, uint8_t* buffer) {
|
||||
Status Emmg::GeneratePrivateData(
|
||||
const std::string& content_provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids, uint8_t* buffer) {
|
||||
DCHECK(buffer);
|
||||
// Generate payload.
|
||||
CaDescriptorPrivateData private_data;
|
||||
private_data.set_provider(content_provider);
|
||||
private_data.set_content_id(content_id);
|
||||
|
||||
if (entitlement_key_ids.size() > kMaxNumOfEntitlementKeyIds) {
|
||||
return Status(
|
||||
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 Status(
|
||||
error::INVALID_ARGUMENT,
|
||||
absl::StrCat("Entitlement key id length must be ",
|
||||
kEntitlementKeyIdLength, ". The offending key id is ",
|
||||
entitlement_key_id));
|
||||
}
|
||||
}
|
||||
for (const auto& entitlement_key_id : entitlement_key_ids) {
|
||||
private_data.add_entitlement_key_ids(entitlement_key_id);
|
||||
}
|
||||
std::string private_data_str = private_data.SerializeAsString();
|
||||
std::string payload_filler(kMaxTsPayloadSize - private_data_str.size(), 0);
|
||||
|
||||
@@ -268,8 +292,13 @@ void Emmg::BuildDataProvision() {
|
||||
&request_length_);
|
||||
|
||||
uint8_t datagram[kTsPacketSize];
|
||||
GeneratePrivateData(emmg_config_->content_provider, emmg_config_->content_id,
|
||||
datagram);
|
||||
Status status = GeneratePrivateData(
|
||||
emmg_config_->content_provider, emmg_config_->content_id,
|
||||
emmg_config_->entitlement_key_ids, datagram);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Fail to generate private data. " << status.ToString();
|
||||
return;
|
||||
}
|
||||
simulcrypt_util::AddParam(EMMG_DATAGRAM, datagram, kTsPacketSize, request_,
|
||||
&request_length_);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "common/status.h"
|
||||
@@ -30,6 +31,7 @@ struct EmmgConfig {
|
||||
uint8_t data_type;
|
||||
std::string content_provider;
|
||||
std::string content_id;
|
||||
std::vector<std::string> entitlement_key_ids;
|
||||
uint16_t bandwidth;
|
||||
uint32_t max_num_message;
|
||||
};
|
||||
@@ -82,8 +84,9 @@ class Emmg {
|
||||
|
||||
void UpdateSendInterval(uint16_t bandwidth_kbps);
|
||||
|
||||
Status GeneratePrivateData(const std::string& content_provider,
|
||||
const std::string& content_id, uint8_t* buffer);
|
||||
Status GeneratePrivateData(
|
||||
const std::string& content_provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids, uint8_t* buffer);
|
||||
void ReceiveResponseAndVerify(uint16_t expected_type);
|
||||
void Send(uint16_t message_type);
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ class EmmgTest : public ::testing::Test {
|
||||
config_.data_type = 0x01;
|
||||
config_.content_provider = "widevine_test";
|
||||
config_.content_id = "CasTsFake";
|
||||
config_.entitlement_key_ids = {"fakeKeyId1KeyId1", "fakeKeyId2KeyId2"};
|
||||
config_.bandwidth = 100;
|
||||
config_.max_num_message = 100;
|
||||
emmg_ = absl::make_unique<TestableEmmg>(&config_);
|
||||
@@ -103,6 +104,25 @@ TEST_F(EmmgTest, BuildDataProvision) {
|
||||
sizeof(kTestEmmgDataProvision)));
|
||||
}
|
||||
|
||||
TEST_F(EmmgTest,
|
||||
BuildDataProvisionFailedWhenNumOfEntitlementKeyIdsExceedLimit) {
|
||||
config_.entitlement_key_ids = {"fakeKeyId1KeyId1", "fakeKeyId2KeyId2",
|
||||
"fakeKeyId3KeyId3"};
|
||||
emmg_ = absl::make_unique<TestableEmmg>(&config_);
|
||||
emmg_->PublicBuildDataProvision();
|
||||
EXPECT_EQ(0, memcmp(kTestEmptyEmmgDataProvision, emmg_->GetRequest(),
|
||||
sizeof(kTestEmptyEmmgDataProvision)));
|
||||
}
|
||||
|
||||
TEST_F(EmmgTest,
|
||||
BuildDataProvisionFailedWhenEntitlementKeyIdLengthExceedLimit) {
|
||||
config_.entitlement_key_ids = {"fakeKeyId1KeyId1", "fakeKeyId2KeyId2KeyId2"};
|
||||
emmg_ = absl::make_unique<TestableEmmg>(&config_);
|
||||
emmg_->PublicBuildDataProvision();
|
||||
EXPECT_EQ(0, memcmp(kTestEmptyEmmgDataProvision, emmg_->GetRequest(),
|
||||
sizeof(kTestEmptyEmmgDataProvision)));
|
||||
}
|
||||
|
||||
TEST_F(EmmgTest, BuildStreamCloseRequest) {
|
||||
emmg_->PublicBuildStreamCloseRequest();
|
||||
EXPECT_EQ(0, memcmp(kTestEmmgStreamCloseRequest, emmg_->GetRequest(),
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "media_cas_packager_sdk/public/wv_cas_ca_descriptor.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
@@ -21,39 +22,44 @@ namespace cas {
|
||||
namespace {
|
||||
|
||||
// Size of fixed portion of CA descriptor (before any private bytes).
|
||||
static constexpr uint32_t kCaDescriptorBaseSize = 6;
|
||||
constexpr uint32_t kCaDescriptorBaseSize = 6;
|
||||
|
||||
// Size of fixed portion of CA descriptor that follows the length field.
|
||||
// This and the size of any private bytes must be placed in the length field.
|
||||
static constexpr uint32_t kCaDescriptorBasePostLengthSize = 4;
|
||||
constexpr uint32_t kCaDescriptorBasePostLengthSize = 4;
|
||||
|
||||
// Bitfield lengths for the CA descriptor fields
|
||||
static constexpr int kNumBitsCaDescriptorTagField = 8;
|
||||
static constexpr int kNumBitsCaDescriptorLengthField = 8;
|
||||
static constexpr int kNumBitsCaSystemIdField = 16;
|
||||
static constexpr int kNumBitsCaDescriptorReservedField = 3;
|
||||
static constexpr int kNumBitsCaDescriptorPidField = 13;
|
||||
constexpr int kNumBitsCaDescriptorTagField = 8;
|
||||
constexpr int kNumBitsCaDescriptorLengthField = 8;
|
||||
constexpr int kNumBitsCaSystemIdField = 16;
|
||||
constexpr int kNumBitsCaDescriptorReservedField = 3;
|
||||
constexpr int kNumBitsCaDescriptorPidField = 13;
|
||||
|
||||
// Bitfield constants for the CA descriptor fields.
|
||||
|
||||
// CA descriptor tag value, from table 2-45.
|
||||
static constexpr uint32_t kCaDescriptorTag = 9;
|
||||
constexpr uint32_t kCaDescriptorTag = 9;
|
||||
|
||||
// CA System ID for Widevine. From table in
|
||||
// https://en.wikipedia.org/wiki/Conditional_access
|
||||
static constexpr uint32_t kWidevineCaSystemId = 0x4AD4;
|
||||
constexpr uint32_t kWidevineCaSystemId = 0x4AD4;
|
||||
|
||||
// Value for CA descriptor reserved field should be set to 1.
|
||||
static constexpr uint32_t kReservedBit = 0x0007;
|
||||
constexpr uint32_t kReservedBit = 0x0007;
|
||||
|
||||
// The range of valid PIDs, from section 2.4.3.3, and table 2-3.
|
||||
static constexpr uint32_t kMinValidPID = 0x0010;
|
||||
static constexpr uint32_t kMaxValidPID = 0x1FFE;
|
||||
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,
|
||||
std::string* serialized_ca_desc) const {
|
||||
if (serialized_ca_desc == nullptr) {
|
||||
return {error::INVALID_ARGUMENT,
|
||||
@@ -62,10 +68,25 @@ 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 = "";
|
||||
// Field of Entitlement_key_ids could be empty.
|
||||
if (!provider.empty() && !content_id.empty()) {
|
||||
private_data = GeneratePrivateData(provider, content_id);
|
||||
private_data =
|
||||
GeneratePrivateData(provider, content_id, entitlement_key_ids);
|
||||
}
|
||||
|
||||
const size_t descriptor_length =
|
||||
@@ -107,10 +128,14 @@ size_t WvCasCaDescriptor::CaDescriptorBaseSize() const {
|
||||
}
|
||||
|
||||
std::string WvCasCaDescriptor::GeneratePrivateData(
|
||||
const std::string& provider, const std::string& content_id) const {
|
||||
const std::string& provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_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);
|
||||
}
|
||||
return private_data.SerializeAsString();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "common/status.h"
|
||||
@@ -48,6 +49,9 @@ 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.
|
||||
// |serialized_ca_desc| a std::string object to receive the encoded descriptor.
|
||||
//
|
||||
// Notes:
|
||||
@@ -55,10 +59,10 @@ 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,
|
||||
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>& entitlement_key_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
|
||||
@@ -66,8 +70,9 @@ class WvCasCaDescriptor {
|
||||
virtual size_t CaDescriptorBaseSize() const;
|
||||
|
||||
// Return private data in the CA descriptor.
|
||||
virtual std::string GeneratePrivateData(const std::string& provider,
|
||||
const std::string& content_id) const;
|
||||
virtual std::string GeneratePrivateData(
|
||||
const std::string& provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids) const;
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
|
||||
@@ -20,9 +20,11 @@ namespace cas {
|
||||
namespace {
|
||||
|
||||
// Random value for PID
|
||||
static constexpr int kTestPid = 50;
|
||||
static constexpr char kProvider[] = "widevine_test";
|
||||
static constexpr char kContentId[] = "1234";
|
||||
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
|
||||
|
||||
@@ -31,6 +33,7 @@ class WvCasCaDescriptorTest : public Test {
|
||||
WvCasCaDescriptorTest() {}
|
||||
WvCasCaDescriptor ca_descriptor_;
|
||||
std::string actual_ca_descriptor_;
|
||||
std::vector<std::string> entitlement_key_ids_;
|
||||
};
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, BaseSize) {
|
||||
@@ -38,38 +41,46 @@ TEST_F(WvCasCaDescriptorTest, BaseSize) {
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, BasicGoodGen) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
kTestPid, /*provider=*/"", /*content_id=*/"", /*entitlement_key_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, NoReturnStringFail) {
|
||||
EXPECT_EQ(error::INVALID_ARGUMENT,
|
||||
ca_descriptor_.GenerateCaDescriptor(kTestPid, "", "", nullptr)
|
||||
ca_descriptor_
|
||||
.GenerateCaDescriptor(
|
||||
kTestPid, /*provider=*/"", /*content_id=*/"",
|
||||
/*entitlement_key_ids=*/{}, /*serialized_ca_desc=*/nullptr)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidTooLowFail) {
|
||||
const uint32_t bad_pid = 0x10 - 1;
|
||||
EXPECT_EQ(error::INVALID_ARGUMENT,
|
||||
ca_descriptor_
|
||||
.GenerateCaDescriptor(bad_pid, "", "", &actual_ca_descriptor_)
|
||||
.error_code());
|
||||
EXPECT_EQ(
|
||||
error::INVALID_ARGUMENT,
|
||||
ca_descriptor_
|
||||
.GenerateCaDescriptor(bad_pid, /*provider=*/"", /*content_id=*/"",
|
||||
entitlement_key_ids_, &actual_ca_descriptor_)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidMinOK) {
|
||||
const uint32_t min_pid = 0x10;
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(min_pid, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
min_pid, /*provider=*/"", /*content_id=*/"", /*entitlement_key_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_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidMaxOK) {
|
||||
const uint32_t max_pid = 0x1FFE;
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(max_pid, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
max_pid, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xff\xfe");
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
}
|
||||
@@ -78,83 +89,93 @@ TEST_F(WvCasCaDescriptorTest, PidTooHighFail) {
|
||||
const uint32_t bad_pid = 0x1FFF;
|
||||
EXPECT_EQ(error::INVALID_ARGUMENT,
|
||||
ca_descriptor_
|
||||
.GenerateCaDescriptor(bad_pid, "", "", &actual_ca_descriptor_)
|
||||
.GenerateCaDescriptor(
|
||||
bad_pid, /*provider=*/"", /*content_id=*/"",
|
||||
/*entitlement_key_ids=*/{}, &actual_ca_descriptor_)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidOneByte) {
|
||||
EXPECT_OK(
|
||||
ca_descriptor_.GenerateCaDescriptor(255, "", "", &actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
255, /*provider=*/"", /*content_id=*/"", /*entitlement_key_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_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidSecondByte) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x1F00, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x1F00, /*provider=*/"", /*content_id=*/"", /*entitlement_key_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_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidTwelveBits) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0xFFF, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0xFFF, /*provider=*/"", /*content_id=*/"", /*entitlement_key_ids=*/{},
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x04\x4a\xd4\xef\xff");
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidThirteenthBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x1000, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x1000, /*provider=*/"", /*content_id=*/"", /*entitlement_key_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_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidTwelthBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x800, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x800, /*provider=*/"", /*content_id=*/"", /*entitlement_key_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_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidElevenththBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x400, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x400, /*provider=*/"", /*content_id=*/"", /*entitlement_key_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_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidTenthBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x200, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x200, /*provider=*/"", /*content_id=*/"", /*entitlement_key_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_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PidNinthBit) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(0x100, "", "",
|
||||
&actual_ca_descriptor_));
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
0x100, /*provider=*/"", /*content_id=*/"", /*entitlement_key_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_);
|
||||
}
|
||||
|
||||
TEST_F(WvCasCaDescriptorTest, PrivateDataOnlyProviderIgnored) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, kProvider, "",
|
||||
&actual_ca_descriptor_));
|
||||
TEST_F(WvCasCaDescriptorTest, PrivateDataWithNoContentIdIgnored) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, "", entitlement_key_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, PrivateDataOnlyContentIdIgnored) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, "", kContentId,
|
||||
&actual_ca_descriptor_));
|
||||
TEST_F(WvCasCaDescriptorTest, PrivateDataWithNoProviderIgnored) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(
|
||||
kTestPid, "", kContentId, entitlement_key_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, PrivateData) {
|
||||
TEST_F(WvCasCaDescriptorTest, PrivateDataWithNoEntitlementKeyIds) {
|
||||
EXPECT_OK(ca_descriptor_.GenerateCaDescriptor(kTestPid, kProvider, kContentId,
|
||||
&actual_ca_descriptor_));
|
||||
{}, &actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x19\x4a\xd4\xe0\x32", 6);
|
||||
CaDescriptorPrivateData private_data;
|
||||
private_data.set_provider(kProvider);
|
||||
@@ -163,6 +184,44 @@ TEST_F(WvCasCaDescriptorTest, PrivateData) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
EXPECT_EQ(expected_ca_descriptor + private_data.SerializeAsString(),
|
||||
actual_ca_descriptor_);
|
||||
}
|
||||
|
||||
class FakePrivateDataCaDescriptor : public WvCasCaDescriptor {
|
||||
public:
|
||||
void set_private_data(std::string private_data) {
|
||||
@@ -170,8 +229,8 @@ class FakePrivateDataCaDescriptor : public WvCasCaDescriptor {
|
||||
}
|
||||
|
||||
std::string GeneratePrivateData(
|
||||
const std::string& provider,
|
||||
const std::string& content_id) const override {
|
||||
const std::string& provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids) const override {
|
||||
return private_data_;
|
||||
}
|
||||
|
||||
@@ -183,7 +242,8 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataOneByte) {
|
||||
FakePrivateDataCaDescriptor fake_descriptor;
|
||||
fake_descriptor.set_private_data("X");
|
||||
EXPECT_OK(fake_descriptor.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, kContentId, &actual_ca_descriptor_));
|
||||
kTestPid, kProvider, kContentId, *kEntitlementKeyIds,
|
||||
&actual_ca_descriptor_));
|
||||
const std::string expected_ca_descriptor("\x09\x05\x4a\xd4\xe0\x32X", 7);
|
||||
EXPECT_EQ(expected_ca_descriptor, actual_ca_descriptor_);
|
||||
}
|
||||
@@ -193,7 +253,8 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataMultipleBytes) {
|
||||
FakePrivateDataCaDescriptor fake_descriptor;
|
||||
fake_descriptor.set_private_data(private_data_bytes);
|
||||
EXPECT_OK(fake_descriptor.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, kContentId, &actual_ca_descriptor_));
|
||||
kTestPid, kProvider, kContentId, *kEntitlementKeyIds,
|
||||
&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_);
|
||||
}
|
||||
@@ -203,7 +264,8 @@ TEST_F(WvCasCaDescriptorTest, PrivateDataMaxNumberBytes) {
|
||||
FakePrivateDataCaDescriptor fake_descriptor;
|
||||
fake_descriptor.set_private_data(private_data_bytes);
|
||||
EXPECT_OK(fake_descriptor.GenerateCaDescriptor(
|
||||
kTestPid, kProvider, kContentId, &actual_ca_descriptor_));
|
||||
kTestPid, kProvider, kContentId, *kEntitlementKeyIds,
|
||||
&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_);
|
||||
}
|
||||
@@ -212,11 +274,12 @@ 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,
|
||||
&actual_ca_descriptor_)
|
||||
.error_code());
|
||||
EXPECT_EQ(
|
||||
error::INVALID_ARGUMENT,
|
||||
fake_descriptor
|
||||
.GenerateCaDescriptor(kTestPid, kProvider, kContentId,
|
||||
*kEntitlementKeyIds, &actual_ca_descriptor_)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
using testing::_;
|
||||
using testing::DoAll;
|
||||
using testing::Return;
|
||||
using testing::SetArgumentPointee;
|
||||
using testing::SetArgPointee;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -59,9 +59,10 @@ class HardcodedWvCasKeyFetcher : public WvCasKeyFetcher {
|
||||
const std::string& signing_iv)
|
||||
: WvCasKeyFetcher(signing_provider, signing_key, signing_iv) {}
|
||||
~HardcodedWvCasKeyFetcher() override {}
|
||||
MOCK_CONST_METHOD2(MakeHttpRequest,
|
||||
Status(const std::string& signed_request_json,
|
||||
std::string* http_response_json));
|
||||
MOCK_METHOD(Status, MakeHttpRequest,
|
||||
(const std::string& signed_request_json,
|
||||
std::string* http_response_json),
|
||||
(const, override));
|
||||
};
|
||||
|
||||
class MockWvCasKeyFetcher : public WvCasKeyFetcher {
|
||||
@@ -168,7 +169,7 @@ TEST_F(WvCasKeyFetcherTest, TestRequestEntitlementKey) {
|
||||
EXPECT_EQ(signed_request_json, kSignedCasEncryptionRequest);
|
||||
|
||||
EXPECT_CALL(mock_key_fetcher, MakeHttpRequest(kSignedCasEncryptionRequest, _))
|
||||
.WillOnce(DoAll(SetArgumentPointee<1>(std::string(kHttpResponse)),
|
||||
.WillOnce(DoAll(SetArgPointee<1>(std::string(kHttpResponse)),
|
||||
Return(OkStatus())));
|
||||
std::string actual_signed_response;
|
||||
EXPECT_OK(mock_key_fetcher.MakeHttpRequest(signed_request_json,
|
||||
|
||||
@@ -43,6 +43,9 @@ ABSL_FLAG(int32_t, data_id, 0, "EMMG data_id.");
|
||||
ABSL_FLAG(int32_t, data_type, 1, "EMMG data_type");
|
||||
ABSL_FLAG(std::string, content_provider, "", "Content provider");
|
||||
ABSL_FLAG(std::string, content_id, "", "Content id");
|
||||
ABSL_FLAG(
|
||||
std::vector<std::string>, entitlement_key_ids, {},
|
||||
"Comma-separated list of entitlement_key_ids to put into private data");
|
||||
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");
|
||||
@@ -59,6 +62,7 @@ void BuildEmmgConfig(widevine::cas::EmmgConfig *config) {
|
||||
config->data_type = absl::GetFlag(FLAGS_data_type);
|
||||
config->content_provider = absl::GetFlag(FLAGS_content_provider);
|
||||
config->content_id = absl::GetFlag(FLAGS_content_id);
|
||||
config->entitlement_key_ids = absl::GetFlag(FLAGS_entitlement_key_ids);
|
||||
config->bandwidth = absl::GetFlag(FLAGS_bandwidth);
|
||||
config->max_num_message = absl::GetFlag(FLAGS_max_num_message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user