1) Change return type to WvCasStatus for functions in wv_cas_types.cc.

2) Add a binary wv_cas_types_example.
3) Surface wv_cas_key_fetcher *source code* to partner to serve as an example of how they would make a HTTP request to acquire an entitlement key from license server.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=229953562
This commit is contained in:
Fang Yu
2019-01-18 10:38:34 -08:00
parent 6e1f377329
commit e7173c82cd
25 changed files with 1213 additions and 173 deletions

View File

@@ -56,9 +56,10 @@ cc_library(
srcs = ["ecm_generator.cc"],
hdrs = ["ecm_generator.h"],
deps = [
":ecm",
"//base",
"@abseil_repo//absl/base:core_headers",
"//common:status",
"//media_cas_packager_sdk/internal:ecm",
],
)
@@ -81,12 +82,14 @@ cc_library(
hdrs = [
"ecmg_client_handler.h",
"ecmg_constants.h",
"simulcrypt_constants.h",
],
deps = [
":ecm",
":ecm_generator",
":fixed_key_fetcher",
":mpeg2ts",
":simulcrypt_util",
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
@@ -112,6 +115,36 @@ cc_test(
],
)
cc_library(
name = "emmg",
srcs = ["emmg.cc"],
hdrs = [
"emmg.h",
"emmg_constants.h",
"simulcrypt_constants.h",
],
deps = [
":simulcrypt_util",
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
"//common:status",
"//example:test_emmg_messages",
],
)
cc_test(
name = "emmg_test",
size = "small",
srcs = ["emmg_test.cc"],
deps = [
":emmg",
"//testing:gunit_main",
"@abseil_repo//absl/memory",
"//example:test_emmg_messages",
],
)
cc_library(
name = "fixed_key_fetcher",
srcs = [
@@ -146,6 +179,21 @@ cc_library(
],
)
cc_library(
name = "simulcrypt_util",
srcs = ["simulcrypt_util.cc"],
hdrs = [
"simulcrypt_constants.h",
"simulcrypt_util.h",
],
deps = [
":util",
"//base",
"@abseil_repo//absl/base:core_headers",
"//common:status",
],
)
cc_library(
name = "ts_packet",
srcs = [

View File

@@ -10,6 +10,7 @@
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <memory>
#include <utility>
@@ -19,10 +20,13 @@
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "common/crypto_util.h"
#include "common/status.h"
#include "example/constants.h"
#include "media_cas_packager_sdk/internal/ecm_generator.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_constants.h"
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
#include "media_cas_packager_sdk/internal/util.h"
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
#include "media_cas_packager_sdk/public/wv_cas_types.h"
@@ -183,81 +187,16 @@ Status HandleParameters(const char* const request, size_t request_length,
return OkStatus();
}
// Add 'protocol_version', 'message_type', 'message_length' to the message.
// TODO(user): Per jfore@, consider pass in a pointer to a structure
// #pragma pack(push, 1) // exact fit - no padding
// struct MessageHeader{
// uint8_t protocol_version;
// uint16_t message_type;
// uint16_t message_length;
// };
// #pragma pack(pop) // restore previous pack
void BuildMessageHeader(uint16_t message_type, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
*message_length = 0;
message[*message_length] = ECMG_SCS_PROTOCOL_VERSION;
*message_length += PROTOCOL_VERSION_SIZE;
Host16ToBigEndian(message + *message_length, &message_type);
*message_length += MESSAGE_TYPE_SIZE;
// NOTE: 'message_length' needs to be updated later after we have added all
// the params so we know the exact length of the all the params.
// Use 0 for 'total_param_length' until we know the length of the message.
uint16_t total_param_length = 0;
Host16ToBigEndian(message + *message_length, &total_param_length);
*message_length += MESSAGE_LENGTH_SIZE;
}
// Add a uint16_t parameter to the message.
void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
uint16_t param_length = 2;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
Host16ToBigEndian(message + *message_length, &param_value);
*message_length += param_length;
}
// Add a uint8_t parameter to the message.
void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
uint16_t param_length = 1;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
memcpy(message + *message_length, &param_value, param_length);
*message_length += param_length;
}
// Add a param that is |param_length| bytes long.
void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length,
char* message, size_t* message_length) {
DCHECK(param_value);
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
memcpy(message + *message_length, param_value, param_length);
*message_length += param_length;
}
void BuildChannelError(uint16_t channel_id, uint16_t error_status, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_CHANNEL_ERROR, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ERROR_STATUS, error_status, message, message_length);
simulcrypt_util::BuildMessageHeader(
ECMG_SCS_PROTOCOL_VERSION, ECMG_CHANNEL_ERROR, message, message_length);
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
message_length);
simulcrypt_util::AddUint16Param(ERROR_STATUS, error_status, message,
message_length);
// No setting Error_information parameter yet.
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
@@ -268,25 +207,33 @@ void BuildChannelStatus(uint16_t channel_id, EcmgConfig* config, char* message,
DCHECK(config);
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_CHANNEL_STATUS, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint8Param(SECTION_TSPKT_FLAG, kSectionTSpktFlag, message, message_length);
simulcrypt_util::BuildMessageHeader(
ECMG_SCS_PROTOCOL_VERSION, ECMG_CHANNEL_STATUS, message, message_length);
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
message_length);
simulcrypt_util::AddUint8Param(SECTION_TSPKT_FLAG, kSectionTSpktFlag, message,
message_length);
// No setting AC_delay_start parameter yet.
// No setting AC_delay_stop parameter yet.
AddUint16Param(DELAY_START, config->delay_start, message, message_length);
AddUint16Param(DELAY_STOP, config->delay_stop, message, message_length);
simulcrypt_util::AddUint16Param(DELAY_START, config->delay_start, message,
message_length);
simulcrypt_util::AddUint16Param(DELAY_STOP, config->delay_stop, message,
message_length);
// No setting transition_delay_start parameter yet.
// No setting transition_delay_stop parameter yet.
AddUint16Param(ECM_REP_PERIOD, config->ecm_rep_period, message,
message_length);
AddUint16Param(MAX_STREAMS, kMaxStream, message, message_length);
simulcrypt_util::AddUint16Param(ECM_REP_PERIOD, config->ecm_rep_period,
message, message_length);
simulcrypt_util::AddUint16Param(MAX_STREAMS, kMaxStream, message,
message_length);
// min_CP_duration needs to be at least max_comp_time. So we just use
// max_comp_time here for now.
AddUint16Param(MIN_CP_DURATION, config->max_comp_time, message,
message_length);
AddUint8Param(LEAD_CW, kLeadCw, message, message_length);
AddUint8Param(CW_PER_MESSAGE, kCwPerMsg, message, message_length);
AddUint16Param(MAX_COMP_TIME, config->max_comp_time, message, message_length);
simulcrypt_util::AddUint16Param(MIN_CP_DURATION, config->max_comp_time,
message, message_length);
simulcrypt_util::AddUint8Param(LEAD_CW, kLeadCw, message, message_length);
simulcrypt_util::AddUint8Param(CW_PER_MESSAGE, kCwPerMsg, message,
message_length);
simulcrypt_util::AddUint16Param(MAX_COMP_TIME, config->max_comp_time, message,
message_length);
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}
@@ -295,10 +242,14 @@ void BuildStreamError(uint16_t channel_id, uint16_t stream_id, uint16_t error_st
char* message, size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_STREAM_ERROR, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
AddUint16Param(ERROR_STATUS, error_status, message, message_length);
simulcrypt_util::BuildMessageHeader(
ECMG_SCS_PROTOCOL_VERSION, ECMG_STREAM_ERROR, message, message_length);
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
message_length);
simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message,
message_length);
simulcrypt_util::AddUint16Param(ERROR_STATUS, error_status, message,
message_length);
// No setting Error_information parameter yet.
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
@@ -309,12 +260,16 @@ void BuildStreamStatus(uint16_t channel_id, uint16_t stream_id, uint16_t ecm_id,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_STREAM_STATUS, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
AddUint16Param(ECM_ID, ecm_id, message, message_length);
AddUint8Param(ACCESS_CRITERIA_TRANSFER_MODE, access_criteria_transfer_mode,
message, message_length);
simulcrypt_util::BuildMessageHeader(
ECMG_SCS_PROTOCOL_VERSION, ECMG_STREAM_STATUS, message, message_length);
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
message_length);
simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message,
message_length);
simulcrypt_util::AddUint16Param(ECM_ID, ecm_id, message, message_length);
simulcrypt_util::AddUint8Param(ACCESS_CRITERIA_TRANSFER_MODE,
access_criteria_transfer_mode, message,
message_length);
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}
@@ -323,9 +278,13 @@ void BuildStreamCloseResponse(uint16_t channel_id, uint16_t stream_id,
char* message, size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_STREAM_CLOSE_RESPONSE, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
simulcrypt_util::BuildMessageHeader(ECMG_SCS_PROTOCOL_VERSION,
ECMG_STREAM_CLOSE_RESPONSE, message,
message_length);
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
message_length);
simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message,
message_length);
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}
@@ -336,11 +295,16 @@ void BuildEcmResponse(uint16_t channel_id, uint16_t stream_id, uint16_t cp_numbe
DCHECK(ecm_datagram);
DCHECK(message);
DCHECK(message_length);
BuildMessageHeader(ECMG_ECM_RESPONSE, message, message_length);
AddUint16Param(ECM_CHANNEL_ID, channel_id, message, message_length);
AddUint16Param(ECM_STREAM_ID, stream_id, message, message_length);
AddUint16Param(CP_NUMBER, cp_number, message, message_length);
AddParam(ECM_DATAGRAM, ecm_datagram, kTsPacketSize, message, message_length);
simulcrypt_util::BuildMessageHeader(
ECMG_SCS_PROTOCOL_VERSION, ECMG_ECM_RESPONSE, message, message_length);
simulcrypt_util::AddUint16Param(ECM_CHANNEL_ID, channel_id, message,
message_length);
simulcrypt_util::AddUint16Param(ECM_STREAM_ID, stream_id, message,
message_length);
simulcrypt_util::AddUint16Param(CP_NUMBER, cp_number, message,
message_length);
simulcrypt_util::AddParam(ECM_DATAGRAM, ecm_datagram, kTsPacketSize, message,
message_length);
uint16_t total_param_length = *message_length - 5;
Host16ToBigEndian(message + 3, &total_param_length);
}

