Regular update
Plugin: 1. Process ECM v3 and send fingerprinting/service_blocking events 2. Rmove unused function Ctr128Add 3. Add support for ECM v3 OEMCrypto: 1. Update API description of OEMCrypto_LoadCasECMKeys 2. Fix android build files for ODK 3. Load content keys to shared memory 4. Move KCB check to LoadCasKeys call 5. Support even/odd content keys to share entitlement key
This commit is contained in:
@@ -6,6 +6,8 @@ cc_binary {
|
||||
"src/cas_license_test.cpp",
|
||||
"src/crypto_session_test.cpp",
|
||||
"src/ecm_parser_test.cpp",
|
||||
"src/ecm_parser_v2_test.cpp",
|
||||
"src/ecm_parser_v3_test.cpp",
|
||||
"src/test_properties.cpp",
|
||||
"src/widevine_cas_session_test.cpp",
|
||||
"src/cas_session_map_test.cpp",
|
||||
|
||||
@@ -4,49 +4,23 @@
|
||||
|
||||
#include "ecm_parser.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <bitset>
|
||||
#include <tuple>
|
||||
|
||||
#include "media_cas.pb.h"
|
||||
|
||||
namespace wvcas {
|
||||
namespace {
|
||||
|
||||
constexpr int kCasIdSizeBytes = 2;
|
||||
constexpr int kModeSizeBytes = 1;
|
||||
constexpr int kVersionSizeBytes = 1;
|
||||
constexpr int kIVFlagsSizeBytes = 1;
|
||||
constexpr int kEntitlementKeyIDSizeBytes = 16;
|
||||
constexpr int kContentKeyIDSizeBytes = 16;
|
||||
constexpr int kContentKeyDataSize = 16;
|
||||
constexpr int kWrappedKeyIVSizeBytes = 16;
|
||||
constexpr int kEcmHeaderSizeBytes = kCasIdSizeBytes + kVersionSizeBytes;
|
||||
constexpr int kEcmVersion2 = 2;
|
||||
constexpr int kEcmVersion3 = 3;
|
||||
|
||||
constexpr int kEcmDescriptorSizeBytes =
|
||||
kCasIdSizeBytes + kModeSizeBytes + kVersionSizeBytes + kIVFlagsSizeBytes;
|
||||
|
||||
constexpr int kECMVersion = 2;
|
||||
// The cipher mode flags field in the ECM V2 is 4 bits.
|
||||
constexpr uint8_t kAESCBCCryptoModeFlagsVal = (0x0 << 1);
|
||||
constexpr uint8_t kAESCTRCryptoModeFlagsVal = (0x1 << 1);
|
||||
constexpr uint8_t kDvbCsa2CryptoModeFlagsVal = (0x2 << 1);
|
||||
constexpr uint8_t kDvbCsa3CryptoModeFlagsVal = (0x3 << 1);
|
||||
constexpr uint8_t kDvbOFBCryptoModeFlagsVal = (0x4 << 1);
|
||||
constexpr uint8_t kDvbSCTECryptoModeFlagsVal = (0x5 << 1);
|
||||
constexpr uint8_t kRotationFlag = (0x1 << 0);
|
||||
constexpr uint8_t kContentIVSizeFlag = (0x1 << 6);
|
||||
|
||||
constexpr uint8_t kEntitlementKeyIDFill = '1';
|
||||
constexpr uint8_t kEvenContentKeyIDFill = '2';
|
||||
constexpr uint8_t kEvenContentKeyDataFill = '3';
|
||||
constexpr uint8_t kEvenWrappedKeyIVFill = '4';
|
||||
constexpr uint8_t kEvenContentKeyIVFill = '5';
|
||||
constexpr uint8_t kOddContentKeyIDFill = '6';
|
||||
constexpr uint8_t kOddContentKeyDataFill = '7';
|
||||
constexpr uint8_t kOddWrappedKeyIVFill = '8';
|
||||
constexpr uint8_t kOddContentKeyIVFill = '9';
|
||||
|
||||
constexpr size_t kMaxEcmSizeBytes = 184;
|
||||
constexpr size_t kValidEcmV2SizeBytes = 165;
|
||||
|
||||
constexpr uint16_t kSectionHeader1 = 0x80;
|
||||
constexpr uint16_t kSectionHeader2 = 0x81;
|
||||
@@ -56,242 +30,56 @@ constexpr uint16_t kWidevineCasId = 0x4AD4;
|
||||
// New Widevine CAS IDs 0x56C0 to 0x56C9 (all inclusive).
|
||||
constexpr uint16_t kWidevineNewCasIdLowerBound = 0x56C0;
|
||||
constexpr uint16_t kWidevineNewCasIdUpperBound = 0x56C9;
|
||||
} // namespace
|
||||
|
||||
class EcmParserTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() {
|
||||
BuildEcm(kWidevineCasId, /*with_rotation=*/true, /*content_iv_flag=*/false);
|
||||
std::vector<uint8_t> BuildEcm(uint16_t cas_id, uint8_t version) {
|
||||
std::vector<uint8_t> ecm_data;
|
||||
ecm_data.resize(kEcmHeaderSizeBytes);
|
||||
ecm_data[0] = cas_id >> 8;
|
||||
ecm_data[1] = cas_id & 0xff;
|
||||
ecm_data[2] = version;
|
||||
|
||||
// Put some dummy data to make the ECM a valid one.
|
||||
if (version <= 2) {
|
||||
ecm_data.resize(kValidEcmV2SizeBytes);
|
||||
} else {
|
||||
video_widevine::EcmPayload ecm_payload;
|
||||
ecm_payload.mutable_even_key_data()->set_entitlement_key_id("123");
|
||||
video_widevine::SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
ecm_data.resize(ecm_data.size() + signed_ecm_payload.ByteSize());
|
||||
signed_ecm_payload.SerializeToArray(ecm_data.data() + kEcmHeaderSizeBytes,
|
||||
signed_ecm_payload.ByteSize());
|
||||
}
|
||||
size_t ContentKeyIVSize(bool content_iv_flag);
|
||||
size_t CalculateEcmSize(bool with_rotation, bool content_iv_flag = false);
|
||||
void BuildEcm(uint16_t cas_id, bool with_rotation, bool content_iv_flag);
|
||||
|
||||
std::vector<uint8_t> ecm_data_;
|
||||
std::unique_ptr<const wvcas::EcmParser> parser_;
|
||||
};
|
||||
|
||||
size_t EcmParserTest::ContentKeyIVSize(bool content_iv_flag) {
|
||||
// Content key iv is 8 bytes if Content_IV flag is zero, and 16 bytes
|
||||
// othersize.
|
||||
return content_iv_flag ? 16 : 8;
|
||||
return ecm_data;
|
||||
}
|
||||
|
||||
size_t EcmParserTest::CalculateEcmSize(bool with_rotation,
|
||||
bool content_iv_flag) {
|
||||
size_t ecm_key_data_size =
|
||||
kContentKeyIDSizeBytes + kContentKeyDataSize + kWrappedKeyIVSizeBytes +
|
||||
kEntitlementKeyIDSizeBytes + ContentKeyIVSize(content_iv_flag);
|
||||
return kEcmDescriptorSizeBytes + ecm_key_data_size * (with_rotation ? 2 : 1);
|
||||
// Verifies ECM parser can be created with different version.
|
||||
class EcmParserVersionTest : public testing::Test,
|
||||
public ::testing::WithParamInterface<uint8_t> {};
|
||||
|
||||
TEST_P(EcmParserVersionTest, CreateSuccess) {
|
||||
std::vector<uint8_t> ecm_data = BuildEcm(kWidevineCasId, GetParam());
|
||||
ASSERT_TRUE(wvcas::EcmParser::Create(ecm_data) != nullptr);
|
||||
}
|
||||
|
||||
void EcmParserTest::BuildEcm(uint16_t cas_id, bool with_rotation,
|
||||
bool content_iv_flag) {
|
||||
ecm_data_.clear();
|
||||
ecm_data_.reserve(CalculateEcmSize(with_rotation, content_iv_flag));
|
||||
ecm_data_.resize(kCasIdSizeBytes, 0);
|
||||
ecm_data_[0] = cas_id >> 8;
|
||||
ecm_data_[1] = cas_id & 0xff;
|
||||
ecm_data_.resize(ecm_data_.size() + kVersionSizeBytes, kECMVersion);
|
||||
ecm_data_.resize(ecm_data_.size() + kModeSizeBytes,
|
||||
kAESCBCCryptoModeFlagsVal);
|
||||
uint8_t iv_flag_value = content_iv_flag ? kContentIVSizeFlag : 0;
|
||||
ecm_data_.resize(ecm_data_.size() + kIVFlagsSizeBytes, iv_flag_value);
|
||||
ASSERT_EQ(kEcmDescriptorSizeBytes, ecm_data_.size());
|
||||
|
||||
// Even key fields.
|
||||
ecm_data_.resize(ecm_data_.size() + kEntitlementKeyIDSizeBytes,
|
||||
kEntitlementKeyIDFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kContentKeyIDSizeBytes,
|
||||
kEvenContentKeyIDFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kContentKeyDataSize,
|
||||
kEvenContentKeyDataFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kWrappedKeyIVSizeBytes,
|
||||
kEvenWrappedKeyIVFill);
|
||||
ecm_data_.resize(ecm_data_.size() + ContentKeyIVSize(content_iv_flag),
|
||||
kEvenContentKeyIVFill);
|
||||
ASSERT_EQ(CalculateEcmSize(false, content_iv_flag), ecm_data_.size());
|
||||
|
||||
if (with_rotation) {
|
||||
// Entitlement key id field for odd key.
|
||||
ecm_data_.resize(ecm_data_.size() + kEntitlementKeyIDSizeBytes,
|
||||
kEntitlementKeyIDFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kContentKeyIDSizeBytes,
|
||||
kOddContentKeyIDFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kContentKeyDataSize,
|
||||
kOddContentKeyDataFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kWrappedKeyIVSizeBytes,
|
||||
kOddWrappedKeyIVFill);
|
||||
ecm_data_.resize(ecm_data_.size() + ContentKeyIVSize(content_iv_flag),
|
||||
kOddContentKeyIVFill);
|
||||
ASSERT_EQ(CalculateEcmSize(true, content_iv_flag), ecm_data_.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EcmParserTest, FieldsWithoutKeyRotation) {
|
||||
bool content_key_iv_16b = false;
|
||||
ecm_data_.resize(CalculateEcmSize(false, content_key_iv_16b));
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
ASSERT_FALSE(parser_->rotation_enabled());
|
||||
|
||||
std::vector<uint8_t> test_data;
|
||||
test_data.resize(kEntitlementKeyIDSizeBytes, kEntitlementKeyIDFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->entitlement_key_id(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyIDFill);
|
||||
EXPECT_EQ(test_data, parser_->content_key_id(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyDataFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->wrapped_key_data(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kWrappedKeyIVSizeBytes, kEvenWrappedKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->wrapped_key_iv(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(ContentKeyIVSize(content_key_iv_16b), kEvenContentKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->content_iv(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
EXPECT_TRUE(parser_->content_key_id(wvcas::KeySlotId::kOddKeySlot).empty());
|
||||
EXPECT_TRUE(parser_->wrapped_key_data(wvcas::KeySlotId::kOddKeySlot).empty());
|
||||
EXPECT_TRUE(parser_->wrapped_key_iv(wvcas::KeySlotId::kOddKeySlot).empty());
|
||||
EXPECT_TRUE(parser_->content_iv(wvcas::KeySlotId::kOddKeySlot).empty());
|
||||
}
|
||||
|
||||
TEST_F(EcmParserTest, FieldsWithKeyRotation) {
|
||||
ecm_data_[3] |= kRotationFlag;
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
ASSERT_TRUE(parser_->rotation_enabled());
|
||||
|
||||
std::vector<uint8_t> test_data;
|
||||
test_data.resize(kEntitlementKeyIDSizeBytes, kEntitlementKeyIDFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->entitlement_key_id(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyIDFill);
|
||||
EXPECT_EQ(test_data, parser_->content_key_id(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyDataFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->wrapped_key_data(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kWrappedKeyIVSizeBytes, kEvenWrappedKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->wrapped_key_iv(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
bool content_key_iv_16b = false;
|
||||
test_data.resize(ContentKeyIVSize(content_key_iv_16b), kEvenContentKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->content_iv(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kOddContentKeyIDFill);
|
||||
EXPECT_EQ(test_data, parser_->content_key_id(wvcas::KeySlotId::kOddKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kOddContentKeyDataFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->wrapped_key_data(wvcas::KeySlotId::kOddKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kWrappedKeyIVSizeBytes, kOddWrappedKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->wrapped_key_iv(wvcas::KeySlotId::kOddKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(ContentKeyIVSize(content_key_iv_16b), kOddContentKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->content_iv(wvcas::KeySlotId::kOddKeySlot));
|
||||
}
|
||||
|
||||
TEST_F(EcmParserTest, create) {
|
||||
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
|
||||
ecm_data_.resize(4);
|
||||
EXPECT_FALSE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
|
||||
ecm_data_.resize(4 + CalculateEcmSize(false));
|
||||
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
|
||||
ecm_data_.resize(kMaxEcmSizeBytes);
|
||||
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
|
||||
ecm_data_.resize(CalculateEcmSize(true));
|
||||
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_FALSE(wvcas::EcmParser::create(ecm_data_, nullptr));
|
||||
|
||||
ecm_data_.resize(CalculateEcmSize(true));
|
||||
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_FALSE(wvcas::EcmParser::create(ecm_data_, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(EcmParserTest, crypto_mode) {
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kAesCBC);
|
||||
|
||||
ecm_data_[3] = kAESCTRCryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kAesCTR);
|
||||
|
||||
ecm_data_[3] = kDvbCsa2CryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kDvbCsa2);
|
||||
|
||||
ecm_data_[3] = kDvbCsa3CryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kDvbCsa3);
|
||||
|
||||
ecm_data_[3] = kDvbOFBCryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kAesOFB);
|
||||
|
||||
ecm_data_[3] = kDvbSCTECryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kAesSCTE);
|
||||
}
|
||||
|
||||
TEST_F(EcmParserTest, ContentKeyIVSizes) {
|
||||
bool with_rotation = true;
|
||||
bool iv_flag = false;
|
||||
ecm_data_.resize(CalculateEcmSize(with_rotation, iv_flag));
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->content_iv_size(), ContentKeyIVSize(iv_flag));
|
||||
|
||||
iv_flag = true;
|
||||
ecm_data_[4] = kContentIVSizeFlag;
|
||||
ecm_data_.resize(CalculateEcmSize(with_rotation, iv_flag));
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->content_iv_size(), ContentKeyIVSize(iv_flag));
|
||||
}
|
||||
|
||||
TEST_F(EcmParserTest, AgeRestriction) {
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(0, parser_->age_restriction());
|
||||
|
||||
uint8_t age_restriction = 16;
|
||||
ecm_data_[4] |= age_restriction << 1;
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(age_restriction, parser_->age_restriction());
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(EcmParserVersionTest, EcmParserVersionTest,
|
||||
::testing::Values(kEcmVersion2, kEcmVersion3));
|
||||
|
||||
// Verifies CAS ID returned by the parser must be expected ones.
|
||||
class EcmParserCasIdTest
|
||||
: public EcmParserTest,
|
||||
public ::testing::WithParamInterface<::testing::tuple<uint16_t, bool>> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const uint16_t cas_id = ::testing::get<0>(GetParam());
|
||||
BuildEcm(cas_id, /*with_rotation=*/true, /*content_iv_flag=*/false);
|
||||
}
|
||||
};
|
||||
: public testing::Test,
|
||||
public ::testing::WithParamInterface<::testing::tuple<uint16_t, bool>> {};
|
||||
|
||||
TEST_P(EcmParserCasIdTest, ValidateCasIds) {
|
||||
bool expected_result = ::testing::get<1>(GetParam());
|
||||
ASSERT_EQ(wvcas::EcmParser::create(ecm_data_, &parser_), expected_result);
|
||||
const uint16_t cas_id = ::testing::get<0>(GetParam());
|
||||
std::vector<uint8_t> ecm_data = BuildEcm(cas_id, kEcmVersion2);
|
||||
|
||||
const bool is_valid_id = ::testing::get<1>(GetParam());
|
||||
if (is_valid_id) {
|
||||
ASSERT_TRUE(wvcas::EcmParser::Create(ecm_data) != nullptr);
|
||||
} else {
|
||||
ASSERT_TRUE(wvcas::EcmParser::Create(ecm_data) == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(EcmWithLegacyCasId, EcmParserCasIdTest,
|
||||
@@ -315,23 +103,28 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
|
||||
// Verifies Section header and pointer field may be prepended to ECM.
|
||||
class EcmParserSectionHeaderTest
|
||||
: public EcmParserTest,
|
||||
: public testing::Test,
|
||||
public ::testing::WithParamInterface<uint8_t> {};
|
||||
|
||||
TEST_P(EcmParserSectionHeaderTest, EcmWithSectionHeaderOnly) {
|
||||
std::vector<uint8_t> ecm_data = BuildEcm(kWidevineCasId, kEcmVersion2);
|
||||
const std::vector<uint8_t> section_header = {GetParam(), 0, 0};
|
||||
ecm_data_.insert(ecm_data_.begin(), section_header.begin(),
|
||||
section_header.end());
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
ecm_data.insert(ecm_data.begin(), section_header.begin(),
|
||||
section_header.end());
|
||||
ASSERT_TRUE(wvcas::EcmParser::Create(ecm_data) != nullptr);
|
||||
}
|
||||
|
||||
TEST_P(EcmParserSectionHeaderTest, EcmWithSectionHeaderAndPointerField) {
|
||||
std::vector<uint8_t> ecm_data = BuildEcm(kWidevineCasId, kEcmVersion2);
|
||||
const std::vector<uint8_t> section_header = {kPointerFieldZero, GetParam(), 0,
|
||||
0};
|
||||
ecm_data_.insert(ecm_data_.begin(), section_header.begin(),
|
||||
section_header.end());
|
||||
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser_));
|
||||
ecm_data.insert(ecm_data.begin(), section_header.begin(),
|
||||
section_header.end());
|
||||
ASSERT_TRUE(wvcas::EcmParser::Create(ecm_data) != nullptr);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(EcmWithSectionHeader, EcmParserSectionHeaderTest,
|
||||
::testing::Values(kSectionHeader1, kSectionHeader2));
|
||||
|
||||
} // namespace
|
||||
} // namespace wvcas
|
||||
270
tests/src/ecm_parser_v2_test.cpp
Normal file
270
tests/src/ecm_parser_v2_test.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#include "ecm_parser_v2.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kCasIdSizeBytes = 2;
|
||||
constexpr int kModeSizeBytes = 1;
|
||||
constexpr int kVersionSizeBytes = 1;
|
||||
constexpr int kIVFlagsSizeBytes = 1;
|
||||
constexpr int kEntitlementKeyIDSizeBytes = 16;
|
||||
constexpr int kContentKeyIDSizeBytes = 16;
|
||||
constexpr int kContentKeyDataSize = 16;
|
||||
constexpr int kWrappedKeyIVSizeBytes = 16;
|
||||
|
||||
constexpr int kEcmDescriptorSizeBytes =
|
||||
kCasIdSizeBytes + kModeSizeBytes + kVersionSizeBytes + kIVFlagsSizeBytes;
|
||||
|
||||
constexpr int kECMVersion = 2;
|
||||
// The cipher mode flags field in the ECM V2 is 4 bits.
|
||||
constexpr uint8_t kAESCBCCryptoModeFlagsVal = (0x0 << 1);
|
||||
constexpr uint8_t kAESCTRCryptoModeFlagsVal = (0x1 << 1);
|
||||
constexpr uint8_t kDvbCsa2CryptoModeFlagsVal = (0x2 << 1);
|
||||
constexpr uint8_t kDvbCsa3CryptoModeFlagsVal = (0x3 << 1);
|
||||
constexpr uint8_t kDvbOFBCryptoModeFlagsVal = (0x4 << 1);
|
||||
constexpr uint8_t kDvbSCTECryptoModeFlagsVal = (0x5 << 1);
|
||||
constexpr uint8_t kRotationFlag = (0x1 << 0);
|
||||
constexpr uint8_t kContentIVSizeFlag = (0x1 << 6);
|
||||
|
||||
constexpr uint8_t kEntitlementKeyIDFill = '1';
|
||||
constexpr uint8_t kEvenContentKeyIDFill = '2';
|
||||
constexpr uint8_t kEvenContentKeyDataFill = '3';
|
||||
constexpr uint8_t kEvenWrappedKeyIVFill = '4';
|
||||
constexpr uint8_t kEvenContentKeyIVFill = '5';
|
||||
constexpr uint8_t kOddContentKeyIDFill = '6';
|
||||
constexpr uint8_t kOddContentKeyDataFill = '7';
|
||||
constexpr uint8_t kOddWrappedKeyIVFill = '8';
|
||||
constexpr uint8_t kOddContentKeyIVFill = '9';
|
||||
|
||||
constexpr size_t kMaxEcmSizeBytes = 184;
|
||||
constexpr uint16_t kWidevineCasId = 0x4AD4;
|
||||
|
||||
} // namespace
|
||||
|
||||
class EcmParserV2Test : public testing::Test {
|
||||
protected:
|
||||
void SetUp() { BuildEcm(/*with_rotation=*/true, /*content_iv_flag=*/false); }
|
||||
size_t ContentKeyIVSize(bool content_iv_flag);
|
||||
size_t CalculateEcmSize(bool with_rotation, bool content_iv_flag = false);
|
||||
void BuildEcm(bool with_rotation, bool content_iv_flag);
|
||||
|
||||
std::vector<uint8_t> ecm_data_;
|
||||
std::unique_ptr<const wvcas::EcmParserV2> parser_;
|
||||
};
|
||||
|
||||
size_t EcmParserV2Test::ContentKeyIVSize(bool content_iv_flag) {
|
||||
// Content key iv is 8 bytes if Content_IV flag is zero, and 16 bytes
|
||||
// othersize.
|
||||
return content_iv_flag ? 16 : 8;
|
||||
}
|
||||
|
||||
size_t EcmParserV2Test::CalculateEcmSize(bool with_rotation,
|
||||
bool content_iv_flag) {
|
||||
size_t ecm_key_data_size =
|
||||
kContentKeyIDSizeBytes + kContentKeyDataSize + kWrappedKeyIVSizeBytes +
|
||||
kEntitlementKeyIDSizeBytes + ContentKeyIVSize(content_iv_flag);
|
||||
return kEcmDescriptorSizeBytes + ecm_key_data_size * (with_rotation ? 2 : 1);
|
||||
}
|
||||
|
||||
void EcmParserV2Test::BuildEcm(bool with_rotation, bool content_iv_flag) {
|
||||
ecm_data_.clear();
|
||||
ecm_data_.reserve(CalculateEcmSize(with_rotation, content_iv_flag));
|
||||
ecm_data_.resize(kCasIdSizeBytes, 0);
|
||||
ecm_data_[0] = kWidevineCasId >> 8;
|
||||
ecm_data_[1] = kWidevineCasId & 0xff;
|
||||
ecm_data_.resize(ecm_data_.size() + kVersionSizeBytes, kECMVersion);
|
||||
ecm_data_.resize(ecm_data_.size() + kModeSizeBytes,
|
||||
kAESCBCCryptoModeFlagsVal);
|
||||
uint8_t iv_flag_value = content_iv_flag ? kContentIVSizeFlag : 0;
|
||||
ecm_data_.resize(ecm_data_.size() + kIVFlagsSizeBytes, iv_flag_value);
|
||||
ASSERT_EQ(kEcmDescriptorSizeBytes, ecm_data_.size());
|
||||
|
||||
// Even key fields.
|
||||
ecm_data_.resize(ecm_data_.size() + kEntitlementKeyIDSizeBytes,
|
||||
kEntitlementKeyIDFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kContentKeyIDSizeBytes,
|
||||
kEvenContentKeyIDFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kContentKeyDataSize,
|
||||
kEvenContentKeyDataFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kWrappedKeyIVSizeBytes,
|
||||
kEvenWrappedKeyIVFill);
|
||||
ecm_data_.resize(ecm_data_.size() + ContentKeyIVSize(content_iv_flag),
|
||||
kEvenContentKeyIVFill);
|
||||
ASSERT_EQ(CalculateEcmSize(false, content_iv_flag), ecm_data_.size());
|
||||
|
||||
if (with_rotation) {
|
||||
// Entitlement key id field for odd key.
|
||||
ecm_data_.resize(ecm_data_.size() + kEntitlementKeyIDSizeBytes,
|
||||
kEntitlementKeyIDFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kContentKeyIDSizeBytes,
|
||||
kOddContentKeyIDFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kContentKeyDataSize,
|
||||
kOddContentKeyDataFill);
|
||||
ecm_data_.resize(ecm_data_.size() + kWrappedKeyIVSizeBytes,
|
||||
kOddWrappedKeyIVFill);
|
||||
ecm_data_.resize(ecm_data_.size() + ContentKeyIVSize(content_iv_flag),
|
||||
kOddContentKeyIVFill);
|
||||
ASSERT_EQ(CalculateEcmSize(true, content_iv_flag), ecm_data_.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EcmParserV2Test, FieldsWithoutKeyRotation) {
|
||||
bool content_key_iv_16b = false;
|
||||
ecm_data_.resize(CalculateEcmSize(false, content_key_iv_16b));
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
ASSERT_FALSE(parser_->rotation_enabled());
|
||||
|
||||
std::vector<uint8_t> test_data;
|
||||
test_data.resize(kEntitlementKeyIDSizeBytes, kEntitlementKeyIDFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->entitlement_key_id(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyIDFill);
|
||||
EXPECT_EQ(test_data, parser_->content_key_id(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyDataFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->wrapped_key_data(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kWrappedKeyIVSizeBytes, kEvenWrappedKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->wrapped_key_iv(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(ContentKeyIVSize(content_key_iv_16b), kEvenContentKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->content_iv(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
EXPECT_TRUE(parser_->content_key_id(wvcas::KeySlotId::kOddKeySlot).empty());
|
||||
EXPECT_TRUE(parser_->wrapped_key_data(wvcas::KeySlotId::kOddKeySlot).empty());
|
||||
EXPECT_TRUE(parser_->wrapped_key_iv(wvcas::KeySlotId::kOddKeySlot).empty());
|
||||
EXPECT_TRUE(parser_->content_iv(wvcas::KeySlotId::kOddKeySlot).empty());
|
||||
}
|
||||
|
||||
TEST_F(EcmParserV2Test, FieldsWithKeyRotation) {
|
||||
ecm_data_[3] |= kRotationFlag;
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
ASSERT_TRUE(parser_->rotation_enabled());
|
||||
|
||||
std::vector<uint8_t> test_data;
|
||||
test_data.resize(kEntitlementKeyIDSizeBytes, kEntitlementKeyIDFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->entitlement_key_id(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyIDFill);
|
||||
EXPECT_EQ(test_data, parser_->content_key_id(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyDataFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->wrapped_key_data(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kWrappedKeyIVSizeBytes, kEvenWrappedKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->wrapped_key_iv(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
bool content_key_iv_16b = false;
|
||||
test_data.resize(ContentKeyIVSize(content_key_iv_16b), kEvenContentKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->content_iv(wvcas::KeySlotId::kEvenKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kOddContentKeyIDFill);
|
||||
EXPECT_EQ(test_data, parser_->content_key_id(wvcas::KeySlotId::kOddKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kContentKeyIDSizeBytes, kOddContentKeyDataFill);
|
||||
EXPECT_EQ(test_data,
|
||||
parser_->wrapped_key_data(wvcas::KeySlotId::kOddKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(kWrappedKeyIVSizeBytes, kOddWrappedKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->wrapped_key_iv(wvcas::KeySlotId::kOddKeySlot));
|
||||
|
||||
test_data.clear();
|
||||
test_data.resize(ContentKeyIVSize(content_key_iv_16b), kOddContentKeyIVFill);
|
||||
EXPECT_EQ(test_data, parser_->content_iv(wvcas::KeySlotId::kOddKeySlot));
|
||||
}
|
||||
|
||||
TEST_F(EcmParserV2Test, create) {
|
||||
EXPECT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
|
||||
ecm_data_.resize(4);
|
||||
EXPECT_FALSE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
|
||||
ecm_data_.resize(4 + CalculateEcmSize(false));
|
||||
EXPECT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
|
||||
ecm_data_.resize(kMaxEcmSizeBytes);
|
||||
EXPECT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
|
||||
ecm_data_.resize(CalculateEcmSize(true));
|
||||
EXPECT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_FALSE(wvcas::EcmParserV2::create(ecm_data_, nullptr));
|
||||
|
||||
ecm_data_.resize(CalculateEcmSize(true));
|
||||
EXPECT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_FALSE(wvcas::EcmParserV2::create(ecm_data_, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(EcmParserV2Test, crypto_mode) {
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kAesCBC);
|
||||
|
||||
ecm_data_[3] = kAESCTRCryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kAesCTR);
|
||||
|
||||
ecm_data_[3] = kDvbCsa2CryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kDvbCsa2);
|
||||
|
||||
ecm_data_[3] = kDvbCsa3CryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kDvbCsa3);
|
||||
|
||||
ecm_data_[3] = kDvbOFBCryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kAesOFB);
|
||||
|
||||
ecm_data_[3] = kDvbSCTECryptoModeFlagsVal;
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->crypto_mode(), wvcas::CryptoMode::kAesSCTE);
|
||||
}
|
||||
|
||||
TEST_F(EcmParserV2Test, ContentKeyIVSizes) {
|
||||
bool with_rotation = true;
|
||||
bool iv_flag = false;
|
||||
ecm_data_.resize(CalculateEcmSize(with_rotation, iv_flag));
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->content_iv_size(), ContentKeyIVSize(iv_flag));
|
||||
|
||||
iv_flag = true;
|
||||
ecm_data_[4] = kContentIVSizeFlag;
|
||||
ecm_data_.resize(CalculateEcmSize(with_rotation, iv_flag));
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->content_iv_size(), ContentKeyIVSize(iv_flag));
|
||||
}
|
||||
|
||||
TEST_F(EcmParserV2Test, AgeRestriction) {
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(0, parser_->age_restriction());
|
||||
|
||||
uint8_t age_restriction = 16;
|
||||
ecm_data_[4] |= age_restriction << 1;
|
||||
ASSERT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(age_restriction, parser_->age_restriction());
|
||||
}
|
||||
|
||||
TEST_F(EcmParserV2Test, Version) {
|
||||
EXPECT_TRUE(wvcas::EcmParserV2::create(ecm_data_, &parser_));
|
||||
EXPECT_EQ(parser_->version(), kECMVersion);
|
||||
}
|
||||
291
tests/src/ecm_parser_v3_test.cpp
Normal file
291
tests/src/ecm_parser_v3_test.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#include "ecm_parser_v3.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "media_cas.pb.h"
|
||||
|
||||
namespace wvcas {
|
||||
namespace {
|
||||
|
||||
using video_widevine::EcmMetaData;
|
||||
using video_widevine::EcmPayload;
|
||||
using video_widevine::SignedEcmPayload;
|
||||
|
||||
constexpr int kEcmVersion = 3;
|
||||
constexpr uint16_t kWidevineCasId = 0x4AD4;
|
||||
constexpr int kEcmHeaderSizeByte = 3;
|
||||
|
||||
constexpr char kWrappedKeyIv[] = "wrapped_key_iv..";
|
||||
constexpr char kWrappedKeyIv2[] = "wrapped_key_iv.2";
|
||||
constexpr char kEntitlementId[] = "entitlement_id..";
|
||||
constexpr char kEntitlementId2[] = "entitlement_id.2";
|
||||
constexpr char kContentIv[] = "c_iv....c_iv....";
|
||||
constexpr char kContentIv2[] = "c_iv....c_iv...2";
|
||||
constexpr char kWrappedContentKey[] = "wrapped_key.....";
|
||||
constexpr char kWrappedContentKey2[] = "wrapped_key....2";
|
||||
|
||||
void WriteEcmHeader(std::vector<uint8_t>* ecm) {
|
||||
ecm->push_back(kWidevineCasId >> 8);
|
||||
ecm->push_back(kWidevineCasId & 0xff);
|
||||
ecm->push_back(kEcmVersion);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GenerateEcm(const SignedEcmPayload& signed_ecm_payload) {
|
||||
std::vector<uint8_t> ecm;
|
||||
WriteEcmHeader(&ecm);
|
||||
ecm.resize(kEcmHeaderSizeByte + signed_ecm_payload.ByteSize());
|
||||
signed_ecm_payload.SerializeToArray(ecm.data() + kEcmHeaderSizeByte,
|
||||
signed_ecm_payload.ByteSize());
|
||||
return ecm;
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, CreateWithEmptyEcmFail) {
|
||||
std::vector<uint8_t> ecm;
|
||||
EXPECT_TRUE(EcmParserV3::Create(ecm) == nullptr);
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, CreateWithOnlyEcmHeaderFail) {
|
||||
std::vector<uint8_t> ecm;
|
||||
WriteEcmHeader(&ecm);
|
||||
EXPECT_TRUE(EcmParserV3::Create(ecm) == nullptr);
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, CreateWithInvalidSignedEcmPayloadFail) {
|
||||
std::vector<uint8_t> ecm;
|
||||
WriteEcmHeader(&ecm);
|
||||
// appends some chars as payload
|
||||
ecm.resize(100, 'x');
|
||||
|
||||
EXPECT_TRUE(EcmParserV3::Create(ecm) == nullptr);
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, CreateWithInvalidSerializedEcmFail) {
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload("invalid");
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
EXPECT_TRUE(EcmParserV3::Create(ecm) == nullptr);
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, CreateWithEvenKeySuccess) {
|
||||
EcmPayload ecm_payload;
|
||||
ecm_payload.mutable_even_key_data()->set_entitlement_key_id(kEntitlementId);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_data(kWrappedContentKey);
|
||||
ecm_payload.mutable_even_key_data()->set_content_iv(kContentIv);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_iv(kWrappedKeyIv);
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->version(), kEcmVersion);
|
||||
EXPECT_EQ(parser->age_restriction(), 0);
|
||||
EXPECT_EQ(parser->crypto_mode(), CryptoMode::kInvalid);
|
||||
EXPECT_FALSE(parser->has_fingerprinting());
|
||||
EXPECT_FALSE(parser->has_service_blocking());
|
||||
EXPECT_EQ(parser->ecm_serialized_payload(), ecm_payload.SerializeAsString());
|
||||
EXPECT_TRUE(parser->signature().empty());
|
||||
EXPECT_FALSE(parser->rotation_enabled());
|
||||
EXPECT_EQ(parser->content_iv_size(), 16);
|
||||
std::vector<uint8_t> result =
|
||||
parser->entitlement_key_id(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kEntitlementId);
|
||||
result = parser->wrapped_key_data(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedContentKey);
|
||||
result = parser->content_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv);
|
||||
result = parser->wrapped_key_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv);
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, CreateWithEvenOddKeysSuccess) {
|
||||
EcmPayload ecm_payload;
|
||||
ecm_payload.mutable_even_key_data()->set_entitlement_key_id(kEntitlementId);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_data(kWrappedContentKey);
|
||||
ecm_payload.mutable_even_key_data()->set_content_iv(kContentIv);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_iv(kWrappedKeyIv);
|
||||
ecm_payload.mutable_odd_key_data()->set_entitlement_key_id(kEntitlementId2);
|
||||
ecm_payload.mutable_odd_key_data()->set_wrapped_key_data(kWrappedContentKey2);
|
||||
ecm_payload.mutable_odd_key_data()->set_content_iv(kContentIv2);
|
||||
ecm_payload.mutable_odd_key_data()->set_wrapped_key_iv(kWrappedKeyIv2);
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->rotation_enabled());
|
||||
std::vector<uint8_t> result =
|
||||
parser->entitlement_key_id(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kEntitlementId);
|
||||
result = parser->wrapped_key_data(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedContentKey);
|
||||
result = parser->content_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv);
|
||||
result = parser->wrapped_key_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv);
|
||||
result = parser->entitlement_key_id(KeySlotId::kOddKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kEntitlementId2);
|
||||
result = parser->wrapped_key_data(KeySlotId::kOddKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedContentKey2);
|
||||
result = parser->content_iv(KeySlotId::kOddKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv2);
|
||||
result = parser->wrapped_key_iv(KeySlotId::kOddKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv2);
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, CreateWithOmittedOddKeyFieldsSuccess) {
|
||||
EcmPayload ecm_payload;
|
||||
ecm_payload.mutable_even_key_data()->set_entitlement_key_id(kEntitlementId);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_data(kWrappedContentKey);
|
||||
ecm_payload.mutable_even_key_data()->set_content_iv(kContentIv);
|
||||
ecm_payload.mutable_even_key_data()->set_wrapped_key_iv(kWrappedKeyIv);
|
||||
ecm_payload.mutable_odd_key_data()->set_wrapped_key_data(kWrappedContentKey2);
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->rotation_enabled());
|
||||
std::vector<uint8_t> result =
|
||||
parser->entitlement_key_id(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kEntitlementId);
|
||||
result = parser->wrapped_key_data(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedContentKey);
|
||||
result = parser->content_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv);
|
||||
result = parser->wrapped_key_iv(KeySlotId::kEvenKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv);
|
||||
result = parser->entitlement_key_id(KeySlotId::kOddKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kEntitlementId);
|
||||
result = parser->wrapped_key_data(KeySlotId::kOddKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedContentKey2);
|
||||
result = parser->content_iv(KeySlotId::kOddKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv);
|
||||
result = parser->wrapped_key_iv(KeySlotId::kOddKeySlot);
|
||||
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv);
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, AgeRestrictionSuccess) {
|
||||
const int expected_age_restriction = 3;
|
||||
EcmPayload ecm_payload;
|
||||
ecm_payload.mutable_meta_data()->set_age_restriction(
|
||||
expected_age_restriction);
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->age_restriction(), expected_age_restriction);
|
||||
}
|
||||
|
||||
class EcmParserV3AgeRestrictionTest
|
||||
: public testing::Test,
|
||||
public testing::WithParamInterface<uint8_t> {};
|
||||
|
||||
TEST_P(EcmParserV3AgeRestrictionTest, ExpectedAgeRestriction) {
|
||||
const uint8_t expected_age_restriction = GetParam();
|
||||
EcmPayload ecm_payload;
|
||||
ecm_payload.mutable_meta_data()->set_age_restriction(
|
||||
expected_age_restriction);
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->age_restriction(), expected_age_restriction);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(EcmParserV3AgeRestrictionTest,
|
||||
EcmParserV3AgeRestrictionTest,
|
||||
testing::Values(0, 3, 18));
|
||||
|
||||
class EcmParserV3CipherModeTest
|
||||
: public testing::Test,
|
||||
public testing::WithParamInterface<
|
||||
testing::tuple<CryptoMode, EcmMetaData::CipherMode>> {};
|
||||
|
||||
TEST_P(EcmParserV3CipherModeTest, ExpectedCipherMode) {
|
||||
const CryptoMode expected = std::get<0>(GetParam());
|
||||
EcmPayload ecm_payload;
|
||||
ecm_payload.mutable_meta_data()->set_cipher_mode(std::get<1>(GetParam()));
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->crypto_mode(), expected);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
EcmParserV3CipherModeTest, EcmParserV3CipherModeTest,
|
||||
testing::Values(
|
||||
std::make_tuple(CryptoMode::kAesCBC, EcmMetaData::AES_CBC),
|
||||
std::make_tuple(CryptoMode::kAesCTR, EcmMetaData::AES_CTR),
|
||||
std::make_tuple(CryptoMode::kDvbCsa2, EcmMetaData::DVB_CSA2),
|
||||
std::make_tuple(CryptoMode::kDvbCsa3, EcmMetaData::DVB_CSA3),
|
||||
std::make_tuple(CryptoMode::kAesOFB, EcmMetaData::AES_OFB),
|
||||
std::make_tuple(CryptoMode::kAesSCTE, EcmMetaData::AES_SCTE52)));
|
||||
|
||||
TEST(EcmParserV3Test, FingerprintingSuccess) {
|
||||
EcmPayload ecm_payload;
|
||||
ecm_payload.mutable_fingerprinting()->set_control("control");
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->has_fingerprinting());
|
||||
EXPECT_EQ(parser->fingerprinting().SerializeAsString(),
|
||||
ecm_payload.fingerprinting().SerializeAsString());
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, ServiceBlockingSuccess) {
|
||||
EcmPayload ecm_payload;
|
||||
ecm_payload.mutable_service_blocking()->add_device_groups("group");
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_serialized_payload(ecm_payload.SerializeAsString());
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_TRUE(parser->has_service_blocking());
|
||||
EXPECT_EQ(parser->service_blocking().SerializeAsString(),
|
||||
ecm_payload.service_blocking().SerializeAsString());
|
||||
}
|
||||
|
||||
TEST(EcmParserV3Test, SignatureSuccess) {
|
||||
const std::string expected_signature = "signature";
|
||||
SignedEcmPayload signed_ecm_payload;
|
||||
signed_ecm_payload.set_signature(expected_signature);
|
||||
std::vector<uint8_t> ecm = GenerateEcm(signed_ecm_payload);
|
||||
|
||||
std::unique_ptr<const EcmParserV3> parser = EcmParserV3::Create(ecm);
|
||||
|
||||
ASSERT_TRUE(parser != nullptr);
|
||||
EXPECT_EQ(parser->signature(), expected_signature);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace wvcas
|
||||
@@ -115,3 +115,6 @@ TEST(IntegrationTests, TestSessionEventPassing) {
|
||||
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestSessionEventPassing"));
|
||||
}
|
||||
|
||||
TEST(IntegrationTests, TestProcessEcmV3) {
|
||||
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestProcessEcmV3"));
|
||||
}
|
||||
|
||||
@@ -91,6 +91,14 @@ class MockEventListener : public wvcas::CasEventListener {
|
||||
MOCK_METHOD2(OnAgeRestrictionUpdated,
|
||||
void(const wvcas::WvCasSessionId& sessionId,
|
||||
uint8_t ecm_age_restriction));
|
||||
MOCK_METHOD(void, OnSessionFingerprintingUpdated,
|
||||
(const int32_t& sessionId,
|
||||
const std::vector<uint8_t>& fingerprinting),
|
||||
(override));
|
||||
MOCK_METHOD(void, OnSessionServiceBlockingUpdated,
|
||||
(const int32_t& sessionId,
|
||||
const std::vector<uint8_t>& service_blocking),
|
||||
(override));
|
||||
};
|
||||
|
||||
class TestablePolicyEngine : public wvcas::PolicyEngine {
|
||||
|
||||
@@ -85,6 +85,14 @@ class MockEventListener : public wvcas::CasEventListener {
|
||||
MOCK_METHOD2(OnAgeRestrictionUpdated,
|
||||
void(const wvcas::WvCasSessionId& sessionId,
|
||||
uint8_t ecm_age_restriction));
|
||||
MOCK_METHOD(void, OnSessionFingerprintingUpdated,
|
||||
(const int32_t& sessionId,
|
||||
const std::vector<uint8_t>& fingerprinting),
|
||||
(override));
|
||||
MOCK_METHOD(void, OnSessionServiceBlockingUpdated,
|
||||
(const int32_t& sessionId,
|
||||
const std::vector<uint8_t>& service_blocking),
|
||||
(override));
|
||||
};
|
||||
typedef StrictMock<MockEventListener> StrictMockEventListener;
|
||||
|
||||
@@ -125,19 +133,9 @@ class MockFileSystem : public wvutil::FileSystem {
|
||||
typedef NiceMock<MockFileSystem> NiceMockFileSystem;
|
||||
|
||||
class MockWidevineSession : public wvcas::WidevineCasSession {
|
||||
class EcmParser : public wvcas::EcmParser {
|
||||
public:
|
||||
EcmParser() {}
|
||||
~EcmParser() override {}
|
||||
};
|
||||
|
||||
public:
|
||||
MockWidevineSession() {}
|
||||
~MockWidevineSession() override {}
|
||||
std::unique_ptr<const wvcas::EcmParser> getEcmParser(
|
||||
const wvcas::CasEcm& ecm) const override {
|
||||
return make_unique<EcmParser>();
|
||||
}
|
||||
MOCK_METHOD2(processEcm, wvcas::CasStatus(const wvcas::CasEcm& ecm,
|
||||
uint8_t parental_control_age));
|
||||
MOCK_METHOD2(HandleProcessEcm,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "widevine_cas_session.h"
|
||||
|
||||
#include <cas_events.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -11,10 +12,13 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cas_types.h"
|
||||
#include "cas_util.h"
|
||||
#include "media_cas.pb.h"
|
||||
#include "mock_crypto_session.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
namespace {
|
||||
using ::testing::_;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::Eq;
|
||||
@@ -109,20 +113,24 @@ MATCHER(IsValidKeyOddSlotData, "") {
|
||||
|
||||
class MockEcmParser : public wvcas::EcmParser {
|
||||
public:
|
||||
MOCK_CONST_METHOD0(sequence_count, uint8_t());
|
||||
MOCK_CONST_METHOD0(version, uint8_t());
|
||||
MOCK_CONST_METHOD0(age_restriction, uint8_t());
|
||||
MOCK_CONST_METHOD0(crypto_mode, wvcas::CryptoMode());
|
||||
MOCK_CONST_METHOD0(rotation_enabled, bool());
|
||||
MOCK_CONST_METHOD0(content_iv_size, size_t());
|
||||
MOCK_CONST_METHOD1(entitlement_key_id,
|
||||
const std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD1(content_key_id,
|
||||
const std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD1(content_key_id, std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD1(wrapped_key_data,
|
||||
const std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD1(wrapped_key_iv,
|
||||
const std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD1(content_iv,
|
||||
const std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD1(wrapped_key_iv, std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD1(content_iv, std::vector<uint8_t>(wvcas::KeySlotId id));
|
||||
MOCK_CONST_METHOD0(has_fingerprinting, bool());
|
||||
MOCK_CONST_METHOD0(fingerprinting, video_widevine::Fingerprinting());
|
||||
MOCK_CONST_METHOD0(has_service_blocking, bool());
|
||||
MOCK_CONST_METHOD0(service_blocking, video_widevine::ServiceBlocking());
|
||||
MOCK_CONST_METHOD0(ecm_serialized_payload, std::string());
|
||||
MOCK_CONST_METHOD0(signature, std::string());
|
||||
};
|
||||
|
||||
class CasSessionTest : public ::testing::Test {
|
||||
@@ -138,7 +146,7 @@ class TestCasSession : public wvcas::WidevineCasSession {
|
||||
virtual ~TestCasSession() {}
|
||||
|
||||
std::unique_ptr<const wvcas::EcmParser> getEcmParser(
|
||||
const wvcas::CasEcm& ecm) const;
|
||||
const wvcas::CasEcm& ecm) const override;
|
||||
|
||||
std::vector<uint8_t> entitlement_key_id(wvcas::KeySlotId id) const {
|
||||
std::string key_id;
|
||||
@@ -194,15 +202,30 @@ class TestCasSession : public wvcas::WidevineCasSession {
|
||||
age_restriction_ = age_restriction;
|
||||
}
|
||||
|
||||
void set_fingerprinting_control(const std::string& control) {
|
||||
fingerprinting_.clear_control();
|
||||
if (!control.empty()) {
|
||||
fingerprinting_.set_control(control);
|
||||
}
|
||||
}
|
||||
|
||||
void set_service_blocking_groups(const std::vector<std::string>& groups) {
|
||||
service_blocking_.clear_device_groups();
|
||||
for (auto const& group : groups) {
|
||||
service_blocking_.add_device_groups(group);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t age_restriction_ = 0;
|
||||
video_widevine::Fingerprinting fingerprinting_;
|
||||
video_widevine::ServiceBlocking service_blocking_;
|
||||
};
|
||||
|
||||
std::unique_ptr<const wvcas::EcmParser> TestCasSession::getEcmParser(
|
||||
const wvcas::CasEcm& ecm) const {
|
||||
std::unique_ptr<NiceMock<MockEcmParser>> mock_ecm_parser(
|
||||
new NiceMock<MockEcmParser>);
|
||||
ON_CALL(*mock_ecm_parser, sequence_count()).WillByDefault(Return(0));
|
||||
ON_CALL(*mock_ecm_parser, age_restriction())
|
||||
.WillByDefault(Return(age_restriction_));
|
||||
ON_CALL(*mock_ecm_parser, crypto_mode())
|
||||
@@ -218,6 +241,14 @@ std::unique_ptr<const wvcas::EcmParser> TestCasSession::getEcmParser(
|
||||
.WillByDefault(Invoke(this, &TestCasSession::wrapped_key_iv));
|
||||
ON_CALL(*mock_ecm_parser, content_iv(_))
|
||||
.WillByDefault(Invoke(this, &TestCasSession::content_iv));
|
||||
ON_CALL(*mock_ecm_parser, has_fingerprinting())
|
||||
.WillByDefault(Return(fingerprinting_.has_control()));
|
||||
ON_CALL(*mock_ecm_parser, fingerprinting())
|
||||
.WillByDefault(Return(fingerprinting_));
|
||||
ON_CALL(*mock_ecm_parser, has_service_blocking())
|
||||
.WillByDefault(Return(service_blocking_.device_groups_size() > 0));
|
||||
ON_CALL(*mock_ecm_parser, service_blocking())
|
||||
.WillByDefault(Return(service_blocking_));
|
||||
return std::unique_ptr<const wvcas::EcmParser>(mock_ecm_parser.release());
|
||||
}
|
||||
|
||||
@@ -231,7 +262,8 @@ TEST_F(CasSessionTest, processEcm) {
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kEntitledKeySessionId),
|
||||
Return(wvcas::CasStatusCode::kNoError)));
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
session.initialize(mock, &session_id).status_code());
|
||||
session.initialize(mock, /*event_listener=*/nullptr, &session_id)
|
||||
.status_code());
|
||||
EXPECT_EQ(session_id, kEntitledKeySessionId);
|
||||
|
||||
wvcas::CasEcm ecm(184);
|
||||
@@ -250,7 +282,8 @@ TEST_F(CasSessionTest, parentalControl) {
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kEntitledKeySessionId),
|
||||
Return(wvcas::CasStatusCode::kNoError)));
|
||||
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
|
||||
session.initialize(mock, &session_id).status_code());
|
||||
session.initialize(mock, /*event_listener=*/nullptr, &session_id)
|
||||
.status_code());
|
||||
EXPECT_EQ(session_id, kEntitledKeySessionId);
|
||||
|
||||
EXPECT_CALL(*mock, LoadCasECMKeys(session_id, IsValidKeyEvenSlotData(),
|
||||
@@ -281,3 +314,141 @@ TEST_F(CasSessionTest, parentalControl) {
|
||||
session.processEcm(ecm, 3).status_code());
|
||||
EXPECT_CALL(*mock, RemoveEntitledKeySession(session_id));
|
||||
}
|
||||
|
||||
class MockEventListener : public wvcas::CasEventListener {
|
||||
public:
|
||||
MockEventListener() {}
|
||||
~MockEventListener() override {}
|
||||
MOCK_METHOD0(OnSessionRenewalNeeded, void());
|
||||
MOCK_METHOD2(OnSessionKeysChange, void(const wvcas::KeyStatusMap& keys_status,
|
||||
bool has_new_usable_key));
|
||||
MOCK_METHOD1(OnExpirationUpdate, void(int64_t new_expiry_time_seconds));
|
||||
MOCK_METHOD1(OnNewRenewalServerUrl,
|
||||
void(const std::string& renewal_server_url));
|
||||
MOCK_METHOD0(OnLicenseExpiration, void());
|
||||
MOCK_METHOD2(OnAgeRestrictionUpdated,
|
||||
void(const wvcas::WvCasSessionId& sessionId,
|
||||
uint8_t ecm_age_restriction));
|
||||
MOCK_METHOD(void, OnSessionFingerprintingUpdated,
|
||||
(const int32_t& sessionId,
|
||||
const std::vector<uint8_t>& fingerprinting),
|
||||
(override));
|
||||
MOCK_METHOD(void, OnSessionServiceBlockingUpdated,
|
||||
(const int32_t& sessionId,
|
||||
const std::vector<uint8_t>& service_blocking),
|
||||
(override));
|
||||
};
|
||||
|
||||
TEST_F(CasSessionTest, FingerprintingSuccess) {
|
||||
TestCasSession session;
|
||||
auto mock_crypto = std::make_shared<MockCryptoSession>();
|
||||
MockEventListener mock_listener;
|
||||
uint32_t session_id;
|
||||
ASSERT_EQ(session.initialize(mock_crypto, &mock_listener, &session_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
session.set_fingerprinting_control("control");
|
||||
std::vector<uint8_t> expected_message = {0x00, 0x00, 0x07, 'c', 'o',
|
||||
'n', 't', 'r', 'o', 'l'};
|
||||
EXPECT_CALL(mock_listener,
|
||||
OnSessionFingerprintingUpdated(session_id, expected_message))
|
||||
.Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, RepeatedFingerprintingNoEventSuccess) {
|
||||
TestCasSession session;
|
||||
auto mock_crypto = std::make_shared<MockCryptoSession>();
|
||||
MockEventListener mock_listener;
|
||||
uint32_t session_id;
|
||||
ASSERT_EQ(session.initialize(mock_crypto, &mock_listener, &session_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
session.set_fingerprinting_control("control");
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
// Same fingerprinting will not trigger event.
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(0);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, DifferentFingerprintingTriggerEventSuccess) {
|
||||
TestCasSession session;
|
||||
auto mock_crypto = std::make_shared<MockCryptoSession>();
|
||||
MockEventListener mock_listener;
|
||||
uint32_t session_id;
|
||||
ASSERT_EQ(session.initialize(mock_crypto, &mock_listener, &session_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
session.set_fingerprinting_control("control");
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
// Different fingerprinting will trigger event.
|
||||
session.set_fingerprinting_control("control2");
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(1);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0);
|
||||
// Different fingerprinting (including empty) will trigger event.
|
||||
session.set_fingerprinting_control("");
|
||||
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(1);
|
||||
session.processEcm(wvcas::CasEcm(184, '2'), 0);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, ServiceBlockingSuccess) {
|
||||
TestCasSession session;
|
||||
auto mock_crypto = std::make_shared<MockCryptoSession>();
|
||||
MockEventListener mock_listener;
|
||||
uint32_t session_id;
|
||||
ASSERT_EQ(session.initialize(mock_crypto, &mock_listener, &session_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
session.set_service_blocking_groups({"Group1", "g2"});
|
||||
std::vector<uint8_t> expected_message = {0x00, 0x00, 0x06, 'G', 'r',
|
||||
'o', 'u', 'p', '1', 0x00,
|
||||
0x00, 0x02, 'g', '2'};
|
||||
EXPECT_CALL(mock_listener,
|
||||
OnSessionServiceBlockingUpdated(session_id, expected_message))
|
||||
.Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, RepeatedServiceBlockingNoEventSuccess) {
|
||||
TestCasSession session;
|
||||
auto mock_crypto = std::make_shared<MockCryptoSession>();
|
||||
MockEventListener mock_listener;
|
||||
uint32_t session_id;
|
||||
ASSERT_EQ(session.initialize(mock_crypto, &mock_listener, &session_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
session.set_service_blocking_groups({"Group1", "g2"});
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(0);
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0);
|
||||
}
|
||||
|
||||
TEST_F(CasSessionTest, DifferentServiceBlockingTriggerEventSuccess) {
|
||||
TestCasSession session;
|
||||
auto mock_crypto = std::make_shared<MockCryptoSession>();
|
||||
MockEventListener mock_listener;
|
||||
uint32_t session_id;
|
||||
ASSERT_EQ(session.initialize(mock_crypto, &mock_listener, &session_id)
|
||||
.status_code(),
|
||||
wvcas::CasStatusCode::kNoError);
|
||||
session.set_service_blocking_groups({"Group1", "g2"});
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
|
||||
|
||||
session.processEcm(wvcas::CasEcm(184, '0'), 0);
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
|
||||
session.set_service_blocking_groups({"Group1"});
|
||||
session.processEcm(wvcas::CasEcm(184, '1'), 0);
|
||||
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
|
||||
session.set_service_blocking_groups({});
|
||||
session.processEcm(wvcas::CasEcm(184, '2'), 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user