Add support for Widevine ECM v3
Widevine ECM v3 is redesigned mainly based on protobuf, and supports new features including carrying fingerprinting and service blocking information. Existing clients must upgrade the Widevine CAS plugin to use the new ECM v3.
This commit is contained in:
@@ -104,7 +104,7 @@ constexpr char kTestEcmgChannelStatus[] = {
|
||||
constexpr char kTestEcmgStreamSetupWithPrivateParameters[] = {
|
||||
'\x03', // protocol_version
|
||||
'\x01', '\x01', // message_type - Stream_setup
|
||||
'\x00', '\xae', // message_length
|
||||
'\x00', '\xa8', // message_length
|
||||
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||
'\x00', '\x02', // parameter_length
|
||||
'\x00', '\x01', // parameter_value
|
||||
@@ -117,9 +117,6 @@ constexpr char kTestEcmgStreamSetupWithPrivateParameters[] = {
|
||||
'\x00', '\x10', // parameter_type - nominal_CP_duration
|
||||
'\x00', '\x02', // parameter_length
|
||||
'\x00', '\x64', // parameter_value
|
||||
'\x80', '\x02', // parameter_type - STREAM_TRACK_TYPE
|
||||
'\x00', '\x02', // parameter_length
|
||||
'S', 'D', // parameter_value
|
||||
'\x80', '\x03', // parameter_type - CONTENT_IV
|
||||
'\x00', '\x10', // parameter_length
|
||||
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', //
|
||||
@@ -245,7 +242,7 @@ constexpr char kTestEcmgCwProvision[] = {
|
||||
constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = {
|
||||
'\x03', // protocol_version
|
||||
'\x02', '\x01', // message_type - CW_provision
|
||||
'\x00', '\xee', // message_length
|
||||
'\x00', '\xe8', // message_length
|
||||
'\x00', '\x0e', // parameter_type - ECM_channel_id
|
||||
'\x00', '\x02', // parameter_length
|
||||
'\x00', '\x01', // parameter_value
|
||||
@@ -269,16 +266,13 @@ constexpr char kTestEcmgCwProvisionWithAccessCriteria[] = {
|
||||
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', //
|
||||
'\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', //
|
||||
'\x00', '\x0d', // parameter_type - access_criteria
|
||||
'\x00', '\xa6', // parameter_length
|
||||
'\x00', '\xa0', // parameter_length
|
||||
'\x80', '\x00', // access_criteria parameter_type - AGE_RESTRICTION
|
||||
'\x00', '\x01', // parameter_length
|
||||
'\x00', // parameter_value
|
||||
'\x80', '\x01', // access_criteria parameter_type - CRYPTO_MODE
|
||||
'\x00', '\x07', // parameter_length
|
||||
'A', 'e', 's', 'S', 'c', 't', 'e', // parameter_value
|
||||
'\x80', '\x02', // access_criteria parameter_type - STREAM_TRACK_TYPE
|
||||
'\x00', '\x02', // parameter_length
|
||||
'S', 'D', // parameter_value
|
||||
'\x80', '\x03', // access_criteria parameter_type - CONTENT_IV
|
||||
'\x00', '\x10', // parameter_length
|
||||
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', //
|
||||
|
||||
@@ -21,37 +21,60 @@
|
||||
#include "media_cas_packager_sdk/public/wv_cas_ecm.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_types.h"
|
||||
|
||||
const size_t kContentIvSize = 16; // 8 or 16
|
||||
const bool kKeyRotation = true; // whether key rotation is enabled
|
||||
const char kCryptoMode[] =
|
||||
namespace {
|
||||
|
||||
constexpr int kEcmVersion = 3; // Choices are 2 and 3.
|
||||
|
||||
constexpr size_t kContentIvSize = 16; // 8 or 16
|
||||
constexpr bool kKeyRotation = true; // whether key rotation is enabled
|
||||
constexpr char kCryptoMode[] =
|
||||
"AesScte"; // "AesCbc", "AesCtr", "DvbCsa2", "DvbCsa3", "AesOfb", "AesScte"
|
||||
const int kEcmPid = 149; // PID for the ECM packet
|
||||
const int kAgeRestriction = 0; // Age restriction for the ECM
|
||||
const char kOutputFile[] =
|
||||
"/tmp/ecm.ts"; // ECM TS packet will be output to here
|
||||
const uint8_t kTableId = 0x80; // 0x80 or 0x81
|
||||
const char kDefaultTrackTypeSd[] = "SD";
|
||||
constexpr int kEcmPid = 149; // PID for the ECM packet
|
||||
constexpr int kAgeRestriction = 0; // Age restriction for the ECM
|
||||
constexpr char kOutputFile[] =
|
||||
"/tmp/ecm.ts"; // ECM TS packet will be output to here
|
||||
constexpr uint8_t kTableId = 0x80; // 0x80 or 0x81
|
||||
constexpr char kDefaultTrackTypeSd[] = "SD";
|
||||
|
||||
const char kEvenKey[] = "even_key........"; // 16 bytes
|
||||
const char kEvenKeyId[] = "even_key_id....."; // 16 bytes
|
||||
const char kEvenContentIv8Bytes[] = "even_iv."; // 8 bytes
|
||||
const char kEvenContentIv16Bytes[] = "even_iv.even_iv."; // 16 bytes
|
||||
const char kEvenEntitlementKeyId[] = "fake_key_id1...."; // 16 bytes
|
||||
const char kEvenEntitlementKey[] =
|
||||
"fakefakefakefakefakefakefake1..."; // 32 bytes
|
||||
const char kEvenWrapIv[] = "even_warp_iv...."; // 16 bytes
|
||||
constexpr char kEvenKey[] = "even_key........"; // 16 bytes
|
||||
constexpr char kEvenKeyId[] = "even_key_id....."; // 16 bytes
|
||||
constexpr char kEvenContentIv8Bytes[] = "even_iv."; // 8 bytes
|
||||
constexpr char kEvenContentIv16Bytes[] = "even_iv.even_iv."; // 16 bytes
|
||||
constexpr char kEvenEntitlementKeyId[] = "fake_key_id1...."; // 16 bytes
|
||||
constexpr char kEvenEntitlementKey[] =
|
||||
"fakefakefakefakefakefakefake1..."; // 32 bytes
|
||||
constexpr char kEvenWrapIv[] = "even_warp_iv...."; // 16 bytes
|
||||
|
||||
const char kOddKey[] = "odd_key........."; // 16 bytes
|
||||
const char kOddKeyId[] = "odd_key_id......"; // 16 bytes
|
||||
const char kOddContentIv8Bytes[] = "odd_iv.."; // 8 bytes
|
||||
const char kOddContentIv16Bytes[] = "odd_iv..odd_iv.."; // 16 bytes
|
||||
const char kOddEntitlementKeyId[] = "fake_key_id2...."; // 16 bytes
|
||||
const char kOddEntitlementKey[] =
|
||||
"fakefakefakefakefakefakefake2..."; // 32 bytes
|
||||
const char kOddWrapIv[] = "odd_warp_iv....."; // 16 bytes
|
||||
constexpr char kOddKey[] = "odd_key........."; // 16 bytes
|
||||
constexpr char kOddKeyId[] = "odd_key_id......"; // 16 bytes
|
||||
constexpr char kOddContentIv8Bytes[] = "odd_iv.."; // 8 bytes
|
||||
constexpr char kOddContentIv16Bytes[] = "odd_iv..odd_iv.."; // 16 bytes
|
||||
constexpr char kOddEntitlementKeyId[] = "fake_key_id2...."; // 16 bytes
|
||||
constexpr char kOddEntitlementKey[] =
|
||||
"fakefakefakefakefakefakefake2..."; // 32 bytes
|
||||
constexpr char kOddWrapIv[] = "odd_warp_iv....."; // 16 bytes
|
||||
|
||||
// Fingerprinting and service blocking are only available with ECM v3+.
|
||||
constexpr char kFingerprintingControl[] = "ctr";
|
||||
constexpr char kServiceBlockingDeviceGroup1[] = "group";
|
||||
constexpr char kServiceBlockingDeviceGroup2[] = "g2";
|
||||
constexpr unsigned char kTestECPrivateKey2Secp256r1[] = {
|
||||
0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x34, 0x9a, 0xf2, 0x95,
|
||||
0x94, 0xd4, 0xca, 0xb9, 0xa0, 0x81, 0xe4, 0x1c, 0xf5, 0xde, 0x8d,
|
||||
0x23, 0xf6, 0x79, 0xba, 0x3c, 0x6e, 0xc9, 0x0b, 0x56, 0x0f, 0x07,
|
||||
0x5e, 0x9f, 0xe9, 0x38, 0x18, 0xfc, 0xa0, 0x0a, 0x06, 0x08, 0x2a,
|
||||
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42,
|
||||
0x00, 0x04, 0x7b, 0x2a, 0x61, 0x59, 0xe5, 0x1b, 0xb6, 0x30, 0x7b,
|
||||
0x59, 0x98, 0x42, 0x59, 0x37, 0xfb, 0x46, 0xfe, 0x53, 0xfe, 0x32,
|
||||
0xa1, 0x5c, 0x93, 0x36, 0x11, 0xb0, 0x5a, 0xae, 0xa4, 0x48, 0xe3,
|
||||
0x20, 0x12, 0xce, 0x78, 0xa7, 0x7f, 0xfd, 0x73, 0x5e, 0x09, 0x77,
|
||||
0x53, 0x77, 0x8f, 0xd6, 0x1b, 0x26, 0xfa, 0xc4, 0x2c, 0xc4, 0x11,
|
||||
0xfa, 0x72, 0x6a, 0xbe, 0x94, 0x78, 0x4d, 0x74, 0x20, 0x27, 0x86};
|
||||
|
||||
const size_t kTsPacketSize = 188;
|
||||
const size_t kMaxPossibleTsPacketsSizeBytes = 6 * kTsPacketSize;
|
||||
|
||||
using widevine::cas::EcmVersion;
|
||||
using widevine::cas::EntitlementKeyInfo;
|
||||
using widevine::cas::WvCasContentKeyInfo;
|
||||
using widevine::cas::WvCasEcmParameters;
|
||||
@@ -68,6 +91,9 @@ WvCasEcmParameters CreateWvCasEcmParameters(bool key_rotation,
|
||||
std::cerr << "Unsupported crypto mode " << kCryptoMode << std::endl;
|
||||
}
|
||||
params.age_restriction = kAgeRestriction;
|
||||
params.ecm_version = kEcmVersion <= 2 ? EcmVersion::kV2 : EcmVersion::kV3;
|
||||
params.ecc_private_signing_key.assign(std::begin(kTestECPrivateKey2Secp256r1),
|
||||
std::end(kTestECPrivateKey2Secp256r1));
|
||||
return params;
|
||||
}
|
||||
|
||||
@@ -115,6 +141,8 @@ std::vector<WvCasContentKeyInfo> CreateContentKeyInfo(bool key_rotation,
|
||||
return content_keys;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
WvCasEcmParameters params =
|
||||
CreateWvCasEcmParameters(kKeyRotation, kContentIvSize);
|
||||
@@ -122,6 +150,17 @@ int main(int argc, char** argv) {
|
||||
CreateInjectedEntitlements(kKeyRotation);
|
||||
widevine::cas::WvCasEcm wv_cas_ecm(params, entitlements);
|
||||
|
||||
if (params.ecm_version == EcmVersion::kV3) {
|
||||
widevine::cas::EcmFingerprintingParams fingerprinting_params;
|
||||
fingerprinting_params.control = kFingerprintingControl;
|
||||
wv_cas_ecm.SetFingerprinting(&fingerprinting_params);
|
||||
|
||||
widevine::cas::EcmServiceBlockingParams service_blocking_params;
|
||||
service_blocking_params.device_groups = {kServiceBlockingDeviceGroup1,
|
||||
kServiceBlockingDeviceGroup2};
|
||||
wv_cas_ecm.SetServiceBlocking(&service_blocking_params);
|
||||
}
|
||||
|
||||
std::vector<WvCasContentKeyInfo> content_keys =
|
||||
CreateContentKeyInfo(kKeyRotation, kContentIvSize);
|
||||
std::string ecm;
|
||||
@@ -147,16 +186,17 @@ int main(int argc, char** argv) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
// Generate ECM TS Packet.
|
||||
uint8_t packet[kTsPacketSize];
|
||||
uint8_t packet[kMaxPossibleTsPacketsSizeBytes];
|
||||
ssize_t packet_size = kMaxPossibleTsPacketsSizeBytes;
|
||||
uint8_t continuity_counter; // not used.
|
||||
status = wv_cas_ecm.GenerateTsPacket(ecm, kEcmPid, kTableId,
|
||||
&continuity_counter, packet);
|
||||
status = wv_cas_ecm.GenerateTsPacket(
|
||||
ecm, kEcmPid, kTableId, &continuity_counter, packet, &packet_size);
|
||||
if (!status.ok()) {
|
||||
std::cerr << "Failed to create ECM TS packet: " << status << std::endl;
|
||||
return -1;
|
||||
} else {
|
||||
std::cout << "TS packet bytes: ";
|
||||
for (size_t i = 0; i < kTsPacketSize; i++) {
|
||||
for (size_t i = 0; i < packet_size; i++) {
|
||||
printf("'\\x%02x', ", static_cast<uint16_t>(packet[i]));
|
||||
}
|
||||
std::cout << std::endl;
|
||||
@@ -165,7 +205,7 @@ int main(int argc, char** argv) {
|
||||
std::ofstream file;
|
||||
file.open(kOutputFile, std::ios_base::binary);
|
||||
assert(file.is_open());
|
||||
file.write(reinterpret_cast<char*>(packet), kTsPacketSize);
|
||||
file.write(reinterpret_cast<char*>(packet), packet_size);
|
||||
file.close();
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -34,6 +34,18 @@ using widevine::cas::WvCasEcmgClientHandler;
|
||||
constexpr int kServerPortNumber = 1234;
|
||||
constexpr int kListenQueueSize = 20;
|
||||
constexpr int kBufferSizeBytes = 2048;
|
||||
constexpr unsigned char kTestECPrivateKey2Secp256r1[] = {
|
||||
0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x34, 0x9a, 0xf2, 0x95,
|
||||
0x94, 0xd4, 0xca, 0xb9, 0xa0, 0x81, 0xe4, 0x1c, 0xf5, 0xde, 0x8d,
|
||||
0x23, 0xf6, 0x79, 0xba, 0x3c, 0x6e, 0xc9, 0x0b, 0x56, 0x0f, 0x07,
|
||||
0x5e, 0x9f, 0xe9, 0x38, 0x18, 0xfc, 0xa0, 0x0a, 0x06, 0x08, 0x2a,
|
||||
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42,
|
||||
0x00, 0x04, 0x7b, 0x2a, 0x61, 0x59, 0xe5, 0x1b, 0xb6, 0x30, 0x7b,
|
||||
0x59, 0x98, 0x42, 0x59, 0x37, 0xfb, 0x46, 0xfe, 0x53, 0xfe, 0x32,
|
||||
0xa1, 0x5c, 0x93, 0x36, 0x11, 0xb0, 0x5a, 0xae, 0xa4, 0x48, 0xe3,
|
||||
0x20, 0x12, 0xce, 0x78, 0xa7, 0x7f, 0xfd, 0x73, 0x5e, 0x09, 0x77,
|
||||
0x53, 0x77, 0x8f, 0xd6, 0x1b, 0x26, 0xfa, 0xc4, 0x2c, 0xc4, 0x11,
|
||||
0xfa, 0x72, 0x6a, 0xbe, 0x94, 0x78, 0x4d, 0x74, 0x20, 0x27, 0x86};
|
||||
|
||||
void BuildEcmgConfig(EcmgConfig* config) {
|
||||
config->delay_start = 200; // in milliseconds.
|
||||
@@ -43,6 +55,9 @@ void BuildEcmgConfig(EcmgConfig* config) {
|
||||
config->access_criteria_transfer_mode = 0;
|
||||
config->number_of_content_keys = 2;
|
||||
config->crypto_mode = widevine::cas::CryptoMode::kAesCtr;
|
||||
config->ecc_private_signing_key.assign(
|
||||
std::begin(kTestECPrivateKey2Secp256r1),
|
||||
std::end(kTestECPrivateKey2Secp256r1));
|
||||
}
|
||||
|
||||
void PrintMessage(const std::string& description, const char* const message,
|
||||
|
||||
Reference in New Issue
Block a user