View File

@@ -56,27 +56,31 @@ TEST_F(EcmgClientHandlerTest, SuccessSequence) {
char response[BUFFER_SIZE];
size_t response_length;
client_handler_->HandleRequest(kTestChannelSetup, response, &response_length);
client_handler_->HandleRequest(kTestEcmgChannelSetup, response,
&response_length);
EXPECT_EQ(62, response_length);
EXPECT_EQ(0, memcmp(kTestChannelStatus, response, response_length));
EXPECT_EQ(0, memcmp(kTestEcmgChannelStatus, response, response_length));
client_handler_->HandleRequest(kTestStreamSetup, response, &response_length);
client_handler_->HandleRequest(kTestEcmgStreamSetup, response,
&response_length);
EXPECT_EQ(28, response_length);
EXPECT_EQ(0, memcmp(kTestStreamStatus, response, response_length));
EXPECT_EQ(0, memcmp(kTestEcmgStreamStatus, response, response_length));
client_handler_->HandleRequest(kTestCwProvision, response, &response_length);
client_handler_->HandleRequest(kTestEcmgCwProvision, response,
&response_length);
EXPECT_EQ(215, response_length);
// Only comparing the bytes in front of the ECM_datagram, because
// random wrapping IV is generated each time causing the ECM_datagram
// to be non-deterministic.
EXPECT_EQ(0, memcmp(kTestEcmResponse, response, 27));
EXPECT_EQ(0, memcmp(kTestEcmgEcmResponse, response, 27));
client_handler_->HandleRequest(kTestStreamCloseRequest, response,
client_handler_->HandleRequest(kTestEcmgStreamCloseRequest, response,
&response_length);
EXPECT_EQ(17, response_length);
EXPECT_EQ(0, memcmp(kTestStreamCloseResponse, response, response_length));
EXPECT_EQ(0, memcmp(kTestEcmgStreamCloseResponse, response, response_length));
client_handler_->HandleRequest(kTestChannelClose, response, &response_length);
client_handler_->HandleRequest(kTestEcmgChannelClose, response,
&response_length);
EXPECT_EQ(0, response_length);
}

View File

@@ -62,35 +62,30 @@
// ECMG protocol error values.
#define INVAID_MESSAGE (0x0001)
#define UNSUPPORTED_PROTOCOL_VERSION (0X0002)
#define UNKNOWN_MESSAGE_TYPE_VALUE (0X0003)
#define MESSAGE_TOO_LONG (0X0004)
#define UNKNOWN_SUPER_CAS_ID_VALUE (0X0005)
#define UNKNOWN_ECM_CHANNEL_ID_VALUE (0X0006)
#define UNKNOWN_ECM_STREAM_ID_VALUE (0X0007)
#define TOO_MANY_CHANNELS_ON_THIS_ECMG (0X0008)
#define TOO_MANY_ECM_STREAMS_ON_THIS_CHANNEL (0X0009)
#define TOO_MANY_ECM_STREAMS_ON_THIS_ECMG (0X000A)
#define NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM (0X000B)
#define ECMG_OUT_OF_STORAGE_CAPACITY (0X000C)
#define ECMG_OUT_OF_COMPUTATIONAL_RESOURCES (0X000D)
#define UNKNOWN_PARAMETER_TYPE_VALUE (0X000E)
#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0X000F)
#define MISSING_MANDATORY_DVB_PARAMETER (0X0010)
#define INVALID_VALUE_FOR_DVB_PARAMETER (0X0011)
#define UNKNOWN_ECM_ID_VALUE (0X0012)
#define ECM_CHANNEL_ID_VALUE_ALREADY_IN_USE (0X0013)
#define ECM_STREAM_ID_VALUE_ALREADY_IN_USE (0X0014)
#define ECM_ID_VALUE_ALREADY_IN_USE (0X0015)
#define UNKNOWN_ERROR (0X7000)
#define UNRECOVERABLE_ERROR (0X7001)
#define UNSUPPORTED_PROTOCOL_VERSION (0x0002)
#define UNKNOWN_MESSAGE_TYPE_VALUE (0x0003)
#define MESSAGE_TOO_LONG (0x0004)
#define UNKNOWN_SUPER_CAS_ID_VALUE (0x0005)
#define UNKNOWN_ECM_CHANNEL_ID_VALUE (0x0006)
#define UNKNOWN_ECM_STREAM_ID_VALUE (0x0007)
#define TOO_MANY_CHANNELS_ON_THIS_ECMG (0x0008)
#define TOO_MANY_ECM_STREAMS_ON_THIS_CHANNEL (0x0009)
#define TOO_MANY_ECM_STREAMS_ON_THIS_ECMG (0x000A)
#define NOT_ENOUGH_CONTROL_WORDS_TO_COMPUTE_ECM (0x000B)
#define ECMG_OUT_OF_STORAGE_CAPACITY (0x000C)
#define ECMG_OUT_OF_COMPUTATIONAL_RESOURCES (0x000D)
#define UNKNOWN_PARAMETER_TYPE_VALUE (0x000E)
#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0x000F)
#define MISSING_MANDATORY_DVB_PARAMETER (0x0010)
#define INVALID_VALUE_FOR_DVB_PARAMETER (0x0011)
#define UNKNOWN_ECM_ID_VALUE (0x0012)
#define ECM_CHANNEL_ID_VALUE_ALREADY_IN_USE (0x0013)
#define ECM_STREAM_ID_VALUE_ALREADY_IN_USE (0x0014)
#define ECM_ID_VALUE_ALREADY_IN_USE (0x0015)
#define UNKNOWN_ERROR (0x7000)
#define UNRECOVERABLE_ERROR (0x7001)
// Size (in # of bytes) of various fields.
#define PROTOCOL_VERSION_SIZE (1)
#define MESSAGE_TYPE_SIZE (2)
#define MESSAGE_LENGTH_SIZE (2)
#define PARAMETER_TYPE_SIZE (2)
#define PARAMETER_LENGTH_SIZE (2)
#define SUPER_CAS_ID_SIZE (4)
#define ECM_CHANNEL_ID_SIZE (2)
#define ECM_ID_SIZE (2)

View File

@@ -0,0 +1,247 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 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/emmg.h"
#include <sys/socket.h>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <iostream>
#include "glog/logging.h"
#include "example/test_emmg_messages.h"
#include "media_cas_packager_sdk/internal/emmg_constants.h"
#include "media_cas_packager_sdk/internal/simulcrypt_constants.h"
#include "media_cas_packager_sdk/internal/simulcrypt_util.h"
#include "media_cas_packager_sdk/internal/util.h"
namespace widevine {
namespace cas {
namespace {
void Print(const char* const msg, size_t msg_size) {
for (size_t i = 0; i < msg_size; i++) {
printf("'\\x%02x', ", static_cast<uint16_t>(*(msg + i)));
}
std::cout << std::endl;
}
std::string MessageTypeToString(uint16_t message_type) {
switch (message_type) {
case EMMG_CHANNEL_SETUP:
return "EMMG_CHANNEL_SETUP";
case EMMG_CHANNEL_STATUS:
return "EMMG_CHANNEL_STATUS";
case EMMG_CHANNEL_CLOSE:
return "EMMG_CHANNEL_CLOSE";
case EMMG_CHANNEL_ERROR:
return "EMMG_CHANNEL_ERROR";
case EMMG_STREAM_SETUP:
return "EMMG_STREAM_SETUP";
case EMMG_STREAM_TEST:
return "EMMG_STREAM_TEST";
case EMMG_STREAM_STATUS:
return "EMMG_STREAM_STATUS";
case EMMG_STREAM_CLOSE_REQUEST:
return "EMMG_STREAM_CLOSE_REQUEST";
case EMMG_STREAM_CLOSE_RESPONSE:
return "EMMG_STREAM_CLOSE_RESPONSE";
case EMMG_STREAM_ERROR:
return "EMMG_STREAM_ERROR";
case EMMG_STREAM_BW_REQUEST:
return "EMMG_STREAM_BW_REQUEST";
case EMMG_STREAM_BW_ALLOCATION:
return "";
case EMMG_DATA_PROVISION:
return "EMMG_DATA_PROVISION";
default:
return "UNRECOGNIZED_MESSAGE_TYPE";
}
}
} // namespace
Emmg::Emmg(EmmgConfig* emmg_config, int server_socket_fd) {
CHECK(emmg_config);
emmg_config_ = emmg_config;
server_socket_fd_ = server_socket_fd;
}
void Emmg::Start() {
SendChannelSetup();
SendStreamSetup();
// TODO(user): Send stream_BW_request message.
// TODO(user): Send stream_BW_allocation message.
SendDataProvision();
SendStreamCloseRequest();
SendChannelClose();
}
void Emmg::BuildChannelSetup() {
bzero(request_, BUFFER_SIZE);
request_length_ = 0;
simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION,
EMMG_CHANNEL_SETUP, request_,
&request_length_);
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
request_, &request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
emmg_config_->data_channel_id, request_,
&request_length_);
simulcrypt_util::AddUint8Param(EMMG_SECTION_TSPKT_FLAG,
emmg_config_->section_tspkt_flag, request_,
&request_length_);
uint16_t total_param_length = request_length_ - 5;
Host16ToBigEndian(request_ + 3, &total_param_length);
}
void Emmg::BuildStreamSetup() {
bzero(request_, BUFFER_SIZE);
request_length_ = 0;
simulcrypt_util::BuildMessageHeader(
EMMG_MUX_PROTOCOL_VERSION, EMMG_STREAM_SETUP, request_, &request_length_);
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
request_, &request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
emmg_config_->data_channel_id, request_,
&request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_STREAM_ID,
emmg_config_->data_stream_id, request_,
&request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_ID, emmg_config_->data_id, request_,
&request_length_);
simulcrypt_util::AddUint8Param(EMMG_DATA_TYPE, emmg_config_->data_type,
request_, &request_length_);
uint16_t total_param_length = request_length_ - 5;
Host16ToBigEndian(request_ + 3, &total_param_length);
}
void Emmg::BuildDataProvision() {
bzero(request_, BUFFER_SIZE);
request_length_ = 0;
simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION,
EMMG_DATA_PROVISION, request_,
&request_length_);
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
request_, &request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
emmg_config_->data_channel_id, request_,
&request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_STREAM_ID,
emmg_config_->data_stream_id, request_,
&request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_ID, emmg_config_->data_id, request_,
&request_length_);
// Add a fake TS packet.
uint16_t datagram_type = EMMG_DATAGRAM;
Host16ToBigEndian(request_ + request_length_, &datagram_type);
request_length_ += 2;
uint16_t param_length = sizeof(kTestEmmgTsPacket); // Should be 188.
Host16ToBigEndian(request_ + request_length_, &param_length);
request_length_ += 2;
memcpy(request_ + request_length_, kTestEmmgTsPacket, param_length);
request_length_ += param_length;
uint16_t total_param_length = request_length_ - 5;
Host16ToBigEndian(request_ + 3, &total_param_length);
}
void Emmg::BuildStreamCloseRequest() {
bzero(request_, BUFFER_SIZE);
request_length_ = 0;
simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION,
EMMG_STREAM_CLOSE_REQUEST, request_,
&request_length_);
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
request_, &request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
emmg_config_->data_channel_id, request_,
&request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_STREAM_ID,
emmg_config_->data_stream_id, request_,
&request_length_);
uint16_t total_param_length = request_length_ - 5;
Host16ToBigEndian(request_ + 3, &total_param_length);
}
void Emmg::BuildChannelClose() {
bzero(request_, BUFFER_SIZE);
request_length_ = 0;
simulcrypt_util::BuildMessageHeader(EMMG_MUX_PROTOCOL_VERSION,
EMMG_CHANNEL_CLOSE, request_,
&request_length_);
simulcrypt_util::AddUint32Param(EMMG_CLIENT_ID, emmg_config_->client_id,
request_, &request_length_);
simulcrypt_util::AddUint16Param(EMMG_DATA_CHANNEL_ID,
emmg_config_->data_channel_id, request_,
&request_length_);
uint16_t total_param_length = request_length_ - 5;
Host16ToBigEndian(request_ + 3, &total_param_length);
}
void Emmg::SendChannelSetup() {
BuildChannelSetup();
Send(EMMG_CHANNEL_SETUP);
ReceiveResponseAndVerify(EMMG_CHANNEL_STATUS);
}
void Emmg::SendStreamSetup() {
BuildStreamSetup();
Send(EMMG_STREAM_SETUP);
ReceiveResponseAndVerify(EMMG_STREAM_STATUS);
}
void Emmg::SendDataProvision() {
BuildDataProvision();
Send(EMMG_DATA_PROVISION);
// No response for Data_provision message.
}
void Emmg::SendStreamCloseRequest() {
BuildStreamCloseRequest();
Send(EMMG_STREAM_CLOSE_REQUEST);
ReceiveResponseAndVerify(EMMG_STREAM_CLOSE_RESPONSE);
}
void Emmg::SendChannelClose() {
BuildChannelClose();
Send(EMMG_CHANNEL_CLOSE);
// No response for Channel_close message.
}
void Emmg::ReceiveResponseAndVerify(uint16_t expected_type) {
// Read response from socket.
bzero(response_, BUFFER_SIZE);
response_length_ = recv(server_socket_fd_, response_, BUFFER_SIZE, 0);
if (response_length_ == 0) {
LOG(FATAL) << "Expect a response from server but nothing was received";
}
// Verify request type (2nd and 3rd bytes) match the expected type.
uint16_t request_type;
BigEndianToHost16(&request_type, response_ + 1);
if (request_type != expected_type) {
LOG(ERROR) << "Unexpected non " << MessageTypeToString(expected_type)
<< " type message received";
LOG(ERROR) << "Response: ";
Print(response_, response_length_);
return;
}
}
void Emmg::Send(uint16_t message_type) {
if (send(server_socket_fd_, request_, request_length_, 0) < 0) {
LOG(FATAL) << "Failed to send " << MessageTypeToString(message_type)
<< " message to server";
}
}
} // namespace cas
} // namespace widevine

