V18.4.0 CAS plugin

Note that this version does not have Widevine Provisioning 4.0 support.
It is only suitable for device upgrades. A new patch with provisioning
4.0 support will be made later.
This commit is contained in:
Lu Chen
2024-02-22 13:45:32 -08:00
parent ff9728aaa2
commit 5f209e6980
92 changed files with 25729 additions and 0 deletions

42
tests/Android.bp Normal file
View File

@@ -0,0 +1,42 @@
cc_binary {
name: "wv_cas_tests",
proprietary: true,
srcs: [
"src/cas_license_test.cpp",
"src/cas_session_map_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/emm_parser_test.cpp",
"src/license_key_status_test.cpp",
"src/policy_engine_test.cpp",
"src/test_properties.cpp",
"src/timer_test.cpp",
"src/widevine_cas_api_test.cpp",
"src/widevine_cas_session_test.cpp",
"src/widevine_media_cas_plugin_test.cpp",
"src/wv_cas_test_main.cpp",
],
header_libs: [
"//vendor/widevine/libwvmediacas/oemcrypto:oemcastroheaders",
"media_plugin_headers",
],
static_libs: [
"//vendor/widevine/libwvmediacas/wvutil:libcasutil",
"//vendor/widevine/libwvmediacas/plugin:libwvcasplugins",
"//vendor/widevine/libwvmediacas/protos:libcas_protos",
"libgmock",
"libgtest",
],
shared_libs: [
"libcrypto",
"libutils",
"liblog",
"libprotobuf-cpp-lite",
"libhidlbase",
],
proto: {
type: "lite",
},
}

View File

