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:
Fang Yu
2018-10-17 15:38:59 -07:00
parent 08829edd17
commit fcdd9fa38c
17 changed files with 703 additions and 38 deletions

View File

@@ -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",
],
)

View File

@@ -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>

View 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

View 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_

View 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