View File

@@ -0,0 +1,80 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 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_EMMG_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_
#include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <cstdint>
#include "glog/logging.h"
#include "common/status.h"
#define BUFFER_SIZE (1024)
namespace widevine {
namespace cas {
// A struct that captures the Emmg configs.
struct EmmgConfig {
uint32_t client_id;
uint8_t section_tspkt_flag;
uint16_t data_channel_id;
uint16_t data_stream_id;
uint16_t data_id;
uint8_t data_type;
};
// A class that sends EMMG/PDG message to the MUX server.
class Emmg {
public:
Emmg(EmmgConfig* emmg_config, int server_socket_fd);
Emmg(const Emmg&) = delete;
Emmg& operator=(const Emmg&) = delete;
virtual ~Emmg() = default;
void Start();
protected:
// Protected visibility for unit testing.
void BuildChannelSetup();
void BuildStreamSetup();
void BuildDataProvision();
void BuildStreamCloseRequest();
void BuildChannelClose();
size_t request_length_ = 0;
char request_[BUFFER_SIZE];
private:
void SendChannelSetup();
void SendStreamSetup();
void SendDataProvision();
void SendStreamCloseRequest();
void SendChannelClose();
void ReceiveResponseAndVerify(uint16_t expected_type);
void Send(uint16_t message_type);
char response_[BUFFER_SIZE];
size_t response_length_ = 0;
EmmgConfig* emmg_config_;
// |server_socket_fd| is a file descriptor we can use to communicate
// with the MUX server.
int server_socket_fd_;
};
} // namespace cas
} // namespace widevine
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_H_