@@ -0,0 +1,777 @@
// 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 "cas_license.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <openssl/sha.h>
#include <memory>
#include "cas_status.h"
#include "cas_util.h"
#include "device_files.pb.h"
#include "license_protocol.pb.h"
#include "mock_crypto_session.h"
#include "string_conversions.h"
namespace wvcas {
namespace {
using ::testing::_;
using ::testing::AllOf;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SetArgPointee;
using ::testing::SetArgReferee;
using ::testing::StrictMock;
using wvutil::Base64Decode;
using wvutil::Base64SafeDecode;
using wvutil::Base64SafeEncodeNoPad;
using video_widevine_client::sdk::DeviceCertificate;
using video_widevine_client::sdk::File;
using video_widevine_client::sdk::HashedFile;
static constexpr char kKeyboxToken[] = "KeyBoxToken";
static constexpr char kExpectedRenewalRequest[] = "ExpectedRenewalRequest";
static constexpr char kExpectedSignature[] = "ExpectedSignature";
static constexpr char kDeviceRsaKey[] = "DeviceRSAKeyDeviceRSAKeyDeviceRSAKey";
static constexpr char kDeviceRsaKeyIV[] = "0123456789abcdef";
static constexpr char kInitializationData[] = "CasInitializationData";
static constexpr char kSessionKey[] = "fedcba9876543210";
static constexpr uint32_t kNonce = 0x5555;
static constexpr size_t kExpectedKeyboxSizeBytes = 72;
static constexpr char kJsonStartSubstr[] = "\"signedResponse\": \"";
static constexpr char kJsonEndSubstr[] = "\"";
static constexpr char kKeyIDVideo[] = "KeyIdVideo";
static constexpr char kKeyIDAudio[] = "KeyIdAudio";
static constexpr char kKeyVideoIV[] = "KeyVideoIV";
static constexpr char kKeyAudioIV[] = "KeyAudioIV";
static constexpr char kKeyVideo[] = "KeyVideo";
static constexpr char kKeyAudio[] = "KeyAudio";
static constexpr char kKeyControlVideo[] = "KeyControlVideo";
static constexpr char kKeyControlAudio[] = "KeyControlAudio";
static constexpr char kKeyControlIVVideo[] = "KeyControlIVVideo";
static constexpr char kKeyControlIVAudio[] = "KeyControlIVAudio";
static constexpr char kTrackTypeVideo[] = "Video";
static constexpr char kTrackTypeAudio[] = "Audio";
static constexpr char kKeyCompanyName[] = "company_name";
static constexpr char kKeyModelName[] = "model_name";
static constexpr char kKeyArchitectureName[] = "architecture_name";
static constexpr char kKeyDeviceName[] = "device_name";
static constexpr char kKeyProductName[] = "product_name";
static constexpr char kKeyBuildInfo[] = "build_info";
static constexpr char kKeyDeviceId[] = "device_id";
static constexpr char kKeyOemCryptoSecurityPatchLevel[] =
"oem_crypto_security_patch_level";
static constexpr char kRenewalSereverURL[] = "ExpectedRenewalURL";
static constexpr char kCoreMessage[] = "CoreMessage";
typedef StrictMock<MockCryptoSession> StrictMockCryptoSession;
class MockPolicyEngine : public wvcas::PolicyEngine {
public:
MockPolicyEngine() {}
~MockPolicyEngine() override {}
MOCK_METHOD2(initialize,
void(std::shared_ptr<wvcas::CryptoSession> crypto_session,
wvcas::CasEventListener* event_listener));
MOCK_METHOD1(SetLicense, void(const video_widevine::License& license));
MOCK_METHOD1(UpdateLicense, void(const video_widevine::License& license));
MOCK_CONST_METHOD0(CanRenew, bool());
MOCK_CONST_METHOD0(renewal_server_url, const std::string&());
MOCK_CONST_METHOD0(IsExpired, bool());
MOCK_CONST_METHOD0(CanPersist, bool());
MOCK_CONST_METHOD0(always_include_client_id, bool());
};
typedef StrictMock<MockPolicyEngine> StrictMockPolicyEngine;
class TestCasLicense : public wvcas::CasLicense {
public:
explicit TestCasLicense() {}
~TestCasLicense() override{};
std::unique_ptr<wvcas::PolicyEngine> GetPolicyEngine() override {
policy_engine_ = pass_thru_.get();
return std::move(pass_thru_);
}
std::unique_ptr<StrictMockPolicyEngine> pass_thru_ =
make_unique<StrictMockPolicyEngine>();
StrictMockPolicyEngine* policy_engine_ = pass_thru_.get();
};
class CasLicenseTest : public ::testing::TestWithParam<bool> {
public:
CasLicenseTest() {}
virtual ~CasLicenseTest() {}
void SetKeyboxData(uint8_t* keyData, size_t* keyDataLength) {
ASSERT_EQ(kExpectedKeyboxSizeBytes, *keyDataLength);
memset(keyData, 0, *keyDataLength);
memcpy(keyData, kKeyboxToken, 11);
}
std::string CreateProvisioningResponse();
std::shared_ptr<StrictMockCryptoSession> strict_mock_;
std::string wrapped_rsa_key_;
std::string device_certificate_;
};
std::string CasLicenseTest::CreateProvisioningResponse() {
video_widevine::SignedProvisioningMessage signed_message;
signed_message.set_signature(kExpectedSignature);
signed_message.set_oemcrypto_core_message(kCoreMessage);
video_widevine::ProvisioningResponse response;
std::string* nonce = response.mutable_nonce();
nonce->resize(4);
memcpy(&nonce->at(0), &kNonce, 4);
response.set_device_rsa_key(kDeviceRsaKey);
response.set_device_rsa_key_iv(kDeviceRsaKeyIV);
response.SerializeToString(signed_message.mutable_message());
std::vector<uint8_t> b64_message(signed_message.ByteSize());
signed_message.SerializeToArray(&b64_message[0], b64_message.size());
return kJsonStartSubstr + Base64SafeEncodeNoPad(b64_message) + kJsonEndSubstr;
}
bool Hash(const std::string& data, std::string* hash) {
if (!hash) return false;
hash->resize(SHA256_DIGEST_LENGTH);
const unsigned char* input =
reinterpret_cast<const unsigned char*>(data.data());
unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]);
SHA256(input, data.size(), output);
return true;
}
std::string CreateEntitlementResponse() {
video_widevine::License license;
auto* key = license.add_key();
key->set_type(video_widevine::License_KeyContainer::ENTITLEMENT);
video_widevine::SignedMessage signed_message;
license.SerializeToString(signed_message.mutable_msg());
signed_message.set_type(video_widevine::SignedMessage::CAS_LICENSE);
signed_message.set_signature(kExpectedSignature);
signed_message.set_session_key(kExpectedSignature);
signed_message.set_oemcrypto_core_message(kCoreMessage);
return signed_message.SerializeAsString();
}
std::string CreateEntitlementRenewalResponse() {
video_widevine::License license;
video_widevine::SignedMessage signed_message;
license.SerializeToString(signed_message.mutable_msg());
signed_message.set_type(video_widevine::SignedMessage::CAS_LICENSE);
signed_message.set_signature(kExpectedSignature);
signed_message.set_oemcrypto_core_message(kCoreMessage);
return signed_message.SerializeAsString();
}
std::string CreateLicenseFileData() {
File file;
file.set_type(File::LICENSE);
video_widevine::SignedMessage license_request;
license_request.set_msg("license_request");
license_request.set_signature("license_request_signature");
license_request.set_type(video_widevine::SignedMessage::CAS_LICENSE_REQUEST);
video_widevine::SignedMessage renewal_request;
renewal_request.set_msg("renewal_request");
renewal_request.set_signature("renewal_request_signature");
renewal_request.set_type(video_widevine::SignedMessage::CAS_LICENSE_REQUEST);
video_widevine_client::sdk::License* license = file.mutable_license();
license_request.SerializeToString(license->mutable_license_request());
license->set_license(CreateEntitlementResponse());
renewal_request.SerializeToString(license->mutable_renewal_request());
license->set_renewal(CreateEntitlementRenewalResponse());
HashedFile hashed_file;
file.SerializeToString(hashed_file.mutable_file());
Hash(hashed_file.file(), hashed_file.mutable_hash());
return hashed_file.SerializeAsString();
}
TEST_F(CasLicenseTest, GenerateDeviceProvisioningRequest) {
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
TestCasLicense cas_license;
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
std::string serialized_provisioning_request;
std::string expected_signature(kExpectedSignature);
EXPECT_CALL(*strict_mock_, provisioning_method())
.WillRepeatedly(Return(wvcas::Keybox));
EXPECT_CALL(*strict_mock_, GetKeyData(NotNull(), NotNull()))
.WillOnce(DoAll(Invoke(this, &CasLicenseTest::SetKeyboxData),
Return(wvcas::CasStatusCode::kNoError)));
EXPECT_CALL(*strict_mock_, supported_certificates())
.WillOnce(Return(wvcas::SupportedCertificates(0x13)));
EXPECT_CALL(*strict_mock_, GenerateNonce(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kNonce),
Return(wvcas::CasStatusCode::kNoError)));
EXPECT_CALL(*strict_mock_, GenerateDerivedKeys(_, _, _, _))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_,
PrepareAndSignProvisioningRequest(_, NotNull(), NotNull(), _, _))
.WillOnce(DoAll(SetArgPointee<2>(kExpectedSignature),
SetArgReferee<3>(false),
Return(wvcas::CasStatusCode::kNoError)));
EXPECT_CALL(*strict_mock_, GetDeviceID(NotNull()))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
wvcas::CasStatus status = cas_license.GenerateDeviceProvisioningRequest(
&serialized_provisioning_request);
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
// Verify the provisioning request.
video_widevine::SignedProvisioningMessage signed_message;
auto data = Base64Decode(serialized_provisioning_request);
EXPECT_TRUE(signed_message.ParseFromArray(&data[0], data.size()));
EXPECT_EQ(kExpectedSignature, signed_message.signature());
EXPECT_EQ(video_widevine::SignedProvisioningMessage::PROVISIONING_20,
signed_message.protocol_version());
video_widevine::ProvisioningRequest provisioning_request;
EXPECT_TRUE(provisioning_request.ParseFromString(signed_message.message()));
auto& client_id = provisioning_request.client_id();
EXPECT_EQ(video_widevine::ClientIdentification::KEYBOX, client_id.type());
std::string token(kKeyboxToken);
token.resize(kExpectedKeyboxSizeBytes, 0);
EXPECT_EQ(token, client_id.token());
ASSERT_EQ(sizeof(uint32_t), provisioning_request.nonce().size());
EXPECT_EQ(kNonce, *reinterpret_cast<const uint32_t*>(
provisioning_request.nonce().data()));
}
TEST_F(CasLicenseTest, HandleProvisioningResponse) {
const std::string provisioning_response = CreateProvisioningResponse();
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
TestCasLicense cas_license;
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
EXPECT_CALL(*strict_mock_, reset())
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_, LoadProvisioning(_, _, _, _));
wvcas::CasStatus status = cas_license.HandleDeviceProvisioningResponse(
provisioning_response, &device_certificate_, &wrapped_rsa_key_, nullptr);
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
}
TEST_F(CasLicenseTest, HandleProvisioningResponseWithDeviceFileOutput) {
const std::string provisioning_response = CreateProvisioningResponse();
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
TestCasLicense cas_license;
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
EXPECT_CALL(*strict_mock_, reset())
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_, LoadProvisioning(_, _, _, _));
std::string device_cert_file;
wvcas::CasStatus status = cas_license.HandleDeviceProvisioningResponse(
provisioning_response, &device_certificate_, &wrapped_rsa_key_,
&device_cert_file);
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
EXPECT_FALSE(device_cert_file.empty());
}
TEST_F(CasLicenseTest, GenerateEntitlementRequest) {
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
TestCasLicense cas_license;
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
EXPECT_CALL(*strict_mock_, GetOEMPublicCertificate(_, _))
.WillOnce(Return(wvcas::CasStatusCode::kCryptoSessionError));
EXPECT_CALL(*strict_mock_, APIVersion(_))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_, GenerateNonce(_))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_,
PrepareAndSignLicenseRequest(_, NotNull(), NotNull(), _, _))
.WillOnce(DoAll(SetArgPointee<2>(kExpectedSignature),
SetArgReferee<3>(false),
Return(wvcas::CasStatusCode::kNoError)));
EXPECT_CALL(*strict_mock_, LoadDeviceRSAKey(_, _));
std::string serialized_entitlement_request;
wvcas::CasStatus status = cas_license.GenerateEntitlementRequest(
kInitializationData, device_certificate_, wrapped_rsa_key_,
wvcas::LicenseType::kStreaming, &serialized_entitlement_request);
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
}
TEST_F(CasLicenseTest, HandleEntitlementResponse) {
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
TestCasLicense cas_license;
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
EXPECT_CALL(*strict_mock_, GetOEMPublicCertificate(_, _))
.WillOnce(Return(wvcas::CasStatusCode::kCryptoSessionError));
EXPECT_CALL(*strict_mock_, APIVersion(_))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_, GenerateNonce(_))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_,
PrepareAndSignLicenseRequest(_, NotNull(), NotNull(), _, _))
.WillOnce(DoAll(SetArgPointee<2>(kExpectedSignature),
SetArgReferee<3>(false),
Return(wvcas::CasStatusCode::kNoError)));
EXPECT_CALL(*strict_mock_, LoadDeviceRSAKey(_, _));
std::string serialized_entitlement_request;
wvcas::CasStatus status = cas_license.GenerateEntitlementRequest(
kInitializationData, device_certificate_, wrapped_rsa_key_,
wvcas::LicenseType::kStreaming, &serialized_entitlement_request);
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
video_widevine::SignedMessage signed_message;
ASSERT_TRUE(signed_message.ParseFromString(serialized_entitlement_request));
const std::string entitlement_response = CreateEntitlementResponse();
EXPECT_CALL(*strict_mock_, DeriveKeysFromSessionKey(_, _, _, _, _, _))
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_, LoadLicense(_, _, _))
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
// Valid.
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
EXPECT_CALL(*cas_license.policy_engine_, CanPersist())
.WillOnce(Return(false));
status = cas_license.HandleEntitlementResponse(entitlement_response,
/*device_file=*/nullptr);
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
// Not a group license.
EXPECT_TRUE(cas_license.GetGroupId().empty());
EXPECT_TRUE(cas_license.GetContentIdList().empty());
EXPECT_FALSE(cas_license.IsGroupLicense());
EXPECT_FALSE(cas_license.IsMultiContentLicense());
// Valid with device file.
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
EXPECT_CALL(*cas_license.policy_engine_, CanPersist())
.WillOnce(Return(false));
std::string device_file;
status =
cas_license.HandleEntitlementResponse(entitlement_response, &device_file);
EXPECT_TRUE(device_file.empty());
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
// Valid with device file and can_persist = true.
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
EXPECT_CALL(*cas_license.policy_engine_, CanPersist()).WillOnce(Return(true));
status =
cas_license.HandleEntitlementResponse(entitlement_response, &device_file);
EXPECT_FALSE(device_file.empty());
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
HashedFile hashed_file;
ASSERT_TRUE(hashed_file.ParseFromString(device_file));
std::string hash;
Hash(hashed_file.file(), &hash);
EXPECT_EQ(std::vector<uint8_t>(hash.begin(), hash.end()),
std::vector<uint8_t>(hashed_file.hash().begin(),
hashed_file.hash().end()));
File file;
ASSERT_TRUE(file.ParseFromString(hashed_file.file()));
const auto& license_file = file.license();
EXPECT_FALSE(license_file.license_request().empty());
EXPECT_FALSE(license_file.license().empty());
EXPECT_TRUE(license_file.renewal_request().empty());
EXPECT_TRUE(license_file.renewal().empty());
std::string emm_request = *signed_message.mutable_msg();
EXPECT_EQ(std::vector<uint8_t>(emm_request.begin(), emm_request.end()),
std::vector<uint8_t>(license_file.license_request().begin(),
license_file.license_request().end()));
EXPECT_EQ(std::vector<uint8_t>(entitlement_response.begin(),
entitlement_response.end()),
std::vector<uint8_t>(license_file.license().begin(),
license_file.license().end()));
}
TEST_P(CasLicenseTest, GenerateRenewalRequest) {
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
TestCasLicense cas_license;
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_CALL(*cas_license.policy_engine_, always_include_client_id())
.WillOnce(Return(GetParam()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
std::string serialized_renewal_request;
// Policy can_renew is false.
EXPECT_CALL(*cas_license.policy_engine_, CanRenew()).WillOnce(Return(false));
EXPECT_NE(wvcas::CasStatusCode::kNoError,
cas_license
.GenerateEntitlementRenewalRequest(device_certificate_,
&serialized_renewal_request)
.status_code());
// Policy can_renew is true.
EXPECT_CALL(*cas_license.policy_engine_, CanRenew()).WillOnce(Return(true));
EXPECT_CALL(*strict_mock_,
PrepareAndSignRenewalRequest(_, NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(kExpectedSignature),
Return(wvcas::CasStatusCode::kNoError)));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license
.GenerateEntitlementRenewalRequest(device_certificate_,
&serialized_renewal_request)
.status_code());
video_widevine::SignedMessage signed_message;
ASSERT_TRUE(signed_message.ParseFromString(serialized_renewal_request));
video_widevine::LicenseRequest license_request;
ASSERT_TRUE(license_request.ParseFromString(signed_message.msg()));
if (GetParam()) {
// Always include client id == true.
ASSERT_TRUE(license_request.has_client_id());
EXPECT_EQ(device_certificate_, license_request.client_id().token());
} else {
ASSERT_FALSE(license_request.has_client_id());
}
}
// Test renewal request generation with always_include_client_id == true and
// false;
// Suppress warning "INSTANTIATE_TEST_CASE_P is deprecated".
// TODO(b/142954362): Remove the suppression once gtest on pi-tv-dev branch
// updates to the latest version.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
INSTANTIATE_TEST_CASE_P(GenerateRenewalRequest, CasLicenseTest,
::testing::Bool());
#pragma GCC diagnostic pop
TEST_F(CasLicenseTest, HandleRenewalResponse) {
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
TestCasLicense cas_license;
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_CALL(*cas_license.policy_engine_, always_include_client_id())
.WillOnce(Return(false));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
std::string serialized_renewal_request;
// Policy can_renew is false.
EXPECT_CALL(*cas_license.policy_engine_, CanRenew()).WillOnce(Return(false));
EXPECT_NE(wvcas::CasStatusCode::kNoError,
cas_license
.GenerateEntitlementRenewalRequest(device_certificate_,
&serialized_renewal_request)
.status_code());
const std::string renewal_response = CreateEntitlementRenewalResponse();
// Policy can_renew is true. Set expectations to build a renewal request.
EXPECT_CALL(*cas_license.policy_engine_, CanRenew()).WillOnce(Return(true));
EXPECT_CALL(*strict_mock_,
PrepareAndSignRenewalRequest(_, NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(kExpectedSignature),
Return(wvcas::CasStatusCode::kNoError)));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license
.GenerateEntitlementRenewalRequest(device_certificate_,
&serialized_renewal_request)
.status_code());
video_widevine::SignedMessage signed_message;
ASSERT_TRUE(signed_message.ParseFromString(serialized_renewal_request));
std::string device_file;
EXPECT_CALL(*strict_mock_, LoadRenewal(_, _, _))
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
// Policy can_persist is false. no file information is generated.
EXPECT_CALL(*cas_license.policy_engine_, UpdateLicense(_));
EXPECT_CALL(*cas_license.policy_engine_, CanPersist())
.WillOnce(Return(false));
EXPECT_EQ(
wvcas::CasStatusCode::kNoError,
cas_license
.HandleEntitlementRenewalResponse(renewal_response, &device_file)
.status_code());
EXPECT_TRUE(device_file.empty());
// Policy can_persist is true. Validate that device_file is populated with a
// valid license file.
EXPECT_CALL(*cas_license.policy_engine_, CanPersist()).WillOnce(Return(true));
EXPECT_CALL(*cas_license.policy_engine_, UpdateLicense(_));
ASSERT_EQ(
wvcas::CasStatusCode::kNoError,
cas_license
.HandleEntitlementRenewalResponse(renewal_response, &device_file)
.status_code());
ASSERT_FALSE(device_file.empty());
HashedFile hashed_file;
ASSERT_TRUE(hashed_file.ParseFromString(device_file));
std::string hash;
Hash(hashed_file.file(), &hash);
EXPECT_EQ(std::vector<uint8_t>(hash.begin(), hash.end()),
std::vector<uint8_t>(hashed_file.hash().begin(),
hashed_file.hash().end()));
File file;
ASSERT_TRUE(file.ParseFromString(hashed_file.file()));
const auto& license_file = file.license();
EXPECT_TRUE(license_file.license_request().empty());
EXPECT_TRUE(license_file.license().empty());
EXPECT_FALSE(license_file.renewal_request().empty());
EXPECT_FALSE(license_file.renewal().empty());
std::string renewal_request = *signed_message.mutable_msg();
EXPECT_EQ(
std::vector<uint8_t>(renewal_request.begin(), renewal_request.end()),
std::vector<uint8_t>(license_file.renewal_request().begin(),
license_file.renewal_request().end()));
EXPECT_EQ(
std::vector<uint8_t>(renewal_response.begin(), renewal_response.end()),
std::vector<uint8_t>(license_file.renewal().begin(),
license_file.renewal().end()));
}
TEST_F(CasLicenseTest, IsExpired) {
TestCasLicense cas_license;
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
EXPECT_CALL(*cas_license.policy_engine_, IsExpired())
.WillOnce(Return(false))
.WillOnce(Return(true));
EXPECT_FALSE(cas_license.IsExpired());
EXPECT_TRUE(cas_license.IsExpired());
}
TEST_F(CasLicenseTest, RestoreLicense) {
TestCasLicense cas_license;
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
std::string license_file_data = CreateLicenseFileData();
EXPECT_CALL(*strict_mock_, LoadDeviceRSAKey(_, _));
EXPECT_CALL(*strict_mock_, DeriveKeysFromSessionKey(_, _, _, _, _, _))
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_, LoadLicense(_, _, _))
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_, LoadRenewal(_, _, _))
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
EXPECT_CALL(*cas_license.policy_engine_, UpdateLicense(_));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.HandleStoredLicense(wrapped_rsa_key_, license_file_data)
.status_code());
}
TEST_F(CasLicenseTest, HandleMultiContentEntitlementResponse) {
strict_mock_ = std::make_shared<StrictMockCryptoSession>();
TestCasLicense cas_license;
EXPECT_CALL(*cas_license.policy_engine_, initialize(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_license.initialize(strict_mock_, nullptr).status_code());
EXPECT_CALL(*strict_mock_, GetOEMPublicCertificate(_, _))
.WillOnce(Return(wvcas::CasStatusCode::kCryptoSessionError));
EXPECT_CALL(*strict_mock_, APIVersion(_))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_, GenerateNonce(_))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_,
PrepareAndSignLicenseRequest(_, NotNull(), NotNull(), _, _))
.WillOnce(DoAll(SetArgPointee<2>(kExpectedSignature),
SetArgReferee<3>(false),
Return(wvcas::CasStatusCode::kNoError)));
EXPECT_CALL(*strict_mock_, LoadDeviceRSAKey(_, _));
std::string serialized_entitlement_request;
wvcas::CasStatus status = cas_license.GenerateEntitlementRequest(
kInitializationData, device_certificate_, wrapped_rsa_key_,
wvcas::LicenseType::kStreaming, &serialized_entitlement_request);
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
// Create multi content entitlement response.
video_widevine::LicenseCategorySpec license_category_spec;
license_category_spec.set_license_category(
video_widevine::LicenseCategorySpec::MULTI_CONTENT_LICENSE);
license_category_spec.set_group_id("group_id");
video_widevine::License license;
*license.mutable_license_category_spec() = license_category_spec;
video_widevine::License::KeyContainer::KeyCategorySpec key_category_spec;
key_category_spec.set_content_id("content_id_1");
auto* key = license.add_key();
key->set_type(video_widevine::License_KeyContainer::ENTITLEMENT);
*key->mutable_key_category_spec() = key_category_spec;
key = license.add_key();
key->set_type(video_widevine::License_KeyContainer::ENTITLEMENT);
*key->mutable_key_category_spec() = key_category_spec;
key = license.add_key();
key->set_type(video_widevine::License_KeyContainer::ENTITLEMENT);
key_category_spec.set_content_id("content_id_2");
*key->mutable_key_category_spec() = key_category_spec;
video_widevine::SignedMessage signed_message;
license.SerializeToString(signed_message.mutable_msg());
signed_message.set_type(video_widevine::SignedMessage::CAS_LICENSE);
signed_message.set_signature(kExpectedSignature);
signed_message.set_session_key(kExpectedSignature);
signed_message.set_oemcrypto_core_message(kCoreMessage);
std::string entitlement_response = signed_message.SerializeAsString();
EXPECT_CALL(*strict_mock_, DeriveKeysFromSessionKey(_, _, _, _, _, _))
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_, LoadLicense(_, _, _))
.WillRepeatedly(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*cas_license.policy_engine_, SetLicense(_));
EXPECT_CALL(*cas_license.policy_engine_, CanPersist()).WillOnce(Return(true));
status = cas_license.HandleEntitlementResponse(entitlement_response,
/*device_file=*/nullptr);
EXPECT_EQ(status.status_code(), wvcas::CasStatusCode::kNoError);
// Not a group license.
EXPECT_EQ(cas_license.GetGroupId(), "group_id");
EXPECT_THAT(cas_license.GetContentIdList(),
testing::ElementsAre("content_id_1", "content_id_2"));
EXPECT_FALSE(cas_license.IsGroupLicense());
EXPECT_TRUE(cas_license.IsMultiContentLicense());
}
TEST(GetEntitlementPeriodIndexTest, ValidIndexSuccess) {
uint32_t expected_index = 123;
// Create a valid pssh with entitlement key period index.
video_widevine::WidevinePsshData pssh;
pssh.set_entitlement_period_index(expected_index);
// Create a license request containing the pssh.
video_widevine::LicenseRequest license_request;
license_request.mutable_content_id()->mutable_cenc_id_deprecated()->add_pssh(
pssh.SerializeAsString());
// Create a license file.
File file;
file.set_type(File::LICENSE);
license_request.SerializeToString(
file.mutable_license()->mutable_license_request());
// Hash the created file
HashedFile hashed_file;
file.SerializeToString(hashed_file.mutable_file());
Hash(hashed_file.file(), hashed_file.mutable_hash());
uint32_t actual_index;
EXPECT_TRUE(CasLicense::GetEntitlementPeriodIndexFromStoredLicense(
hashed_file.SerializeAsString(), actual_index)
.ok());
EXPECT_EQ(actual_index, expected_index);
}
TEST(GetEntitlementPeriodIndexTest, PsshHasNoIndexFail) {
// Create a valid pssh without entitlement key period index.
video_widevine::WidevinePsshData pssh;
pssh.set_content_id("content_id");
// Create a license request containing the pssh.
video_widevine::LicenseRequest license_request;
license_request.mutable_content_id()->mutable_cenc_id_deprecated()->add_pssh(
pssh.SerializeAsString());
// Create a license file.
File file;
file.set_type(File::LICENSE);
license_request.SerializeToString(
file.mutable_license()->mutable_license_request());
// Hash the created file
HashedFile hashed_file;
file.SerializeToString(hashed_file.mutable_file());
Hash(hashed_file.file(), hashed_file.mutable_hash());
uint32_t actual_index;
EXPECT_FALSE(CasLicense::GetEntitlementPeriodIndexFromStoredLicense(
hashed_file.SerializeAsString(), actual_index)
.ok());
}
TEST(GetEntitlementPeriodIndexTest, NoLicenseDataFail) {
File file;
file.set_type(File::LICENSE);
// Hash the created file
HashedFile hashed_file;
file.SerializeToString(hashed_file.mutable_file());
Hash(hashed_file.file(), hashed_file.mutable_hash());
uint32_t actual_index;
EXPECT_FALSE(CasLicense::GetEntitlementPeriodIndexFromStoredLicense(
hashed_file.SerializeAsString(), actual_index)
.ok());
}
TEST(GetEntitlementPeriodIndexTest, InvalidHashFail) {
// Create a valid pssh with entitlement key period index.
video_widevine::WidevinePsshData pssh;
pssh.set_entitlement_period_index(123);
// Create a license request containing the pssh.
video_widevine::LicenseRequest license_request;
license_request.mutable_content_id()->mutable_cenc_id_deprecated()->add_pssh(
pssh.SerializeAsString());
// Create a license file.
File file;
file.set_type(File::LICENSE);
license_request.SerializeToString(
file.mutable_license()->mutable_license_request());
// Hash the created file
HashedFile hashed_file;
file.SerializeToString(hashed_file.mutable_file());
hashed_file.set_hash("invalid_hash");
uint32_t actual_index;
EXPECT_FALSE(CasLicense::GetEntitlementPeriodIndexFromStoredLicense(
hashed_file.SerializeAsString(), actual_index)
.ok());
}
TEST(GetEntitlementPeriodIndexTest, InvalidFileFail) {
uint32_t actual_index;
EXPECT_FALSE(CasLicense::GetEntitlementPeriodIndexFromStoredLicense(
"invalid file", actual_index)
.ok());
}
} // namespace
} // namespace wvcas

