Update SimulCrypt ECMG on injecting entitlement keys

This commit is contained in:
Lu Chen
2020-02-14 15:46:03 -08:00
parent 688dd62dae
commit d71d62d272
5 changed files with 141 additions and 170 deletions

View File

@@ -29,7 +29,7 @@ constexpr char kTestEcmgChannelSetup[] = {
constexpr char kTestEcmgChannelSetupWithPrivateParameters[] = { constexpr char kTestEcmgChannelSetupWithPrivateParameters[] = {
'\x03', // protocol_version '\x03', // protocol_version
'\x00', '\x01', // message_type - Channel_setup '\x00', '\x01', // message_type - Channel_setup
'\x00', '\x8c', // message_length '\x00', '\x1e', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id '\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length '\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value '\x00', '\x01', // parameter_value
@@ -42,29 +42,7 @@ constexpr char kTestEcmgChannelSetupWithPrivateParameters[] = {
'\x80', '\x01', // parameter_type - CRYPTO_MODE '\x80', '\x01', // parameter_type - CRYPTO_MODE
'\x00', '\x07', // parameter_length '\x00', '\x07', // parameter_length
'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value 'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value
'\x80', '\x02', // parameter_type - TRACK_TYPES };
'\x00', '\x02', // parameter_length
'S', 'D', // parameter_value
'\x80', '\x05', // parameter_type - ENTITLEMENT_ID_KEY_COMBINATION
'\x00', '\x30', // parameter_length
// parameter_value - ENTITLEMENT_ID (16 bytes)
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
// parameter_value (continued) - ENTITLEMENT_KEY (32 bytes)
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x80', '\x05', // parameter_type - ENTITLEMENT_ID_KEY_COMBINATION
'\x00', '\x30', // parameter_length
// parameter_value - ENTITLEMENT_ID (16 bytes)
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
// parameter_value (continued) - ENTITLEMENT_KEY (32 bytes)
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68'};
constexpr char kTestEcmgChannelTest[] = { constexpr char kTestEcmgChannelTest[] = {
'\x03', // protocol_version '\x03', // protocol_version
@@ -126,7 +104,7 @@ constexpr char kTestEcmgChannelStatus[] = {
constexpr char kTestEcmgStreamSetupWithPrivateParameters[] = { constexpr char kTestEcmgStreamSetupWithPrivateParameters[] = {
'\x03', // protocol_version '\x03', // protocol_version
'\x01', '\x01', // message_type - Stream_setup '\x01', '\x01', // message_type - Stream_setup
'\x00', '\x46', // message_length '\x00', '\xae', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id '\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length '\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value '\x00', '\x01', // parameter_value
@@ -139,17 +117,37 @@ constexpr char kTestEcmgStreamSetupWithPrivateParameters[] = {
'\x00', '\x10', // parameter_type - nominal_CP_duration '\x00', '\x10', // parameter_type - nominal_CP_duration
'\x00', '\x02', // parameter_length '\x00', '\x02', // parameter_length
'\x00', '\x64', // parameter_value '\x00', '\x64', // parameter_value
'\x80', '\x03', // parameter_type - STREAM_TRACK_TYPE '\x80', '\x02', // parameter_type - STREAM_TRACK_TYPE
'\x00', '\x02', // parameter_length '\x00', '\x02', // parameter_length
'S', 'D', // parameter_value 'S', 'D', // parameter_value
'\x80', '\x04', // parameter_type - CONTENT_IV '\x80', '\x03', // parameter_type - CONTENT_IV
'\x00', '\x10', // parameter_length '\x00', '\x10', // parameter_length
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', //
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', //
'\x80', '\x04', // parameter_type - CONTENT_IV '\x80', '\x03', // parameter_type - CONTENT_IV
'\x00', '\x10', // parameter_length '\x00', '\x10', // parameter_length
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', //
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f'}; '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', //
'\x80', '\x04', // parameter_type - ENTITLEMENT_ID_KEY_COMBINATION
'\x00', '\x30', // parameter_length
// parameter_value - ENTITLEMENT_ID (16 bytes)
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
// parameter_value (continued) - ENTITLEMENT_KEY (32 bytes)
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x80', '\x04', // parameter_type - ENTITLEMENT_ID_KEY_COMBINATION
'\x00', '\x30', // parameter_length
// parameter_value - ENTITLEMENT_ID (16 bytes)
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
// parameter_value (continued) - ENTITLEMENT_KEY (32 bytes)
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68'};
constexpr char kTestEcmgStreamSetup[] = { constexpr char kTestEcmgStreamSetup[] = {
'\x03', // protocol_version '\x03', // protocol_version
@@ -247,7 +245,7 @@ constexpr char kTestEcmgCwProvision[] = {
constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = { constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = {
'\x03', // protocol_version '\x03', // protocol_version
'\x02', '\x01', // message_type - CW_provision '\x02', '\x01', // message_type - CW_provision
'\x00', '\xf4', // message_length '\x00', '\xee', // message_length
'\x00', '\x0e', // parameter_type - ECM_channel_id '\x00', '\x0e', // parameter_type - ECM_channel_id
'\x00', '\x02', // parameter_length '\x00', '\x02', // parameter_length
'\x00', '\x01', // parameter_value '\x00', '\x01', // parameter_value
@@ -271,28 +269,25 @@ constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = {
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', // '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', //
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', // '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', //
'\x00', '\x0d', // parameter_type - access_criteria '\x00', '\x0d', // parameter_type - access_criteria
'\x00', '\xac', // parameter_length '\x00', '\xa6', // parameter_length
'\x80', '\x00', // access_criteria parameter_type - AGE_RESTRICTION '\x80', '\x00', // access_criteria parameter_type - AGE_RESTRICTION
'\x00', '\x01', // parameter_length '\x00', '\x01', // parameter_length
'\x00', // parameter_value '\x00', // parameter_value
'\x80', '\x01', // access_criteria parameter_type - CRYPTO_MODE '\x80', '\x01', // access_criteria parameter_type - CRYPTO_MODE
'\x00', '\x07', // parameter_length '\x00', '\x07', // parameter_length
'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value 'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value
'\x80', '\x02', // access_criteria parameter_type - TRACK_TYPES '\x80', '\x02', // access_criteria parameter_type - STREAM_TRACK_TYPE
'\x00', '\x02', // parameter_length '\x00', '\x02', // parameter_length
'S', 'D', // parameter_value 'S', 'D', // parameter_value
'\x80', '\x03', // access_criteria parameter_type - STREAM_TRACK_TYPE '\x80', '\x03', // access_criteria parameter_type - CONTENT_IV
'\x00', '\x02', // parameter_length
'S', 'D', // parameter_value
'\x80', '\x04', // access_criteria parameter_type - CONTENT_IV
'\x00', '\x10', // parameter_length '\x00', '\x10', // parameter_length
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', // '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', //
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', // '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', //
'\x80', '\x04', // access_criteria parameter_type - CONTENT_IV '\x80', '\x03', // access_criteria parameter_type - CONTENT_IV
'\x00', '\x10', // parameter_length '\x00', '\x10', // parameter_length
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', // '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', //
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', // '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', //
'\x80', '\x05', // parameter_type - ENTITLEMENT_ID_KEY_COMBINATION '\x80', '\x04', // parameter_type - ENTITLEMENT_ID_KEY_COMBINATION
'\x00', '\x30', // parameter_length '\x00', '\x30', // parameter_length
// parameter_value - ENTITLEMENT_ID (16 bytes) // parameter_value - ENTITLEMENT_ID (16 bytes)
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', // '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
@@ -302,7 +297,7 @@ constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = {
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', // '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', // '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', // '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', //
'\x80', '\x05', // parameter_type - ENTITLEMENT_ID_KEY_COMBINATION '\x80', '\x04', // parameter_type - ENTITLEMENT_ID_KEY_COMBINATION
'\x00', '\x30', // parameter_length '\x00', '\x30', // parameter_length
// parameter_value - ENTITLEMENT_ID (16 bytes) // parameter_value - ENTITLEMENT_ID (16 bytes)
'\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', // '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', //

View File

@@ -54,11 +54,6 @@ Status ProcessPrivateParameters(const char* const request, uint16_t param_type,
params->crypto_mode = std::string(request + *offset, param_length); params->crypto_mode = std::string(request + *offset, param_length);
*offset += param_length; *offset += param_length;
break; break;
case TRACK_TYPES:
params->track_types.push_back(
std::string(request + *offset, param_length));
*offset += param_length;
break;
case STREAM_TRACK_TYPE: case STREAM_TRACK_TYPE:
params->stream_track_type = std::string(request + *offset, param_length); params->stream_track_type = std::string(request + *offset, param_length);
*offset += param_length; *offset += param_length;
@@ -722,8 +717,7 @@ void EcmgClientHandler::HandleCwProvision(const EcmgParameters& params,
return; return;
} }
// Request entitlement keys if they do not exist yet. if (streams_info_.at(params.ecm_stream_id)->ecm == nullptr) {
if (ecm_ == nullptr) {
status = CheckAndInitializeEcm(params); status = CheckAndInitializeEcm(params);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << status.ToString(); LOG(ERROR) << status.ToString();
@@ -758,9 +752,6 @@ Status EcmgClientHandler::UpdateCommonPrivateParameters(
} }
age_restriction_ = params.age_restriction; age_restriction_ = params.age_restriction;
} }
if (!params.track_types.empty()) {
track_types_.assign(params.track_types.begin(), params.track_types.end());
}
if (!params.content_ivs.empty()) { if (!params.content_ivs.empty()) {
if (params.content_ivs.size() < ecmg_config_->number_of_content_keys) { if (params.content_ivs.size() < ecmg_config_->number_of_content_keys) {
return {error::INVALID_ARGUMENT, return {error::INVALID_ARGUMENT,
@@ -778,10 +769,6 @@ Status EcmgClientHandler::UpdateCommonPrivateParameters(
} }
} }
} }
if (!params.entitlement_comb.empty()) {
entitlement_comb_.assign(params.entitlement_comb.begin(),
params.entitlement_comb.end());
}
return OkStatus(); return OkStatus();
} }
@@ -825,26 +812,29 @@ Status EcmgClientHandler::UpdateStreamPrivateParameters(
stream_info->content_ivs.assign(params.content_ivs.begin(), stream_info->content_ivs.assign(params.content_ivs.begin(),
params.content_ivs.end()); params.content_ivs.end());
} }
if (!params.entitlement_comb.empty()) {
stream_info->entitlement_comb.assign(params.entitlement_comb.begin(),
params.entitlement_comb.end());
}
return OkStatus(); return OkStatus();
} }
Status EcmgClientHandler::CheckAndInitializeEcm(const EcmgParameters& params) { Status EcmgClientHandler::CheckAndInitializeEcm(const EcmgParameters& params) {
DCHECK(ecm_ == nullptr); DCHECK(streams_info_.contains(params.ecm_stream_id));
EcmgStreamInfo* stream_info = streams_info_.at(params.ecm_stream_id).get(); EcmgStreamInfo* stream_info = streams_info_.at(params.ecm_stream_id).get();
DCHECK(stream_info->ecm == nullptr);
if (stream_info->content_ivs.empty() && content_ivs_.empty()) { if (stream_info->content_ivs.empty() && content_ivs_.empty()) {
return {error::NOT_FOUND, "Content iv not specified."}; return {error::NOT_FOUND, "Content iv not specified."};
} }
if (track_types_.empty()) { if (stream_info->entitlement_comb.empty()) {
return {error::NOT_FOUND, "Track type not specified."};
}
if (entitlement_comb_.empty()) {
return {error::NOT_FOUND, "Entitlement key id comb not specified."}; return {error::NOT_FOUND, "Entitlement key id comb not specified."};
} }
if (entitlement_comb_.size() != if (stream_info->entitlement_comb.size() !=
track_types_.size() * ecmg_config_->number_of_content_keys) { ecmg_config_->number_of_content_keys) {
return {error::NOT_FOUND, return {error::NOT_FOUND,
"Number of injected entitlement keys must equal to number of " "Number of injected entitlement keys must equal to number of "
"track types * number of content keys per ecm."}; "content keys per ecm."};
} }
bool key_rotation = ecmg_config_->number_of_content_keys > 1; bool key_rotation = ecmg_config_->number_of_content_keys > 1;
@@ -862,25 +852,26 @@ Status EcmgClientHandler::CheckAndInitializeEcm(const EcmgParameters& params) {
} }
std::vector<EntitlementKeyInfo> entitlements; std::vector<EntitlementKeyInfo> entitlements;
entitlements.reserve(entitlement_comb_.size()); entitlements.reserve(stream_info->entitlement_comb.size());
for (size_t i = 0; i < entitlement_comb_.size(); i++) { for (size_t i = 0; i < stream_info->entitlement_comb.size(); i++) {
entitlements.emplace_back(); entitlements.emplace_back();
EntitlementKeyInfo* entitlement = &entitlements.back(); EntitlementKeyInfo* entitlement = &entitlements.back();
entitlement->track_type = track_types_.at(key_rotation ? i / 2 : i); entitlement->track_type = stream_info->track_type;
entitlement->is_even_key = key_rotation ? i % 2 == 0 : true; entitlement->is_even_key = key_rotation ? i % 2 == 0 : true;
entitlement->key_id = entitlement_comb_[i].key_id; entitlement->key_id = stream_info->entitlement_comb[i].key_id;
entitlement->key_value = entitlement_comb_[i].key_value; entitlement->key_value = stream_info->entitlement_comb[i].key_value;
} }
ecm_ = absl::make_unique<Ecm>(); stream_info->ecm = absl::make_unique<Ecm>();
return ecm_->Initialize(ecm_init_params, entitlements); return stream_info->ecm->Initialize(ecm_init_params, entitlements);
} }
Status EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params, Status EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params,
uint8_t* ecm_datagram) const { uint8_t* ecm_datagram) const {
DCHECK(ecm_datagram); DCHECK(ecm_datagram);
DCHECK(ecm_); DCHECK(streams_info_.contains(params.ecm_stream_id));
EcmgStreamInfo* stream_info = streams_info_.at(params.ecm_stream_id).get(); EcmgStreamInfo* stream_info = streams_info_.at(params.ecm_stream_id).get();
DCHECK(stream_info->ecm);
// Generate serialized ECM. // Generate serialized ECM.
std::vector<EntitledKeyInfo> keys; std::vector<EntitledKeyInfo> keys;
@@ -901,11 +892,11 @@ Status EcmgClientHandler::BuildEcmDatagram(const EcmgParameters& params,
Status status; Status status;
std::string serialized_ecm; std::string serialized_ecm;
if (ecmg_config_->number_of_content_keys > 1) { if (ecmg_config_->number_of_content_keys > 1) {
status = ecm_->GenerateEcm(&keys[0], &keys[1], stream_info->track_type, status = stream_info->ecm->GenerateEcm(
&serialized_ecm); &keys[0], &keys[1], stream_info->track_type, &serialized_ecm);
} else { } else {
status = ecm_->GenerateSingleKeyEcm(&keys[0], stream_info->track_type, status = stream_info->ecm->GenerateSingleKeyEcm(
&serialized_ecm); &keys[0], stream_info->track_type, &serialized_ecm);
} }
if (!status.ok()) { if (!status.ok()) {
return status; return status;

View File

@@ -62,9 +62,6 @@ struct EcmgParameters {
// User defined paremeters below. // User defined paremeters below.
uint8_t age_restriction = 0xff; // Assume 0xff (255) is an invalid value. uint8_t age_restriction = 0xff; // Assume 0xff (255) is an invalid value.
std::string crypto_mode; std::string crypto_mode;
// All track types that need to be supported in the channel.
// Used to request entitlement keys.
std::vector<std::string> track_types;
std::string stream_track_type; std::string stream_track_type;
std::vector<std::string> content_ivs; // 8 or 16 bytes, one for each key. std::vector<std::string> content_ivs; // 8 or 16 bytes, one for each key.
std::vector<EntitlementIdKeyComb> entitlement_comb; std::vector<EntitlementIdKeyComb> entitlement_comb;
@@ -77,6 +74,8 @@ struct EcmgStreamInfo {
CryptoMode crypto_mode = CryptoMode::kInvalid; CryptoMode crypto_mode = CryptoMode::kInvalid;
// 8 or 16 bytes, one for each key. Will use |content_ivs_| if empty. // 8 or 16 bytes, one for each key. Will use |content_ivs_| if empty.
std::vector<std::string> content_ivs; std::vector<std::string> content_ivs;
std::vector<EntitlementIdKeyComb> entitlement_comb;
std::unique_ptr<Ecm> ecm;
}; };
// A class that handles one (and only one) ECMG client. // A class that handles one (and only one) ECMG client.
@@ -133,10 +132,7 @@ class EcmgClientHandler {
uint16_t channel_id_; uint16_t channel_id_;
// Channel specific information. // Channel specific information.
std::unique_ptr<Ecm> ecm_; // |ecm_| is shared within the channel.
uint8_t age_restriction_ = 0; uint8_t age_restriction_ = 0;
std::vector<std::string> track_types_;
std::vector<EntitlementIdKeyComb> entitlement_comb_;
std::vector<std::string> content_ivs_; std::vector<std::string> content_ivs_;
// Map from ECM_stream_id to EcmgStreamInfo. // Map from ECM_stream_id to EcmgStreamInfo.

View File

@@ -92,10 +92,8 @@ class EcmgClientHandlerTest : public ::testing::Test {
void BuildChannelSetupRequest(uint16_t channel_id, uint32_t super_cas_id, void BuildChannelSetupRequest(uint16_t channel_id, uint32_t super_cas_id,
uint8_t age_restriction, uint8_t age_restriction,
const std::string& crypto_mode, const std::string& crypto_mode, char* message,
const std::vector<std::string>& track_types, size_t* message_length) {
const std::vector<std::string>& entitlements,
char* message, size_t* message_length) {
EXPECT_TRUE(message != nullptr); EXPECT_TRUE(message != nullptr);
EXPECT_TRUE(message_length != nullptr); EXPECT_TRUE(message_length != nullptr);
BuildMessageHeader(ECMG_SCS_PROTOCOL_VERSION, ECMG_CHANNEL_SETUP, message, BuildMessageHeader(ECMG_SCS_PROTOCOL_VERSION, ECMG_CHANNEL_SETUP, message,
@@ -110,21 +108,6 @@ class EcmgClientHandlerTest : public ::testing::Test {
AddParam(CRYPTO_MODE, reinterpret_cast<const uint8_t*>(crypto_mode.c_str()), AddParam(CRYPTO_MODE, reinterpret_cast<const uint8_t*>(crypto_mode.c_str()),
crypto_mode.size(), message, message_length); crypto_mode.size(), message, message_length);
} }
if (!track_types.empty()) {
for (const auto& track_type : track_types) {
AddParam(TRACK_TYPES,
reinterpret_cast<const uint8_t*>(track_type.c_str()),
track_type.size(), message, message_length);
}
}
if (!entitlements.empty()) {
for (const auto& entitlement : entitlements) {
AddParam(ENTITLEMENT_ID_KEY_COMBINATION,
reinterpret_cast<const uint8_t*>(entitlement.c_str()),
entitlement.size(), message, message_length);
}
}
uint16_t total_param_length = *message_length - 5; uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length); Host16ToBigEndian(message + 3, &total_param_length);
} }
@@ -132,6 +115,7 @@ class EcmgClientHandlerTest : public ::testing::Test {
void BuildStreamSetupRequest(uint16_t channel_id, uint16_t stream_id, void BuildStreamSetupRequest(uint16_t channel_id, uint16_t stream_id,
uint16_t ecm_id, uint16_t nominal_CP_duration, uint16_t ecm_id, uint16_t nominal_CP_duration,
const std::string& stream_track_type, const std::string& stream_track_type,
const std::vector<std::string>& entitlements,
const std::vector<std::string>& content_ivs, const std::vector<std::string>& content_ivs,
char* message, size_t* message_length) { char* message, size_t* message_length) {
EXPECT_TRUE(message != nullptr); EXPECT_TRUE(message != nullptr);
@@ -149,6 +133,13 @@ class EcmgClientHandlerTest : public ::testing::Test {
reinterpret_cast<const uint8_t*>(stream_track_type.c_str()), reinterpret_cast<const uint8_t*>(stream_track_type.c_str()),
stream_track_type.size(), message, message_length); stream_track_type.size(), message, message_length);
} }
if (!entitlements.empty()) {
for (const auto& entitlement : entitlements) {
AddParam(ENTITLEMENT_ID_KEY_COMBINATION,
reinterpret_cast<const uint8_t*>(entitlement.c_str()),
entitlement.size(), message, message_length);
}
}
if (!content_ivs.empty()) { if (!content_ivs.empty()) {
for (auto& content_iv : content_ivs) { for (auto& content_iv : content_ivs) {
AddParam(CONTENT_IV, reinterpret_cast<const uint8_t*>(content_iv.c_str()), AddParam(CONTENT_IV, reinterpret_cast<const uint8_t*>(content_iv.c_str()),
@@ -293,25 +284,29 @@ TEST_F(EcmgClientHandlerTest, SuccessSequenceWithPrivateParameters) {
} }
TEST_F(EcmgClientHandlerTest, SuccessSequenceInjectedEntitlements) { TEST_F(EcmgClientHandlerTest, SuccessSequenceInjectedEntitlements) {
BuildChannelSetupRequest( BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
kChannelId, kSuperCasId, kAgeRestriction, kCryptoMode, kCryptoMode, request_, &request_len_);
{kTrackTypesHD, kTrackTypesSD},
{absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd),
absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_); EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_)); EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_));
BuildStreamSetupRequest(kChannelId, kStreamId, kEcmId, kNominalCpDuration, BuildStreamSetupRequest(
kTrackTypesSD, {kContentKeyEven, kContentKeyEven}, kChannelId, kStreamId, kEcmId, kNominalCpDuration, kTrackTypesSD,
request_, &request_len_); {absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
{kContentKeyEven, kContentKeyEven}, request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_); EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_)); EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_));
BuildStreamSetupRequest(
kChannelId, kStreamId + 1, kEcmId, kNominalCpDuration, kTrackTypesHD,
{absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
{kContentKeyEven, kContentKeyEven}, request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_);
const std::vector<EcmgCpCwCombination> cp_cw_combination = { const std::vector<EcmgCpCwCombination> cp_cw_combination = {
{kCpNumber, kContentKeyEven}, {kCpNumber + 1, kContentKeyOdd}}; {kCpNumber, kContentKeyEven}, {kCpNumber + 1, kContentKeyOdd}};
BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination, BuildCwProvisionRequest(kChannelId, kStreamId, kCpNumber, cp_cw_combination,
@@ -319,8 +314,13 @@ TEST_F(EcmgClientHandlerTest, SuccessSequenceInjectedEntitlements) {
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgEcmResponse), response_len_); EXPECT_EQ(sizeof(kTestEcmgEcmResponse), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgEcmResponse, response_, 27)); EXPECT_EQ(0, memcmp(kTestEcmgEcmResponse, response_, 27));
}
BuildCwProvisionRequest(kChannelId, kStreamId + 1, kCpNumber,
cp_cw_combination, request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgEcmResponse), response_len_);
}
//
TEST_F(EcmgClientHandlerTest, SuccessChannelError) { TEST_F(EcmgClientHandlerTest, SuccessChannelError) {
SetupValidChannel(); SetupValidChannel();
handler_->HandleRequest(kTestEcmgChannelError, response_, &response_len_); handler_->HandleRequest(kTestEcmgChannelError, response_, &response_len_);
@@ -410,11 +410,7 @@ TEST_F(EcmgClientHandlerTest, WrongParameterInsufficientKey) {
TEST_F(EcmgClientHandlerTest, WrongParameterSuperCasId) { TEST_F(EcmgClientHandlerTest, WrongParameterSuperCasId) {
// Setup a channel with an unexpected super cas id (expecting kSuperCasId). // Setup a channel with an unexpected super cas id (expecting kSuperCasId).
BuildChannelSetupRequest( BuildChannelSetupRequest(kChannelId, 0, kAgeRestriction, kCryptoMode,
kChannelId, 0, kAgeRestriction, kCryptoMode,
{kTrackTypesHD, kTrackTypesSD},
{absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
request_, &request_len_); request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
CheckChannelError(UNKNOWN_SUPER_CAS_ID_VALUE, response_, response_len_); CheckChannelError(UNKNOWN_SUPER_CAS_ID_VALUE, response_, response_len_);
@@ -423,20 +419,18 @@ TEST_F(EcmgClientHandlerTest, WrongParameterSuperCasId) {
TEST_F(EcmgClientHandlerTest, WrongParameterChannelId) { TEST_F(EcmgClientHandlerTest, WrongParameterChannelId) {
SetupValidChannel(); SetupValidChannel();
// Setup a stream with an unexpected channel id (expecting kChannelId). // Setup a stream with an unexpected channel id (expecting kChannelId).
BuildStreamSetupRequest(0, kStreamId, kEcmId, kNominalCpDuration, BuildStreamSetupRequest(
kTrackTypesSD, {kContentKeyEven, kContentKeyEven}, 0, kStreamId, kEcmId, kNominalCpDuration, kTrackTypesSD,
request_, &request_len_); {absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
{kContentKeyEven, kContentKeyEven}, request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
CheckStreamError(UNKNOWN_ECM_CHANNEL_ID_VALUE, response_, response_len_); CheckStreamError(UNKNOWN_ECM_CHANNEL_ID_VALUE, response_, response_len_);
} }
TEST_F(EcmgClientHandlerTest, WrongParameterCryptoMode) { TEST_F(EcmgClientHandlerTest, WrongParameterCryptoMode) {
BuildChannelSetupRequest( BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
kChannelId, kSuperCasId, kAgeRestriction, "someCryptoMode", "someCryptoMode", request_, &request_len_);
{kTrackTypesHD, kTrackTypesSD},
{absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
CheckChannelError(INVALID_VALUE_FOR_DVB_PARAMETER, response_, response_len_); CheckChannelError(INVALID_VALUE_FOR_DVB_PARAMETER, response_, response_len_);
} }
@@ -451,15 +445,15 @@ TEST_F(EcmgClientHandlerTest, WrongParameterLengthSuperCasId) {
TEST_F(EcmgClientHandlerTest, NoInjectedEntitlements) { TEST_F(EcmgClientHandlerTest, NoInjectedEntitlements) {
BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction, BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
kCryptoMode, {kTrackTypesSD}, kCryptoMode, request_, &request_len_);
/*entitlements*/ {}, request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_); EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_)); EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_));
BuildStreamSetupRequest(kChannelId, kStreamId, kEcmId, kNominalCpDuration, BuildStreamSetupRequest(
kTrackTypesSD, {kContentKeyEven, kContentKeyEven}, kChannelId, kStreamId, kEcmId, kNominalCpDuration, kTrackTypesSD,
request_, &request_len_); /*entitlements*/ {}, {kContentKeyEven, kContentKeyEven}, request_,
&request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_); EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_)); EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_));
@@ -473,19 +467,16 @@ TEST_F(EcmgClientHandlerTest, NoInjectedEntitlements) {
} }
TEST_F(EcmgClientHandlerTest, NotEnoughInjectedEntitlements) { TEST_F(EcmgClientHandlerTest, NotEnoughInjectedEntitlements) {
BuildChannelSetupRequest( BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
kChannelId, kSuperCasId, kAgeRestriction, kCryptoMode, kCryptoMode, request_, &request_len_);
{kTrackTypesHD, kTrackTypesSD},
{absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd)},
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_); EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_)); EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_));
BuildStreamSetupRequest(kChannelId, kStreamId, kEcmId, kNominalCpDuration, BuildStreamSetupRequest(
kTrackTypesSD, {kContentKeyEven, kContentKeyEven}, kChannelId, kStreamId, kEcmId, kNominalCpDuration, kTrackTypesSD,
request_, &request_len_); {absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven)},
{kContentKeyEven, kContentKeyEven}, request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_); EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_)); EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_));
@@ -499,19 +490,18 @@ TEST_F(EcmgClientHandlerTest, NotEnoughInjectedEntitlements) {
} }
TEST_F(EcmgClientHandlerTest, TooManyInjectedEntitlements) { TEST_F(EcmgClientHandlerTest, TooManyInjectedEntitlements) {
BuildChannelSetupRequest( BuildChannelSetupRequest(kChannelId, kSuperCasId, kAgeRestriction,
kChannelId, kSuperCasId, kAgeRestriction, kCryptoMode, {kTrackTypesSD}, kCryptoMode, request_, &request_len_);
{absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd),
absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven)},
request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_); EXPECT_EQ(sizeof(kTestEcmgChannelStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_)); EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response_, response_len_));
BuildStreamSetupRequest(kChannelId, kStreamId, kEcmId, kNominalCpDuration, BuildStreamSetupRequest(
kTrackTypesSD, {kContentKeyEven, kContentKeyEven}, kChannelId, kStreamId, kEcmId, kNominalCpDuration, kTrackTypesSD,
request_, &request_len_); {absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven),
absl::StrCat(kEntitlementKeyIdOdd, kEntitlementKeyValueOdd),
absl::StrCat(kEntitlementKeyIdEven, kEntitlementKeyValueEven)},
{kContentKeyEven, kContentKeyEven}, request_, &request_len_);
handler_->HandleRequest(request_, response_, &response_len_); handler_->HandleRequest(request_, response_, &response_len_);
EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_); EXPECT_EQ(sizeof(kTestEcmgStreamStatus), response_len_);
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_)); EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response_, response_len_));

View File

@@ -63,10 +63,9 @@
// User defined ECMG parameter type values - 0x8000 to 0xFFFF. // User defined ECMG parameter type values - 0x8000 to 0xFFFF.
#define AGE_RESTRICTION (0x8000) #define AGE_RESTRICTION (0x8000)
#define CRYPTO_MODE (0x8001) #define CRYPTO_MODE (0x8001)
#define TRACK_TYPES (0x8002) #define STREAM_TRACK_TYPE (0x8002)
#define STREAM_TRACK_TYPE (0x8003) #define CONTENT_IV (0x8003)
#define CONTENT_IV (0x8004) #define ENTITLEMENT_ID_KEY_COMBINATION (0x8004)
#define ENTITLEMENT_ID_KEY_COMBINATION (0x8005)
// ECMG protocol error values. // ECMG protocol error values.
#define INVALID_MESSAGE (0x0001) #define INVALID_MESSAGE (0x0001)