View File

@@ -0,0 +1,68 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 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_EMMG_CONSTANTS_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_CONSTANTS_H_
// EMMG/PDG <=> MUX protocol_version.
#define EMMG_MUX_PROTOCOL_VERSION (0x02)
// EMMG message type values.
#define EMMG_CHANNEL_SETUP (0x0011)
#define EMMG_CHANNEL_TEST (0x0012)
#define EMMG_CHANNEL_STATUS (0x0013)
#define EMMG_CHANNEL_CLOSE (0x0014)
#define EMMG_CHANNEL_ERROR (0x0015)
#define EMMG_STREAM_SETUP (0x0111)
#define EMMG_STREAM_TEST (0x0112)
#define EMMG_STREAM_STATUS (0x0113)
#define EMMG_STREAM_CLOSE_REQUEST (0x0114)
#define EMMG_STREAM_CLOSE_RESPONSE (0x0115)
#define EMMG_STREAM_ERROR (0x0116)
#define EMMG_STREAM_BW_REQUEST (0x0117)
#define EMMG_STREAM_BW_ALLOCATION (0x0118)
#define EMMG_DATA_PROVISION (0x0211)
// EMMG parameter type values.
#define EMMG_DVB_RESERVED (0x0000)
#define EMMG_CLIENT_ID (0x0001)
#define EMMG_SECTION_TSPKT_FLAG (0x0002)
#define EMMG_DATA_CHANNEL_ID (0x0003)
#define EMMG_DATA_STREAM_ID (0x0004)
#define EMMG_DATAGRAM (0x0005)
#define EMMG_BANDWIDTH (0x0006)
#define EMMG_DATA_TYPE (0x0007)
#define EMMG_DATA_ID (0x0008)
#define EMMG_ERROR_STATUS (0x7000)
#define EMMG_ERROR_INFORMATION (0x7001)
// EMMG protocol error values.
#define INVALID_MESSAGE (0x0001)
#define UNSUPPORTED_PROTOCOL_VERSION (0x0002)
#define UNKNOWN_MESSAGE_TYPE_VALUE (0x0003)
#define MESSAGE_TOO_LONG (0x0004)
#define UNKNOWN_DATA_STREAM_ID_VALUE (0x0005)
#define UNKNOWN_DATA_CHANNEL_ID_VALUE (0x0006)
#define TOO_MANY_CHANNELS_ON_THIS_MUX (0x0007)
#define TOO_MANY_DATA_STREAMS_ON_THIS_CHANNEL (0x0008)
#define TOO_MANY_DATA_STREAMS_ON_THIS_MUX (0x0009)
#define UNKNOWN_PARAMETER_TYPE (0x000A)
#define INCONSISTENT_LENGTH_FOR_DVB_PARAMETER (0x000B)
#define MISSING_MANDATORY_DVB_PARAMETER (0x000C)
#define INVALID_VALUE_FOR_DVB_PARAMETER (0x000D)
#define UNKNOWN_CLIENT_ID_VALUE (0x000E)
#define EXCEEDED_BANDWIDTH (0x000F)
#define UNKNOWN_DATA_ID_VALUE (0x0010)
#define DATA_CHANNEL_ID_VALUE_ALREADY_IN_USE (0x0011)
#define DATA_STREAM_ID_VALUE_ALREADY_IN_USE (0x0012)
#define DATA_ID_VALUE_ALREADY_IN_USE (0x0013)
#define CLIENT_ID_VALUE_ALREADY_IN_USE (0x0014)
#define UNKNOWN_ERROR (0x7000)
#define UNRECOVERABLE_ERROR (0x7001)
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_EMMG_CONSTANTS_H_