View File

@@ -0,0 +1,25 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include "widevine_cas_session.h"
#include "widevine_cas_session_map.h"
using wvcas::CasSessionPtr;
using wvcas::WidevineCasSession;
using wvcas::WidevineCasSessionMap;
TEST(WidevineCasSessionMap, HappyPath) {
std::vector<uint8_t> base_key = {0x01, 0x02, 0x03};
WidevineCasSessionMap& map = WidevineCasSessionMap::instance();
CasSessionPtr session = std::make_shared<WidevineCasSession>();
EXPECT_TRUE(map.AddSession(base_key, session));
EXPECT_FALSE(map.AddSession(base_key, session));
EXPECT_EQ(map.GetSession(base_key).get(), session.get());
map.RemoveSession(base_key);
EXPECT_EQ(map.GetSession(base_key).get(), nullptr);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
// 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.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <tuple>
#include "media_cas.pb.h"
namespace wvcas {
namespace {
constexpr int kCasIdSizeBytes = 2;
constexpr int kVersionSizeBytes = 1;
constexpr int kEcmHeaderSizeBytes = kCasIdSizeBytes + kVersionSizeBytes;
constexpr int kEcmVersion2 = 2;
constexpr int kEcmVersion3 = 3;
constexpr size_t kValidEcmV2SizeBytes = 165;
constexpr uint16_t kSectionHeader1 = 0x80;
constexpr uint16_t kSectionHeader2 = 0x81;
constexpr uint8_t kPointerFieldZero = 0x00;
constexpr uint16_t kWidevineCasId = 0x4AD4;
// New Widevine CAS IDs 0x56C0 to 0x56C9 (all inclusive).
constexpr uint16_t kWidevineNewCasIdLowerBound = 0x56C0;
constexpr uint16_t kWidevineNewCasIdUpperBound = 0x56C9;
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());
}
return ecm_data;
}
// 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);
}
INSTANTIATE_TEST_SUITE_P(EcmParserVersionTest, EcmParserVersionTest,
::testing::Values(kEcmVersion2, kEcmVersion3));
// Verifies CAS ID returned by the parser must be expected ones.
class EcmParserCasIdTest
: public testing::Test,
public ::testing::WithParamInterface<::testing::tuple<uint16_t, bool>> {};
TEST_P(EcmParserCasIdTest, ValidateCasIds) {
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,
::testing::Values(std::make_tuple(kWidevineCasId,
true)));
INSTANTIATE_TEST_SUITE_P(
EcmWithNewCasId, EcmParserCasIdTest,
::testing::Combine(
::testing::Range(static_cast<uint16_t>(kWidevineNewCasIdLowerBound),
static_cast<uint16_t>(kWidevineNewCasIdUpperBound +
1)),
::testing::Values(true)));
INSTANTIATE_TEST_SUITE_P(
EcmWithInvalidCasId, EcmParserCasIdTest,
::testing::Combine(::testing::Values(0, kWidevineCasId - 1,
kWidevineCasId + 1,
kWidevineNewCasIdLowerBound - 1,
kWidevineNewCasIdUpperBound + 1),
::testing::Values(false)));
// Verifies Section header and pointer field may be prepended to ECM.
class EcmParserSectionHeaderTest
: 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) != 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) != nullptr);
}
INSTANTIATE_TEST_SUITE_P(EcmWithSectionHeader, EcmParserSectionHeaderTest,
::testing::Values(kSectionHeader1, kSectionHeader2));
} // namespace
} // namespace wvcas

View 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<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);
}

View File

@@ -0,0 +1,443 @@
// 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::EcmGroupKeyData;
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<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<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<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<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<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<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),
std::make_tuple(CryptoMode::kAesECB, EcmMetaData::AES_ECB)));
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<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<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<EcmParserV3> parser = EcmParserV3::Create(ecm);
ASSERT_TRUE(parser != nullptr);
EXPECT_EQ(parser->signature(), expected_signature);
}
TEST(EcmParserV3Test, SetGroupIdSuccess) {
const std::string group_id = "group_id";
const std::string group_id2 = "another_group";
EcmPayload ecm_payload;
EcmGroupKeyData* group_key_data = ecm_payload.add_group_key_data();
group_key_data->set_group_id(group_id);
group_key_data = ecm_payload.add_group_key_data();
group_key_data->set_group_id(group_id2);
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<EcmParserV3> parser = EcmParserV3::Create(ecm);
ASSERT_TRUE(parser != nullptr);
EXPECT_TRUE(parser->set_group_id(group_id));
EXPECT_TRUE(parser->set_group_id(group_id2));
}
TEST(EcmParserV3Test, SetUnknownGroupIdFail) {
EcmPayload ecm_payload;
EcmGroupKeyData* group_key_data = ecm_payload.add_group_key_data();
group_key_data->set_group_id("group_id");
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<EcmParserV3> parser = EcmParserV3::Create(ecm);
ASSERT_TRUE(parser != nullptr);
EXPECT_FALSE(parser->set_group_id("unknown"));
}
TEST(EcmParserV3Test, ParserWithGroupIdSuccess) {
const std::string group_id = "group_id";
EcmPayload ecm_payload;
EcmGroupKeyData* group_key_data = ecm_payload.add_group_key_data();
group_key_data->set_group_id(group_id);
group_key_data->mutable_even_key_data()->set_entitlement_key_id(
kEntitlementId);
group_key_data->mutable_even_key_data()->set_wrapped_key_data(
kWrappedContentKey);
group_key_data->mutable_even_key_data()->set_content_iv(kContentIv);
group_key_data->mutable_even_key_data()->set_wrapped_key_iv(kWrappedKeyIv);
ecm_payload.mutable_even_key_data()->set_entitlement_key_id(kEntitlementId2);
ecm_payload.mutable_even_key_data()->set_wrapped_key_data(
kWrappedContentKey2);
ecm_payload.mutable_even_key_data()->set_content_iv(kContentIv2);
ecm_payload.mutable_even_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<EcmParserV3> parser = EcmParserV3::Create(ecm);
ASSERT_TRUE(parser != nullptr);
// If group Id is not set, the normal keys will be returned.
std::vector<uint8_t> result =
parser->entitlement_key_id(KeySlotId::kEvenKeySlot);
EXPECT_EQ(std::string(result.begin(), result.end()), kEntitlementId2);
result = parser->wrapped_key_data(KeySlotId::kEvenKeySlot);
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedContentKey2);
result = parser->content_iv(KeySlotId::kEvenKeySlot);
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv2);
result = parser->wrapped_key_iv(KeySlotId::kEvenKeySlot);
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv2);
// Now set the group id.
EXPECT_TRUE(parser->set_group_id(group_id));
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, ParserGroupKeysWithOmittedFieldsSuccess) {
const std::string group_id = "group_id";
EcmPayload ecm_payload;
EcmGroupKeyData* group_key_data = ecm_payload.add_group_key_data();
group_key_data->set_group_id(group_id);
group_key_data->mutable_even_key_data()->set_entitlement_key_id(
kEntitlementId);
group_key_data->mutable_even_key_data()->set_wrapped_key_data(
kWrappedContentKey);
// Content IV and wrapped key iv is omitted in |group_key_data|/
ecm_payload.mutable_even_key_data()->set_entitlement_key_id(kEntitlementId2);
ecm_payload.mutable_even_key_data()->set_wrapped_key_data(
kWrappedContentKey2);
ecm_payload.mutable_even_key_data()->set_content_iv(kContentIv2);
ecm_payload.mutable_even_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<EcmParserV3> parser = EcmParserV3::Create(ecm);
ASSERT_TRUE(parser != nullptr);
EXPECT_TRUE(parser->set_group_id(group_id));
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);
// Content IV and wrapped key iv are from normal non-group key.
result = parser->content_iv(KeySlotId::kEvenKeySlot);
EXPECT_EQ(std::string(result.begin(), result.end()), kContentIv2);
result = parser->wrapped_key_iv(KeySlotId::kEvenKeySlot);
EXPECT_EQ(std::string(result.begin(), result.end()), kWrappedKeyIv2);
}
TEST(EcmParserV3Test, EntitlementRotationEnabledSuccess) {
const uint32_t entitlement_period_index = 10;
const uint32_t entitlement_rotation_window_left = 100;
EcmPayload ecm_payload;
ecm_payload.mutable_meta_data()->set_entitlement_period_index(
entitlement_period_index);
ecm_payload.mutable_meta_data()->set_entitlement_rotation_window_left(
entitlement_rotation_window_left);
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<EcmParserV3> parser = EcmParserV3::Create(ecm);
ASSERT_TRUE(parser != nullptr);
EXPECT_TRUE(parser->is_entitlement_rotation_enabled());
EXPECT_EQ(parser->entitlement_period_index(), entitlement_period_index);
EXPECT_EQ(parser->entitlement_rotation_window_left(),
entitlement_rotation_window_left);
}
TEST(EcmParserV3Test, EntitlementRotationDefaultDisabledSuccess) {
EcmPayload ecm_payload;
// Put something in the payload just to make the ECM valid.
ecm_payload.mutable_even_key_data()->set_entitlement_key_id(kEntitlementId);
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<EcmParserV3> parser = EcmParserV3::Create(ecm);
ASSERT_TRUE(parser != nullptr);
EXPECT_FALSE(parser->is_entitlement_rotation_enabled());
}
} // namespace
} // namespace wvcas

View File

@@ -0,0 +1,133 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "emm_parser.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include "cas_types.h"
namespace wvcas {
namespace {
using video_widevine::EmmPayload;
using video_widevine::SignedEmmPayload;
constexpr uint8_t kSectionHeader = 0x82;
constexpr int64_t kDefaultTimestamp = 1598905921;
constexpr char kDefaultSignature[] = "signature";
class EmmParserTest : public testing::Test {
protected:
EmmParserTest()
: timestamp_(kDefaultTimestamp), signature_(kDefaultSignature) {}
void SetSectionHeader(const std::vector<uint8_t> section_header) {
section_header_.assign(section_header.begin(), section_header.end());
}
void SetTimestamp(uint64_t timestamp) { timestamp_ = timestamp; }
void SetSignedEmm(const std::string& signed_emm) {
serialized_signed_emm_ = signed_emm;
}
void SetEmmPayload(const std::string& serialized_payload) {
serialized_emm_payload_ = serialized_payload;
}
void SetSignature(const std::string& signature) { signature_ = signature; }
std::vector<uint8_t> BuildEmm() const {
std::vector<uint8_t> emm_data(section_header_.begin(),
section_header_.end());
if (!serialized_signed_emm_.empty()) {
emm_data.insert(emm_data.end(), serialized_signed_emm_.begin(),
serialized_signed_emm_.end());
return emm_data;
}
SignedEmmPayload signed_emm;
if (serialized_emm_payload_.empty()) {
EmmPayload emm_payload;
emm_payload.set_timestamp_secs(timestamp_);
emm_payload.SerializeToString(signed_emm.mutable_serialized_payload());
} else {
signed_emm.set_serialized_payload(serialized_emm_payload_);
}
signed_emm.set_signature(signature_);
emm_data.resize(emm_data.size() + signed_emm.ByteSizeLong());
signed_emm.SerializeToArray(&emm_data[section_header_.size()],
emm_data.size());
return emm_data;
}
void ValidateParserAgainstDefault(const EmmParser* const parser) {
ASSERT_NE(parser, nullptr);
EXPECT_EQ(parser->timestamp(), kDefaultTimestamp);
EmmPayload expected_emm_payload;
expected_emm_payload.set_timestamp_secs(timestamp_);
EXPECT_EQ(parser->emm_payload().SerializeAsString(),
expected_emm_payload.SerializeAsString());
EXPECT_EQ(parser->signature(), kDefaultSignature);
}
private:
std::vector<uint8_t> section_header_;
uint64_t timestamp_;
std::string signature_;
std::string serialized_signed_emm_;
std::string serialized_emm_payload_;
};
TEST_F(EmmParserTest, ParseDefaultSuccess) {
auto parser = EmmParser::Create(BuildEmm());
ValidateParserAgainstDefault(parser.get());
}
TEST_F(EmmParserTest, EmmWithSectionHeaderSuccess) {
SetSectionHeader({kSectionHeader, 0, 0});
auto parser = EmmParser::Create(BuildEmm());
ValidateParserAgainstDefault(parser.get());
}
TEST_F(EmmParserTest, EmmWithSectionHeaderAndPointerFieldSuccess) {
SetSectionHeader({0, kSectionHeader, 0, 0});
auto parser = EmmParser::Create(BuildEmm());
ValidateParserAgainstDefault(parser.get());
}
TEST_F(EmmParserTest, EmmWithMalformedEmmCreateFail) {
SetSignedEmm("some emm");
EXPECT_THAT(EmmParser::Create(BuildEmm()), ::testing::IsNull());
}
TEST_F(EmmParserTest, EmmWithMalformedPayloadCreateFail) {
SetEmmPayload("some payload");
EXPECT_THAT(EmmParser::Create(BuildEmm()), ::testing::IsNull());
}
TEST_F(EmmParserTest, EmmWithNoSignatureCreateFail) {
SetSignature("");
EXPECT_THAT(EmmParser::Create(BuildEmm()), ::testing::IsNull());
}
class EmmParserWrongPrefixTest
: public EmmParserTest,
public ::testing::WithParamInterface<std::vector<uint8_t>> {};
TEST_P(EmmParserWrongPrefixTest, EmmWithWrongPrefixCreateFail) {
SetSectionHeader(GetParam());
EXPECT_THAT(EmmParser::Create(BuildEmm()), ::testing::IsNull());
}
INSTANTIATE_TEST_SUITE_P(
EmmParserWrongPrefixes, EmmParserWrongPrefixTest,
::testing::Values(std::vector<uint8_t>({0}),
std::vector<uint8_t>({1, 0, 0}),
std::vector<uint8_t>({kSectionHeader, 0}),
std::vector<uint8_t>({1, kSectionHeader, 0, 0})));
} // namespace
} // namespace wvcas

