Specify widevine/media_cas_packager_sdk/presubmit in media_cas_packager_sdk METADATA file.
------------- Moves ecm_generator to media_cas_packager_sdk/internal. ------------- Add a simple TCP server listening on a port. My intention is to use this server to support the Simulcrypt APIs (TODO). Also add a simple TCP client binary for testing the server and also demo how to call the Simulcrypt APIs (TODO). ------------- If only a single key is in the ECM, it is the EVEN key. To make the code matches this understanding, change a parameter from 'false' to 'true'. But this change has NO impact on the produced ECM, regardless this parameter is 'false' or 'true' (i.e., whether using push_front or push_back), only a single key is in the ECM. ------------- Add classes that process Simulcrypt ECMG messages 1) Stream_set-up 2) CW_provision ------------- Renames server and client binaries. ------------- Make ecmg call ecm_generator to generate ecm. The return of the ecm to Simulcrypt caller will be implemented in the next CL. For now, using the 'key' (control word) in CW_provision message also as the 'key_id'. ------------- Move common folder ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=217358698
This commit is contained in:
@@ -20,13 +20,15 @@ PUBLIC_COPTS = ["-fvisibility=default"]
|
||||
|
||||
filegroup(
|
||||
name = "binary_release_files",
|
||||
srcs = glob(["*.h"]),
|
||||
srcs = glob(["*.h"]) + [
|
||||
"simulcrypt_server.cc",
|
||||
":simulcrypt_server",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "libmedia_cas_packager_sdk.so",
|
||||
linkshared = 1,
|
||||
deps = [":ecm_generator"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
@@ -38,31 +40,14 @@ cc_library(
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//util:status",
|
||||
"//media_cas_packager_sdk/internal:ecm",
|
||||
"//media_cas_packager_sdk/internal:ecm_generator",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "ecm_generator",
|
||||
srcs = ["ecm_generator.cc"],
|
||||
hdrs = ["ecm_generator.h"],
|
||||
copts = PUBLIC_COPTS,
|
||||
cc_binary(
|
||||
name = "simulcrypt_server",
|
||||
srcs = ["simulcrypt_server.cc"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//util:status",
|
||||
"//media_cas_packager_sdk/internal:ecm",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ecm_generator_test",
|
||||
size = "small",
|
||||
srcs = ["ecm_generator_test.cc"],
|
||||
deps = [
|
||||
":ecm_generator",
|
||||
"//testing:gunit_main",
|
||||
"@abseil_repo//absl/memory",
|
||||
"//common:aes_cbc_util",
|
||||
"//protos/public:media_cas_encryption_proto",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 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/public/ecm_generator.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
static constexpr int kKeyDataSize = 16;
|
||||
static constexpr int kKeyIvSize8 = 8;
|
||||
static constexpr int kKeyIvSize16 = 16;
|
||||
static constexpr int kMaxBytesKeyIdField = 16;
|
||||
|
||||
std::string CasEcmGenerator::GenerateEcm(const EcmParameters& params) {
|
||||
std::vector<EntitledKeyInfo> keys;
|
||||
util::Status status = ProcessEcmParameters(params, &keys);
|
||||
if (!status.ok() || !initialized_) {
|
||||
LOG(ERROR) << " EcmParameters is not set up properly: " << status;
|
||||
return "";
|
||||
}
|
||||
std::string serialized_ecm;
|
||||
uint32_t generation;
|
||||
// TODO(user): need track_type
|
||||
std::string track_type = "SD";
|
||||
if (params.rotation_enabled) {
|
||||
status = ecm_->GenerateEcm(&keys[0], &keys[1], track_type, &serialized_ecm,
|
||||
&generation);
|
||||
} else {
|
||||
status = ecm_->GenerateSingleKeyEcm(&keys[0], track_type, &serialized_ecm,
|
||||
&generation);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << " Call to CasEcm's ECM Generator failed: " << status;
|
||||
return "";
|
||||
}
|
||||
return serialized_ecm;
|
||||
}
|
||||
|
||||
util::Status CasEcmGenerator::ProcessEcmParameters(
|
||||
const EcmParameters& ecm_params, std::vector<EntitledKeyInfo>* keys) {
|
||||
initialized_ = false;
|
||||
rotation_enabled_ = ecm_params.rotation_enabled;
|
||||
|
||||
// Validate and add key data
|
||||
keys->clear();
|
||||
uint32_t keys_needed = ecm_params.rotation_enabled ? 2 : 1;
|
||||
if (ecm_params.key_params.size() < keys_needed) {
|
||||
return {util::error::INVALID_ARGUMENT,
|
||||
"Number of supplied keys is wrong (check rotation periods)."};
|
||||
}
|
||||
for (int i = 0; i < keys_needed; i++) {
|
||||
keys->emplace_back(EntitledKeyInfo());
|
||||
EntitledKeyInfo& key = keys->back();
|
||||
key.key_id = ecm_params.key_params[i].key_id;
|
||||
key.key_value = ecm_params.key_params[i].key_data;
|
||||
key.content_iv = ecm_params.key_params[i].content_ivs[0];
|
||||
}
|
||||
current_key_index_ = 0;
|
||||
current_key_even_ = true;
|
||||
initialized_ = true;
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status CasEcmGenerator::ValidateKeyId(const std::string& id) {
|
||||
if (id.empty()) {
|
||||
return {util::error::INVALID_ARGUMENT, "Key id is empty."};
|
||||
}
|
||||
if (id.size() > kMaxBytesKeyIdField) {
|
||||
return {util::error::INVALID_ARGUMENT, "Key id is too long."};
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status CasEcmGenerator::ValidateKeyData(const std::string& key_data) {
|
||||
if (key_data.empty()) {
|
||||
return {util::error::INVALID_ARGUMENT, "Key data is empty."};
|
||||
}
|
||||
if (key_data.size() != kKeyDataSize) {
|
||||
return {util::error::INVALID_ARGUMENT, "Key data is wrong size."};
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status CasEcmGenerator::ValidateIv(const std::string& iv,
|
||||
size_t required_size) {
|
||||
if (iv.empty()) {
|
||||
return {util::error::INVALID_ARGUMENT, "IV is empty."};
|
||||
}
|
||||
if (required_size != 8 && required_size != 16) {
|
||||
return {util::error::INTERNAL, "IV size has not been set up correctly."};
|
||||
}
|
||||
|
||||
if (iv.size() != required_size) {
|
||||
return {util::error::INVALID_ARGUMENT,
|
||||
"IV has wrong or inconsistent size."};
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status CasEcmGenerator::ValidateWrappedKeyIv(const std::string& iv) {
|
||||
// All wrapped key IVs must be 16 bytes.
|
||||
util::Status status = ValidateIv(iv, kIvSize16);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << " Wrapped key IV is not valid: " << status;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
util::Status CasEcmGenerator::ValidateContentIv(const std::string& iv) {
|
||||
// If content_iv_size_ is zero, use this IV as the size for all future IVs in
|
||||
// this stream.
|
||||
if (content_iv_size_ == 0) {
|
||||
content_iv_size_ = iv.size();
|
||||
}
|
||||
util::Status status = ValidateIv(iv, content_iv_size_);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << " Content IV is not valid: " << status;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
util::Status CasEcmGenerator::ValidateKeyParameters(
|
||||
const KeyParameters& key_params) {
|
||||
util::Status status;
|
||||
status = ValidateKeyId(key_params.key_id);
|
||||
if (!status.ok()) return status;
|
||||
status = ValidateKeyData(key_params.wrapped_key_data);
|
||||
if (!status.ok()) return status;
|
||||
status = ValidateWrappedKeyIv(key_params.wrapped_key_iv);
|
||||
if (!status.ok()) return status;
|
||||
if (key_params.content_ivs.empty()) {
|
||||
return {util::error::INVALID_ARGUMENT, "Content IVs is empty."};
|
||||
}
|
||||
for (int i = 0; i < key_params.content_ivs.size(); i++) {
|
||||
status = ValidateContentIv(key_params.content_ivs[i]);
|
||||
if (!status.ok()) return status;
|
||||
}
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
@@ -1,95 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 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_PUBLIC_ECM_GENERATOR_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include "util/status.h"
|
||||
#include "media_cas_packager_sdk/internal/ecm.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
// KeyParameters carries key information for a single encryption key.
|
||||
// Instances of this struct are owned by an EcmParameters struct.
|
||||
struct KeyParameters {
|
||||
std::string key_id;
|
||||
std::string key_data;
|
||||
std::string wrapped_key_data;
|
||||
std::string wrapped_key_iv;
|
||||
std::vector<std::string> content_ivs;
|
||||
};
|
||||
|
||||
// EcmParameters holds information that is needed by the EcmGenerator. It is
|
||||
// partially set up with data from a KeyGenerator (obtained via GenerateKey()).
|
||||
// IVs are added later.
|
||||
// TODO(user): may need a starting crypto period index.
|
||||
struct EcmParameters {
|
||||
static constexpr int kDefaultIVSize = 8;
|
||||
int iv_size = kDefaultIVSize;
|
||||
bool current_key_even = true;
|
||||
uint32_t current_key_index = 0;
|
||||
std::string entitlement_key_id;
|
||||
bool rotation_enabled = true;
|
||||
uint32_t rotation_periods;
|
||||
std::vector<KeyParameters> key_params;
|
||||
uint16_t program_id;
|
||||
uint64_t rotation_period_microseconds;
|
||||
// For video, this is zero. For audio, this is the size of the audio frame,
|
||||
// which is a constant in the audio track.
|
||||
uint16_t offset = 0;
|
||||
};
|
||||
|
||||
// ECM Generator for Widevine/MediaCAS entitled keys.
|
||||
class CasEcmGenerator {
|
||||
public:
|
||||
CasEcmGenerator() = default;
|
||||
virtual ~CasEcmGenerator() = default;
|
||||
|
||||
virtual std::string GenerateEcm(const EcmParameters& params);
|
||||
|
||||
// Query the state of this ECM Generator
|
||||
bool initialized() { return initialized_; }
|
||||
bool rotation_enabled() { return rotation_enabled_; }
|
||||
|
||||
void set_ecm(std::unique_ptr<CasEcm> ecm) { ecm_ = std::move(ecm); }
|
||||
|
||||
private:
|
||||
friend class CasEcmGeneratorTest;
|
||||
|
||||
util::Status ProcessEcmParameters(const EcmParameters& ecm_params,
|
||||
std::vector<EntitledKeyInfo>* keys);
|
||||
|
||||
util::Status ProcessEcmParameters(const EcmParameters& ecm_params);
|
||||
util::Status ValidateKeyId(const std::string& id);
|
||||
util::Status ValidateKeyData(const std::string& key_data);
|
||||
util::Status ValidateWrappedKeyIv(const std::string& iv);
|
||||
util::Status ValidateIv(const std::string& iv, size_t required_size);
|
||||
util::Status ValidateContentIv(const std::string& iv);
|
||||
util::Status ValidateKeyParameters(const KeyParameters& key_params);
|
||||
|
||||
bool initialized_ = false;
|
||||
uint32_t generation_ = 0;
|
||||
bool rotation_enabled_ = false;
|
||||
uint32_t current_key_index_ = 0;
|
||||
bool current_key_even_ = true;
|
||||
uint32_t content_iv_size_ = 0;
|
||||
std::unique_ptr<CasEcm> ecm_;
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
#endif // MEDIA_CAS_PACKAGER_SDK_PUBLIC_ECM_GENERATOR_H_
|
||||
@@ -1,311 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 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/public/ecm_generator.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "common/aes_cbc_util.h"
|
||||
#include "protos/public/media_cas_encryption.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kCADescriptor[] = "TestCa";
|
||||
constexpr char kEcm[] = "TestEcm";
|
||||
constexpr char kProvider[] = "Gfiber";
|
||||
constexpr char kContentId[] = "TestContent";
|
||||
constexpr int kDefaultKeyRotationPeriodMilliseconds = 30 * 60 * 1000;
|
||||
constexpr char kPsshData[] = "TestPsshData";
|
||||
|
||||
constexpr char kEntitlementKeySingle[] = "testEKId12345678";
|
||||
constexpr char kEntitlementKeyDouble[] = "testEKIdabcdefgh";
|
||||
|
||||
constexpr char kEcmKeyIdSingle[] = "key-id-One123456";
|
||||
constexpr char kEcmKeyDataSingle[] = "0123456701234567";
|
||||
constexpr char kEcmWrappedKeySingle[] = "1234567890123456";
|
||||
constexpr char kEcmWrappedKeyIvSingle[] = "abcdefghacbdefgh";
|
||||
constexpr char kEcmContentIvSingle[] = "ABCDEFGH";
|
||||
|
||||
constexpr char kEcmKeyIdEven[] = "key-Id-One123456";
|
||||
constexpr char kEcmKeyDataEven[] = "KKEEYYDDAATTAAee";
|
||||
constexpr char kEcmWrappedKeyEven[] = "1234567890123456";
|
||||
constexpr char kEcmWrappedKeyIvEven[] = "abcdefghacbdefgh";
|
||||
constexpr char kEcmContentIvEven[] = "ABCDEFGH";
|
||||
|
||||
constexpr char kEcmKeyIdOdd[] = "key-Id-Two456789";
|
||||
constexpr char kEcmKeyDataOdd[] = "kkeeyyddaattaaOO";
|
||||
constexpr char kEcmWrappedKeyOdd[] = "9876543210654321";
|
||||
constexpr char kEcmWrappedKeyIvOdd[] = "a1c2e3g4a1c2e3g4";
|
||||
constexpr char kEcmContentIvOdd[] = "AaCbEcGd";
|
||||
|
||||
constexpr char kFakeCasEncryptionResponseKeyId[] = "fake_key_id.....";
|
||||
constexpr char kFakeCasEncryptionResponseKeyData[] =
|
||||
"fakefakefakefakefakefakefakefake";
|
||||
|
||||
util::Status HandleCasEncryptionRequest(const std::string& request_string,
|
||||
std::string* signed_response_string) {
|
||||
CasEncryptionRequest request;
|
||||
request.ParseFromString(request_string);
|
||||
|
||||
CasEncryptionResponse response;
|
||||
response.set_status(CasEncryptionResponse_Status_OK);
|
||||
response.set_content_id(request.content_id());
|
||||
for (const auto& track_type : request.track_types()) {
|
||||
if (request.key_rotation()) {
|
||||
// Add the Even key.
|
||||
auto key = response.add_entitlement_keys();
|
||||
key->set_key_id(kFakeCasEncryptionResponseKeyId);
|
||||
key->set_key(kFakeCasEncryptionResponseKeyData);
|
||||
key->set_track_type(track_type);
|
||||
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_EVEN);
|
||||
// Add the Odd key.
|
||||
key = response.add_entitlement_keys();
|
||||
key->set_key_id(kFakeCasEncryptionResponseKeyId);
|
||||
key->set_key(kFakeCasEncryptionResponseKeyData);
|
||||
key->set_track_type(track_type);
|
||||
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_ODD);
|
||||
} else {
|
||||
auto key = response.add_entitlement_keys();
|
||||
key->set_key_id(kFakeCasEncryptionResponseKeyId);
|
||||
key->set_key(kFakeCasEncryptionResponseKeyData);
|
||||
key->set_track_type(track_type);
|
||||
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_SINGLE);
|
||||
}
|
||||
}
|
||||
std::string response_string;
|
||||
response.SerializeToString(&response_string);
|
||||
SignedCasEncryptionResponse signed_response;
|
||||
signed_response.set_response(response_string);
|
||||
signed_response.SerializeToString(signed_response_string);
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class CasEcmGeneratorTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
}
|
||||
|
||||
util::Status ProcessEcmParameters(const EcmParameters& params,
|
||||
std::vector<EntitledKeyInfo>* keys) {
|
||||
return ecm_gen_.ProcessEcmParameters(params, keys);
|
||||
}
|
||||
|
||||
int EntitlementKeySize(const EcmParameters& params) const {
|
||||
return params.entitlement_key_id.size();
|
||||
}
|
||||
|
||||
void SetTestConfig1(EcmParameters* params) {
|
||||
params->entitlement_key_id = kEntitlementKeySingle;
|
||||
params->rotation_enabled = false;
|
||||
params->key_params.push_back(kKeyParamsSingle);
|
||||
}
|
||||
|
||||
void SetTestConfig2(EcmParameters* params) {
|
||||
KeyParameters even_key_params;
|
||||
KeyParameters odd_key_params;
|
||||
params->entitlement_key_id = kEntitlementKeyDouble;
|
||||
params->rotation_enabled = true;
|
||||
params->rotation_periods = 2;
|
||||
params->key_params.push_back(kKeyParamsEven);
|
||||
params->key_params.push_back(kKeyParamsOdd);
|
||||
}
|
||||
|
||||
// Call this to setup the guts (CasEcm) of the ECM Generator.
|
||||
void PrepareEcmGenerator(bool key_rotation_enabled) {
|
||||
ecm_ = absl::make_unique<CasEcm>();
|
||||
std::string entitlement_request;
|
||||
std::string entitlement_response;
|
||||
ecm_init_params_.key_rotation_enabled = key_rotation_enabled;
|
||||
ecm_init_params_.track_types.push_back("SD");
|
||||
ASSERT_OK(ecm_->Initialize(kContentId, kProvider, ecm_init_params_,
|
||||
&entitlement_request));
|
||||
ASSERT_OK(
|
||||
HandleCasEncryptionRequest(entitlement_request, &entitlement_response));
|
||||
ASSERT_OK(ecm_->ProcessCasEncryptionResponse(entitlement_response));
|
||||
ecm_gen_.set_ecm(std::move(ecm_));
|
||||
}
|
||||
|
||||
const KeyParameters kKeyParamsSingle{kEcmKeyIdSingle,
|
||||
kEcmKeyDataSingle,
|
||||
kEcmWrappedKeySingle,
|
||||
kEcmWrappedKeyIvSingle,
|
||||
{kEcmContentIvSingle}};
|
||||
const KeyParameters kKeyParamsEven{kEcmKeyIdEven,
|
||||
kEcmKeyDataEven,
|
||||
kEcmWrappedKeyEven,
|
||||
kEcmWrappedKeyIvEven,
|
||||
{kEcmContentIvEven}};
|
||||
const KeyParameters kKeyParamsOdd{kEcmKeyIdOdd,
|
||||
kEcmKeyDataOdd,
|
||||
kEcmWrappedKeyOdd,
|
||||
kEcmWrappedKeyIvOdd,
|
||||
{kEcmContentIvOdd}};
|
||||
|
||||
std::unique_ptr<CasEcm> ecm_;
|
||||
EcmInitParameters ecm_init_params_;
|
||||
CasEcmGenerator ecm_gen_;
|
||||
};
|
||||
|
||||
TEST_F(CasEcmGeneratorTest, InitializeNoRotation) {
|
||||
EXPECT_FALSE(ecm_gen_.initialized());
|
||||
|
||||
PrepareEcmGenerator(false);
|
||||
|
||||
EcmParameters ecm_params;
|
||||
std::vector<EntitledKeyInfo> keys;
|
||||
|
||||
SetTestConfig1(&ecm_params);
|
||||
|
||||
util::Status status = ProcessEcmParameters(ecm_params, &keys);
|
||||
ASSERT_OK(status);
|
||||
|
||||
ASSERT_EQ(EntitlementKeySize(ecm_params), 16);
|
||||
ASSERT_TRUE(ecm_gen_.initialized());
|
||||
EXPECT_FALSE(ecm_gen_.rotation_enabled());
|
||||
}
|
||||
|
||||
TEST_F(CasEcmGeneratorTest, GenerateNoRotation) {
|
||||
PrepareEcmGenerator(false);
|
||||
EcmParameters ecm_params;
|
||||
|
||||
SetTestConfig1(&ecm_params);
|
||||
|
||||
std::string ecm_string = ecm_gen_.GenerateEcm(ecm_params);
|
||||
|
||||
// Expected size (bytes):
|
||||
// CA system ID: 2 bytes
|
||||
// version: 1 byte
|
||||
// generation + flags: 1 byte
|
||||
// flags: 1 byte
|
||||
// entitlement key ID: 16 bytes
|
||||
// Single key: ID (16), Data (16), IV (16), IV (8) = 56
|
||||
// total = 77
|
||||
ASSERT_EQ(77, ecm_string.size());
|
||||
EXPECT_EQ('\x4A', ecm_string[0]); // CA System ID first byte.
|
||||
EXPECT_EQ('\xD4', ecm_string[1]); // CA System ID second byte.
|
||||
EXPECT_EQ('\x01', ecm_string[2]); // ECM version
|
||||
EXPECT_EQ('\x02', ecm_string[3]); // generation + flags
|
||||
EXPECT_EQ('\x80', ecm_string[4]); // flags
|
||||
EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(5, 16));
|
||||
EXPECT_EQ(kEcmKeyIdSingle, ecm_string.substr(21, 16));
|
||||
EXPECT_NE(kEcmWrappedKeySingle, ecm_string.substr(37, 16));
|
||||
EXPECT_NE(kEcmWrappedKeyIvSingle, ecm_string.substr(53, 16));
|
||||
// Unwrap key and compare with original.
|
||||
std::string wrapping_key = kFakeCasEncryptionResponseKeyData;
|
||||
std::string wrapping_iv = ecm_string.substr(53, 16);
|
||||
std::string wrapped_key = ecm_string.substr(37, 16);
|
||||
std::string unwrapped_key =
|
||||
crypto_util::DecryptAesCbcNoPad(wrapping_key, wrapping_iv, wrapped_key);
|
||||
EXPECT_EQ(kEcmKeyDataSingle, unwrapped_key);
|
||||
EXPECT_EQ(kEcmContentIvSingle, ecm_string.substr(69, 8));
|
||||
}
|
||||
|
||||
TEST_F(CasEcmGeneratorTest, Generate2NoRotation) {
|
||||
PrepareEcmGenerator(false);
|
||||
EcmParameters ecm_params;
|
||||
|
||||
SetTestConfig1(&ecm_params);
|
||||
|
||||
std::string ecm_string = ecm_gen_.GenerateEcm(ecm_params);
|
||||
ecm_string = ecm_gen_.GenerateEcm(ecm_params);
|
||||
|
||||
// Second generate should have higher generation number.
|
||||
ASSERT_EQ(77, ecm_string.size());
|
||||
EXPECT_EQ('\x4A', ecm_string[0]); // CA System ID first byte.
|
||||
EXPECT_EQ('\xD4', ecm_string[1]); // CA System ID second byte.
|
||||
EXPECT_EQ('\x01', ecm_string[2]); // ECM version
|
||||
EXPECT_EQ('\x0A', ecm_string[3]); // generation + flags
|
||||
EXPECT_EQ('\x80', ecm_string[4]); // flags
|
||||
EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(5, 16));
|
||||
EXPECT_EQ(kEcmKeyIdSingle, ecm_string.substr(21, 16));
|
||||
EXPECT_NE(kEcmWrappedKeySingle, ecm_string.substr(37, 16));
|
||||
EXPECT_NE(kEcmWrappedKeyIvSingle, ecm_string.substr(53, 16));
|
||||
// Unwrap key and compare with original.
|
||||
std::string wrapping_key = kFakeCasEncryptionResponseKeyData;
|
||||
std::string wrapping_iv = ecm_string.substr(53, 16);
|
||||
std::string wrapped_key = ecm_string.substr(37, 16);
|
||||
std::string unwrapped_key =
|
||||
crypto_util::DecryptAesCbcNoPad(wrapping_key, wrapping_iv, wrapped_key);
|
||||
EXPECT_EQ(kEcmKeyDataSingle, unwrapped_key);
|
||||
EXPECT_EQ(kEcmContentIvSingle, ecm_string.substr(69, 8));
|
||||
}
|
||||
|
||||
TEST_F(CasEcmGeneratorTest, InitializeSimpleRotation) {
|
||||
EXPECT_FALSE(ecm_gen_.initialized());
|
||||
EcmParameters ecm_params;
|
||||
std::vector<EntitledKeyInfo> keys;
|
||||
|
||||
PrepareEcmGenerator(true);
|
||||
|
||||
SetTestConfig2(&ecm_params);
|
||||
ecm_init_params_.key_rotation_enabled = true;
|
||||
|
||||
util::Status status = ProcessEcmParameters(ecm_params, &keys);
|
||||
|
||||
EXPECT_TRUE(status.ok());
|
||||
EXPECT_TRUE(ecm_gen_.initialized());
|
||||
EXPECT_TRUE(ecm_gen_.rotation_enabled());
|
||||
}
|
||||
|
||||
TEST_F(CasEcmGeneratorTest, GenerateSimpleRotation) {
|
||||
EcmParameters ecm_params;
|
||||
|
||||
PrepareEcmGenerator(true);
|
||||
|
||||
SetTestConfig2(&ecm_params);
|
||||
|
||||
std::string ecm_string = ecm_gen_.GenerateEcm(ecm_params);
|
||||
|
||||
// Expected size (bytes):
|
||||
// same as no rotation case = 77
|
||||
// second entitlement key ID: 16 bytes
|
||||
// Second key: ID (16), Data (16), IV (16), IV (8) = 56
|
||||
// total = 149
|
||||
ASSERT_EQ(149, ecm_string.size());
|
||||
EXPECT_EQ('\x4A', ecm_string[0]); // CA System ID first byte.
|
||||
EXPECT_EQ('\xD4', ecm_string[1]); // CA System ID second byte.
|
||||
EXPECT_EQ('\x01', ecm_string[2]); // ECM version
|
||||
EXPECT_EQ('\x03', ecm_string[3]); // generation + flags
|
||||
EXPECT_EQ('\x80', ecm_string[4]); // flags
|
||||
EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(5, 16));
|
||||
EXPECT_EQ(kEcmKeyIdEven, ecm_string.substr(21, 16));
|
||||
EXPECT_NE(kEcmWrappedKeyEven, ecm_string.substr(37, 16));
|
||||
EXPECT_NE(kEcmWrappedKeyIvEven, ecm_string.substr(53, 16));
|
||||
EXPECT_EQ(kEcmContentIvEven, ecm_string.substr(69, 8));
|
||||
EXPECT_EQ(kFakeCasEncryptionResponseKeyId, ecm_string.substr(77, 16));
|
||||
EXPECT_EQ(kEcmKeyIdOdd, ecm_string.substr(93, 16));
|
||||
EXPECT_NE(kEcmWrappedKeyOdd, ecm_string.substr(109, 16));
|
||||
EXPECT_NE(kEcmWrappedKeyIvOdd, ecm_string.substr(125, 16));
|
||||
EXPECT_EQ(kEcmContentIvOdd, ecm_string.substr(141, 8));
|
||||
// Unwrap even key and compare with original.
|
||||
std::string wrapping_key_even = kFakeCasEncryptionResponseKeyData;
|
||||
std::string wrapping_iv_even = ecm_string.substr(53, 16);
|
||||
std::string wrapped_key_even = ecm_string.substr(37, 16);
|
||||
std::string unwrapped_key_even = crypto_util::DecryptAesCbcNoPad(
|
||||
wrapping_key_even, wrapping_iv_even, wrapped_key_even);
|
||||
EXPECT_EQ(kEcmKeyDataEven, unwrapped_key_even);
|
||||
// Unwrap odd key and compare with original.
|
||||
std::string wrapping_key_odd = kFakeCasEncryptionResponseKeyData;
|
||||
std::string wrapping_iv_odd = ecm_string.substr(125, 16);
|
||||
std::string wrapped_key_odd = ecm_string.substr(109, 16);
|
||||
std::string unwrapped_key_odd = crypto_util::DecryptAesCbcNoPad(
|
||||
wrapping_key_odd, wrapping_iv_odd, wrapped_key_odd);
|
||||
EXPECT_EQ(kEcmKeyDataOdd, unwrapped_key_odd);
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
84
media_cas_packager_sdk/public/simulcrypt_server.cc
Normal file
84
media_cas_packager_sdk/public/simulcrypt_server.cc
Normal file
@@ -0,0 +1,84 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2018 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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Example server that listens on a port for Simulcrypt API messages.
|
||||
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include "glog/logging.h"
|
||||
|
||||
DEFINE_int32(port, 0, "Server port number");
|
||||
|
||||
constexpr uint32_t kBufferSize = 256;
|
||||
constexpr uint32_t kLicenseBacklog = 5;
|
||||
constexpr uint32_t kWriteChunkSize = 18;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
CHECK(FLAGS_port != 0) << "need --port";
|
||||
|
||||
struct sockaddr_in server_address;
|
||||
bzero(reinterpret_cast<char *>(&server_address), sizeof(server_address));
|
||||
server_address.sin_family = AF_INET;
|
||||
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
server_address.sin_port = htons(FLAGS_port);
|
||||
|
||||
int listen_socket_fd = socket(AF_INET, SOCK_STREAM, /* protocol= */ 0);
|
||||
CHECK(listen_socket_fd >= 0) << "failed to open socket";
|
||||
CHECK(bind(listen_socket_fd, (struct sockaddr *)&server_address,
|
||||
sizeof(server_address)) >= 0)
|
||||
<< "error on binding";
|
||||
std::cout << "Server listening ..." << std::endl << std::flush;
|
||||
int return_val = listen(listen_socket_fd, kLicenseBacklog);
|
||||
switch (return_val) {
|
||||
case EADDRINUSE:
|
||||
LOG(FATAL) << "Another socket is already listening on the same port.";
|
||||
break;
|
||||
case EBADF:
|
||||
LOG(FATAL) << "The argument sockfd is not a valid descriptor.";
|
||||
break;
|
||||
case ENOTSOCK:
|
||||
LOG(FATAL) << "The argument sockfd is not a socket.";
|
||||
break;
|
||||
case EOPNOTSUPP:
|
||||
LOG(FATAL) << "The socket is not of a type that supports the listen() "
|
||||
"operation.";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
struct sockaddr_in client_address;
|
||||
socklen_t clilet_address_size = sizeof(client_address);
|
||||
int client_socket_fd = accept(
|
||||
listen_socket_fd, reinterpret_cast<struct sockaddr *>(&client_address),
|
||||
&clilet_address_size);
|
||||
CHECK(client_socket_fd >= 0) << "error on accept";
|
||||
|
||||
char buffer[kBufferSize];
|
||||
bzero(buffer, kBufferSize);
|
||||
if (read(client_socket_fd, buffer, kBufferSize - 1) < 0) {
|
||||
LOG(FATAL) << "ERROR reading from socket";
|
||||
}
|
||||
printf("Here is the message: %s", buffer);
|
||||
if (write(client_socket_fd, "I got your message", kWriteChunkSize) < 0) {
|
||||
LOG(FATAL) << "ERROR writing to socket";
|
||||
}
|
||||
|
||||
close(client_socket_fd);
|
||||
close(listen_socket_fd);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user