View File

@@ -0,0 +1,93 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 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/emmg.h"
#include "testing/gunit.h"
#include "absl/memory/memory.h"
#include "example/test_emmg_messages.h"
namespace widevine {
namespace cas {
namespace {
class TestableEmmg : public Emmg {
public:
explicit TestableEmmg(EmmgConfig* emmg_config)
: Emmg(emmg_config, /* server_socket_fd= */ 1) {}
void PublicBuildChannelSetup() { BuildChannelSetup(); }
void PublicBuildStreamSetup() { BuildStreamSetup(); }
void PublicBuildDataProvision() { BuildDataProvision(); }
void PublicBuildStreamCloseRequest() { BuildStreamCloseRequest(); }
void PublicBuildChannelClose() { BuildChannelClose(); }
char* GetRequest() { return request_; }
};
class EmmgTest : public ::testing::Test {
protected:
EmmgTest() {
config_.client_id = 0x4AD40000;
config_.section_tspkt_flag = 0x01;
config_.data_channel_id = 0x0001;
config_.data_stream_id = 0x0001;
config_.data_id = 0x0001;
config_.data_type = 0x01;
emmg_ = absl::make_unique<TestableEmmg>(&config_);
}
protected:
// Helper function for debugging the tests.
void PrintMessage(const std::string& description, const char* const message,
size_t length) {
printf("%s ", description.c_str());
fflush(stdout);
for (size_t i = 0; i < length; i++) {
printf("'\\x%02x', ", static_cast<uint16_t>(*(message + i)));
fflush(stdout);
}
printf("\n");
fflush(stdout);
}
EmmgConfig config_;
std::unique_ptr<TestableEmmg> emmg_;
};
TEST_F(EmmgTest, BuildChannelSetup) {
emmg_->PublicBuildChannelSetup();
EXPECT_EQ(0, memcmp(kTestEmmgChannelSetup, emmg_->GetRequest(),
sizeof(kTestEmmgChannelSetup)));
}
TEST_F(EmmgTest, BuildStreamSetup) {
emmg_->PublicBuildStreamSetup();
EXPECT_EQ(0, memcmp(kTestEmmgStreamSetup, emmg_->GetRequest(),
sizeof(kTestEmmgStreamSetup)));
}
TEST_F(EmmgTest, BuildDataProvision) {
emmg_->PublicBuildDataProvision();
EXPECT_EQ(0, memcmp(kTestEmmgDataProvision, emmg_->GetRequest(),
sizeof(kTestEmmgDataProvision)));
}
TEST_F(EmmgTest, BuildStreamCloseRequest) {
emmg_->PublicBuildStreamCloseRequest();
EXPECT_EQ(0, memcmp(kTestEmmgStreamCloseRequest, emmg_->GetRequest(),
sizeof(kTestEmmgStreamCloseRequest)));
}
TEST_F(EmmgTest, BuildChannelClose) {
emmg_->PublicBuildChannelClose();
EXPECT_EQ(0, memcmp(kTestEmmgChannelClose, emmg_->GetRequest(),
sizeof(kTestEmmgChannelClose)));
}
} // namespace
} // namespace cas
} // namespace widevine