View File

@@ -0,0 +1,937 @@
// 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 "license_key_status.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <vector>
#include "cas_types.h"
namespace wvcas {
namespace {
static const uint32_t dev_lo_res = 200;
static const uint32_t dev_hi_res = 400;
static const uint32_t dev_top_res = 800;
static const uint32_t key_lo_res_min = 151;
static const uint32_t key_lo_res_max = 300;
static const uint32_t key_hi_res_min = 301;
static const uint32_t key_hi_res_max = 450;
static const uint32_t key_top_res_min = 451;
static const uint32_t key_top_res_max = 650;
// Content Keys
static const KeyId ck_sw_crypto = "c_key_SW_SECURE_CRYPTO";
static const KeyId ck_sw_decode = "c_key_SW_SECURE_DECODE";
static const KeyId ck_hw_crypto = "c_key_HW_SECURE_CRYPTO";
static const KeyId ck_hw_decode = "c_key_HW_SECURE_DECODE";
static const KeyId ck_hw_secure = "c_key_HW_SECURE_ALL";
// Operator Session Keys
static const KeyId osk_decrypt = "os_key_generic_decrypt";
static const KeyId osk_encrypt = "os_key_generic_encrypt";
static const KeyId osk_sign = "os_key_generic_sign";
static const KeyId osk_verify = "os_key_generic_verify";
static const KeyId osk_encrypt_decrypt = "os_key_generic_encrypt_decrypt";
static const KeyId osk_sign_verify = "os_key_generic_sign_verify";
static const KeyId osk_all = "os_key_generic_all";
// HDCP test keys
static const KeyId ck_sw_crypto_NO_HDCP = "ck_sw_crypto_NO_HDCP";
static const KeyId ck_hw_secure_NO_HDCP = "ck_hw_secure_NO_HDCP";
static const KeyId ck_sw_crypto_HDCP_V2_1 = "ck_sw_crypto_HDCP_V2_1";
static const KeyId ck_hw_secure_HDCP_V2_1 = "ck_hw_secure_HDCP_V2_1";
static const KeyId ck_sw_crypto_HDCP_NO_OUTPUT = "ck_sw_crypto_HDCP_NO_OUT";
static const KeyId ck_hw_secure_HDCP_NO_OUTPUT = "ck_hw_secure_HDCP_NO_OUT";
// Constraint test keys
static const KeyId ck_NO_HDCP_lo_res = "ck_NO_HDCP_lo_res";
static const KeyId ck_HDCP_NO_OUTPUT_hi_res = "ck_HDCP_NO_OUTPUT_hi_res";
static const KeyId ck_HDCP_V2_1_max_res = "ck_HDCP_V2_1_max_res";
static const KeyId ck_NO_HDCP_dual_res = "ck_NO_HDCP_dual_res";
} // namespace
// protobuf generated classes.
using video_widevine::License;
using video_widevine::LicenseIdentification;
using video_widevine::OFFLINE;
using video_widevine::STREAMING;
typedef ::video_widevine::License::KeyContainer KeyContainer;
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
class LicenseKeysTest : public ::testing::Test {
protected:
enum KeyFlag { kKeyFlagNull, kKeyFlagFalse, kKeyFlagTrue };
static const KeyFlag kEncryptNull = kKeyFlagNull;
static const KeyFlag kEncryptFalse = kKeyFlagFalse;
static const KeyFlag kEncryptTrue = kKeyFlagTrue;
static const KeyFlag kDecryptNull = kKeyFlagNull;
static const KeyFlag kDecryptFalse = kKeyFlagFalse;
static const KeyFlag kDecryptTrue = kKeyFlagTrue;
static const KeyFlag kSignNull = kKeyFlagNull;
static const KeyFlag kSignFalse = kKeyFlagFalse;
static const KeyFlag kSignTrue = kKeyFlagTrue;
static const KeyFlag kVerifyNull = kKeyFlagNull;
static const KeyFlag kVerifyFalse = kKeyFlagFalse;
static const KeyFlag kVerifyTrue = kKeyFlagTrue;
static const KeyFlag kContentSecureFalse = kKeyFlagFalse;
static const KeyFlag kContentSecureTrue = kKeyFlagTrue;
static const KeyFlag kContentClearFalse = kKeyFlagFalse;
static const KeyFlag kContentClearTrue = kKeyFlagTrue;
virtual void SetUp() {
LicenseIdentification* id = license_.mutable_id();
id->set_version(1);
id->set_type(STREAMING);
}
virtual void AddContentKey(
const KeyId& key_id, bool set_level = false,
KeyContainer::SecurityLevel level = KeyContainer::SW_SECURE_CRYPTO,
bool set_hdcp = false,
KeyContainer::OutputProtection::HDCP hdcp_value =
KeyContainer::OutputProtection::HDCP_NONE,
bool set_constraints = false,
std::vector<VideoResolutionConstraint>* constraints = NULL) {
KeyContainer* key = license_.add_key();
key->set_type(KeyContainer::CONTENT);
if (set_level) {
key->set_level(level);
}
if (set_hdcp) {
KeyContainer::OutputProtection* pro = key->mutable_required_protection();
pro->set_hdcp(hdcp_value);
}
if (set_constraints) {
for (std::vector<VideoResolutionConstraint>::iterator it =
constraints->begin();
it != constraints->end(); ++it) {
VideoResolutionConstraint* constraint =
key->add_video_resolution_constraints();
constraint->set_min_resolution_pixels(it->min_resolution_pixels());
constraint->set_max_resolution_pixels(it->max_resolution_pixels());
constraint->mutable_required_protection()->set_hdcp(
it->required_protection().hdcp());
}
}
key->set_id(key_id);
}
virtual void AddEntitlementKey(
const KeyId& key_id, bool set_level = false,
KeyContainer::SecurityLevel level = KeyContainer::SW_SECURE_CRYPTO,
bool set_hdcp = false,
KeyContainer::OutputProtection::HDCP hdcp_value =
KeyContainer::OutputProtection::HDCP_NONE,
bool set_constraints = false,
std::vector<VideoResolutionConstraint>* constraints = NULL) {
AddContentKey(key_id, set_level, level, set_hdcp, hdcp_value,
set_constraints, constraints);
license_.mutable_key(license_.key_size() - 1)
->set_type(KeyContainer::ENTITLEMENT);
}
virtual void AddOperatorSessionKey(const KeyId& key_id,
bool set_perms = false,
KeyFlag encrypt = kKeyFlagNull,
KeyFlag decrypt = kKeyFlagNull,
KeyFlag sign = kKeyFlagNull,
KeyFlag verify = kKeyFlagNull) {
KeyContainer* non_content_key = license_.add_key();
non_content_key->set_type(KeyContainer::OPERATOR_SESSION);
non_content_key->set_id(key_id);
if (set_perms) {
KeyContainer::OperatorSessionKeyPermissions* permissions =
non_content_key->mutable_operator_session_key_permissions();
if (encrypt != kKeyFlagNull) {
permissions->set_allow_encrypt(encrypt == kKeyFlagTrue);
}
if (decrypt != kKeyFlagNull) {
permissions->set_allow_decrypt(decrypt == kKeyFlagTrue);
}
if (sign != kKeyFlagNull) {
permissions->set_allow_sign(sign == kKeyFlagTrue);
}
if (verify != kKeyFlagNull) {
permissions->set_allow_signature_verify(verify == kKeyFlagTrue);
}
}
}
virtual void AddSigningKey(const KeyId& key_id) {
KeyContainer* key = license_.add_key();
key->set_type(KeyContainer::SIGNING);
key->set_id(key_id);
}
virtual void ExpectAllowedUsageContent(const KeyAllowedUsage& key_usage,
KeyFlag secure, KeyFlag clear,
KeySecurityLevel key_security_level) {
EXPECT_EQ(key_usage.decrypt_to_secure_buffer, secure == kKeyFlagTrue);
EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == kKeyFlagTrue);
EXPECT_EQ(key_usage.key_security_level_, key_security_level);
EXPECT_FALSE(key_usage.generic_encrypt);
EXPECT_FALSE(key_usage.generic_decrypt);
EXPECT_FALSE(key_usage.generic_sign);
EXPECT_FALSE(key_usage.generic_verify);
}
virtual void ExpectAllowedUsageOperator(const KeyAllowedUsage& key_usage,
KeyFlag encrypt, KeyFlag decrypt,
KeyFlag sign, KeyFlag verify) {
EXPECT_FALSE(key_usage.decrypt_to_secure_buffer);
EXPECT_FALSE(key_usage.decrypt_to_clear_buffer);
EXPECT_EQ(key_usage.generic_encrypt, encrypt == kKeyFlagTrue);
EXPECT_EQ(key_usage.generic_decrypt, decrypt == kKeyFlagTrue);
EXPECT_EQ(key_usage.generic_sign, sign == kKeyFlagTrue);
EXPECT_EQ(key_usage.generic_verify, verify == kKeyFlagTrue);
}
virtual int NumContentKeys() { return content_key_count_; }
virtual void StageContentKeys() {
content_key_count_ = 0;
AddContentKey(ck_sw_crypto, true, KeyContainer::SW_SECURE_CRYPTO);
content_key_count_++;
AddContentKey(ck_sw_decode, true, KeyContainer::SW_SECURE_DECODE);
content_key_count_++;
AddContentKey(ck_hw_crypto, true, KeyContainer::HW_SECURE_CRYPTO);
content_key_count_++;
AddContentKey(ck_hw_decode, true, KeyContainer::HW_SECURE_DECODE);
content_key_count_++;
AddContentKey(ck_hw_secure, true, KeyContainer::HW_SECURE_ALL);
content_key_count_++;
license_keys_.SetFromLicense(license_);
}
virtual void StageOperatorSessionKeys() {
AddOperatorSessionKey(osk_decrypt, true, kEncryptNull, kDecryptTrue,
kSignNull, kVerifyNull);
AddOperatorSessionKey(osk_encrypt, true, kEncryptTrue, kDecryptNull,
kSignNull, kVerifyNull);
AddOperatorSessionKey(osk_sign, true, kEncryptNull, kDecryptNull, kSignTrue,
kVerifyNull);
AddOperatorSessionKey(osk_verify, true, kEncryptNull, kDecryptNull,
kSignNull, kVerifyTrue);
AddOperatorSessionKey(osk_encrypt_decrypt, true, kEncryptTrue, kDecryptTrue,
kSignNull, kVerifyNull);
AddOperatorSessionKey(osk_sign_verify, true, kEncryptNull, kDecryptNull,
kSignTrue, kVerifyTrue);
AddOperatorSessionKey(osk_all, true, kEncryptTrue, kDecryptTrue, kSignTrue,
kVerifyTrue);
license_keys_.SetFromLicense(license_);
}
virtual void StageHdcpKeys() {
content_key_count_ = 0;
AddContentKey(ck_sw_crypto_NO_HDCP, true, KeyContainer::SW_SECURE_CRYPTO,
true, KeyContainer::OutputProtection::HDCP_NONE);
content_key_count_++;
AddContentKey(ck_hw_secure_NO_HDCP, true, KeyContainer::HW_SECURE_ALL, true,
KeyContainer::OutputProtection::HDCP_NONE);
content_key_count_++;
AddContentKey(ck_sw_crypto_HDCP_V2_1, true, KeyContainer::SW_SECURE_CRYPTO,
true, KeyContainer::OutputProtection::HDCP_V2_1);
content_key_count_++;
AddContentKey(ck_hw_secure_HDCP_V2_1, true, KeyContainer::HW_SECURE_ALL,
true, KeyContainer::OutputProtection::HDCP_V2_1);
content_key_count_++;
AddContentKey(ck_sw_crypto_HDCP_NO_OUTPUT, true,
KeyContainer::SW_SECURE_CRYPTO, true,
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
content_key_count_++;
AddContentKey(ck_hw_secure_HDCP_NO_OUTPUT, true,
KeyContainer::HW_SECURE_ALL, true,
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
content_key_count_++;
license_keys_.SetFromLicense(license_);
}
virtual void AddConstraint(
std::vector<VideoResolutionConstraint>& constraints, uint32_t min_res,
uint32_t max_res, bool set_hdcp = false,
KeyContainer::OutputProtection::HDCP hdcp =
KeyContainer::OutputProtection::HDCP_NONE) {
VideoResolutionConstraint constraint;
constraint.set_min_resolution_pixels(min_res);
constraint.set_max_resolution_pixels(max_res);
if (set_hdcp) {
constraint.mutable_required_protection()->set_hdcp(hdcp);
}
constraints.push_back(constraint);
}
virtual void StageConstraintKeys() {
content_key_count_ = 0;
std::vector<VideoResolutionConstraint> constraints;
AddConstraint(constraints, key_lo_res_min, key_lo_res_max);
AddContentKey(ck_NO_HDCP_lo_res, true, KeyContainer::SW_SECURE_CRYPTO, true,
KeyContainer::OutputProtection::HDCP_NONE, true,
&constraints);
content_key_count_++;
constraints.clear();
AddConstraint(constraints, key_hi_res_min, key_hi_res_max);
AddContentKey(ck_HDCP_NO_OUTPUT_hi_res, true,
KeyContainer::SW_SECURE_CRYPTO, true,
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT, true,
&constraints);
content_key_count_++;
constraints.clear();
AddConstraint(constraints, key_top_res_min, key_top_res_max);
AddContentKey(ck_HDCP_V2_1_max_res, true, KeyContainer::SW_SECURE_CRYPTO,
true, KeyContainer::OutputProtection::HDCP_V2_1, true,
&constraints);
content_key_count_++;
constraints.clear();
AddConstraint(constraints, key_lo_res_min, key_lo_res_max);
AddConstraint(constraints, key_hi_res_min, key_hi_res_max, true,
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
AddContentKey(ck_NO_HDCP_dual_res, true, KeyContainer::HW_SECURE_ALL, true,
KeyContainer::OutputProtection::HDCP_NONE, true,
&constraints);
content_key_count_++;
license_keys_.SetFromLicense(license_);
}
virtual void ExpectKeyStatusesEqual(KeyStatusMap& key_status_map,
KeyStatus expected_status) {
for (KeyStatusMap::iterator it = key_status_map.begin();
it != key_status_map.end(); ++it) {
EXPECT_TRUE(it->second == expected_status);
}
}
virtual void ExpectKeyStatusEqual(KeyStatusMap& key_status_map,
const KeyId& key_id,
KeyStatus expected_status) {
for (KeyStatusMap::iterator it = key_status_map.begin();
it != key_status_map.end(); ++it) {
if (key_id == it->first) {
EXPECT_TRUE(it->second == expected_status);
}
}
}
size_t content_key_count_;
LicenseKeys license_keys_;
License license_;
};
TEST_F(LicenseKeysTest, Empty) { EXPECT_TRUE(license_keys_.Empty()); }
TEST_F(LicenseKeysTest, NotEmpty) {
const KeyId c_key = "content_key";
AddContentKey(c_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.Empty());
}
TEST_F(LicenseKeysTest, BadKeyId) {
const KeyId c_key = "content_key";
const KeyId os_key = "op_sess_key";
const KeyId unk_key = "unknown_key";
KeyAllowedUsage allowed_usage;
AddContentKey(c_key);
AddOperatorSessionKey(os_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.IsContentKey(unk_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(unk_key));
EXPECT_TRUE(license_keys_.MeetsConstraints(unk_key));
EXPECT_FALSE(license_keys_.GetAllowedUsage(unk_key, &allowed_usage));
}
TEST_F(LicenseKeysTest, SigningKey) {
const KeyId c_key = "content_key";
const KeyId os_key = "op_sess_key";
const KeyId sign_key = "signing_key";
KeyAllowedUsage allowed_usage;
AddSigningKey(sign_key);
AddContentKey(c_key);
AddOperatorSessionKey(os_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.IsContentKey(sign_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(sign_key));
EXPECT_TRUE(license_keys_.MeetsConstraints(sign_key));
EXPECT_FALSE(license_keys_.GetAllowedUsage(sign_key, &allowed_usage));
}
TEST_F(LicenseKeysTest, ContentKey) {
const KeyId c_key = "content_key";
AddContentKey(c_key);
EXPECT_FALSE(license_keys_.IsContentKey(c_key));
license_keys_.SetFromLicense(license_);
EXPECT_TRUE(license_keys_.IsContentKey(c_key));
}
TEST_F(LicenseKeysTest, EntitlementKey) {
const KeyId e_key = "entitlement_key";
const KeyId c_key = "content_key";
AddEntitlementKey(e_key);
EXPECT_FALSE(license_keys_.IsContentKey(e_key));
license_keys_.SetFromLicense(license_);
// TODO(juce, rfrias): For simplicity entitlement keys are indicated as
// content keys. It doesn't break anything, but CanDecryptContent returns true
// for and entitlement key id.
EXPECT_TRUE(license_keys_.IsContentKey(e_key));
std::vector<WidevinePsshData_EntitledKey> entitled_keys(1);
entitled_keys[0].set_entitlement_key_id(e_key);
entitled_keys[0].set_key_id(c_key);
EXPECT_FALSE(license_keys_.IsContentKey(c_key));
license_keys_.SetEntitledKeys(entitled_keys);
EXPECT_TRUE(license_keys_.IsContentKey(c_key));
}
TEST_F(LicenseKeysTest, OperatorSessionKey) {
const KeyId os_key = "op_sess_key";
EXPECT_FALSE(license_keys_.IsContentKey(os_key));
AddOperatorSessionKey(os_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.IsContentKey(os_key));
}
TEST_F(LicenseKeysTest, CanDecrypt) {
const KeyId os_key = "op_sess_key";
const KeyId c_key = "content_key";
const KeyId e_key = "entitlement_key";
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(e_key));
AddOperatorSessionKey(os_key);
AddContentKey(c_key);
AddEntitlementKey(e_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(e_key));
bool new_usable_keys = false;
bool any_change = false;
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
any_change =
license_keys_.ApplyStatusChange(kKeyStatusExpired, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(e_key));
}
TEST_F(LicenseKeysTest, AllowedUsageNull) {
const KeyId os_key = "op_sess_key";
const KeyId c_key = "content_key";
const KeyId sign_key = "signing_key";
const KeyId e_key = "entitlement_key";
AddOperatorSessionKey(os_key);
AddContentKey(c_key);
AddSigningKey(sign_key);
AddEntitlementKey(e_key);
license_keys_.SetFromLicense(license_);
KeyAllowedUsage usage_1;
EXPECT_FALSE(license_keys_.GetAllowedUsage(sign_key, &usage_1));
KeyAllowedUsage usage_2;
EXPECT_TRUE(license_keys_.GetAllowedUsage(c_key, &usage_2));
ExpectAllowedUsageContent(usage_2, kContentClearTrue, kContentSecureTrue,
kKeySecurityLevelUnset);
KeyAllowedUsage usage_3;
EXPECT_TRUE(license_keys_.GetAllowedUsage(os_key, &usage_3));
ExpectAllowedUsageContent(usage_3, kContentClearFalse, kContentSecureFalse,
kKeySecurityLevelUnset);
KeyAllowedUsage usage_4;
EXPECT_TRUE(license_keys_.GetAllowedUsage(os_key, &usage_4));
ExpectAllowedUsageContent(usage_4, kContentClearFalse, kContentSecureFalse,
kKeySecurityLevelUnset);
}
TEST_F(LicenseKeysTest, AllowedUsageContent) {
StageContentKeys();
KeyAllowedUsage u_sw_crypto;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_crypto, &u_sw_crypto));
ExpectAllowedUsageContent(u_sw_crypto, kContentSecureTrue, kContentClearTrue,
kSoftwareSecureCrypto);
KeyAllowedUsage u_sw_decode;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_decode, &u_sw_decode));
ExpectAllowedUsageContent(u_sw_decode, kContentSecureTrue, kContentClearTrue,
kSoftwareSecureDecode);
KeyAllowedUsage u_hw_crypto;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_crypto, &u_hw_crypto));
ExpectAllowedUsageContent(u_hw_crypto, kContentSecureTrue, kContentClearTrue,
kHardwareSecureCrypto);
KeyAllowedUsage u_hw_decode;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_decode, &u_hw_decode));
ExpectAllowedUsageContent(u_hw_decode, kContentSecureTrue, kContentClearFalse,
kHardwareSecureDecode);
KeyAllowedUsage u_hw_secure;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_secure, &u_hw_secure));
ExpectAllowedUsageContent(u_hw_secure, kContentSecureTrue, kContentClearFalse,
kHardwareSecureAll);
}
TEST_F(LicenseKeysTest, AllowedUsageOperatorSession) {
StageOperatorSessionKeys();
KeyAllowedUsage u_encrypt;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_encrypt, &u_encrypt));
ExpectAllowedUsageOperator(u_encrypt, kEncryptTrue, kDecryptFalse, kSignFalse,
kVerifyFalse);
KeyAllowedUsage u_decrypt;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_decrypt, &u_decrypt));
ExpectAllowedUsageOperator(u_decrypt, kEncryptFalse, kDecryptTrue, kSignFalse,
kVerifyFalse);
KeyAllowedUsage u_sign;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_sign, &u_sign));
ExpectAllowedUsageOperator(u_sign, kEncryptFalse, kDecryptFalse, kSignTrue,
kVerifyFalse);
KeyAllowedUsage u_verify;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_verify, &u_verify));
ExpectAllowedUsageOperator(u_verify, kEncryptFalse, kDecryptFalse, kSignFalse,
kVerifyTrue);
KeyAllowedUsage u_encrypt_decrypt;
EXPECT_TRUE(
license_keys_.GetAllowedUsage(osk_encrypt_decrypt, &u_encrypt_decrypt));
ExpectAllowedUsageOperator(u_encrypt_decrypt, kEncryptTrue, kDecryptTrue,
kSignFalse, kVerifyFalse);
KeyAllowedUsage u_sign_verify;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_sign_verify, &u_sign_verify));
ExpectAllowedUsageOperator(u_sign_verify, kEncryptFalse, kDecryptFalse,
kSignTrue, kVerifyTrue);
KeyAllowedUsage u_all;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_all, &u_all));
ExpectAllowedUsageOperator(u_all, kEncryptTrue, kDecryptTrue, kSignTrue,
kVerifyTrue);
}
TEST_F(LicenseKeysTest, ExtractKeyStatuses) {
KeyStatusMap key_status_map;
StageOperatorSessionKeys();
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(0u, key_status_map.size());
StageContentKeys();
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError);
}
TEST_F(LicenseKeysTest, KeyStatusChanges) {
bool new_usable_keys = false;
bool any_change = false;
KeyStatusMap key_status_map;
StageOperatorSessionKeys();
StageContentKeys();
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError);
// change to pending
any_change =
license_keys_.ApplyStatusChange(kKeyStatusPending, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
// change to pending (again)
any_change =
license_keys_.ApplyStatusChange(kKeyStatusPending, &new_usable_keys);
EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
// change to usable
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
// change to usable (again)
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
// change to expired
any_change =
license_keys_.ApplyStatusChange(kKeyStatusExpired, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusExpired);
}
TEST_F(LicenseKeysTest, HdcpChanges) {
bool new_usable_keys = false;
bool any_change = false;
KeyStatusMap key_status_map;
StageHdcpKeys();
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ApplyConstraints(100, HDCP_NONE);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
license_keys_.ApplyConstraints(100, HDCP_V1);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
license_keys_.ApplyConstraints(100, HDCP_V2_2);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
license_keys_.ApplyConstraints(100, HDCP_NO_DIGITAL_OUTPUT);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusUsable);
license_keys_.ApplyConstraints(100, HDCP_NONE);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
}
TEST_F(LicenseKeysTest, ConstraintChanges) {
bool new_usable_keys = false;
bool any_change = false;
KeyStatusMap key_status_map;
StageConstraintKeys();
// No constraints set by device
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
// Low-res device, no HDCP support
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
// Hi-res device, HDCP_V1 support
license_keys_.ApplyConstraints(dev_hi_res, HDCP_V1);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
// Lo-res device, HDCP V2.2 support
license_keys_.ApplyConstraints(dev_lo_res, HDCP_V2_2);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
// Hi-res device, Maximal HDCP support
license_keys_.ApplyConstraints(dev_hi_res, HDCP_NO_DIGITAL_OUTPUT);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusUsable);
// Lo-res device, no HDCP support
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
// Too-high-res -- all keys rejected
license_keys_.ApplyConstraints(dev_top_res, HDCP_NONE);
any_change =
license_keys_.ApplyStatusChange(kKeyStatusUsable, &new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
}
} // namespace wvcas

