Implement a set of "Simplified APIs" for ECM generation for castlabs.com.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=217601738
This commit is contained in:
@@ -16,12 +16,15 @@ package(
|
||||
filegroup(
|
||||
name = "binary_release_files",
|
||||
srcs = [
|
||||
"simulcrypt_client.cc",
|
||||
"test_simulcrypt_messages.h",
|
||||
":simulcrypt_client",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "constants",
|
||||
hdrs = ["constants.h"],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "simulcrypt_client",
|
||||
srcs = ["simulcrypt_client.cc"],
|
||||
|
||||
30
example/constants.h
Normal file
30
example/constants.h
Normal file
@@ -0,0 +1,30 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Some constant values for testing / demo purpose.
|
||||
|
||||
#ifndef MEDIA_CAS_PACKAGER_SDK_EXAMPLE_CONSTANTS_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_EXAMPLE_CONSTANTS_H_
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
const char kDefaultContentId[] = "21140844";
|
||||
const char kDefaultProvider[] = "widevine";
|
||||
// Size of this IV needs to match ecm_init_params.content_iv_size.
|
||||
const char kDefaultContentIv8Bytes[] = {'\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01'};
|
||||
const char kDefaultContentIv16Bytes[] = {
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01', '\x01'};
|
||||
const char kDefaultTrackTypeSd[] = "SD";
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
#endif // MEDIA_CAS_PACKAGER_SDK_EXAMPLE_CONSTANTS_H_
|
||||
@@ -6,7 +6,7 @@
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Example client that talks to server_main.
|
||||
// Example client for demonstrating network calls to public/simulcrypt_server.
|
||||
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
|
||||
@@ -55,7 +55,6 @@ cc_library(
|
||||
hdrs = ["ecm_generator.h"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//util:status",
|
||||
"//media_cas_packager_sdk/internal:ecm",
|
||||
],
|
||||
@@ -91,6 +90,7 @@ cc_library(
|
||||
"@abseil_repo//absl/memory",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//util:status",
|
||||
"//example:constants",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -299,7 +299,12 @@ util::Status CasEcm::WrapEntitledKeys(
|
||||
for (auto entitled_key : keys) {
|
||||
entitled_key->entitlement_key_id = entitlement_key->key_id;
|
||||
// Wrap key using entitlement key. First generate new IV.
|
||||
// TODO(user): Do not randomly generate 'wrapped_key_iv' here ever,
|
||||
// enforce the caller of 'ecm generator' to provide it.
|
||||
// And check the provided 'wrapped_key_iv' has valid size.
|
||||
if (entitled_key->wrapped_key_iv.empty()) {
|
||||
CHECK(RandomBytes(kWrappedKeyIvSizeBytes, &entitled_key->wrapped_key_iv));
|
||||
}
|
||||
entitled_key->wrapped_key_value =
|
||||
WrapKey(entitlement_key->key_value, entitled_key->wrapped_key_iv,
|
||||
entitled_key->key_value);
|
||||
|
||||
@@ -75,6 +75,8 @@ struct EcmInitParameters {
|
||||
class CasEcm {
|
||||
public:
|
||||
CasEcm() = default;
|
||||
CasEcm(const CasEcm&) = delete;
|
||||
CasEcm& operator=(const CasEcm&) = delete;
|
||||
virtual ~CasEcm() = default;
|
||||
|
||||
// Perform initialization for a new ECM stream.
|
||||
|
||||
@@ -64,6 +64,7 @@ util::Status CasEcmGenerator::ProcessEcmParameters(
|
||||
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.wrapped_key_iv = ecm_params.key_params[i].wrapped_key_iv;
|
||||
key.content_iv = ecm_params.key_params[i].content_ivs[0];
|
||||
}
|
||||
current_key_index_ = 0;
|
||||
|
||||
@@ -59,6 +59,8 @@ struct EcmParameters {
|
||||
class CasEcmGenerator {
|
||||
public:
|
||||
CasEcmGenerator() = default;
|
||||
CasEcmGenerator(const CasEcmGenerator&) = delete;
|
||||
CasEcmGenerator& operator=(const CasEcmGenerator&) = delete;
|
||||
virtual ~CasEcmGenerator() = default;
|
||||
|
||||
virtual std::string GenerateEcm(const EcmParameters& params);
|
||||
|
||||
@@ -203,7 +203,7 @@ TEST_F(CasEcmGeneratorTest, GenerateNoRotation) {
|
||||
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));
|
||||
EXPECT_EQ(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);
|
||||
@@ -233,7 +233,7 @@ TEST_F(CasEcmGeneratorTest, Generate2NoRotation) {
|
||||
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));
|
||||
EXPECT_EQ(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);
|
||||
@@ -284,12 +284,12 @@ TEST_F(CasEcmGeneratorTest, GenerateSimpleRotation) {
|
||||
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(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(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;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "util/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/util.h"
|
||||
@@ -28,13 +29,6 @@ namespace cas {
|
||||
|
||||
namespace {
|
||||
|
||||
static const char kDefaultContentId[] = "21140844";
|
||||
static const char kDefaultProvider[] = "widevine";
|
||||
// Size of this IV needs to match ecm_init_params.content_iv_size.
|
||||
static const char kDefaultContentIv[] = {'\x01', '\x01', '\x01', '\x01',
|
||||
'\x01', '\x01', '\x01', '\x01'};
|
||||
static const char kDefaultTrackTypeSd[] = "SD";
|
||||
|
||||
// Local helper function that processes all the parameters in an ECMG message.
|
||||
util::Status ProcessParameters(const char* message, size_t message_length,
|
||||
EcmgParameters* parameters) {
|
||||
@@ -271,7 +265,7 @@ util::Status Ecmg::ProcessCwProvisionMessage(const char* message,
|
||||
ecm_param.key_params[i].key_id = ecm_param.key_params[i].key_data;
|
||||
// TODO(user): MUST have a better way to generate/retrieve content_iv.
|
||||
ecm_param.key_params[i].content_ivs.push_back(
|
||||
std::string(kDefaultContentIv));
|
||||
std::string(kDefaultContentIv8Bytes));
|
||||
}
|
||||
std::string serialized_ecm = ecm_generator.GenerateEcm(ecm_param);
|
||||
std::cout << "serialized_ecm: " << serialized_ecm << std::endl;
|
||||
|
||||
@@ -14,16 +14,6 @@
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
namespace {
|
||||
|
||||
// Key IDs are 16 bytes, keys are 32 bytes.
|
||||
static const char* kKeyId1 = "fake_key_id1....";
|
||||
static const char* kKey1 = "fakefakefakefakefakefakefake1...";
|
||||
static const char* kKeyId2 = "fake_key_id2....";
|
||||
static const char* kKey2 = "fakefakefakefakefakefakefake2...";
|
||||
|
||||
} // namespace
|
||||
|
||||
util::Status FixedKeyFetcher::RequestEntitlementKey(
|
||||
const std::string& request_string, std::string* signed_response_string) {
|
||||
CasEncryptionRequest request;
|
||||
@@ -36,20 +26,20 @@ util::Status FixedKeyFetcher::RequestEntitlementKey(
|
||||
if (request.key_rotation()) {
|
||||
// Add the Even key.
|
||||
auto key = response.add_entitlement_keys();
|
||||
key->set_key_id(kKeyId1);
|
||||
key->set_key(kKey1);
|
||||
key->set_key_id(even_entitlement_key_id_);
|
||||
key->set_key(even_entitlement_key_);
|
||||
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(kKeyId2);
|
||||
key->set_key(kKey2);
|
||||
key->set_key_id(odd_entitlement_key_id_);
|
||||
key->set_key(odd_entitlement_key_);
|
||||
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(kKeyId1);
|
||||
key->set_key(kKey1);
|
||||
key->set_key_id(even_entitlement_key_id_);
|
||||
key->set_key(even_entitlement_key_);
|
||||
key->set_track_type(track_type);
|
||||
key->set_key_slot(CasEncryptionResponse_KeyInfo_KeySlot_SINGLE);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,26 @@ namespace cas {
|
||||
// locally-constructed response that has known (predefined) entitlement keys.
|
||||
class FixedKeyFetcher : public KeyFetcher {
|
||||
public:
|
||||
FixedKeyFetcher() {}
|
||||
// Key IDs are 16 bytes, keys are 32 bytes.
|
||||
// TODO(user): There should be a single entitlement key for both even
|
||||
// and odd keys. Shouldn't have two different types of entitlement keys.
|
||||
FixedKeyFetcher()
|
||||
: even_entitlement_key_id_("fake_key_id1...."),
|
||||
even_entitlement_key_("fakefakefakefakefakefakefake1..."),
|
||||
odd_entitlement_key_id_("fake_key_id2...."),
|
||||
odd_entitlement_key_("fakefakefakefakefakefakefake2...") {}
|
||||
// Explictly provide the key_id and entitlement keys rather than using the
|
||||
// hardcoded default.
|
||||
FixedKeyFetcher(const std::string& even_entitlement_key_id,
|
||||
const std::string& even_entitlement_key,
|
||||
const std::string& odd_entitlement_key_id,
|
||||
const std::string& odd_entitlement_key)
|
||||
: even_entitlement_key_id_(even_entitlement_key_id),
|
||||
even_entitlement_key_(even_entitlement_key),
|
||||
odd_entitlement_key_id_(odd_entitlement_key_id),
|
||||
odd_entitlement_key_(odd_entitlement_key) {}
|
||||
FixedKeyFetcher(const FixedKeyFetcher&) = delete;
|
||||
FixedKeyFetcher& operator=(const FixedKeyFetcher&) = delete;
|
||||
~FixedKeyFetcher() override = default;
|
||||
|
||||
// Get entitlement keys. Process a CasEncryptionRequest message to
|
||||
@@ -32,6 +51,12 @@ class FixedKeyFetcher : public KeyFetcher {
|
||||
// WvCasEcm::ProcessCasEncryptionResponse().
|
||||
util::Status RequestEntitlementKey(const std::string& request_string,
|
||||
std::string* signed_response_string) override;
|
||||
|
||||
private:
|
||||
std::string even_entitlement_key_id_;
|
||||
std::string even_entitlement_key_;
|
||||
std::string odd_entitlement_key_id_;
|
||||
std::string odd_entitlement_key_;
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
|
||||
@@ -21,7 +21,6 @@ PUBLIC_COPTS = ["-fvisibility=default"]
|
||||
filegroup(
|
||||
name = "binary_release_files",
|
||||
srcs = glob(["*.h"]) + [
|
||||
"simulcrypt_server.cc",
|
||||
":simulcrypt_server",
|
||||
],
|
||||
)
|
||||
@@ -29,6 +28,7 @@ filegroup(
|
||||
cc_binary(
|
||||
name = "libmedia_cas_packager_sdk.so",
|
||||
linkshared = 1,
|
||||
deps = [":wv_cas_ecm"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
@@ -37,10 +37,11 @@ cc_library(
|
||||
hdrs = glob(["*.h"]),
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"//util:status",
|
||||
"//media_cas_packager_sdk/internal:ecm",
|
||||
"//media_cas_packager_sdk/internal:ecm_generator",
|
||||
"//protos/public:media_cas_encryption_proto",
|
||||
"//protos/public:media_cas_proto",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -49,5 +50,36 @@ cc_binary(
|
||||
srcs = ["simulcrypt_server.cc"],
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "wv_cas_ecm",
|
||||
srcs = ["wv_cas_ecm.cc"],
|
||||
hdrs = ["wv_cas_ecm.h"],
|
||||
copts = PUBLIC_COPTS,
|
||||
deps = [
|
||||
"//base",
|
||||
"@abseil_repo//absl/base:core_headers",
|
||||
"@abseil_repo//absl/memory",
|
||||
"@abseil_repo//absl/strings",
|
||||
"//util:status",
|
||||
"//example:constants",
|
||||
"//media_cas_packager_sdk/internal:ecm",
|
||||
"//media_cas_packager_sdk/internal:ecm_generator",
|
||||
"//media_cas_packager_sdk/internal:fixed_key_fetcher",
|
||||
"//protos/public:media_cas_proto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "wv_cas_ecm_test",
|
||||
size = "small",
|
||||
srcs = ["wv_cas_ecm_test.cc"],
|
||||
deps = [
|
||||
":wv_cas_ecm",
|
||||
"//testing:gunit_main",
|
||||
"//util:status",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -11,10 +11,8 @@
|
||||
#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>
|
||||
|
||||
|
||||
236
media_cas_packager_sdk/public/wv_cas_ecm.cc
Normal file
236
media_cas_packager_sdk/public/wv_cas_ecm.cc
Normal file
@@ -0,0 +1,236 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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/wv_cas_ecm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "util/status.h"
|
||||
#include "example/constants.h"
|
||||
#include "media_cas_packager_sdk/internal/ecm.h"
|
||||
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
||||
#include "media_cas_packager_sdk/internal/fixed_key_fetcher.h"
|
||||
#include "protos/public/media_cas.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
namespace {
|
||||
static constexpr size_t kContentIvSizeBytes8 = 8;
|
||||
static constexpr size_t kWrappedKeyIvSizeBytes = 16;
|
||||
static constexpr size_t kCryptoModeCbc = 0;
|
||||
static constexpr size_t kCryptoModeCtr = 1;
|
||||
|
||||
int EcmIvSizeToInt(EcmIvSize iv_size) {
|
||||
if (iv_size == kIvSize8) {
|
||||
return 8;
|
||||
} else if (iv_size == kIvSize16) {
|
||||
return 16;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
util::Status WvCasEcm::Initialize(int content_iv_size,
|
||||
bool key_rotation_enabled, int crypto_mode) {
|
||||
if (initialized_) {
|
||||
return util::Status(
|
||||
util::error::INTERNAL,
|
||||
"Cannot initialize an instance of WvCasEcm more than once");
|
||||
}
|
||||
if (content_iv_size != kContentIvSizeBytes8) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Only support content_iv_size being 8 now");
|
||||
}
|
||||
ecm_init_params_.content_iv_size = kIvSize8;
|
||||
ecm_init_params_.key_rotation_enabled = key_rotation_enabled;
|
||||
if (crypto_mode != kCryptoModeCtr) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Only CTR crypto mode is supported by Widevine plugin "
|
||||
"for content encryption");
|
||||
}
|
||||
ecm_init_params_.crypto_mode = CasCryptoMode::CTR;
|
||||
// Internal ECM class can hold entitlement keys for multiple tracks.
|
||||
// So we need to set a default track type here to be associated with
|
||||
// the entitlement keys set later.
|
||||
ecm_init_params_.track_types.push_back(kDefaultTrackTypeSd);
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status WvCasEcm::GenerateEcm(const std::string& even_key,
|
||||
const std::string& even_wrapping_iv,
|
||||
const std::string& even_content_iv,
|
||||
const std::string& odd_key,
|
||||
const std::string& odd_wrapping_iv,
|
||||
const std::string& odd_content_iv,
|
||||
const std::string& entitlement_key_id,
|
||||
const std::string& entitlement_key, std::string* ecm) {
|
||||
DCHECK(ecm);
|
||||
if (!initialized_) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"WvCasEcm has not been properly initialized");
|
||||
}
|
||||
if (!ecm_init_params_.key_rotation_enabled) {
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"Please call GenerateSingleKeyEcm() instead when key "
|
||||
"rotation is disabled");
|
||||
}
|
||||
if (even_wrapping_iv.size() != kWrappedKeyIvSizeBytes ||
|
||||
odd_wrapping_iv.size() != kWrappedKeyIvSizeBytes) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Size of wrapping IV is incorrect");
|
||||
}
|
||||
if (even_content_iv.size() !=
|
||||
EcmIvSizeToInt(ecm_init_params_.content_iv_size) ||
|
||||
odd_content_iv.size() !=
|
||||
EcmIvSizeToInt(ecm_init_params_.content_iv_size)) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Size of content IV is incorrect");
|
||||
}
|
||||
|
||||
// Create an instance of CasEcm in order to set the entitlement keys.
|
||||
std::unique_ptr<CasEcm> cas_ecm = absl::make_unique<CasEcm>();
|
||||
std::string entitlement_request;
|
||||
std::string entitlement_response;
|
||||
// 'content_id' and 'provider' are used in entitlement key request/response
|
||||
// only, NOT needed for constructing the ECM. So we just use hardcoded value
|
||||
// here for now.
|
||||
// TODO(user): When we want to retrieve entitlement key from License Server
|
||||
// we need to figure out a way to provide real 'content_id' and 'provder'
|
||||
// to this function here.
|
||||
util::Status status;
|
||||
if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider,
|
||||
ecm_init_params_, &entitlement_request))
|
||||
.ok()) {
|
||||
return status;
|
||||
}
|
||||
FixedKeyFetcher fixed_key_fetcher(
|
||||
/* even_entitlement_key_id= */ entitlement_key_id,
|
||||
/* even_entitlement_key= */ entitlement_key,
|
||||
/* odd_entitlement_key_id= */ entitlement_key_id,
|
||||
/* odd_entitlement_key= */ entitlement_key);
|
||||
if (!(status = fixed_key_fetcher.RequestEntitlementKey(entitlement_request,
|
||||
&entitlement_response))
|
||||
.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (!(status = cas_ecm->ProcessCasEncryptionResponse(entitlement_response))
|
||||
.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Generate ECM.
|
||||
CasEcmGenerator ecm_generator;
|
||||
ecm_generator.set_ecm(std::move(cas_ecm));
|
||||
EcmParameters ecm_param;
|
||||
ecm_param.rotation_enabled = ecm_init_params_.key_rotation_enabled;
|
||||
// Add even entitlement key.
|
||||
ecm_param.key_params.emplace_back();
|
||||
ecm_param.key_params[0].key_data = even_key;
|
||||
ecm_param.key_params[0].wrapped_key_iv = even_wrapping_iv;
|
||||
// Per 1:1 with hali@ just use entitlement_key_id for key_id here.
|
||||
// TODO(user): Follow up with jfore@ why we need key_id in the ECM.
|
||||
ecm_param.key_params[0].key_id = entitlement_key_id;
|
||||
ecm_param.key_params[0].content_ivs.push_back(even_content_iv);
|
||||
// Add odd entitlement key.
|
||||
ecm_param.key_params.emplace_back();
|
||||
ecm_param.key_params[1].key_data = odd_key;
|
||||
ecm_param.key_params[1].wrapped_key_iv = odd_wrapping_iv;
|
||||
ecm_param.key_params[1].key_id = entitlement_key_id;
|
||||
ecm_param.key_params[1].content_ivs.push_back(odd_content_iv);
|
||||
*ecm = ecm_generator.GenerateEcm(ecm_param);
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status WvCasEcm::GenerateSingleKeyEcm(const std::string& even_key,
|
||||
const std::string& even_wrapping_iv,
|
||||
const std::string& even_content_iv,
|
||||
const std::string& entitlement_key_id,
|
||||
const std::string& entitlement_key,
|
||||
std::string* ecm) {
|
||||
if (!initialized_) {
|
||||
DCHECK(ecm);
|
||||
return util::Status(util::error::INTERNAL,
|
||||
"WvCasEcm has not been properly initialized");
|
||||
}
|
||||
if (ecm_init_params_.key_rotation_enabled) {
|
||||
return util::Status(
|
||||
util::error::INTERNAL,
|
||||
"Please call GenerateEcm() instead when key rotation is enabled");
|
||||
}
|
||||
if (even_wrapping_iv.size() != kWrappedKeyIvSizeBytes) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Size of wrapping IV is incorrect");
|
||||
}
|
||||
if (even_content_iv.size() !=
|
||||
EcmIvSizeToInt(ecm_init_params_.content_iv_size)) {
|
||||
return util::Status(util::error::INVALID_ARGUMENT,
|
||||
"Size of content IV is incorrect");
|
||||
}
|
||||
|
||||
// Create an instance of CasEcm in order to set the entitlement keys.
|
||||
std::unique_ptr<CasEcm> cas_ecm = absl::make_unique<CasEcm>();
|
||||
std::string entitlement_request;
|
||||
std::string entitlement_response;
|
||||
// 'content_id' and 'provider' are used in entitlement key request/response
|
||||
// only, NOT needed for constructing the ECM. So we just use hardcoded value
|
||||
// here for now.
|
||||
// TODO(user): When we want to retrieve entitlement key from License Server
|
||||
// we need to figure out a way to provide real 'content_id' and 'provder'
|
||||
// to this function here.
|
||||
util::Status status;
|
||||
if (!(status = cas_ecm->Initialize(kDefaultContentId, kDefaultProvider,
|
||||
ecm_init_params_, &entitlement_request))
|
||||
.ok()) {
|
||||
return status;
|
||||
}
|
||||
FixedKeyFetcher fixed_key_fetcher(
|
||||
/* even_entitlement_key_id= */ entitlement_key_id,
|
||||
/* even_entitlement_key= */ entitlement_key,
|
||||
/* odd_entitlement_key_id= */ "",
|
||||
/* odd_entitlement_key= */ "");
|
||||
if (!(status = fixed_key_fetcher.RequestEntitlementKey(entitlement_request,
|
||||
&entitlement_response))
|
||||
.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (!(status = cas_ecm->ProcessCasEncryptionResponse(entitlement_response))
|
||||
.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Generate ECM.
|
||||
CasEcmGenerator ecm_generator;
|
||||
ecm_generator.set_ecm(std::move(cas_ecm));
|
||||
EcmParameters ecm_param;
|
||||
ecm_param.rotation_enabled = ecm_init_params_.key_rotation_enabled;
|
||||
// Add even entitlement key.
|
||||
ecm_param.key_params.emplace_back();
|
||||
ecm_param.key_params[0].key_data = even_key;
|
||||
ecm_param.key_params[0].wrapped_key_iv = even_wrapping_iv;
|
||||
ecm_param.key_params[0].key_id = entitlement_key_id;
|
||||
ecm_param.key_params[0].content_ivs.push_back(even_content_iv);
|
||||
*ecm = ecm_generator.GenerateEcm(ecm_param);
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
128
media_cas_packager_sdk/public/wv_cas_ecm.h
Normal file
128
media_cas_packager_sdk/public/wv_cas_ecm.h
Normal file
@@ -0,0 +1,128 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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_WV_CAS_ECM_H_
|
||||
#define MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "util/status.h"
|
||||
#include "media_cas_packager_sdk/internal/ecm.h"
|
||||
#include "media_cas_packager_sdk/internal/ecm_generator.h"
|
||||
#include "protos/public/media_cas.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
// Class for generating Widevine CAS ECMs.
|
||||
// See wv_cas_ecm_test.cc for example usage.
|
||||
// This class is NOT thread-safe.
|
||||
class WvCasEcm {
|
||||
public:
|
||||
WvCasEcm() = default;
|
||||
WvCasEcm(const WvCasEcm&) = delete;
|
||||
WvCasEcm& operator=(const WvCasEcm&) = delete;
|
||||
virtual ~WvCasEcm() = default;
|
||||
|
||||
// Initialize an instance of this class.
|
||||
//
|
||||
// Args:
|
||||
// - |content_iv_size| iv size in bytes for encrypting the content,
|
||||
// only support 8 bytes now
|
||||
// TODO(user): Double-check with jfore@ regarding only support 8 bytes.
|
||||
// - |key_rotation_enabled| whether key rotation is enabled,
|
||||
// if this is 'true' only subsequent call to GenerateEcm will be allowed,
|
||||
// if this is 'false' only subsequent call to GenerateSingleKeyEcm will
|
||||
// be allowed
|
||||
// - |crypto_mode| crypto mode for encrypting content,
|
||||
// kCryptoModeCbc = 0, kCryptoModeCtr = 1
|
||||
// only CTR is supported by Widevine CAS plugin for now
|
||||
// TODO(user): Check with jfore@ regarding supporting kCryptoModeCbc.
|
||||
//
|
||||
// Returns:
|
||||
// - A status indicating whether there was any error during initialization
|
||||
//
|
||||
// Note:
|
||||
// - 'even'/'odd' key in the ECM will be be encrypted using AEC_CBC
|
||||
util::Status Initialize(int content_iv_size, bool key_rotation_enabled,
|
||||
int crypto_mode);
|
||||
|
||||
// Generate an ECM containing two keys (even and odd). Can be called when
|
||||
// |key_rotation_enabled| is initialized to 'true'.
|
||||
//
|
||||
// Args:
|
||||
// - |even_key| clear even content key
|
||||
// - |even_wrapping_iv| iv used when encrypting |even_key| in the ECM
|
||||
// - |even_content_iv| iv used along with |even_key| for encrypting content
|
||||
// - |odd_key| clear odd content key
|
||||
// - |odd_wrapping_iv| iv used when encrypting |odd_key| in the ECM
|
||||
// - |odd_content_iv| iv used along with |odd_key| for encrypting content
|
||||
// - |entitlement_key_id| key id for |entitlement_key|
|
||||
// - |entitlement_key| entitlement key used to encrypt even and odd keys
|
||||
// - |ecm| for returning the generated ECM, must not be nullptr
|
||||
//
|
||||
// Returns:
|
||||
// - A status indicating whether there was any error during processing
|
||||
//
|
||||
// Note:
|
||||
// - The same |entitlement_key| will be used to encrypt both |even_key|
|
||||
// and |odd_key| in the ECM
|
||||
// - Currently, we only allow |even_content_iv| and |odd_content_iv|
|
||||
// to be of size 8 bytes, so we assume the encryptor would append suffix
|
||||
// {'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'}
|
||||
// to |even_content_iv| and |odd_content_iv| to obtain a 16 bytes IV
|
||||
// before encrypting the content
|
||||
util::Status GenerateEcm(const std::string& even_key,
|
||||
const std::string& even_wrapping_iv,
|
||||
const std::string& even_content_iv, const std::string& odd_key,
|
||||
const std::string& odd_wrapping_iv,
|
||||
const std::string& odd_content_iv,
|
||||
const std::string& entitlement_key_id,
|
||||
const std::string& entitlement_key, std::string* ecm);
|
||||
|
||||
// Generate an ECM containing only a singe even key. Can be called when
|
||||
// |key_rotation_enabled| is initialized to 'false'.
|
||||
//
|
||||
// Args:
|
||||
// - |even_key| clear even content key
|
||||
// - |even_wrapping_iv| iv used when encrypting |even_key| in the ECM
|
||||
// - |even_content_iv| iv used along with |even_key| for encrypting content
|
||||
// - |entitlement_key_id| key id for |entitlement_key|
|
||||
// - |entitlement_key| entitlement key used to encrypt even key
|
||||
// - |ecm| for returning the generated ECM, must not be nullptr
|
||||
//
|
||||
// Returns:
|
||||
// - A status indicating whether there was any error during processing
|
||||
//
|
||||
// Note:
|
||||
//
|
||||
// - Currently, we only allow |even_content_iv|
|
||||
// to be of size 8 bytes, so we assume the encryptor would append suffix
|
||||
// {'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'}
|
||||
// to |even_content_iv| to obtain a 16 bytes IV
|
||||
// before encrypting the content
|
||||
util::Status GenerateSingleKeyEcm(const std::string& even_key,
|
||||
const std::string& even_wrapping_iv,
|
||||
const std::string& even_content_iv,
|
||||
const std::string& entitlement_key_id,
|
||||
const std::string& entitlement_key, std::string* ecm);
|
||||
|
||||
private:
|
||||
bool initialized_ = false;
|
||||
EcmInitParameters ecm_init_params_;
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
|
||||
#endif // MEDIA_CAS_PACKAGER_SDK_PUBLIC_WV_CAS_ECM_H_
|
||||
219
media_cas_packager_sdk/public/wv_cas_ecm_test.cc
Normal file
219
media_cas_packager_sdk/public/wv_cas_ecm_test.cc
Normal file
@@ -0,0 +1,219 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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/wv_cas_ecm.h"
|
||||
#include "testing/gmock.h"
|
||||
#include "testing/gunit.h"
|
||||
|
||||
using ::testing::Test;
|
||||
|
||||
namespace widevine {
|
||||
namespace cas {
|
||||
|
||||
const char kEvenKey[] = "even_content_key"; // 16 bytes
|
||||
const char kEvenWrappingIv[] = "even_wrap_iv...."; // 16 bytes
|
||||
const char kEvenContentIv[] = "evencont"; // 8 bytes
|
||||
const char kOddKey[] = "odd_content_key."; // 16 bytes
|
||||
const char kOddWrappingIv[] = "odd_wrap_iv....."; // 16 bytes
|
||||
const char kOddContentIv[] = "oddcont."; // 8 bytes
|
||||
const char kEntitlementKeyId[] = "ent_key_id......"; // 16 bytes
|
||||
const char kEntitlementKey[] = "entitlement_key................."; // 32 bytes
|
||||
|
||||
class WvCasEcmTest : public Test {
|
||||
protected:
|
||||
WvCasEcmTest() {}
|
||||
WvCasEcm wv_cas_ecm_;
|
||||
};
|
||||
|
||||
TEST_F(WvCasEcmTest, InitializeTwice) {
|
||||
EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ true,
|
||||
/* crypto_mode= */ 1));
|
||||
EXPECT_EQ(util::error::INTERNAL,
|
||||
wv_cas_ecm_
|
||||
.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ true,
|
||||
/* crypto_mode= */ 1)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, InitializeContentIvSize) {
|
||||
EXPECT_EQ(
|
||||
util::error::INVALID_ARGUMENT,
|
||||
wv_cas_ecm_
|
||||
.Initialize(/* content_iv_size= */ 16,
|
||||
/* key_rotation_enabled= */ true, /* crypto_mode= */ 1)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, InitializeCryptoMode) {
|
||||
EXPECT_EQ(
|
||||
util::error::INVALID_ARGUMENT,
|
||||
wv_cas_ecm_
|
||||
.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ true, /* crypto_mode= */ 0)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, InitializeKeyRotationEnabled) {
|
||||
EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ true,
|
||||
/* crypto_mode= */ 1));
|
||||
std::string actual_ecm;
|
||||
EXPECT_EQ(util::error::INTERNAL,
|
||||
wv_cas_ecm_.GenerateSingleKeyEcm("", "", "", "", "", &actual_ecm)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, InitializeKeyRotationDisabled) {
|
||||
EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ false,
|
||||
/* crypto_mode= */ 1));
|
||||
std::string actual_ecm;
|
||||
EXPECT_EQ(util::error::INTERNAL,
|
||||
wv_cas_ecm_.GenerateEcm("", "", "", "", "", "", "", "", &actual_ecm)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, GenerateEcmInvalidWrappingIv) {
|
||||
EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ true,
|
||||
/* crypto_mode= */ 1));
|
||||
std::string actual_ecm;
|
||||
EXPECT_EQ(util::error::INVALID_ARGUMENT,
|
||||
wv_cas_ecm_
|
||||
.GenerateEcm(
|
||||
/* even_key= */ kEvenKey,
|
||||
/* even_wrapping_iv= */ kEvenWrappingIv,
|
||||
/* even_content_iv= */ kEvenContentIv,
|
||||
/* odd_key= */ kOddKey,
|
||||
/* odd_wrapping_iv= */ "12345678",
|
||||
/* odd_content_iv= */ kOddContentIv,
|
||||
/* entitlement_key_id= */ kEntitlementKeyId,
|
||||
/* entitlement_key= */ kEntitlementKey, &actual_ecm)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, GenerateEcmInvalidContentIv) {
|
||||
EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ true,
|
||||
/* crypto_mode= */ 1));
|
||||
std::string actual_ecm;
|
||||
EXPECT_EQ(util::error::INVALID_ARGUMENT,
|
||||
wv_cas_ecm_
|
||||
.GenerateEcm(
|
||||
/* even_key= */ kEvenKey,
|
||||
/* even_wrapping_iv= */ kEvenWrappingIv,
|
||||
/* even_content_iv= */ kEvenContentIv,
|
||||
/* odd_key= */ kOddKey,
|
||||
/* odd_wrapping_iv= */ kOddWrappingIv,
|
||||
/* odd_content_iv= */ "123456789",
|
||||
/* entitlement_key_id= */ kEntitlementKeyId,
|
||||
/* entitlement_key= */ kEntitlementKey, &actual_ecm)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, GenerateSingleKeyEcmInvalidWrappingIv) {
|
||||
EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ false,
|
||||
/* crypto_mode= */ 1));
|
||||
std::string actual_ecm;
|
||||
EXPECT_EQ(util::error::INVALID_ARGUMENT,
|
||||
wv_cas_ecm_
|
||||
.GenerateSingleKeyEcm(
|
||||
/* even_key= */ kEvenKey,
|
||||
/* even_wrapping_iv= */ "12345678",
|
||||
/* even_content_iv= */ kEvenContentIv,
|
||||
/* entitlement_key_id= */ kEntitlementKeyId,
|
||||
/* entitlement_key= */ kEntitlementKey, &actual_ecm)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, GenerateSingleKeyEcmInvalidContentIv) {
|
||||
EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ false,
|
||||
/* crypto_mode= */ 1));
|
||||
std::string actual_ecm;
|
||||
EXPECT_EQ(util::error::INVALID_ARGUMENT,
|
||||
wv_cas_ecm_
|
||||
.GenerateSingleKeyEcm(
|
||||
/* even_key= */ kEvenKey,
|
||||
/* even_wrapping_iv= */ kEvenWrappingIv,
|
||||
/* even_content_iv= */ "1234",
|
||||
/* entitlement_key_id= */ kEntitlementKeyId,
|
||||
/* entitlement_key= */ kEntitlementKey, &actual_ecm)
|
||||
.error_code());
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, GenerateEcmSuccess) {
|
||||
EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ true,
|
||||
/* crypto_mode= */ 1));
|
||||
|
||||
std::string actual_ecm;
|
||||
EXPECT_OK(wv_cas_ecm_.GenerateEcm(
|
||||
/* even_key= */ kEvenKey,
|
||||
/* even_wrapping_iv= */ kEvenWrappingIv,
|
||||
/* even_content_iv= */ kEvenContentIv,
|
||||
/* odd_key= */ kOddKey,
|
||||
/* odd_wrapping_iv= */ kOddWrappingIv,
|
||||
/* odd_content_iv= */ kOddContentIv,
|
||||
/* entitlement_key_id= */ kEntitlementKeyId,
|
||||
/* entitlement_key= */ kEntitlementKey, &actual_ecm));
|
||||
|
||||
// 149 bytes.
|
||||
char expected_ecm[] = {
|
||||
'\x4a', '\xd4', '\x1', '\x3', '\x80', '\x65', '\x6e', '\x74', '\x5f',
|
||||
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x2e', '\x2e', '\x2e',
|
||||
'\x2e', '\x2e', '\x2e', '\x65', '\x6e', '\x74', '\x5f', '\x6b', '\x65',
|
||||
'\x79', '\x5f', '\x69', '\x64', '\x2e', '\x2e', '\x2e', '\x2e', '\x2e',
|
||||
'\x2e', '\x1d', '\xb6', '\x2f', '\x14', '\x1f', '\xa3', '\xd6', '\x68',
|
||||
'\xd8', '\x79', '\xfe', '\x69', '\x5f', '\x3c', '\xad', '\x8b', '\x65',
|
||||
'\x76', '\x65', '\x6e', '\x5f', '\x77', '\x72', '\x61', '\x70', '\x5f',
|
||||
'\x69', '\x76', '\x2e', '\x2e', '\x2e', '\x2e', '\x65', '\x76', '\x65',
|
||||
'\x6e', '\x63', '\x6f', '\x6e', '\x74', '\x65', '\x6e', '\x74', '\x5f',
|
||||
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x2e', '\x2e', '\x2e',
|
||||
'\x2e', '\x2e', '\x2e', '\x65', '\x6e', '\x74', '\x5f', '\x6b', '\x65',
|
||||
'\x79', '\x5f', '\x69', '\x64', '\x2e', '\x2e', '\x2e', '\x2e', '\x2e',
|
||||
'\x2e', '\xcb', '\xb2', '\x4d', '\x7d', '\xd1', '\x27', '\x97', '\xd3',
|
||||
'\xf7', '\xe0', '\x11', '\x93', '\xa3', '\x43', '\xd9', '\x55', '\x6f',
|
||||
'\x64', '\x64', '\x5f', '\x77', '\x72', '\x61', '\x70', '\x5f', '\x69',
|
||||
'\x76', '\x2e', '\x2e', '\x2e', '\x2e', '\x2e', '\x6f', '\x64', '\x64',
|
||||
'\x63', '\x6f', '\x6e', '\x74', '\x2e'};
|
||||
EXPECT_EQ(std::string(expected_ecm, sizeof(expected_ecm)), actual_ecm);
|
||||
}
|
||||
|
||||
TEST_F(WvCasEcmTest, GenerateSingleKeyEcmSuccess) {
|
||||
EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8,
|
||||
/* key_rotation_enabled= */ false,
|
||||
/* crypto_mode= */ 1));
|
||||
|
||||
std::string actual_ecm;
|
||||
EXPECT_OK(wv_cas_ecm_.GenerateSingleKeyEcm(
|
||||
/* even_key= */ kEvenKey,
|
||||
/* even_wrapping_iv= */ kEvenWrappingIv,
|
||||
/* even_content_iv= */ kEvenContentIv,
|
||||
/* entitlement_key_id= */ kEntitlementKeyId,
|
||||
/* entitlement_key= */ kEntitlementKey, &actual_ecm));
|
||||
|
||||
// 77 bytes.
|
||||
char expected_ecm[] = {
|
||||
'\x4a', '\xd4', '\x1', '\x2', '\x80', '\x65', '\x6e', '\x74', '\x5f',
|
||||
'\x6b', '\x65', '\x79', '\x5f', '\x69', '\x64', '\x2e', '\x2e', '\x2e',
|
||||
'\x2e', '\x2e', '\x2e', '\x65', '\x6e', '\x74', '\x5f', '\x6b', '\x65',
|
||||
'\x79', '\x5f', '\x69', '\x64', '\x2e', '\x2e', '\x2e', '\x2e', '\x2e',
|
||||
'\x2e', '\x1d', '\xb6', '\x2f', '\x14', '\x1f', '\xa3', '\xd6', '\x68',
|
||||
'\xd8', '\x79', '\xfe', '\x69', '\x5f', '\x3c', '\xad', '\x8b', '\x65',
|
||||
'\x76', '\x65', '\x6e', '\x5f', '\x77', '\x72', '\x61', '\x70', '\x5f',
|
||||
'\x69', '\x76', '\x2e', '\x2e', '\x2e', '\x2e', '\x65', '\x76', '\x65',
|
||||
'\x6e', '\x63', '\x6f', '\x6e', '\x74'};
|
||||
EXPECT_EQ(std::string(expected_ecm, sizeof(expected_ecm)), actual_ecm);
|
||||
}
|
||||
|
||||
} // namespace cas
|
||||
} // namespace widevine
|
||||
Reference in New Issue
Block a user