View File

@@ -0,0 +1,19 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 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_SIMULCRYPT_CONSTANTS_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_
// Size (in # of bytes) of various fields.
#define PROTOCOL_VERSION_SIZE (1)
#define MESSAGE_TYPE_SIZE (2)
#define MESSAGE_LENGTH_SIZE (2)
#define PARAMETER_TYPE_SIZE (2)
#define PARAMETER_LENGTH_SIZE (2)
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_CONSTANTS_H_

View File

@@ -0,0 +1,93 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 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/simulcrypt_util.h"
#include <cstddef>
#include <cstring>
#include "glog/logging.h"
#include "media_cas_packager_sdk/internal/simulcrypt_constants.h"
#include "media_cas_packager_sdk/internal/util.h"
namespace widevine {
namespace cas {
namespace simulcrypt_util {
void BuildMessageHeader(uint8_t protocol_version, uint16_t message_type,
char* message, size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
*message_length = 0;
message[*message_length] = protocol_version;
*message_length += PROTOCOL_VERSION_SIZE;
Host16ToBigEndian(message + *message_length, &message_type);
*message_length += MESSAGE_TYPE_SIZE;
// NOTE: 'message_length' needs to be updated later after we have added all
// the params so we know the exact length of the all the params.
// Use 0 for 'total_param_length' until we know the length of the message.
uint16_t total_param_length = 0;
Host16ToBigEndian(message + *message_length, &total_param_length);
*message_length += MESSAGE_LENGTH_SIZE;
}
void AddUint32Param(uint16_t param_type, uint32_t param_value, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
uint16_t param_length = 4;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
Host32ToBigEndian(message + *message_length, &param_value);
*message_length += param_length;
}
void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
uint16_t param_length = 2;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
Host16ToBigEndian(message + *message_length, &param_value);
*message_length += param_length;
}
void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message,
size_t* message_length) {
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
uint16_t param_length = 1;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
memcpy(message + *message_length, &param_value, param_length);
*message_length += param_length;
}
void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length,
char* message, size_t* message_length) {
DCHECK(param_value);
DCHECK(message);
DCHECK(message_length);
Host16ToBigEndian(message + *message_length, &param_type);
*message_length += 2;
Host16ToBigEndian(message + *message_length, &param_length);
*message_length += 2;
memcpy(message + *message_length, param_value, param_length);
*message_length += param_length;
}
} // namespace simulcrypt_util
} // namespace cas
} // namespace widevine