View File

@@ -0,0 +1,154 @@
// Dynamically generated header created during build. The Runtest entry point is
// defined in gopkg_carchive.go
#include "golang/src/gowvcas_carchive.h"
#include "gtest/gtest.h"
extern "C" int RunTest(GoString);
constexpr int kIntegrationTestPassed = 0;
// Invokes a test. Tests are named to allow them to be run individually. This
// may be the best solution. The downside is the test prints PASS for each
// individual test.
// It is also possible to not address them individually but run
// them all as a batch. If running the tests as a single batch it may be
// possible to pass command line flags to the test to indicate individual tests.
// The downside of this is that it forces a user to pass in two command line
// flags for each test.
int RunNamedTest(const std::string testname) {
GoString go_testname = {
p : testname.data(),
n : static_cast<unsigned>(testname.size())
};
return RunTest(go_testname);
}
TEST(IntegrationTests, TestCasFactoryCreation) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCasFactoryCreation"));
}
TEST(IntegrationTests, TestCreateCasPlugin) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCreateCasPlugin"));
}
TEST(IntegrationTests, TestCreateCasPluginWithNewCasIds) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestCreateCasPluginWithNewCasIds"));
}
TEST(IntegrationTests, TestCreateCasPluginWithInvalidCasIds) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestCreateCasPluginWithInvalidCasIds"));
}
TEST(IntegrationTests, TestCreateCasPluginWithSessionEvent) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestCreateCasPluginWithSessionEvent"));
}
TEST(IntegrationTests, TestCreateCasPluginWithSessionEventWithNewCasIds) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestCreateCasPluginWithSessionEventWithNewCasIds"));
}
TEST(IntegrationTests, TestCasPluginEventPassing) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCasPluginEventPassing"));
}
TEST(IntegrationTests, TestSessionFailWithoutProvisioning) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestSessionFailWithoutProvisioning"));
}
TEST(IntegrationTests, TestUniqueIdQuery) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestUniqueIdQuery"));
}
TEST(IntegrationTests, TestCasPluginProvision) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCasPluginProvision"));
}
TEST(IntegrationTests, TestCasPluginEmmRequestWithInitData) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestCasPluginEmmRequestWithInitData"));
}
TEST(IntegrationTests, TestCasEmmRequestWithPrivateData) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestCasEmmRequestWithPrivateData"));
}
TEST(IntegrationTests, TestCasWithOfflineEMM) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCasWithOfflineEMM"));
}
TEST(IntegrationTests, TestCasCanStoreOfflineEMM) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCasCanStoreOfflineEMM"));
}
TEST(IntegrationTests, TestCasCanNotStoreOfflineEMM) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestCasCanNotStoreOfflineEMM"));
}
TEST(IntegrationTests, TestSession) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestSession"));
}
TEST(IntegrationTests, TestCasRenewal) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCasRenewal"));
}
TEST(IntegrationTests, TestRestoreRenewalAndExpiredLicense) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestRestoreRenewalAndExpiredLicense"));
}
TEST(IntegrationTests, TestLicenseExpiration) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestLicenseExpiration"));
}
TEST(IntegrationTests, TestParentalControl) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestParentalControl"));
}
TEST(IntegrationTests, TestRemoveLicense) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestRemoveLicense"));
}
TEST(IntegrationTests, TestSessionEventPassing) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestSessionEventPassing"));
}
TEST(IntegrationTests, TestProcessEcmV3) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestProcessEcmV3"));
}
TEST(IntegrationTests, TestGroupLicense) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestGroupLicense"));
}
TEST(IntegrationTests, TestMultiContentLicense) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestMultiContentLicense"));
}
TEST(IntegrationTests, TestAssignGroupLicense) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestAssignGroupLicense"));
}
TEST(IntegrationTests, TestLicenseRequestWithEntitlementPeriodIndex) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestLicenseRequestWithEntitlementPeriodIndex"));
}
TEST(IntegrationTests, TestOfflineLicenseWithEntitlementPeriodIndex) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestOfflineLicenseWithEntitlementPeriodIndex"));
}
TEST(IntegrationTests, TestNewLicenseRequestWithOutdatedOfflineLicense) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestNewLicenseRequestWithOutdatedOfflineLicense"));
}

View File

