diff --git a/common/crypto_util.cc b/common/crypto_util.cc index 48cc37e..901b991 100644 --- a/common/crypto_util.cc +++ b/common/crypto_util.cc @@ -30,6 +30,9 @@ const size_t kSigningKeySizeBytes = 32; const char kIvMasterKey[] = "1234567890123456"; const char kIvLabel[] = "IV_ENCRYPTION"; const int kIvSizeBits = 128; +const char kKeyIdMasterKey[] = "0123456789abcdef"; +const char kKeyIdLabel[] = "KEY_ID_ENCRYPTION"; +const int kKeyIdSizeBits = 128; const char kGroupKeyLabel[] = "GROUP_ENCRYPTION"; // TODO(user): This is a temporary key for development. Replace this with // a real group master key in keystore. @@ -137,6 +140,11 @@ std::string DeriveIv(absl::string_view context) { return DeriveKey(kIvMasterKey, kIvLabel, context, kIvSizeBits); } +// Derives a key ID from the provided info. +std::string DeriveKeyId(absl::string_view context) { + return DeriveKey(kKeyIdMasterKey, kKeyIdLabel, context, kKeyIdSizeBits); +} + std::string DeriveGroupSessionKey(absl::string_view context, const uint32_t size_bits) { return DeriveKey(kPhonyGroupMasterKey, kGroupKeyLabel, context, size_bits); diff --git a/common/crypto_util.h b/common/crypto_util.h index 05eaa41..dc00f4e 100644 --- a/common/crypto_util.h +++ b/common/crypto_util.h @@ -49,6 +49,9 @@ std::string DeriveKey(absl::string_view key, absl::string_view label, // Derives an IV from the provided |context|. std::string DeriveIv(absl::string_view context); +// Derives a key ID from the provided |context|. +std::string DeriveKeyId(absl::string_view context); + // Helper function to derive a key using the group master key and context. std::string DeriveGroupSessionKey(absl::string_view context, const uint32_t size_bits); diff --git a/common/crypto_util_test.cc b/common/crypto_util_test.cc index 7445bbd..b939609 100644 --- a/common/crypto_util_test.cc +++ b/common/crypto_util_test.cc @@ -198,6 +198,21 @@ TEST(CryptoUtilTest, DeriveIv) { } } +TEST(CryptoUtilTest, DeriveKeyId) { + // First value in the pair is the context, second value is the expected id. + std::pair context_id_pairs[] = { + {"1234567890123456", "a3c4a8c0d0e24e96f38f492254186a9d"}, + {"0987654321098765", "084fc6bece9688ccce6b1672d9b47e22"}}; + for (const auto& context_id_pair : context_id_pairs) { + SCOPED_TRACE(absl::StrCat("test case:", context_id_pair.first)); + EXPECT_EQ(context_id_pair.second, + absl::BytesToHexString(DeriveKeyId(context_id_pair.first))); + // Repeat same call to verify derivied result is repeatable. + EXPECT_EQ(context_id_pair.second, + absl::BytesToHexString(DeriveKeyId(context_id_pair.first))); + } +} + TEST(CryptoUtilTest, Verify4CCEncryptionIDFromBadString) { uint32_t cc_code; ASSERT_FALSE(FourCCEncryptionSchemeIDFromString("garbage", &cc_code)); diff --git a/example/BUILD b/example/BUILD index 6a27142..32e54bf 100644 --- a/example/BUILD +++ b/example/BUILD @@ -16,7 +16,9 @@ package( filegroup( name = "binary_release_files", srcs = [ + "wv_cas_ecm_example.cc", ":simulcrypt_client", + ":wv_cas_ecm_example", ], ) @@ -40,3 +42,14 @@ cc_library( "//base", ], ) + +cc_binary( + name = "wv_cas_ecm_example", + srcs = ["wv_cas_ecm_example.cc"], + deps = [ + "//base", + "@abseil_repo//absl/strings", + "//util:status", + "//media_cas_packager_sdk/public:wv_cas_ecm", + ], +) diff --git a/example/wv_cas_ecm_example.cc b/example/wv_cas_ecm_example.cc new file mode 100644 index 0000000..3b5a1f3 --- /dev/null +++ b/example/wv_cas_ecm_example.cc @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 of how to use the wv_cas_ecm library. + +#include + +#include "gflags/gflags.h" +#include "glog/logging.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "util/status.h" +#include "media_cas_packager_sdk/public/wv_cas_ecm.h" + +const char kEvenKey[] = "even_content_key"; // 16 bytes +const char kEvenContentIv8Bytes[] = "evencont"; // 8 bytes +const char kOddKey[] = "odd_content_key."; // 16 bytes +const char kOddContentIv8Bytes[] = "oddcont."; // 8 bytes +const char kEntitlementKeyId[] = "ent_key_id......"; // 16 bytes +const char kEntitlementKey[] = "entitlement_key................."; // 32 bytes + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + + widevine::cas::WvCasEcm wv_cas_ecm; + auto status = wv_cas_ecm.Initialize(/* content_iv_size= */ 16, + /* key_rotation_enabled= */ true, + /* crypto_mode= */ 1); + if (!status.ok()) { + LOG(FATAL) << "Failed to initialize WV CAS ECM, error: " << status; + } + std::string ecm; + status = wv_cas_ecm.GenerateEcm( + /* even_key= */ kEvenKey, + /* even_content_iv= */ + absl::StrCat(kEvenContentIv8Bytes, kEvenContentIv8Bytes), + /* odd_key= */ kOddKey, + /* odd_content_iv= */ + absl::StrCat(kOddContentIv8Bytes, kOddContentIv8Bytes), + /* entitlement_key_id= */ kEntitlementKeyId, + /* entitlement_key= */ kEntitlementKey, &ecm); + if (!status.ok()) { + LOG(FATAL) << "Failed to generate WV CAS ECM, error: " << status; + } else { + LOG(INFO) << "Generated WV CAS ECM (in hex): " + << absl::BytesToHexString(ecm); + } + + return 0; +} diff --git a/media_cas_packager_sdk/public/BUILD b/media_cas_packager_sdk/public/BUILD index cc7baad..49e7b94 100644 --- a/media_cas_packager_sdk/public/BUILD +++ b/media_cas_packager_sdk/public/BUILD @@ -65,6 +65,7 @@ cc_library( "@abseil_repo//absl/memory", "@abseil_repo//absl/strings", "//util:status", + "//common:crypto_util", "//example:constants", "//media_cas_packager_sdk/internal:ecm", "//media_cas_packager_sdk/internal:ecm_generator", @@ -80,6 +81,6 @@ cc_test( deps = [ ":wv_cas_ecm", "//testing:gunit_main", - "//util:status", + "@abseil_repo//absl/strings", ], ) diff --git a/media_cas_packager_sdk/public/wv_cas_ecm.cc b/media_cas_packager_sdk/public/wv_cas_ecm.cc index 79a1f20..a31e843 100644 --- a/media_cas_packager_sdk/public/wv_cas_ecm.cc +++ b/media_cas_packager_sdk/public/wv_cas_ecm.cc @@ -18,6 +18,7 @@ #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "util/status.h" +#include "common/crypto_util.h" #include "example/constants.h" #include "media_cas_packager_sdk/internal/ecm.h" #include "media_cas_packager_sdk/internal/ecm_generator.h" @@ -28,8 +29,6 @@ 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; @@ -51,11 +50,14 @@ util::Status WvCasEcm::Initialize(int content_iv_size, util::error::INTERNAL, "Cannot initialize an instance of WvCasEcm more than once"); } - if (content_iv_size != kContentIvSizeBytes8) { + if (content_iv_size == 8) { + ecm_init_params_.content_iv_size = kIvSize8; + } else if (content_iv_size == 16) { + ecm_init_params_.content_iv_size = kIvSize16; + } else { 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, @@ -74,10 +76,9 @@ util::Status WvCasEcm::Initialize(int content_iv_size, } 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) { @@ -91,11 +92,6 @@ util::Status WvCasEcm::GenerateEcm(const std::string& even_key, "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() != @@ -143,16 +139,14 @@ util::Status WvCasEcm::GenerateEcm(const std::string& even_key, // 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].wrapped_key_iv = crypto_util::DeriveIv(even_key); + ecm_param.key_params[0].key_id = crypto_util::DeriveKeyId(even_key); 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].wrapped_key_iv = crypto_util::DeriveIv(odd_key); + ecm_param.key_params[1].key_id = crypto_util::DeriveKeyId(odd_key); ecm_param.key_params[1].content_ivs.push_back(odd_content_iv); *ecm = ecm_generator.GenerateEcm(ecm_param); @@ -160,7 +154,6 @@ util::Status WvCasEcm::GenerateEcm(const std::string& even_key, } 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, @@ -175,10 +168,6 @@ util::Status WvCasEcm::GenerateSingleKeyEcm(const std::string& even_key, 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, @@ -224,8 +213,8 @@ util::Status WvCasEcm::GenerateSingleKeyEcm(const std::string& even_key, // 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].wrapped_key_iv = crypto_util::DeriveIv(even_key); + ecm_param.key_params[0].key_id = crypto_util::DeriveKeyId(even_key); ecm_param.key_params[0].content_ivs.push_back(even_content_iv); *ecm = ecm_generator.GenerateEcm(ecm_param); diff --git a/media_cas_packager_sdk/public/wv_cas_ecm.h b/media_cas_packager_sdk/public/wv_cas_ecm.h index 54695ab..2b76cc2 100644 --- a/media_cas_packager_sdk/public/wv_cas_ecm.h +++ b/media_cas_packager_sdk/public/wv_cas_ecm.h @@ -38,8 +38,9 @@ class WvCasEcm { // // 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. + // only support 8 bytes or 16 bytes + // When using 8 bytes content_iv, we assume additional 8 bytes of '\x00' are + // appended to the iv to form a 16 bytes AES IV when content is encrypted. // - |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 @@ -47,7 +48,6 @@ class WvCasEcm { // - |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 @@ -62,10 +62,8 @@ class WvCasEcm { // // 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 @@ -77,15 +75,10 @@ class WvCasEcm { // 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 + // - Size of |even_content_iv| and |odd_content_iv| must match + // |content_iv_size| set during initialization 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); @@ -95,7 +88,6 @@ class WvCasEcm { // // 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 @@ -105,14 +97,9 @@ class WvCasEcm { // - 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 + // - Size of |even_content_iv| and |odd_content_iv| must match + // |content_iv_size| set during initialization 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); diff --git a/media_cas_packager_sdk/public/wv_cas_ecm_test.cc b/media_cas_packager_sdk/public/wv_cas_ecm_test.cc index 7412c6f..96da3ff 100644 --- a/media_cas_packager_sdk/public/wv_cas_ecm_test.cc +++ b/media_cas_packager_sdk/public/wv_cas_ecm_test.cc @@ -9,6 +9,7 @@ #include "media_cas_packager_sdk/public/wv_cas_ecm.h" #include "testing/gmock.h" #include "testing/gunit.h" +#include "absl/strings/escaping.h" using ::testing::Test; @@ -16,11 +17,9 @@ 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 kEvenContentIv8Bytes[] = "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 kOddContentIv8Bytes[] = "oddcont."; // 8 bytes const char kEntitlementKeyId[] = "ent_key_id......"; // 16 bytes const char kEntitlementKey[] = "entitlement_key................."; // 32 bytes @@ -46,7 +45,7 @@ TEST_F(WvCasEcmTest, InitializeContentIvSize) { EXPECT_EQ( util::error::INVALID_ARGUMENT, wv_cas_ecm_ - .Initialize(/* content_iv_size= */ 16, + .Initialize(/* content_iv_size= */ 4, /* key_rotation_enabled= */ true, /* crypto_mode= */ 1) .error_code()); } @@ -66,7 +65,7 @@ TEST_F(WvCasEcmTest, InitializeKeyRotationEnabled) { /* crypto_mode= */ 1)); std::string actual_ecm; EXPECT_EQ(util::error::INTERNAL, - wv_cas_ecm_.GenerateSingleKeyEcm("", "", "", "", "", &actual_ecm) + wv_cas_ecm_.GenerateSingleKeyEcm("", "", "", "", &actual_ecm) .error_code()); } @@ -76,26 +75,7 @@ TEST_F(WvCasEcmTest, InitializeKeyRotationDisabled) { /* 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) + wv_cas_ecm_.GenerateEcm("", "", "", "", "", "", &actual_ecm) .error_code()); } @@ -108,32 +88,14 @@ TEST_F(WvCasEcmTest, GenerateEcmInvalidContentIv) { wv_cas_ecm_ .GenerateEcm( /* even_key= */ kEvenKey, - /* even_wrapping_iv= */ kEvenWrappingIv, - /* even_content_iv= */ kEvenContentIv, + /* even_content_iv= */ kEvenContentIv8Bytes, /* 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, @@ -143,14 +105,13 @@ TEST_F(WvCasEcmTest, GenerateSingleKeyEcmInvalidContentIv) { 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) { +TEST_F(WvCasEcmTest, GenerateEcm8BytesContentIvSuccess) { EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8, /* key_rotation_enabled= */ true, /* crypto_mode= */ 1)); @@ -158,37 +119,22 @@ TEST_F(WvCasEcmTest, GenerateEcmSuccess) { std::string actual_ecm; EXPECT_OK(wv_cas_ecm_.GenerateEcm( /* even_key= */ kEvenKey, - /* even_wrapping_iv= */ kEvenWrappingIv, - /* even_content_iv= */ kEvenContentIv, + /* even_content_iv= */ kEvenContentIv8Bytes, /* odd_key= */ kOddKey, - /* odd_wrapping_iv= */ kOddWrappingIv, - /* odd_content_iv= */ kOddContentIv, + /* odd_content_iv= */ kOddContentIv8Bytes, /* 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); + EXPECT_EQ( + "4ad4010380656e745f6b65795f69642e2e2e2e2e2ea5693deeba52b4cb27e7021eefa2f8" + "c2b25f7d48e60627208f4ecca00703aa2467f28b214546a42320e3fa49f936369c657665" + "6e636f6e74656e745f6b65795f69642e2e2e2e2e2e0700509b67763b3f1c356bc1e1dc8b" + "ac99a1e2f95c37d9183cbb96582f3a05fdbe29925c37c6c6a45eb552b5ddf87f8a6f6464" + "636f6e742e", + absl::BytesToHexString(actual_ecm)); } -TEST_F(WvCasEcmTest, GenerateSingleKeyEcmSuccess) { +TEST_F(WvCasEcmTest, GenerateSingleKeyEcm8BytesContentIvSuccess) { EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 8, /* key_rotation_enabled= */ false, /* crypto_mode= */ 1)); @@ -196,23 +142,60 @@ TEST_F(WvCasEcmTest, GenerateSingleKeyEcmSuccess) { std::string actual_ecm; EXPECT_OK(wv_cas_ecm_.GenerateSingleKeyEcm( /* even_key= */ kEvenKey, - /* even_wrapping_iv= */ kEvenWrappingIv, - /* even_content_iv= */ kEvenContentIv, + /* even_content_iv= */ kEvenContentIv8Bytes, /* 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); + EXPECT_EQ( + "4ad4010280656e745f6b65795f69642e2e2e2e2e2ea5693deeba52b4cb27e7021eefa2f8" + "c2b25f7d48e60627208f4ecca00703aa2467f28b214546a42320e3fa49f936369c657665" + "6e636f6e74", + absl::BytesToHexString(actual_ecm)); +} + +TEST_F(WvCasEcmTest, GenerateEcm16BytesContentIvSuccess) { + EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 16, + /* key_rotation_enabled= */ true, + /* crypto_mode= */ 1)); + + std::string actual_ecm; + EXPECT_OK(wv_cas_ecm_.GenerateEcm( + /* even_key= */ kEvenKey, + /* even_content_iv= */ + absl::StrCat(kEvenContentIv8Bytes, kEvenContentIv8Bytes), + /* odd_key= */ kOddKey, + /* odd_content_iv= */ + absl::StrCat(kOddContentIv8Bytes, kOddContentIv8Bytes), + /* entitlement_key_id= */ kEntitlementKeyId, + /* entitlement_key= */ kEntitlementKey, &actual_ecm)); + + EXPECT_EQ( + "4ad40103c0656e745f6b65795f69642e2e2e2e2e2ea5693deeba52b4cb27e7021eefa2f8" + "c2b25f7d48e60627208f4ecca00703aa2467f28b214546a42320e3fa49f936369c657665" + "6e636f6e746576656e636f6e74656e745f6b65795f69642e2e2e2e2e2e0700509b67763b" + "3f1c356bc1e1dc8bac99a1e2f95c37d9183cbb96582f3a05fdbe29925c37c6c6a45eb552" + "b5ddf87f8a6f6464636f6e742e6f6464636f6e742e", + absl::BytesToHexString(actual_ecm)); +} + +TEST_F(WvCasEcmTest, GenerateSingleKeyEcm16BytesContentIvSuccess) { + EXPECT_OK(wv_cas_ecm_.Initialize(/* content_iv_size= */ 16, + /* key_rotation_enabled= */ false, + /* crypto_mode= */ 1)); + + std::string actual_ecm; + EXPECT_OK(wv_cas_ecm_.GenerateSingleKeyEcm( + /* even_key= */ kEvenKey, + /* even_content_iv= */ + absl::StrCat(kEvenContentIv8Bytes, kEvenContentIv8Bytes), + /* entitlement_key_id= */ kEntitlementKeyId, + /* entitlement_key= */ kEntitlementKey, &actual_ecm)); + + EXPECT_EQ( + "4ad40102c0656e745f6b65795f69642e2e2e2e2e2ea5693deeba52b4cb27e7021eefa2f8" + "c2b25f7d48e60627208f4ecca00703aa2467f28b214546a42320e3fa49f936369c657665" + "6e636f6e746576656e636f6e74", + absl::BytesToHexString(actual_ecm)); } } // namespace cas