View File

@@ -0,0 +1,59 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 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.
////////////////////////////////////////////////////////////////////////////////
//
// Utility functions for building Simulcrypt messages.
#ifndef MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_
#define MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_
#include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <cstdint>
#include "common/status.h"
namespace widevine {
namespace cas {
namespace simulcrypt_util {
// Add 'protocol_version', 'message_type', 'message_length' to the message.
// TODO(user): Per jfore@, consider pass in a pointer to a structure
// #pragma pack(push, 1) // exact fit - no padding
// struct MessageHeader{
// uint8_t protocol_version;
// uint16_t message_type;
// uint16_t message_length;
// };
// #pragma pack(pop) // restore previous pack
void BuildMessageHeader(uint8_t protocol_version, uint16_t message_type,
char* message, size_t* message_length);
// Add a uint32_t parameter to the message.
void AddUint32Param(uint16_t param_type, uint32_t param_value, char* message,
size_t* message_length);
// Add a uint16_t parameter to the message.
void AddUint16Param(uint16_t param_type, uint16_t param_value, char* message,
size_t* message_length);
// Add a uint8_t parameter to the message.
void AddUint8Param(uint16_t param_type, uint8_t param_value, char* message,
size_t* message_length);
// Add a param that is |param_length| bytes long.
void AddParam(uint16_t param_type, uint8_t* param_value, uint16_t param_length,
char* message, size_t* message_length);
} // namespace simulcrypt_util
} // namespace cas
} // namespace widevine
#endif // MEDIA_CAS_PACKAGER_SDK_INTERNAL_SIMULCRYPT_UTIL_H_

View File

@@ -57,6 +57,13 @@ void Host16ToBigEndian(void* destination, const int16_t* source) {
memcpy(destination, &big_endian_number, 2);
}
void Host32ToBigEndian(void* destination, const uint32_t* source) {
DCHECK(destination);
DCHECK(source);
uint32_t big_endian_number = htonl(*source);
memcpy(destination, &big_endian_number, 4);
}
Status InsertEcmAsTsPacket(const std::string& ecm, ProgramId pid, uint8_t table_id,
ContinuityCounter* cc, uint8_t* buffer,
ssize_t* bytes_modified) {

View File

@@ -33,6 +33,7 @@ void BigEndianToHost32(uint32_t* destination, const void* source);
// |destination|.
void Host16ToBigEndian(void* destination, const uint16_t* source);
void Host16ToBigEndian(void* destination, const int16_t* source);
void Host32ToBigEndian(void* destination, const uint32_t* source);
// Packages an ECM as a TS packet and inserts it into a buffer.
// Args:

View File

@@ -22,6 +22,8 @@ filegroup(
name = "binary_release_files",
srcs = glob(["*.h"]) + [
":wv_ecmg",
"wv_cas_key_fetcher.cc",
"wv_cas_types.cc",
],
)
@@ -127,7 +129,9 @@ cc_library(
"@abseil_repo//absl/base:core_headers",
"@abseil_repo//absl/strings",
"@curl_repo//:curl",
"//util:error_space",
"//common:signature_util",
"//common:status",
"//media_cas_packager_sdk/internal:key_fetcher",
"//protos/public:media_cas_encryption_proto",
],
@@ -181,3 +185,12 @@ cc_binary(
"//media_cas_packager_sdk/internal:ecmg_client_handler",
],
)
cc_binary(
name = "wv_emmg",
srcs = ["wv_emmg.cc"],
deps = [
"//base",
"//media_cas_packager_sdk/internal:emmg",
],
)

View File