@@ -0,0 +1,93 @@
// 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.
#ifndef MOCK_CRYPTO_SESSION_H
#define MOCK_CRYPTO_SESSION_H
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "crypto_session.h"
class MockCryptoSession : public wvcas::CryptoSession {
public:
MockCryptoSession() {}
virtual ~MockCryptoSession() {}
MOCK_METHOD0(initialize, wvcas::CasStatus());
MOCK_METHOD0(reset, wvcas::CasStatus());
MOCK_METHOD0(close, wvcas::CasStatus());
MOCK_METHOD0(provisioning_method, wvcas::CasProvisioningMethod());
MOCK_METHOD2(GetKeyData,
wvcas::CasStatus(uint8_t* keyData, size_t* keyDataLength));
MOCK_METHOD0(supported_certificates, wvcas::SupportedCertificates());
MOCK_METHOD1(APIVersion, wvcas::CasStatus(uint32_t* api_version));
MOCK_METHOD1(GenerateNonce, wvcas::CasStatus(uint32_t* nonce));
MOCK_METHOD4(GenerateDerivedKeys,
wvcas::CasStatus(const uint8_t* mac_key_context,
uint32_t mac_key_context_length,
const uint8_t* enc_key_context,
uint32_t enc_key_context_length));
MOCK_METHOD5(PrepareAndSignLicenseRequest,
wvcas::CasStatus(const std::string& message,
std::string* core_message,
std::string* signature, bool&,
OEMCrypto_SignatureHashAlgorithm&)
);
MOCK_METHOD3(PrepareAndSignRenewalRequest,
wvcas::CasStatus(const std::string& message,
std::string* core_message,
std::string* signature));
MOCK_METHOD5(PrepareAndSignProvisioningRequest,
wvcas::CasStatus(const std::string& message,
std::string* core_message,
std::string* signature, bool&,
OEMCrypto_SignatureHashAlgorithm&)
);
MOCK_METHOD4(LoadProvisioning,
wvcas::CasStatus(const std::string& signed_message,
const std::string& core_message,
const std::string& signature,
std::string* wrapped_private_key));
MOCK_METHOD2(GetOEMPublicCertificate,
wvcas::CasStatus(uint8_t* public_cert,
size_t* public_cert_length));
MOCK_METHOD5(GenerateRSASignature,
wvcas::CasStatus(const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length,
RSA_Padding_Scheme padding_scheme));
MOCK_METHOD6(DeriveKeysFromSessionKey,
wvcas::CasStatus(const uint8_t* enc_session_key,
size_t enc_session_key_length,
const uint8_t* mac_key_context,
size_t mac_key_context_length,
const uint8_t* enc_key_context,
size_t enc_key_context_length));
MOCK_METHOD3(LoadLicense, wvcas::CasStatus(const std::string& signed_message,
const std::string& core_message,
const std::string& signature));
MOCK_METHOD3(LoadRenewal, wvcas::CasStatus(const std::string& signed_message,
const std::string& core_message,
const std::string& signature));
MOCK_METHOD3(LoadCasECMKeys, wvcas::CasStatus(OEMCrypto_SESSION session,
const wvcas::KeySlot* even_key,
const wvcas::KeySlot* odd_key));
MOCK_METHOD2(GetHdcpCapabilities, bool(wvcas::HdcpCapability* current,
wvcas::HdcpCapability* max));
MOCK_METHOD1(GetDeviceID, wvcas::CasStatus(std::string* buffer));
MOCK_METHOD2(LoadDeviceRSAKey,
wvcas::CasStatus(const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length));
MOCK_METHOD1(CreateEntitledKeySession,
wvcas::CasStatus(uint32_t* entitled_key_session_id));
MOCK_METHOD1(RemoveEntitledKeySession,
wvcas::CasStatus(uint32_t entitled_key_session_id));
MOCK_METHOD(wvcas::CasStatus, ReassociateEntitledKeySession,
(uint32_t entitled_key_session_id));
MOCK_METHOD(wvcas::CasStatus, GetOEMKeyToken,
(OEMCrypto_SESSION entitled_key_session_id,
std::vector<uint8_t>& token));
};
#endif // MOCK_CRYPTO_SESSION_H

View File

@@ -0,0 +1,45 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef MOCK_ECM_PARSER_H
#define MOCK_ECM_PARSER_H
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "ecm_parser.h"
class MockEcmParser : public wvcas::EcmParser {
public:
MOCK_METHOD(uint8_t, version, (), (const, override));
MOCK_METHOD(uint8_t, age_restriction, (), (const, override));
MOCK_METHOD(wvcas::CryptoMode, crypto_mode, (), (const, override));
MOCK_METHOD(bool, rotation_enabled, (), (const, override));
MOCK_METHOD(size_t, content_iv_size, (), (const, override));
MOCK_METHOD(std::vector<uint8_t>, entitlement_key_id, (wvcas::KeySlotId id),
(const, override));
MOCK_METHOD(std::vector<uint8_t>, content_key_id, (wvcas::KeySlotId id),
(const, override));
MOCK_METHOD(std::vector<uint8_t>, wrapped_key_data, (wvcas::KeySlotId id),
(const, override));
MOCK_METHOD(std::vector<uint8_t>, wrapped_key_iv, (wvcas::KeySlotId id),
(const, override));
MOCK_METHOD(std::vector<uint8_t>, content_iv, (wvcas::KeySlotId id),
(const, override));
MOCK_METHOD(bool, set_group_id, (const std::string& group_id), (override));
MOCK_METHOD(bool, has_fingerprinting, (), (const, override));
MOCK_METHOD(video_widevine::Fingerprinting, fingerprinting, (),
(const, override));
MOCK_METHOD(bool, has_service_blocking, (), (const, override));
MOCK_METHOD(video_widevine::ServiceBlocking, service_blocking, (),
(const, override));
MOCK_METHOD(std::string, ecm_serialized_payload, (), (const, override));
MOCK_METHOD(std::string, signature, (), (const, override));
MOCK_METHOD(bool, is_entitlement_rotation_enabled, (), (const, override));
MOCK_METHOD(uint32_t, entitlement_period_index, (), (const, override));
MOCK_METHOD(uint32_t, entitlement_rotation_window_left, (),
(const, override));
};
#endif // MOCK_ECM_PARSER_H

View File

@@ -0,0 +1,47 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef MOCK_EVENT_LISTENER_H
#define MOCK_EVENT_LISTENER_H
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "cas_types.h"
class MockEventListener : public wvcas::CasEventListener {
public:
MockEventListener() {}
virtual ~MockEventListener() {}
MOCK_METHOD(void, OnSessionRenewalNeeded, (), (override));
MOCK_METHOD(void, OnSessionKeysChange,
(const wvcas::KeyStatusMap& keys_status, bool has_new_usable_key),
(override));
MOCK_METHOD(void, OnExpirationUpdate, (int64_t new_expiry_time_seconds),
(override));
MOCK_METHOD(void, OnNewRenewalServerUrl,
(const std::string& renewal_server_url), (override));
MOCK_METHOD(void, OnLicenseExpiration, (), (override));
MOCK_METHOD(void, OnAgeRestrictionUpdated,
(const wvcas::WvCasSessionId& sessionId,
uint8_t ecm_age_restriction),
(override));
MOCK_METHOD(void, OnSessionFingerprintingUpdated,
(const wvcas::WvCasSessionId& sessionId,
const std::vector<uint8_t>& fingerprinting),
(override));
MOCK_METHOD(void, OnSessionServiceBlockingUpdated,
(const wvcas::WvCasSessionId& sessionId,
const std::vector<uint8_t>& service_blocking),
(override));
MOCK_METHOD(void, OnFingerprintingUpdated,
(const std::vector<uint8_t>& fingerprinting), (override));
MOCK_METHOD(void, OnServiceBlockingUpdated,
(const std::vector<uint8_t>& service_blocking), (override));
MOCK_METHOD(void, OnEntitlementPeriodUpdateNeeded,
(const std::string& signed_license_request), (override));
};
#endif // MOCK_EVENT_LISTENER_H

View File

@@ -0,0 +1,340 @@
#include "policy_engine.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "cas_types.h"
#include "cas_util.h"
#include "clock.h"
#include "license_key_status.h"
#include "license_protocol.pb.h"
#include "mock_crypto_session.h"
#include "mock_event_listener.h"
using ::testing::_;
using ::testing::DoAll;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::Sequence;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
using wvcas::HdcpCapability;
using wvcas::KeyAllowedUsage;
using wvcas::KeyId;
using wvcas::KeyStatus;
using wvcas::KeyStatusMap;
using wvcas::WidevinePsshData_EntitledKey;
namespace {
const int64_t kDurationUnlimited = 0;
const int64_t kLicenseStartTime = 1413517500; // ~ 01/01/2013
const int64_t kPlaybackStartTime = kLicenseStartTime + 5;
const int64_t kRentalDuration = 604800; // 7 days
const int64_t kPlaybackDuration = 172800; // 48 hours
const int64_t kLicenseDuration = kRentalDuration + kPlaybackDuration;
const int64_t kLicenseRenewalPeriod = 120; // 2 minutes
const int64_t kLicenseRenewalRetryInterval = 30; // 30 seconds
const int64_t kLicenseRenewalRecoveryDuration = 30; // 30 seconds
const int64_t kLowDuration = 300; // 5 minutes
const int64_t kHighDuration =
std::max(std::max(kRentalDuration, kPlaybackDuration), kLicenseDuration);
const char* kRenewalServerUrl =
"https://test.google.com/license/GetCencLicense";
const KeyId kKeyId = "357adc89f1673433c36c621f1b5c41ee";
const KeyId kEntitlementKeyId = "entitlementkeyid";
const KeyId kAnotherKeyId = "another_key_id";
const KeyId kSomeRandomKeyId = "some_random_key_id";
const KeyId kUnknownKeyId = "some_random_unknown_key_id";
} // namespace
class MockClock : public wvutil::Clock {
public:
MockClock() {}
virtual ~MockClock() {}
MOCK_METHOD0(GetCurrentTime, int64_t());
};
class MockLicenseKeys : public wvcas::LicenseKeys {
public:
MockLicenseKeys() {}
virtual ~MockLicenseKeys() {}
MOCK_METHOD0(Empty, bool());
MOCK_METHOD1(IsContentKey, bool(const KeyId& key_id));
MOCK_METHOD1(CanDecryptContent, bool(const KeyId& key_id));
MOCK_METHOD2(GetAllowedUsage,
bool(const KeyId& key_id, KeyAllowedUsage* allowed_usage));
MOCK_METHOD2(ApplyStatusChange,
bool(KeyStatus new_status, bool* new_usable_keys));
MOCK_METHOD1(GetKeyStatus, KeyStatus(const KeyId& key_id));
MOCK_METHOD1(ExtractKeyStatuses, void(KeyStatusMap* content_keys));
MOCK_METHOD1(MeetsConstraints, bool(const KeyId& key_id));
MOCK_METHOD2(ApplyConstraints,
void(uint32_t new_resolution, HdcpCapability new_hdcp_level));
MOCK_METHOD1(SetFromLicense, void(const video_widevine::License& license));
MOCK_METHOD1(SetEntitledKeys,
void(const std::vector<WidevinePsshData_EntitledKey>& keys));
};
class TestablePolicyEngine : public wvcas::PolicyEngine {
std::unique_ptr<wvcas::LicenseKeys> CreateLicenseKeys() override {
std::unique_ptr<StrictMock<MockLicenseKeys> > license_keys =
make_unique<StrictMock<MockLicenseKeys> >();
license_keys_ = license_keys.get();
return license_keys;
}
std::unique_ptr<wvutil::Clock> CreateClock() override {
std::unique_ptr<StrictMock<MockClock> > clock =
make_unique<StrictMock<MockClock> >();
clock_ = clock.get();
return clock;
}
public:
MockClock* clock_ = nullptr;
MockLicenseKeys* license_keys_ = nullptr;
};
class PolicyEngineTest : public ::testing::Test {
public:
PolicyEngineTest() {}
virtual ~PolicyEngineTest() {}
void SetUp() {
crypto_session_ = std::make_shared<StrictMock<MockCryptoSession> >();
policy_engine_.initialize(crypto_session_, &event_listener_);
ASSERT_NE(policy_engine_.clock_, nullptr);
ASSERT_NE(policy_engine_.license_keys_, nullptr);
license_.set_license_start_time(kLicenseStartTime);
video_widevine::LicenseIdentification* id = license_.mutable_id();
id->set_version(1);
id->set_type(video_widevine::STREAMING);
video_widevine::License::KeyContainer* key = license_.add_key();
key->set_type(video_widevine::License::KeyContainer::CONTENT);
key->set_id(kKeyId);
video_widevine::License_Policy* policy = license_.mutable_policy();
policy = license_.mutable_policy();
policy->set_can_play(true);
policy->set_can_persist(false);
policy->set_can_renew(false);
// This is similar to an OFFLINE policy.
policy->set_rental_duration_seconds(kRentalDuration);
policy->set_playback_duration_seconds(kPlaybackDuration);
policy->set_license_duration_seconds(kLicenseDuration);
policy->set_renewal_recovery_duration_seconds(
kLicenseRenewalRecoveryDuration);
policy->set_renewal_delay_seconds(0);
policy->set_renewal_retry_interval_seconds(kLicenseRenewalRetryInterval);
policy->set_renew_with_usage(false);
}
MockEventListener event_listener_;
std::shared_ptr<StrictMock<MockCryptoSession> > crypto_session_;
video_widevine::License license_;
TestablePolicyEngine policy_engine_;
};
TEST_F(PolicyEngineTest, CanDecryptContent) {
EXPECT_CALL(*policy_engine_.license_keys_, IsContentKey(kKeyId))
.WillOnce(Return(false))
.WillOnce(Return(true))
.WillOnce(Return(true));
EXPECT_CALL(*policy_engine_.license_keys_, CanDecryptContent(kKeyId))
.WillOnce(Return(false))
.WillOnce(Return(true));
// IsContentKey == false : CanDecryptContent is not called.
EXPECT_FALSE(policy_engine_.CanDecryptContent(kKeyId));
// IsContentKey == true : CanDecryptContent is false.
EXPECT_FALSE(policy_engine_.CanDecryptContent(kKeyId));
// IsContentKey == true : CanDecryptContent is true.
EXPECT_TRUE(policy_engine_.CanDecryptContent(kKeyId));
}
TEST_F(PolicyEngineTest, SetLicense_NoKey) {
video_widevine::License license;
license.mutable_policy();
license.set_license_start_time(kLicenseStartTime);
EXPECT_FALSE(policy_engine_.IsExpired());
EXPECT_CALL(*policy_engine_.license_keys_, SetFromLicense(_));
EXPECT_CALL(*policy_engine_.clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1));
EXPECT_CALL(*policy_engine_.license_keys_,
ApplyStatusChange(wvcas::kKeyStatusExpired, _))
.WillOnce(Return(false));
EXPECT_CALL(event_listener_, OnLicenseExpiration);
policy_engine_.SetLicense(license);
EXPECT_TRUE(policy_engine_.IsExpired());
}
TEST_F(PolicyEngineTest, PlaybackSuccess_OfflineLicense) {
// CryptoSession
EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull()))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), Return(true)));
EXPECT_CALL(*policy_engine_.clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kPlaybackStartTime))
.WillOnce(Return(kLicenseStartTime + 10));
EXPECT_CALL(*policy_engine_.license_keys_, Empty())
.WillOnce(Return(true))
.WillRepeatedly(Return(false));
EXPECT_CALL(*policy_engine_.license_keys_, SetFromLicense(_));
EXPECT_CALL(*policy_engine_.license_keys_,
ApplyStatusChange(wvcas::kKeyStatusUsable, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(true), Return(true)))
.WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(false)));
EXPECT_CALL(*policy_engine_.license_keys_, ExtractKeyStatuses(NotNull()));
EXPECT_CALL(event_listener_, OnSessionKeysChange(_, true));
EXPECT_CALL(event_listener_,
OnExpirationUpdate(kLicenseStartTime + kRentalDuration));
EXPECT_CALL(event_listener_,
OnExpirationUpdate(kPlaybackStartTime + kPlaybackDuration));
EXPECT_CALL(*policy_engine_.license_keys_,
ApplyConstraints(_, HDCP_NO_DIGITAL_OUTPUT))
.Times(2);
policy_engine_.SetLicense(license_);
policy_engine_.BeginDecryption();
policy_engine_.OnTimerEvent();
}
TEST_F(PolicyEngineTest, PlaybackSuccess_EntitlementLicenseExpiration) {
EXPECT_CALL(*policy_engine_.license_keys_, SetFromLicense(_));
EXPECT_CALL(*policy_engine_.clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kPlaybackStartTime))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 10));
EXPECT_CALL(*policy_engine_.license_keys_, Empty())
.WillOnce(Return(true))
.WillRepeatedly(Return(false));
EXPECT_CALL(*policy_engine_.license_keys_,
ApplyStatusChange(wvcas::kKeyStatusUsable, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(true), Return(true)))
.WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(false)));
EXPECT_CALL(*policy_engine_.license_keys_,
ApplyStatusChange(wvcas::kKeyStatusExpired, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(true), Return(false)));
EXPECT_CALL(*policy_engine_.license_keys_, ExtractKeyStatuses(NotNull()));
EXPECT_CALL(event_listener_, OnSessionKeysChange(_, true));
EXPECT_CALL(event_listener_, OnLicenseExpiration);
EXPECT_CALL(event_listener_,
OnExpirationUpdate(kLicenseStartTime + kRentalDuration));
EXPECT_CALL(event_listener_,
OnExpirationUpdate(kPlaybackStartTime + kPlaybackDuration));
EXPECT_CALL(*policy_engine_.license_keys_, SetEntitledKeys(_));
video_widevine::License::KeyContainer* key = license_.mutable_key(0);
key->set_type(video_widevine::License::KeyContainer::ENTITLEMENT);
key->set_id(kEntitlementKeyId);
EXPECT_FALSE(policy_engine_.IsExpired());
policy_engine_.SetLicense(license_);
policy_engine_.BeginDecryption();
policy_engine_.OnTimerEvent();
std::vector<WidevinePsshData_EntitledKey> entitled_keys(1);
entitled_keys[0].set_entitlement_key_id(kEntitlementKeyId);
entitled_keys[0].set_key_id(kKeyId);
policy_engine_.SetEntitledLicenseKeys(entitled_keys);
EXPECT_TRUE(policy_engine_.IsExpired());
}
TEST_F(PolicyEngineTest, PlaybackSuccess_StreamingLicense) {
video_widevine::License_Policy* policy = license_.mutable_policy();
policy->set_license_duration_seconds(kLowDuration);
EXPECT_CALL(*policy_engine_.license_keys_, SetFromLicense(_));
EXPECT_CALL(*policy_engine_.clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kPlaybackStartTime))
.WillOnce(Return(kLicenseStartTime + 10));
EXPECT_CALL(*policy_engine_.license_keys_, Empty())
.WillOnce(Return(true))
.WillRepeatedly(Return(false));
EXPECT_CALL(*policy_engine_.license_keys_,
ApplyStatusChange(wvcas::kKeyStatusUsable, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(true), Return(true)))
.WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(false)));
EXPECT_CALL(event_listener_, OnSessionKeysChange(_, true));
EXPECT_CALL(event_listener_,
OnExpirationUpdate(kLicenseStartTime + kLowDuration));
EXPECT_CALL(*policy_engine_.license_keys_, ExtractKeyStatuses(NotNull()));
EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), Return(false)));
EXPECT_CALL(*policy_engine_.license_keys_, ApplyConstraints(_, HDCP_NONE))
.Times(2);
policy_engine_.SetLicense(license_);
policy_engine_.BeginDecryption();
policy_engine_.OnTimerEvent();
}
TEST_F(PolicyEngineTest, RenewalEvents) {
video_widevine::License_Policy* policy = license_.mutable_policy();
policy->set_license_duration_seconds(kLowDuration);
policy->set_can_renew(true);
policy->set_renewal_delay_seconds(kLicenseRenewalPeriod);
;
policy->set_renewal_retry_interval_seconds(kLicenseRenewalRetryInterval);
{
Sequence set_license;
EXPECT_CALL(*policy_engine_.license_keys_, SetFromLicense(_));
EXPECT_CALL(*policy_engine_.clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1));
EXPECT_CALL(*policy_engine_.license_keys_, Empty()).WillOnce(Return(true));
EXPECT_CALL(*policy_engine_.license_keys_,
ApplyStatusChange(wvcas::kKeyStatusUsable, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(true), Return(true)));
EXPECT_CALL(*policy_engine_.license_keys_, ExtractKeyStatuses(NotNull()));
EXPECT_CALL(event_listener_,
OnExpirationUpdate(kLicenseStartTime + kLowDuration));
EXPECT_CALL(event_listener_, OnSessionKeysChange(_, true));
policy_engine_.SetLicense(license_);
}
{
Sequence begin_decryption;
EXPECT_CALL(*policy_engine_.clock_, GetCurrentTime())
.WillOnce(Return(kPlaybackStartTime));
policy_engine_.BeginDecryption();
}
EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), Return(false)));
EXPECT_CALL(*policy_engine_.license_keys_,
ApplyStatusChange(wvcas::kKeyStatusUsable, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(false)));
EXPECT_CALL(*policy_engine_.license_keys_, Empty())
.WillRepeatedly(Return(false));
{
Sequence on_timer;
EXPECT_CALL(*policy_engine_.clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 10));
EXPECT_CALL(*policy_engine_.license_keys_, ApplyConstraints(_, HDCP_NONE))
.Times(2);
policy_engine_.OnTimerEvent();
}
}
TEST_F(PolicyEngineTest, RenewalUrl) {
EXPECT_CALL(*policy_engine_.license_keys_, SetFromLicense(_));
EXPECT_CALL(*policy_engine_.clock_, GetCurrentTime());
EXPECT_CALL(*policy_engine_.license_keys_, ApplyStatusChange(_, NotNull()));
EXPECT_CALL(event_listener_, OnExpirationUpdate(_));
EXPECT_CALL(event_listener_,
OnNewRenewalServerUrl(::testing::StrEq(kRenewalServerUrl)));
video_widevine::License_Policy* policy = license_.mutable_policy();
policy->set_renewal_server_url(kRenewalServerUrl);
policy_engine_.SetLicense(license_);
}

View File

@@ -0,0 +1,71 @@
// 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 "cas_properties.h"
// TODO(widevine-eng): Reevaluate using "www" in the company name and model name
// fields. For now this is consistent with the values used in unit cdm testing.
static constexpr char kGenericCompanyName[] = "www";
static constexpr char kGenericModelName[] = "www";
static constexpr char kProductName[] = "WidevineCasTests";
static constexpr char kKeyArchitectureName[] = "architecture_name";
static constexpr char kKeyDeviceName[] = "device_name";
static constexpr char kOemcPath[] = "cas_oemc_path.so";
namespace wvcas {
bool Properties::GetCompanyName(std::string* company_name) {
if (company_name == nullptr) {
return false;
}
*company_name = kGenericCompanyName;
return true;
}
bool Properties::GetModelName(std::string* model_name) {
if (model_name == nullptr) {
return false;
}
*model_name = kGenericModelName;
return true;
}
bool Properties::GetProductName(std::string* product_name) {
if (product_name == nullptr) {
return false;
}
*product_name = kProductName;
return true;
}
bool Properties::GetArchitectureName(std::string* arch_name) {
if (arch_name == nullptr) {
return false;
}
*arch_name = kKeyArchitectureName;
return true;
}
bool Properties::GetDeviceName(std::string* device_name) {
if (device_name == nullptr) {
return false;
}
*device_name = kKeyDeviceName;
return true;
}
bool Properties::GetOEMCryptoPath(std::string* path) {
if (path == nullptr) {
return false;
}
*path = kOemcPath;
return true;
}
bool Properties::GetWvCasPluginVersion(std::string& version) {
version = "unit-test";
return true;
}
} // namespace wvcas

35
tests/src/timer_test.cpp Normal file
View File

@@ -0,0 +1,35 @@
// 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 "timer.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "clock.h"
class TimerTest : public wvutil::TimerHandler, public ::testing::Test {
public:
void OnTimerEvent() override { ++count_; }
wvutil::Clock clock_;
wvutil::Timer timer_;
uint64_t event_time_ = 0;
uint64_t count_ = 0;
};
TEST_F(TimerTest, Timer) {
timer_.Start(this, 1);
EXPECT_TRUE(timer_.IsRunning());
sleep(2);
timer_.Stop();
EXPECT_EQ(2, count_);
EXPECT_FALSE(timer_.IsRunning());
timer_.Start(this, 1);
EXPECT_TRUE(timer_.IsRunning());
sleep(3);
timer_.Stop();
EXPECT_EQ(5, count_);
EXPECT_FALSE(timer_.IsRunning());
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,436 @@
// 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 "widevine_cas_session.h"
#include <cas_events.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <vector>
#include "cas_types.h"
#include "cas_util.h"
#include "media_cas.pb.h"
#include "mock_crypto_session.h"
#include "mock_ecm_parser.h"
#include "mock_event_listener.h"
#include "string_conversions.h"
namespace wvcas {
namespace {
using ::testing::_;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
static const char kEvenEntitlementKeyId[] = "even_entitlement_key_id";
static const char kOddEntitlementKeyId[] = "odd_entitlement_key_id";
static const char kEvenKeyId[] = "even_key_id";
static const char kOddKeyId[] = "odd_key_id";
static const char kEvenWrappedKey[] = "even_wrapped_content_key";
static const char kOddWrappedKey[] = "odd_wrapped_content_key";
static const char kEvenWrappedKeyIv[] = "even_wrapped_content_key_iv";
static const char kOddWrappedKeyIv[] = "odd_wrapped_content_key_iv";
static const char kEvenContentIv[] = "even_content_iv";
static const char kOddContentIv[] = "odd_content_iv";
static const OEMCrypto_SESSION kEntitledKeySessionId = 0x1111;
constexpr char kEmptyGroupId[] = "";
MATCHER(IsValidKeyEvenSlotData, "") {
if (nullptr == arg) {
*result_listener << " keyslot is nullptr";
return false;
}
std::string value = kEvenEntitlementKeyId;
if (arg->entitlement_key_id !=
std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " entitlement key_id is invalid";
return false;
}
value = kEvenKeyId;
if (arg->key_id != std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " key_id is invalid";
return false;
}
value = kEvenWrappedKey;
if (arg->wrapped_key != std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " wrapped_key is invalid";
return false;
}
value = kEvenWrappedKeyIv;
if (arg->wrapped_key_iv != std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " wapped_key_iv is invalid";
return false;
}
value = kEvenContentIv;
if (arg->content_iv != std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " content_iv is invalid";
return false;
}
return true;
}
MATCHER(IsValidKeyOddSlotData, "") {
if (nullptr == arg) {
*result_listener << " keyslot is nullptr";
return false;
}
std::string value = kOddEntitlementKeyId;
if (arg->entitlement_key_id !=
std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " entitlement key_id is invalid";
return false;
}
value = kOddKeyId;
if (arg->key_id != std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " key_id is invalid";
return false;
}
value = kOddWrappedKey;
if (arg->wrapped_key != std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " wrapped_key is invalid";
return false;
}
value = kOddWrappedKeyIv;
if (arg->wrapped_key_iv != std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " wapped_key_iv is invalid";
return false;
}
value = kOddContentIv;
if (arg->content_iv != std::vector<uint8_t>(value.begin(), value.end())) {
*result_listener << " content_iv is invalid";
return false;
}
return true;
}
class CasSessionTest : public ::testing::Test {
public:
CasSessionTest() {}
virtual ~CasSessionTest() {}
void SetUp() override {
mock_crypto_session_ = std::make_shared<MockCryptoSession>();
ON_CALL(*mock_crypto_session_, CreateEntitledKeySession(NotNull()))
.WillByDefault(DoAll(SetArgPointee<0>(kEntitledKeySessionId),
Return(wvcas::CasStatusCode::kNoError)));
ON_CALL(*mock_crypto_session_, GetOEMKeyToken(kEntitledKeySessionId, _))
.WillByDefault([&](OEMCrypto_SESSION, std::vector<uint8_t>& token) {
token.assign(expected_session_id_.begin(),
expected_session_id_.end());
return CasStatusCode::kNoError;
});
}
std::shared_ptr<MockCryptoSession> mock_crypto_session_;
WvCasSessionId expected_session_id_ = {0x01, 0x02, 0x03};
};
// Allow getEcmParser to return a mocked ecm.
class TestCasSession : public wvcas::WidevineCasSession {
public:
TestCasSession() {}
virtual ~TestCasSession() {}
std::unique_ptr<wvcas::EcmParser> getEcmParser(
const wvcas::CasEcm& ecm) const override;
std::vector<uint8_t> entitlement_key_id(wvcas::KeySlotId id) const {
std::string key_id;
if (id == wvcas::KeySlotId::kEvenKeySlot) {
key_id = kEvenEntitlementKeyId;
} else if (id == wvcas::KeySlotId::kOddKeySlot) {
key_id = kOddEntitlementKeyId;
}
return std::vector<uint8_t>(key_id.begin(), key_id.end());
}
std::vector<uint8_t> content_key_id(wvcas::KeySlotId id) const {
std::string key_id;
if (id == wvcas::KeySlotId::kEvenKeySlot) {
key_id = kEvenKeyId;
} else if (id == wvcas::KeySlotId::kOddKeySlot) {
key_id = kOddKeyId;
}
return std::vector<uint8_t>(key_id.begin(), key_id.end());
}
std::vector<uint8_t> wrapped_key_data(wvcas::KeySlotId id) const {
std::string key;
if (id == wvcas::KeySlotId::kEvenKeySlot) {
key = kEvenWrappedKey;
} else if (id == wvcas::KeySlotId::kOddKeySlot) {
key = kOddWrappedKey;
}
return std::vector<uint8_t>(key.begin(), key.end());
}
std::vector<uint8_t> wrapped_key_iv(wvcas::KeySlotId id) const {
std::string iv;
if (id == wvcas::KeySlotId::kEvenKeySlot) {
iv = kEvenWrappedKeyIv;
} else if (id == wvcas::KeySlotId::kOddKeySlot) {
iv = kOddWrappedKeyIv;
}
return std::vector<uint8_t>(iv.begin(), iv.end());
}
std::vector<uint8_t> content_iv(wvcas::KeySlotId id) const {
std::string iv;
if (id == wvcas::KeySlotId::kEvenKeySlot) {
iv = kEvenContentIv;
} else if (id == wvcas::KeySlotId::kOddKeySlot) {
iv = kOddContentIv;
}
return std::vector<uint8_t>(iv.begin(), iv.end());
}
void set_age_restriction(uint8_t age_restriction) {
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<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, age_restriction())
.WillByDefault(Return(age_restriction_));
ON_CALL(*mock_ecm_parser, crypto_mode())
.WillByDefault(Return(wvcas::CryptoMode::kAesCTR));
ON_CALL(*mock_ecm_parser, rotation_enabled()).WillByDefault(Return(true));
ON_CALL(*mock_ecm_parser, entitlement_key_id(_))
.WillByDefault(Invoke(this, &TestCasSession::entitlement_key_id));
ON_CALL(*mock_ecm_parser, content_key_id(_))
.WillByDefault(Invoke(this, &TestCasSession::content_key_id));
ON_CALL(*mock_ecm_parser, wrapped_key_data(_))
.WillByDefault(Invoke(this, &TestCasSession::wrapped_key_data));
ON_CALL(*mock_ecm_parser, wrapped_key_iv(_))
.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, set_group_id(_)).WillByDefault(Return(true));
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<wvcas::EcmParser>(mock_ecm_parser.release());
}
TEST_F(CasSessionTest, sessionInitializeTest) {
TestCasSession session;
WvCasSessionId session_id;
ASSERT_EQ(session
.initialize(mock_crypto_session_, /*event_listener=*/nullptr,
&session_id)
.status_code(),
CasStatusCode::kNoError);
EXPECT_EQ(session_id, expected_session_id_);
}
TEST_F(CasSessionTest, processEcm) {
TestCasSession session;
WvCasSessionId session_id;
ASSERT_EQ(session
.initialize(mock_crypto_session_, /*event_listener=*/nullptr,
&session_id)
.status_code(),
CasStatusCode::kNoError);
wvcas::CasEcm ecm(184);
EXPECT_CALL(*mock_crypto_session_,
LoadCasECMKeys(kEntitledKeySessionId, IsValidKeyEvenSlotData(),
IsValidKeyOddSlotData()));
session.processEcm(ecm, 0, kEmptyGroupId);
EXPECT_CALL(*mock_crypto_session_,
RemoveEntitledKeySession(kEntitledKeySessionId));
}
TEST_F(CasSessionTest, parentalControl) {
TestCasSession session;
WvCasSessionId session_id;
ASSERT_EQ(session
.initialize(mock_crypto_session_, /*event_listener=*/nullptr,
&session_id)
.status_code(),
CasStatusCode::kNoError);
EXPECT_CALL(*mock_crypto_session_, LoadCasECMKeys(_, IsValidKeyEvenSlotData(),
IsValidKeyOddSlotData()));
wvcas::CasEcm ecm(184);
session.set_age_restriction(0); // No restriction.
// Different Ecm to make sure processEcm() processes this ecm.
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.processEcm(ecm, 0, kEmptyGroupId).status_code());
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.processEcm(ecm, 13, kEmptyGroupId).status_code());
// Parental control age must >= 10 (if non-zero).
session.set_age_restriction(10);
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.processEcm(ecm, 0, kEmptyGroupId).status_code());
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.processEcm(ecm, 10, kEmptyGroupId).status_code());
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.processEcm(ecm, 13, kEmptyGroupId).status_code());
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kAccessDeniedByParentalControl,
session.processEcm(ecm, 3, kEmptyGroupId).status_code());
EXPECT_CALL(*mock_crypto_session_, RemoveEntitledKeySession(_));
}
TEST_F(CasSessionTest, FingerprintingSuccess) {
TestCasSession session;
auto mock_crypto = std::make_shared<MockCryptoSession>();
MockEventListener mock_listener;
WvCasSessionId 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, kEmptyGroupId);
}
TEST_F(CasSessionTest, RepeatedFingerprintingNoEventSuccess) {
TestCasSession session;
auto mock_crypto = std::make_shared<MockCryptoSession>();
MockEventListener mock_listener;
WvCasSessionId 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, kEmptyGroupId);
// Same fingerprinting will not trigger event.
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(0);
session.processEcm(wvcas::CasEcm(184, '1'), 0, kEmptyGroupId);
}
TEST_F(CasSessionTest, DifferentFingerprintingTriggerEventSuccess) {
TestCasSession session;
auto mock_crypto = std::make_shared<MockCryptoSession>();
MockEventListener mock_listener;
WvCasSessionId 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, kEmptyGroupId);
// Different fingerprinting will trigger event.
session.set_fingerprinting_control("control2");
EXPECT_CALL(mock_listener, OnSessionFingerprintingUpdated).Times(1);
session.processEcm(wvcas::CasEcm(184, '1'), 0, kEmptyGroupId);
// 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, kEmptyGroupId);
}
TEST_F(CasSessionTest, ServiceBlockingSuccess) {
TestCasSession session;
auto mock_crypto = std::make_shared<MockCryptoSession>();
MockEventListener mock_listener;
WvCasSessionId 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, kEmptyGroupId);
}
TEST_F(CasSessionTest, RepeatedServiceBlockingNoEventSuccess) {
TestCasSession session;
auto mock_crypto = std::make_shared<MockCryptoSession>();
MockEventListener mock_listener;
WvCasSessionId 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, kEmptyGroupId);
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(0);
session.processEcm(wvcas::CasEcm(184, '1'), 0, kEmptyGroupId);
}
TEST_F(CasSessionTest, DifferentServiceBlockingTriggerEventSuccess) {
TestCasSession session;
auto mock_crypto = std::make_shared<MockCryptoSession>();
MockEventListener mock_listener;
WvCasSessionId 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, kEmptyGroupId);
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
session.set_service_blocking_groups({"Group1"});
session.processEcm(wvcas::CasEcm(184, '1'), 0, kEmptyGroupId);
EXPECT_CALL(mock_listener, OnSessionServiceBlockingUpdated).Times(1);
session.set_service_blocking_groups({});
session.processEcm(wvcas::CasEcm(184, '2'), 0, kEmptyGroupId);
}
} // namespace
} // namespace wvcas