@@ -103,8 +103,6 @@ Status WvCasKeyFetcher::RequestEntitlementKey(const std::string& request_string,
}
// Processes signed response.
// TODO(b/114741232): Seems we are getting CasEncryptionResponse back instead
// of SignedCasEncryptionResponse.
LOG(INFO) << "Json CasEncryptionResponse: " << http_response.response();
CasEncryptionResponse response;
if (!JsonStringToMessage(http_response.response(), &response).ok()) {

View File

@@ -79,8 +79,8 @@ bool StringToCryptoMode(const std::string& str, CryptoMode* mode) {
return false;
}
Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request,
std::string* request_json) {
WvCasStatus CreateWvCasEncryptionRequestJson(
const WvCasEncryptionRequest& request, std::string* request_json) {
CHECK(request_json);
CasEncryptionRequest request_proto;
@@ -100,23 +100,23 @@ Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request,
// NOTE: MessageToJsonString will automatically converts 'bytes' type fields
// to base64. For example content ID '21140844' becomes 'MjExNDA4NDQ='.
if (!MessageToJsonString(request_proto, request_json, print_options).ok()) {
return Status(error::INTERNAL,
"Failed to convert request message to json.");
LOG(ERROR) << "Failed to convert request message to json.";
return INTERNAL;
}
return OkStatus();
return OK;
}
Status ParseWvCasEncryptionResponseJson(const std::string& response_json,
WvCasEncryptionResponse* response) {
WvCasStatus ParseWvCasEncryptionResponseJson(
const std::string& response_json, WvCasEncryptionResponse* response) {
CHECK(response);
CasEncryptionResponse response_proto;
// NOTE: JsonStringToMessage will automatically perform base64 decode for
// 'bytes' type fields.
if (!JsonStringToMessage(response_json, &response_proto).ok()) {
return Status(error::INTERNAL,
"Failed to convert response json to message.");
LOG(ERROR) << "Failed to convert response json to message.";
return INTERNAL;
}
response->status =
@@ -133,7 +133,7 @@ Status ParseWvCasEncryptionResponseJson(const std::string& response_json,
response->entitlement_keys.push_back(key_info);
}
return OkStatus();
return OK;
}
} // namespace cas

View File

@@ -114,15 +114,15 @@ struct WvCasEncryptionResponse {
// request JSON message.
// And that signed JSON message can be sent to Widevine license server for
// aquiring entitlement keys.
Status CreateWvCasEncryptionRequestJson(const WvCasEncryptionRequest& request,
std::string* request_json);
WvCasStatus CreateWvCasEncryptionRequestJson(
const WvCasEncryptionRequest& request, std::string* request_json);
// Parses a WvCasEncryptionResponse in JSON format, returns a
// WvCasEncryptionResponse.
// |response_json| is supposed to be the 'response' field in the signed
// response from Widevine license server.
Status ParseWvCasEncryptionResponseJson(const std::string& response_json,
WvCasEncryptionResponse* response);
WvCasStatus ParseWvCasEncryptionResponseJson(const std::string& response_json,
WvCasEncryptionResponse* response);
} // namespace cas
} // namespace widevine

View File

@@ -56,7 +56,8 @@ TEST(WvCasTypesTest, CreateWvCasEncryptionRequestJson) {
request.key_rotation = true;
std::string actual_request_json;
EXPECT_OK(CreateWvCasEncryptionRequestJson(request, &actual_request_json));
EXPECT_EQ(OK,
CreateWvCasEncryptionRequestJson(request, &actual_request_json));
// Content_id has been base64 encoded.
std::string expected_request_json =
@@ -76,7 +77,8 @@ TEST(WvCasTypesTest, ParseWvCasEncryptionResponseJson) {
"\"key_slot\":\"ODD\"}]}";
WvCasEncryptionResponse actual_response;
EXPECT_OK(ParseWvCasEncryptionResponseJson(response_json, &actual_response));
EXPECT_EQ(OK,
ParseWvCasEncryptionResponseJson(response_json, &actual_response));
EXPECT_EQ(WvCasEncryptionResponse::Status::OK, actual_response.status);
// 21140844 is base64 decode of "MjExNDA4NDQ=".

View File

@@ -6,15 +6,17 @@
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// Example server that listens on a port for Simulcrypt API messages.
// Widevine MediaCAS ECMG server.
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cerrno>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include "gflags/gflags.h"
#include "glog/logging.h"

View File

@@ -0,0 +1,92 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2019 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.
////////////////////////////////////////////////////////////////////////////////
// Widevine MediaCAS EMMG server.
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "media_cas_packager_sdk/internal/emmg.h"
DEFINE_string(mux_address, "", "Mux server address");
DEFINE_int32(mux_port, 0, "Mux server port number");
// EMMG specfic configs.
DEFINE_int32(client_id, 0, "EMMG client_id");
DEFINE_int32(section_tspkt_flag, 1, "EMMG section_tspkt_flag");
DEFINE_int32(data_channel_id, 0, "EMMG data_channel_id");
DEFINE_int32(data_stream_id, 0, "EMMG data_stream_id");
DEFINE_int32(data_id, 0, "EMMG data_id");
// data_type: type of data carried in the datagram in the stream:
// 0x00: EMM;
// 0x01: private data;
// 0x02: DVB reserved (ECM);
// other values: DVB reserved.
DEFINE_int32(data_type, 0, "EMMG data_type");
#define BUFFER_SIZE (1024)
void BuildEmmgConfig(widevine::cas::EmmgConfig *config) {
CHECK(config);
config->client_id = FLAGS_client_id;
config->section_tspkt_flag = FLAGS_section_tspkt_flag;
config->data_channel_id = FLAGS_data_channel_id;
config->data_stream_id = FLAGS_data_stream_id;
config->data_id = FLAGS_data_id;
config->data_type = FLAGS_data_type;
}
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(!FLAGS_mux_address.empty()) << "flag --mux_address is required";
CHECK(FLAGS_mux_port != 0) << "flag --mux_port is required";
// Create server address.
struct hostent ret;
struct hostent *server;
char buffer[BUFFER_SIZE];
int h_errnop = 0;
int return_val = gethostbyname_r(FLAGS_mux_address.c_str(), &ret, buffer,
sizeof(buffer), &server, &h_errnop);
if (return_val != 0 || server == nullptr) {
LOG(FATAL) << "gethostbyname_r failed for " << FLAGS_mux_address;
}
struct sockaddr_in server_address;
bzero(reinterpret_cast<char *>(&server_address), sizeof(server_address));
server_address.sin_family = AF_INET;
bcopy(server->h_addr,
reinterpret_cast<char *>(&server_address.sin_addr.s_addr),
server->h_length);
server_address.sin_port = htons(FLAGS_mux_port);
// Connect to server.
int server_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket_fd < 0) {
LOG(FATAL) << "Failed to create socket.";
}
if (connect(server_socket_fd,
reinterpret_cast<struct sockaddr *>(&server_address),
sizeof(server_address)) < 0) {
LOG(FATAL) << "Failed to connect to server.";
}
// Send EMMG messages.
widevine::cas::EmmgConfig emmg_config;
BuildEmmgConfig(&emmg_config);
widevine::cas::Emmg emmg(&emmg_config, server_socket_fd);
emmg.Start();
close(server_socket_fd);
return 0;
}