View File

@@ -0,0 +1,227 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "widevine_media_cas_plugin.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/String8.h>
#include "cas_events.h"
#include "cas_status.h"
#include "media/cas/CasAPI.h"
#include "media/stagefright/MediaErrors.h"
#include "widevine_cas_api.h"
namespace android {
// Minimalist implementation of Android string class to support test.
std::map<const String8*, std::unique_ptr<std::string> > string8s;
String8::String8(const String8& value)
: String8(value.c_str(), value.length()) {}
String8::String8(char const* data, size_t data_length) {
auto result =
string8s.emplace(this, make_unique<std::string>(data, data_length));
mString = result.first->second->data();
}
size_t String8::length() const {
auto it = string8s.find(this);
return it == string8s.end() ? 0 : it->second->size();
}
String8::~String8() { string8s.erase(this); }
} // namespace android
namespace wvcas {
namespace {
using ::testing::_;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::SetArgReferee;
class MockWidevineCas : public WidevineCas {
public:
MockWidevineCas() {}
~MockWidevineCas() override {}
MOCK_METHOD(CasStatus, openSession, (WvCasSessionId * sessionId), (override));
MOCK_METHOD(bool, is_provisioned, (), (const, override));
MOCK_METHOD(CasStatus, generateEntitlementRequest,
(const std::string& init_data, std::string* entitlement_request,
std::string& license_id),
(override));
MOCK_METHOD(CasStatus, RecordLicenseId, (const std::string& license_id),
(override));
MOCK_METHOD(CasStatus, handleEntitlementResponse,
(const std::string& response, std::string& license_id,
std::string& multi_content_license_info,
std::string& group_license_info),
(override));
};
// Override WidevineCasPlugin to set WidevineCas and mock callbacks.
class TestWidevineCasPlugin : public WidevineCasPlugin {
public:
TestWidevineCasPlugin() : WidevineCasPlugin() {}
~TestWidevineCasPlugin() override {}
void SetWidevineCasApi(
std::unique_ptr<WidevineCas> widevine_cas_api) override {
WidevineCasPlugin::SetWidevineCasApi(std::move(widevine_cas_api));
}
MOCK_METHOD(void, CallBack,
(void* appData, int32_t event, int32_t arg, uint8_t* data,
size_t size, const CasSessionId* sessionId),
(const, override));
};
TEST(WidevineCasPluginTest, openSessionSuccess) {
TestWidevineCasPlugin plugin;
auto pass_through_cas_api = make_unique<MockWidevineCas>();
MockWidevineCas* cas_api = pass_through_cas_api.get();
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
const std::vector<uint8_t> expected_android_session_id = {0x78, 0x56, 0x34,
0x12};
const int32_t created_session_id = 0x12345678;
EXPECT_CALL(*cas_api, is_provisioned).WillOnce(Return(true));
EXPECT_CALL(*cas_api, openSession(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(expected_android_session_id),
Return(CasStatus::OkStatus())));
EXPECT_CALL(plugin, CallBack(_, CAS_SESSION_ID, created_session_id, NotNull(),
expected_android_session_id.size(), IsNull()));
std::vector<uint8_t> session_id;
EXPECT_EQ(plugin.openSession(&session_id), android::OK);
EXPECT_EQ(session_id, expected_android_session_id);
}
TEST(WidevineCasPluginTest, openSessionWithoutProvisionFail) {
TestWidevineCasPlugin plugin;
auto pass_through_cas_api = make_unique<MockWidevineCas>();
MockWidevineCas* cas_api = pass_through_cas_api.get();
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
EXPECT_CALL(*cas_api, is_provisioned).WillOnce(Return(false));
EXPECT_CALL(*cas_api, openSession(NotNull())).Times((0));
std::vector<uint8_t> session_id;
EXPECT_EQ(plugin.openSession(&session_id),
android::ERROR_CAS_NOT_PROVISIONED);
}
TEST(WidevineCasPluginTest,
provisionWithProvisionStringAlreadyProvisionedSuccess) {
TestWidevineCasPlugin plugin;
auto pass_through_cas_api = make_unique<MockWidevineCas>();
MockWidevineCas* cas_api = pass_through_cas_api.get();
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
const std::string provision_string = "init_data";
EXPECT_CALL(*cas_api, is_provisioned).WillOnce(Return(true));
EXPECT_CALL(plugin, CallBack(_, INDIVIDUALIZATION_COMPLETE, _, _, _, _));
// Provision string is init data; it triggers license request.
EXPECT_CALL(*cas_api,
generateEntitlementRequest(Eq(provision_string), NotNull(), _))
.WillOnce(DoAll(SetArgPointee<1>("signed_license_request"),
Return(CasStatus::OkStatus())));
EXPECT_CALL(plugin, CallBack(_, LICENSE_REQUEST, _, _, _, _));
EXPECT_EQ(plugin.provision(android::String8(provision_string.c_str(),
provision_string.size())),
android::OK);
}
TEST(WidevineCasPluginTest, HandleAssignLicenseIDSuccess) {
TestWidevineCasPlugin plugin;
auto pass_through_cas_api = make_unique<MockWidevineCas>();
MockWidevineCas* cas_api = pass_through_cas_api.get();
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
const std::string license_id = "license_id";
EXPECT_CALL(*cas_api, RecordLicenseId(license_id))
.WillOnce(Return(CasStatus::OkStatus()));
EXPECT_CALL(plugin, CallBack(_, LICENSE_ID_ASSIGNED, _, _, _, _)).Times(1);
EXPECT_EQ(plugin.sendEvent(ASSIGN_LICENSE_ID, /*arg=*/0,
{license_id.begin(), license_id.end()}),
android::OK);
}
TEST(WidevineCasPluginTest, HandleAssignLicenseIDApiError) {
TestWidevineCasPlugin plugin;
auto pass_through_cas_api = make_unique<MockWidevineCas>();
MockWidevineCas* cas_api = pass_through_cas_api.get();
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
const std::string license_id = "license_id";
EXPECT_CALL(*cas_api, RecordLicenseId)
.WillOnce(Return(CasStatus(CasStatusCode::kInvalidParameter, "invalid")));
EXPECT_CALL(plugin, CallBack(_, CAS_ERROR, _, _, _, _)).Times(1);
EXPECT_NE(plugin.sendEvent(ASSIGN_LICENSE_ID, /*arg=*/0,
{license_id.begin(), license_id.end()}),
android::OK);
}
TEST(WidevineCasPluginTest, HandleEntitlementResponseSuccess) {
TestWidevineCasPlugin plugin;
auto pass_through_cas_api = make_unique<MockWidevineCas>();
MockWidevineCas* cas_api = pass_through_cas_api.get();
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
const std::string license = "license";
const std::string license_id = "id";
const std::string multi_content_license_info = "info";
const std::string group_license_info = "info2";
EXPECT_CALL(*cas_api, handleEntitlementResponse(_, _, _, _))
.WillOnce(DoAll(SetArgReferee<1>(license_id),
SetArgReferee<2>(multi_content_license_info),
SetArgReferee<3>(group_license_info),
Return(CasStatus::OkStatus())));
EXPECT_CALL(plugin,
CallBack(_, LICENSE_CAS_READY, _, _, license_id.size(), _))
.Times(1);
EXPECT_CALL(plugin, CallBack(_, MULTI_CONTENT_LICENSE_INFO, _, _,
multi_content_license_info.size(), _))
.Times(1);
EXPECT_CALL(plugin, CallBack(_, GROUP_LICENSE_INFO, _, _,
group_license_info.size(), _))
.Times(1);
EXPECT_EQ(plugin.sendEvent(LICENSE_RESPONSE, /*arg=*/0,
{license.begin(), license.end()}),
android::OK);
}
TEST(WidevineCasPluginTest, HandleEntitlementResponseEmptyResponseFail) {
TestWidevineCasPlugin plugin;
auto pass_through_cas_api = make_unique<MockWidevineCas>();
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
EXPECT_CALL(plugin, CallBack(_, CAS_ERROR, _, _, _, _)).Times(1);
EXPECT_NE(plugin.sendEvent(LICENSE_RESPONSE, /*arg=*/0, /*eventData=*/{}),
android::OK);
}
TEST(WidevineCasPluginTest, HandlePluginVersionQuerySuccess) {
TestWidevineCasPlugin plugin;
auto pass_through_cas_api = make_unique<MockWidevineCas>();
plugin.SetWidevineCasApi(std::move(pass_through_cas_api));
std::string expected_version = "uint-test";
EXPECT_CALL(plugin, CallBack(_, WV_CAS_PLUGIN_VERSION, _, NotNull(),
expected_version.size(), _))
.Times(1);
EXPECT_EQ(plugin.sendEvent(QUERY_WV_CAS_PLUGIN_VERSION, /*arg=*/0,
/*eventData=*/{}),
android::OK);
}
} // namespace
} // namespace wvcas

View File

@@ -0,0 +1,20 @@
// 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 <gtest/gtest.h>
#include <iostream>
#include "OEMCryptoCENC.h"
#include "log.h"
namespace wvutil {
extern LogPriority g_cutoff;
} // namespace wvutil
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
wvutil::g_cutoff = wvutil::CDM_LOG_INFO;
return RUN_ALL_TESTS();
}