Widevine MediaCas client code that works with Android R

This commit is contained in:
Lu Chen
2020-08-13 15:18:12 -07:00
parent ff9728aaa2
commit 0f6db6f751
243 changed files with 47012 additions and 0 deletions

34
tests/Android.bp Normal file
View File

@@ -0,0 +1,34 @@
cc_binary {
name: "wv_cas_tests",
proprietary: true,
srcs: [
"src/wv_cas_test_main.cpp",
"src/cas_license_test.cpp",
"src/crypto_session_test.cpp",
"src/ecm_parser_test.cpp",
"src/test_properties.cpp",
"src/widevine_cas_session_test.cpp",
"src/cas_session_map_test.cpp",
"src/license_key_status_test.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",
],
proto: {
type: "lite",
},
}

View File

@@ -0,0 +1,629 @@
// 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include <openssl/sha.h>
#include <memory>
#include "cas_license.h"
#include "cas_status.h"
#include "cas_util.h"
#include "crypto_key.h"
#include "device_files.pb.h"
#include "license_protocol.pb.h"
#include "mock_crypto_session.h"
#include "string_conversions.h"
// Prototype for ExtractEntitlementKeys. This prototype is added here to allow
// this method to be unit tested without being added to CasLicense header.
namespace wvcas {
std::vector<wvcas::CryptoKey> ExtractEntitlementKeys(
const video_widevine::License& license);
} // namespace wvcas
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::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(CasLicenseUtilityTest, ExtractEntitlementKeys) {
video_widevine::License license;
auto* key = license.add_key();
key->set_type(video_widevine::License::KeyContainer::ENTITLEMENT);
key->set_id(kKeyIDVideo);
key->set_iv(kKeyVideoIV);
key->mutable_key_control()->set_key_control_block(kKeyControlVideo);
key->mutable_key_control()->set_iv(kKeyControlIVVideo);
key->set_track_label(kTrackTypeVideo);
key = license.add_key();
key->set_type(video_widevine::License::KeyContainer::ENTITLEMENT);
key->set_id(kKeyIDAudio);
key->set_iv(kKeyAudioIV);
key->mutable_key_control()->set_key_control_block(kKeyControlAudio);
key->mutable_key_control()->set_iv(kKeyControlIVAudio);
key->set_track_label(kTrackTypeAudio);
std::vector<wvcas::CryptoKey> keys = wvcas::ExtractEntitlementKeys(license);
ASSERT_EQ(2, keys.size());
EXPECT_EQ(kKeyIDVideo, keys[0].key_id());
EXPECT_EQ(kKeyVideoIV, keys[0].key_data_iv());
EXPECT_EQ(kKeyControlVideo, keys[0].key_control());
EXPECT_EQ(kKeyControlIVVideo, keys[0].key_control_iv());
EXPECT_EQ(kTrackTypeVideo, keys[0].track_label());
EXPECT_EQ(kKeyIDAudio, keys[1].key_id());
EXPECT_EQ(kKeyAudioIV, keys[1].key_data_iv());
EXPECT_EQ(kKeyControlAudio, keys[1].key_control());
EXPECT_EQ(kKeyControlIVAudio, keys[1].key_control_iv());
EXPECT_EQ(kTrackTypeAudio, keys[1].track_label());
}
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),
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_, GenerateNonce(_))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_,
PrepareAndSignLicenseRequest(_, NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(kExpectedSignature),
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_, GenerateNonce(_))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_CALL(*strict_mock_,
PrepareAndSignLicenseRequest(_, NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(kExpectedSignature),
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, nullptr);
EXPECT_EQ(wvcas::CasStatusCode::kNoError, status.status_code());
// 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());
}

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) {
int base_key = rand();
std::string key = std::to_string(base_key);
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,292 @@
// 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 <arpa/inet.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <bitset>
namespace {
static constexpr int kCaIdSizeBytes = 2;
static constexpr int kModeSizeBytes = 1;
static constexpr int kVersionSizeBytes = 1;
static constexpr int kIVFlagsSizeBytes = 1;
static constexpr int kEntitlementKeyIDSizeBytes = 16;
static constexpr int kContentKeyIDSizeBytes = 16;
static constexpr int kContentKeyDataSize = 16;
static constexpr int kWrappedKeyIVSizeBytes = 16;
static constexpr int kEcmDescriptorSizeBytes =
kCaIdSizeBytes + kModeSizeBytes + kVersionSizeBytes + kIVFlagsSizeBytes;
static constexpr int kECMVersion = 2;
// The cipher mode flags field in the ECM V2 is 4 bits.
static constexpr uint8_t kAESCBCCryptoModeFlagsVal = (0x0 << 1);
static constexpr uint8_t kAESCTRCryptoModeFlagsVal = (0x1 << 1);
static constexpr uint8_t kDvbCsa2CryptoModeFlagsVal = (0x2 << 1);
static constexpr uint8_t kDvbCsa3CryptoModeFlagsVal = (0x3 << 1);
static constexpr uint8_t kDvbOFBCryptoModeFlagsVal = (0x4 << 1);
static constexpr uint8_t kDvbSCTECryptoModeFlagsVal = (0x5 << 1);
static constexpr uint8_t kRotationFlag = (0x1 << 0);
static constexpr uint8_t kContentIVSizeFlag = (0x1 << 6);
static constexpr uint8_t kEntitlementKeyIDFill = '1';
static constexpr uint8_t kEvenContentKeyIDFill = '2';
static constexpr uint8_t kEvenContentKeyDataFill = '3';
static constexpr uint8_t kEvenWrappedKeyIVFill = '4';
static constexpr uint8_t kEvenContentKeyIVFill = '5';
static constexpr uint8_t kOddContentKeyIDFill = '6';
static constexpr uint8_t kOddContentKeyDataFill = '7';
static constexpr uint8_t kOddWrappedKeyIVFill = '8';
static constexpr uint8_t kOddContentKeyIVFill = '9';
static constexpr size_t kMaxEcmSizeBytes = 184;
static constexpr uint16_t kSectionHeader1 = 0x80;
static constexpr uint16_t kSectionHeader2 = 0x81;
static constexpr size_t kSectionHeaderSize = 4;
} // namespace
class EcmParserTest : public testing::Test {
public:
void SetUp() { BuildEcm(); }
size_t ContentKeyIVSize(bool content_iv_flag);
size_t CalculateEcmSize(bool with_rotation, bool content_iv_flag = false);
std::vector<uint8_t> ecm_data_;
private:
void BuildEcm(bool with_rotation = true, bool content_iv_flag = false);
};
size_t EcmParserTest::ContentKeyIVSize(bool content_iv_flag) {
// Content key iv is 8 bytes if Content_IV flag is zero, and 16 bytes
// othersize.
return content_iv_flag ? 16 : 8;
}
size_t EcmParserTest::CalculateEcmSize(bool with_rotation,
bool content_iv_flag) {
size_t ecm_key_data_size =
kContentKeyIDSizeBytes + kContentKeyDataSize + kWrappedKeyIVSizeBytes +
kEntitlementKeyIDSizeBytes + ContentKeyIVSize(content_iv_flag);
return kEcmDescriptorSizeBytes + ecm_key_data_size * (with_rotation ? 2 : 1);
}
void EcmParserTest::BuildEcm(bool with_rotation, bool content_iv_flag) {
ecm_data_.clear();
ecm_data_.reserve(CalculateEcmSize(with_rotation, content_iv_flag));
ecm_data_.resize(kCaIdSizeBytes, 0);
ecm_data_[0] = 0x4A;
ecm_data_[1] = 0xD4;
ecm_data_.resize(ecm_data_.size() + kVersionSizeBytes, kECMVersion);
ecm_data_.resize(ecm_data_.size() + kModeSizeBytes,
kAESCBCCryptoModeFlagsVal);
uint8_t iv_flag_value = content_iv_flag ? kContentIVSizeFlag : 0;
ecm_data_.resize(ecm_data_.size() + kIVFlagsSizeBytes, iv_flag_value);
ASSERT_EQ(kEcmDescriptorSizeBytes, ecm_data_.size());
// Even key fields.
ecm_data_.resize(ecm_data_.size() + kEntitlementKeyIDSizeBytes,
kEntitlementKeyIDFill);
ecm_data_.resize(ecm_data_.size() + kContentKeyIDSizeBytes,
kEvenContentKeyIDFill);
ecm_data_.resize(ecm_data_.size() + kContentKeyDataSize,
kEvenContentKeyDataFill);
ecm_data_.resize(ecm_data_.size() + kWrappedKeyIVSizeBytes,
kEvenWrappedKeyIVFill);
ecm_data_.resize(ecm_data_.size() + ContentKeyIVSize(content_iv_flag),
kEvenContentKeyIVFill);
ASSERT_EQ(CalculateEcmSize(false, content_iv_flag), ecm_data_.size());
if (with_rotation) {
// Entitlement key id field for odd key.
ecm_data_.resize(ecm_data_.size() + kEntitlementKeyIDSizeBytes,
kEntitlementKeyIDFill);
ecm_data_.resize(ecm_data_.size() + kContentKeyIDSizeBytes,
kOddContentKeyIDFill);
ecm_data_.resize(ecm_data_.size() + kContentKeyDataSize,
kOddContentKeyDataFill);
ecm_data_.resize(ecm_data_.size() + kWrappedKeyIVSizeBytes,
kOddWrappedKeyIVFill);
ecm_data_.resize(ecm_data_.size() + ContentKeyIVSize(content_iv_flag),
kOddContentKeyIVFill);
ASSERT_EQ(CalculateEcmSize(true, content_iv_flag), ecm_data_.size());
}
}
TEST_F(EcmParserTest, FieldsWithoutKeyRotation) {
bool content_key_iv_16b = false;
ecm_data_.resize(CalculateEcmSize(false, content_key_iv_16b));
std::unique_ptr<const wvcas::EcmParser> parser;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
ASSERT_FALSE(parser->rotation_enabled());
std::vector<uint8_t> test_data;
test_data.resize(kEntitlementKeyIDSizeBytes, kEntitlementKeyIDFill);
EXPECT_EQ(test_data,
parser->entitlement_key_id(wvcas::KeySlotId::kEvenKeySlot));
test_data.clear();
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyIDFill);
EXPECT_EQ(test_data, parser->content_key_id(wvcas::KeySlotId::kEvenKeySlot));
test_data.clear();
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyDataFill);
EXPECT_EQ(test_data,
parser->wrapped_key_data(wvcas::KeySlotId::kEvenKeySlot));
test_data.clear();
test_data.resize(kWrappedKeyIVSizeBytes, kEvenWrappedKeyIVFill);
EXPECT_EQ(test_data, parser->wrapped_key_iv(wvcas::KeySlotId::kEvenKeySlot));
test_data.clear();
test_data.resize(ContentKeyIVSize(content_key_iv_16b), kEvenContentKeyIVFill);
EXPECT_EQ(test_data, parser->content_iv(wvcas::KeySlotId::kEvenKeySlot));
EXPECT_TRUE(parser->content_key_id(wvcas::KeySlotId::kOddKeySlot).empty());
EXPECT_TRUE(parser->wrapped_key_data(wvcas::KeySlotId::kOddKeySlot).empty());
EXPECT_TRUE(parser->wrapped_key_iv(wvcas::KeySlotId::kOddKeySlot).empty());
EXPECT_TRUE(parser->content_iv(wvcas::KeySlotId::kOddKeySlot).empty());
}
TEST_F(EcmParserTest, FieldsWithKeyRotation) {
ecm_data_[3] |= kRotationFlag;
std::unique_ptr<const wvcas::EcmParser> parser;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
ASSERT_TRUE(parser->rotation_enabled());
std::vector<uint8_t> test_data;
test_data.resize(kEntitlementKeyIDSizeBytes, kEntitlementKeyIDFill);
EXPECT_EQ(test_data,
parser->entitlement_key_id(wvcas::KeySlotId::kEvenKeySlot));
test_data.clear();
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyIDFill);
EXPECT_EQ(test_data, parser->content_key_id(wvcas::KeySlotId::kEvenKeySlot));
test_data.clear();
test_data.resize(kContentKeyIDSizeBytes, kEvenContentKeyDataFill);
EXPECT_EQ(test_data,
parser->wrapped_key_data(wvcas::KeySlotId::kEvenKeySlot));
test_data.clear();
test_data.resize(kWrappedKeyIVSizeBytes, kEvenWrappedKeyIVFill);
EXPECT_EQ(test_data, parser->wrapped_key_iv(wvcas::KeySlotId::kEvenKeySlot));
test_data.clear();
bool content_key_iv_16b = false;
test_data.resize(ContentKeyIVSize(content_key_iv_16b), kEvenContentKeyIVFill);
EXPECT_EQ(test_data, parser->content_iv(wvcas::KeySlotId::kEvenKeySlot));
test_data.clear();
test_data.resize(kContentKeyIDSizeBytes, kOddContentKeyIDFill);
EXPECT_EQ(test_data, parser->content_key_id(wvcas::KeySlotId::kOddKeySlot));
test_data.clear();
test_data.resize(kContentKeyIDSizeBytes, kOddContentKeyDataFill);
EXPECT_EQ(test_data, parser->wrapped_key_data(wvcas::KeySlotId::kOddKeySlot));
test_data.clear();
test_data.resize(kWrappedKeyIVSizeBytes, kOddWrappedKeyIVFill);
EXPECT_EQ(test_data, parser->wrapped_key_iv(wvcas::KeySlotId::kOddKeySlot));
test_data.clear();
test_data.resize(ContentKeyIVSize(content_key_iv_16b), kOddContentKeyIVFill);
EXPECT_EQ(test_data, parser->content_iv(wvcas::KeySlotId::kOddKeySlot));
}
TEST_F(EcmParserTest, create) {
std::unique_ptr<const wvcas::EcmParser> parser;
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
ecm_data_.resize(4);
EXPECT_FALSE(wvcas::EcmParser::create(ecm_data_, &parser));
ecm_data_.resize(4 + CalculateEcmSize(false));
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
ecm_data_.resize(kMaxEcmSizeBytes);
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
ecm_data_.resize(CalculateEcmSize(true));
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_FALSE(wvcas::EcmParser::create(ecm_data_, nullptr));
ecm_data_.resize(CalculateEcmSize(true));
EXPECT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_FALSE(wvcas::EcmParser::create(ecm_data_, nullptr));
}
TEST_F(EcmParserTest, crypto_mode) {
std::unique_ptr<const wvcas::EcmParser> parser;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(parser->crypto_mode(), wvcas::CryptoMode::kAesCBC);
ecm_data_[3] = kAESCTRCryptoModeFlagsVal;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(parser->crypto_mode(), wvcas::CryptoMode::kAesCTR);
ecm_data_[3] = kDvbCsa2CryptoModeFlagsVal;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(parser->crypto_mode(), wvcas::CryptoMode::kDvbCsa2);
ecm_data_[3] = kDvbCsa3CryptoModeFlagsVal;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(parser->crypto_mode(), wvcas::CryptoMode::kDvbCsa3);
ecm_data_[3] = kDvbOFBCryptoModeFlagsVal;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(parser->crypto_mode(), wvcas::CryptoMode::kAesOFB);
ecm_data_[3] = kDvbSCTECryptoModeFlagsVal;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(parser->crypto_mode(), wvcas::CryptoMode::kAesSCTE);
}
TEST_F(EcmParserTest, ContentKeyIVSizes) {
std::unique_ptr<const wvcas::EcmParser> parser;
bool with_rotation = true;
bool iv_flag = false;
ecm_data_.resize(CalculateEcmSize(with_rotation, iv_flag));
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(parser->content_iv_size(), ContentKeyIVSize(iv_flag));
iv_flag = true;
ecm_data_[4] = kContentIVSizeFlag;
ecm_data_.resize(CalculateEcmSize(with_rotation, iv_flag));
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(parser->content_iv_size(), ContentKeyIVSize(iv_flag));
}
TEST_F(EcmParserTest, EcmWithSectionHeader) {
std::unique_ptr<const wvcas::EcmParser> parser;
std::vector<uint8_t> section_header;
section_header.resize(kSectionHeaderSize);
*reinterpret_cast<uint32_t*>(section_header.data()) = htons(kSectionHeader1);
// If ECM is prepended with section header, parsing must still work.
ecm_data_.insert(ecm_data_.begin(), section_header.begin(),
section_header.end());
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
*((uint16_t*)ecm_data_.data()) = htons(kSectionHeader2);
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
// Change header size, parsing should still work
ecm_data_.erase(ecm_data_.begin());
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
}
TEST_F(EcmParserTest, AgeRestriction) {
std::unique_ptr<const wvcas::EcmParser> parser;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(0, parser->age_restriction());
uint8_t age_restriction = 16;
ecm_data_[4] |= age_restriction << 1;
ASSERT_TRUE(wvcas::EcmParser::create(ecm_data_, &parser));
EXPECT_EQ(age_restriction, parser->age_restriction());
}

View File

@@ -0,0 +1,934 @@
// 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include <vector>
#include "cas_types.h"
#include "license_key_status.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,143 @@
// Dynamically generated header created during build. The Runtest entry point is
// defined in gopkg_carchive.go
#include "gowvcas_carchive.h"
#include "gtest/gtest.h"
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, TestDescramblerFactoryCreation) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestDescramblerFactoryCreation"));
}
TEST(IntegrationTests, TestCreateCasPlugin) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCreateCasPlugin"));
}
TEST(IntegrationTests, TestCreateCasPluginWithSessionEvent) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestCreateCasPluginWithSessionEvent"));
}
TEST(IntegrationTests, TestCasPluginEventPassing) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCasPluginEventPassing"));
}
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, TestCasPrivateDataWithGroupLicense) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestCasPrivateDataWithGroupLicense"));
}
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, TestDescrambler) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestDescrambler"));
}
TEST(IntegrationTests, TestSession) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestSession"));
}
TEST(IntegrationTests, TestPESDecrypt) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestPESDecrypt"));
}
TEST(IntegrationTests, TestConcatentatedPESDecrypt) {
EXPECT_EQ(kIntegrationTestPassed,
RunNamedTest("TestConcatentatedPESDecrypt"));
}
TEST(IntegrationTests, TestPlaybackDecrypt) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestPlaybackDecrypt"));
}
TEST(IntegrationTests, TestCasRenewal) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestCasRenewal"));
}
TEST(IntegrationTests, TestRestoreRenewalAndExpiredLicense) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestRestoreRenewalAndExpiredLicense"));
}
TEST(IntegrationTests, TestPesHeaderDecrypt) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestPesHeaderDecrypt"));
}
TEST(IntegrationTests, TestPesHeaderDecryptInTSPacket) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestPesHeaderDecryptInTSPacket"));
}
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, TestTSDecrypt) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestTSDecrypt"));
}
TEST(IntegrationTests, TestTSDecryptWithKeyRotation) {
EXPECT_EQ(kIntegrationTestPassed, RunNamedTest("TestTSDecryptWithKeyRotation"));
}

View File

@@ -0,0 +1,86 @@
// 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(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_METHOD3(PrepareAndSignLicenseRequest,
wvcas::CasStatus(const std::string& message,
std::string* core_message,
std::string* signature));
MOCK_METHOD3(PrepareAndSignRenewalRequest,
wvcas::CasStatus(const std::string& message,
std::string* core_message,
std::string* signature));
MOCK_METHOD3(PrepareAndSignProvisioningRequest,
wvcas::CasStatus(const std::string& message,
std::string* core_message,
std::string* signature));
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_METHOD3(SelectKey, wvcas::CasStatus(OEMCrypto_SESSION session,
const std::vector<uint8_t>& key_id,
wvcas::CryptoMode crypto_mode));
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));
};
#endif // MOCK_CRYPTO_SESSION_H

View File

@@ -0,0 +1,356 @@
#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"
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 MockEventListener : public wvcas::CasEventListener {
public:
MockEventListener() {}
virtual ~MockEventListener() {}
MOCK_METHOD0(OnSessionRenewalNeeded, void());
MOCK_METHOD2(OnSessionKeysChange,
void(const KeyStatusMap& keys_status, bool has_new_usable_key));
MOCK_METHOD1(OnExpirationUpdate, void(int64_t new_expiry_time_seconds));
MOCK_METHOD1(OnNewRenewalServerUrl,
void(const std::string& renewal_server_url));
MOCK_METHOD0(OnLicenseExpiration, void());
MOCK_METHOD2(OnAgeRestrictionUpdated,
void(const wvcas::WvCasSessionId& sessionId,
uint8_t ecm_age_restriction));
};
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,66 @@
// 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;
}
} // namespace wvcas

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

@@ -0,0 +1,34 @@
// 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "clock.h"
#include "timer.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());
}

View File

@@ -0,0 +1,562 @@
// 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include <openssl/sha.h>
#include "cas_util.h"
#include "cas_license.h"
#include "ecm_parser.h"
#include "mock_crypto_session.h"
#include "string_conversions.h"
#include "widevine_cas_api.h"
#include "widevine_cas_session_map.h"
using ::testing::_;
using ::testing::DoAll;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
typedef StrictMock<MockCryptoSession> StrictMockCryptoSession;
class MockLicense : public wvcas::CasLicense {
public:
MockLicense() {}
~MockLicense() override {}
MOCK_CONST_METHOD0(IsExpired, bool());
MOCK_METHOD5(GenerateEntitlementRequest,
wvcas::CasStatus(const std::string& init_data,
const std::string& device_certificate,
const std::string& wrapped_rsa_key,
wvcas::LicenseType license_type,
std::string* signed_license_request));
MOCK_METHOD2(HandleStoredLicense,
wvcas::CasStatus(const std::string& wrapped_rsa_key,
const std::string& license_file));
MOCK_METHOD2(GenerateEntitlementRenewalRequest,
wvcas::CasStatus(const std::string& device_certificate,
std::string* signed_renewal_request));
MOCK_METHOD2(HandleEntitlementRenewalResponse,
wvcas::CasStatus(const std::string& renewal_response,
std::string* device_file));
MOCK_METHOD2(HandleEntitlementResponse,
wvcas::CasStatus(const std::string& entitlement_response,
std::string* device_file));
MOCK_METHOD0(BeginDecryption, void());
MOCK_METHOD0(UpdateLicenseForLicenseRemove, void());
};
typedef StrictMock<MockLicense> StrictMockLicense;
class MockEventListener : public wvcas::CasEventListener {
public:
MockEventListener() {}
~MockEventListener() override {}
MOCK_METHOD0(OnSessionRenewalNeeded, void());
MOCK_METHOD2(OnSessionKeysChange, void(const wvcas::KeyStatusMap& keys_status,
bool has_new_usable_key));
MOCK_METHOD1(OnExpirationUpdate, void(int64_t new_expiry_time_seconds));
MOCK_METHOD1(OnNewRenewalServerUrl,
void(const std::string& renewal_server_url));
MOCK_METHOD0(OnLicenseExpiration, void());
MOCK_METHOD2(OnAgeRestrictionUpdated,
void(const wvcas::WvCasSessionId& sessionId,
uint8_t ecm_age_restriction));
};
typedef StrictMock<MockEventListener> StrictMockEventListener;
class MockFile : public wvutil::File {
public:
MockFile() {}
~MockFile() override {}
MOCK_METHOD2(Read, ssize_t(char* buffer, size_t bytes));
MOCK_METHOD2(Write, ssize_t(const char* buffer, size_t bytes));
MOCK_METHOD0(Close, void());
};
typedef StrictMock<MockFile> StrictMockFile;
class MockFileSystem : public wvutil::FileSystem {
public:
MockFileSystem() {}
~MockFileSystem() override {}
// Until gmock is updated to a version post-April 2017, we need this
// workaround to test functions that take or return smart pointers.
// See
// https://github.com/abseil/googletest/blob/master/googlemock/docs/CookBook.md#legacy-workarounds-for-move-only-types
std::unique_ptr<wvutil::File> Open(const std::string& buffer, int flags) {
return std::unique_ptr<wvutil::File>(DoOpen(buffer, flags));
}
MOCK_METHOD2(DoOpen, wvutil::File*(const std::string& file_path, int flags));
MOCK_METHOD1(Exists, bool(const std::string& file_path));
MOCK_METHOD1(Remove, bool(const std::string& file_path));
MOCK_METHOD1(FileSize, ssize_t(const std::string& file_path));
MOCK_METHOD2(List, bool(const std::string& dir_path,
std::vector<std::string>* names));
MOCK_CONST_METHOD0(origin, const std::string&());
MOCK_METHOD1(SetOrigin, void(const std::string& origin));
MOCK_CONST_METHOD0(identifier, const std::string&());
MOCK_METHOD1(SetIdentifier, void(const std::string& identifier));
MOCK_CONST_METHOD0(IsGlobal, bool());
};
typedef NiceMock<MockFileSystem> NiceMockFileSystem;
class MockWidevineSession : public wvcas::WidevineCasSession {
class EcmParser : public wvcas::EcmParser {
public:
EcmParser() {}
~EcmParser() override {}
};
public:
MockWidevineSession() {}
~MockWidevineSession() override {}
std::unique_ptr<const wvcas::EcmParser> getEcmParser(
const wvcas::CasEcm& ecm) const override {
return make_unique<EcmParser>();
}
MOCK_METHOD2(processEcm, wvcas::CasStatus(const wvcas::CasEcm& ecm,
uint8_t parental_control_age));
MOCK_METHOD2(HandleProcessEcm,
wvcas::CasStatus(const wvcas::WvCasSessionId& sessionId,
const wvcas::CasEcm& ecm));
};
class TestWidevineCas : public wvcas::WidevineCas {
public:
TestWidevineCas() {
pass_thru_license_ = make_unique<StrictMockLicense>();
pass_thru_crypto_session_ = make_unique<StrictMockCryptoSession>();
pass_thru_file_system_ = make_unique<NiceMockFileSystem>();
crypto_session_ = pass_thru_crypto_session_.get();
license_ = pass_thru_license_.get();
file_system_ = pass_thru_file_system_.get();
}
~TestWidevineCas() override {}
std::shared_ptr<wvcas::CryptoSession> getCryptoSession() override {
return std::move(pass_thru_crypto_session_);
}
std::unique_ptr<wvcas::CasLicense> getCasLicense() override {
return std::move(pass_thru_license_);
}
std::unique_ptr<wvutil::FileSystem> getFileSystem() override {
return std::move(pass_thru_file_system_);
}
std::shared_ptr<wvcas::WidevineCasSession> newCasSession() override {
return std::make_shared<StrictMock<MockWidevineSession> >();
}
std::unique_ptr<StrictMockLicense> pass_thru_license_;
std::unique_ptr<StrictMockCryptoSession> pass_thru_crypto_session_;
std::unique_ptr<NiceMockFileSystem> pass_thru_file_system_;
StrictMockLicense* license_ = nullptr;
StrictMockCryptoSession* crypto_session_ = nullptr;
NiceMockFileSystem* file_system_ = nullptr;
};
struct EcmTestParams {
bool defer_ecm;
bool stored_license;
};
class WidevineCasTest : public testing::TestWithParam<EcmTestParams> {
public:
WidevineCasTest() {}
~WidevineCasTest() override {}
StrictMockEventListener event_listener_;
};
TEST_F(WidevineCasTest, initialize) {
TestWidevineCas cas_api;
EXPECT_CALL(*(cas_api.crypto_session_), initialize())
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.initialize(nullptr).status_code());
}
TEST_F(WidevineCasTest, openAndcloseSession) {
TestWidevineCas cas_api;
// Invalid
EXPECT_NE(wvcas::CasStatusCode::kNoError,
cas_api.openSession(nullptr).status_code());
EXPECT_NE(wvcas::CasStatusCode::kNoError,
cas_api.closeSession(wvcas::WvCasSessionId()).status_code());
// Valid
EXPECT_CALL(*(cas_api.crypto_session_), initialize())
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.initialize(nullptr).status_code());
EXPECT_CALL(*(cas_api.crypto_session_), CreateEntitledKeySession(_))
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
wvcas::WvCasSessionId sid;
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.openSession(&sid).status_code());
EXPECT_CALL(*(cas_api.crypto_session_), RemoveEntitledKeySession(_))
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.closeSession(sid).status_code());
}
TEST_F(WidevineCasTest, generateEntitlementRequest) {
TestWidevineCas cas_api;
EXPECT_CALL(*(cas_api.crypto_session_), initialize())
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.initialize(nullptr).status_code());
// Invalid parameter.
std::string request, init_data;
EXPECT_NE(
wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRequest(init_data, nullptr).status_code());
// GenerateEntitlementRequest returns an error.
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRequest(_, _, _, _, _))
.WillRepeatedly(Return(wvcas::CasStatus(
wvcas::CasStatusCode::kCasLicenseError, "forced failure")));
EXPECT_NE(
wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRequest(init_data, &request).status_code());
// HandleStoredLicense returns an error.
// Call to Open will return a unique_ptr, freeing this mock_file object.
MockFile* mock_file = new MockFile();
size_t mock_filesize = 10;
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*cas_api.file_system_, FileSize(_))
.WillRepeatedly(Return(mock_filesize));
EXPECT_CALL(*cas_api.file_system_, DoOpen(_, _))
.WillRepeatedly(Return(mock_file));
EXPECT_CALL(*mock_file, Read(_, _)).WillRepeatedly(Return(mock_filesize));
EXPECT_CALL(*cas_api.license_, HandleStoredLicense(_, _))
.WillRepeatedly(Return(wvcas::CasStatus(
wvcas::CasStatusCode::kCasLicenseError, "forced failure")));
EXPECT_NE(
wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRequest(init_data, &request).status_code());
mock_file = new MockFile();
EXPECT_CALL(*cas_api.file_system_,
DoOpen(_, _)).WillRepeatedly(Return(mock_file));
EXPECT_CALL(*mock_file, Read(_, _)).WillRepeatedly(Return(mock_filesize));
// For expired license file, remove it successfully
// and return CasLicenseError.
EXPECT_CALL(*cas_api.license_, HandleStoredLicense(_, _))
.WillRepeatedly(Return(wvcas::CasStatus::OkStatus()));
EXPECT_CALL(*cas_api.license_, IsExpired()).WillRepeatedly(Return(true));
EXPECT_CALL(*cas_api.file_system_, Remove(_)).WillRepeatedly(Return(true));
EXPECT_EQ(
wvcas::CasStatusCode::kCasLicenseError,
cas_api.generateEntitlementRequest(init_data, &request).status_code());
// Unable to remove the expired license file, return InvalidLicenseFile error.
mock_file = new MockFile();
EXPECT_CALL(*cas_api.file_system_,
DoOpen(_, _)).WillRepeatedly(Return(mock_file));
EXPECT_CALL(*mock_file, Read(_, _)).WillRepeatedly(Return(mock_filesize));
// Unable to remove the expired license file, return InvalidLicenseFile error
EXPECT_CALL(*cas_api.file_system_, Remove(_)).WillOnce(Return(false));
EXPECT_EQ(
wvcas::CasStatusCode::kInvalidLicenseFile,
cas_api.generateEntitlementRequest(init_data, &request).status_code());
// For stored license file not expired, return valid response.
mock_file = new MockFile();
EXPECT_CALL(*cas_api.file_system_,
DoOpen(_, _)).WillRepeatedly(Return(mock_file));
EXPECT_CALL(*mock_file, Read(_, _)).WillRepeatedly(Return(mock_filesize));
// For stored license file not expired, return valid response
EXPECT_CALL(*cas_api.license_, IsExpired()).WillRepeatedly(Return(false));
EXPECT_EQ(
wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRequest(init_data, &request).status_code());
// Valid response for new request.
mock_file = new MockFile();
EXPECT_CALL(*cas_api.file_system_,
DoOpen(_, _)).WillRepeatedly(Return(mock_file));
// Valid response for new request
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillRepeatedly(Return(false));
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRequest(_, _, _, _, _))
.WillRepeatedly(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(
wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRequest(init_data, &request).status_code());
}
TEST_F(WidevineCasTest, GenerateLicenseRenewal) {
TestWidevineCas cas_api;
EXPECT_CALL(*(cas_api.crypto_session_), initialize())
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.initialize(nullptr).status_code());
// Invalid parameter.
EXPECT_NE(wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRenewalRequest(nullptr).status_code());
// GenerateEntitlementRenewalRequest returns an error.
std::string request;
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRenewalRequest(_, _))
.WillOnce(Return(wvcas::CasStatus(wvcas::CasStatusCode::kCasLicenseError,
"forced failure")));
EXPECT_NE(wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRenewalRequest(&request).status_code());
// Valid
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRenewalRequest(_, _))
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRenewalRequest(&request).status_code());
}
TEST_F(WidevineCasTest, EntitlementRenewalResponse) {
TestWidevineCas cas_api;
EXPECT_CALL(*cas_api.crypto_session_, initialize())
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.initialize(nullptr).status_code());
// Empty response.
std::string init_data;
EXPECT_EQ(wvcas::CasStatusCode::kCasLicenseError,
cas_api.handleEntitlementRenewalResponse("", init_data).status_code());
// Valid.
EXPECT_CALL(*cas_api.license_, HandleEntitlementRenewalResponse(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.handleEntitlementRenewalResponse("response", init_data).status_code());
}
TEST_F(WidevineCasTest, RemoveExpiredLicenseInTimerEvent) {
TestWidevineCas cas_api;
EXPECT_CALL(*cas_api.crypto_session_, initialize())
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.initialize(nullptr).status_code());
// Unable to remove: License is expired but mediaId object is null.
EXPECT_CALL(*cas_api.license_, IsExpired()).WillOnce(Return(true));
EXPECT_CALL(*cas_api.file_system_, Exists(_)).Times(0);
cas_api.OnTimerEvent();
// CasMediaId has been initialized.
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillOnce(Return(false));
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRequest(_, _, _, _, _))
.WillRepeatedly(Return(wvcas::CasStatus::OkStatus()));
std::string request, init_data;
EXPECT_EQ(
wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRequest(init_data, &request).status_code());
// Valid: License is expired then start to remove.
EXPECT_CALL(*cas_api.license_, IsExpired()).WillOnce(Return(true));
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*cas_api.file_system_, Remove(_)).WillOnce(Return(true));
cas_api.OnTimerEvent();
// Valid: License is expired but now it does not exist on file system.
EXPECT_CALL(*cas_api.license_, IsExpired()).WillOnce(Return(true));
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillOnce(Return(false));
EXPECT_CALL(*cas_api.file_system_, Remove(_)).Times(0);
cas_api.OnTimerEvent();
}
// Test ecm processing with both license storage and license request.
EcmTestParams params[] = {
{false, false}, // No deferral, No offline license
{true, false}, // Defer ecms, No offline license
{false, true}, // No deferral, Offline license
{true, true}, // Defer ecms, Offline license
};
INSTANTIATE_TEST_SUITE_P(ECMProcessing, WidevineCasTest,
testing::ValuesIn(params));
TEST_P(WidevineCasTest, ECMProcessing) {
TestWidevineCas cas_api;
bool test_deferred_ecm = GetParam().defer_ecm;
bool test_stored_license = GetParam().stored_license;
EXPECT_CALL(*cas_api.crypto_session_, initialize())
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_CALL(*cas_api.license_, IsExpired()).WillRepeatedly(Return(false));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.initialize(nullptr).status_code());
wvcas::WvCasSessionId video_sid;
wvcas::WvCasSessionId audio_sid;
// In real implementation, these ids are generated by OEMCrypto.
uint32_t video_session_id = 1;
uint32_t audio_session_id = 2;
EXPECT_CALL(*(cas_api.crypto_session_), CreateEntitledKeySession(_))
.WillOnce(DoAll(SetArgPointee<0>(video_session_id),
Return(wvcas::CasStatus::OkStatus())));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.openSession(&video_sid).status_code());
EXPECT_EQ(video_sid, video_session_id);
EXPECT_CALL(*(cas_api.crypto_session_), CreateEntitledKeySession(_))
.WillOnce(DoAll(SetArgPointee<0>(audio_session_id),
Return(wvcas::CasStatus::OkStatus())));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.openSession(&audio_sid).status_code());
EXPECT_EQ(audio_sid, audio_session_id);
wvcas::CasSessionPtr video_session =
wvcas::WidevineCasSessionMap::instance().GetSession(video_sid);
wvcas::CasSessionPtr audio_session =
wvcas::WidevineCasSessionMap::instance().GetSession(audio_sid);
ASSERT_TRUE(video_session);
ASSERT_TRUE(audio_session);
std::string video_ecm_str("video_ecm");
std::string audio_ecm_str("audio_ecm");
wvcas::CasEcm video_ecm(video_ecm_str.begin(), video_ecm_str.end());
wvcas::CasEcm audio_ecm(audio_ecm_str.begin(), audio_ecm_str.end());
int expected_process_ecm_calls = 1;
if (test_deferred_ecm) {
expected_process_ecm_calls = 2;
EXPECT_EQ(wvcas::CasStatusCode::kDeferedEcmProcessing,
cas_api.processEcm(video_sid, video_ecm).status_code());
EXPECT_EQ(wvcas::CasStatusCode::kDeferedEcmProcessing,
cas_api.processEcm(audio_sid, audio_ecm).status_code());
}
// Two ecm streams * the number of processed ecms.
int expected_begin_decryption_calls = expected_process_ecm_calls * 2;
EXPECT_CALL(*reinterpret_cast<MockWidevineSession*>(video_session.get()),
processEcm(video_ecm, 0))
.Times(expected_process_ecm_calls);
EXPECT_CALL(*reinterpret_cast<MockWidevineSession*>(audio_session.get()),
processEcm(audio_ecm, 0))
.Times(expected_process_ecm_calls);
EXPECT_CALL(*cas_api.license_, BeginDecryption())
.Times(expected_begin_decryption_calls);
if (test_stored_license) {
std::string request;
std::string file("license_file");
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillOnce(Return(true));
EXPECT_CALL(*cas_api.file_system_, FileSize(_))
.WillOnce(Return(file.size()));
auto* file_handle = new NiceMock<MockFile>;
EXPECT_CALL(*cas_api.file_system_, DoOpen(_, _))
.WillOnce(Return(file_handle));
EXPECT_CALL(*file_handle, Read(_, _)).WillOnce(Return(file.size()));
EXPECT_CALL(*cas_api.license_, HandleStoredLicense(_, _));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRequest("init_data", &request)
.status_code());
} else {
// Empty response.
std::string init_data;
EXPECT_CALL(*cas_api.license_, HandleEntitlementResponse(_, _))
.WillOnce(Return(wvcas::CasStatusCode::kNoError));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.handleEntitlementResponse("response", init_data)
.status_code());
}
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.processEcm(video_sid, video_ecm).status_code());
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.processEcm(audio_sid, audio_ecm).status_code());
EXPECT_CALL(*(cas_api.crypto_session_),
RemoveEntitledKeySession(video_session_id));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.closeSession(video_sid).status_code());
EXPECT_CALL(*(cas_api.crypto_session_),
RemoveEntitledKeySession(audio_session_id));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.closeSession(audio_sid).status_code());
}
TEST_F(WidevineCasTest, RemoveLicense) {
TestWidevineCas cas_api;
EXPECT_CALL(*cas_api.crypto_session_, initialize())
.WillOnce(Return(wvcas::CasStatus::OkStatus()));
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.initialize(nullptr).status_code());
// mediaId is not initialized.
std::string mocked_file_name;
EXPECT_EQ(wvcas::CasStatusCode::kCasLicenseError,
cas_api.RemoveLicense(mocked_file_name).status_code());
// CasMediaId has been initialized.
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillOnce(Return(false));
EXPECT_CALL(*cas_api.license_, GenerateEntitlementRequest(_, _, _, _, _))
.WillRepeatedly(Return(wvcas::CasStatus::OkStatus()));
std::string request, init_data;
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.generateEntitlementRequest(init_data, &request)
.status_code());
// Unable to remove license file, return InvalidLicenseFile error.
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillOnce(Return(true));
EXPECT_CALL(*cas_api.file_system_, Remove(_)).WillOnce(Return(false));
EXPECT_EQ(wvcas::CasStatusCode::kInvalidLicenseFile,
cas_api.RemoveLicense(mocked_file_name).status_code());
// Happy case: remove the unused license file
MockFile mock_file;
size_t mock_filesize = 10;
EXPECT_CALL(*cas_api.file_system_, Exists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*cas_api.file_system_, FileSize(_)).WillRepeatedly(Return(mock_filesize));
EXPECT_CALL(*cas_api.file_system_, DoOpen(_, _)).WillRepeatedly(Return(&mock_file));
EXPECT_CALL(mock_file, Read(_, _)).WillRepeatedly(Return(mock_filesize));
EXPECT_CALL(mock_file, Close()).WillRepeatedly(Return());
EXPECT_CALL(*cas_api.file_system_, Remove(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*cas_api.license_, UpdateLicenseForLicenseRemove()).Times(0);
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.RemoveLicense(mocked_file_name).status_code());
// Happy case: remove the in used license file
std::string hash;
std::string kBasePathPrefix = "/data/vendor/mediacas/IDM/widevine/";
hash.resize(SHA256_DIGEST_LENGTH);
const auto* input =
reinterpret_cast<const unsigned char*>(mocked_file_name.data());
auto* output = reinterpret_cast<unsigned char*>(&hash[0]);
SHA256(input, mocked_file_name.size(), output);
std::string full_file_name = kBasePathPrefix + wvutil::b2a_hex(hash)
+ std::string(".lic");
EXPECT_CALL(*cas_api.license_, UpdateLicenseForLicenseRemove()).Times(1);
EXPECT_EQ(wvcas::CasStatusCode::kNoError,
cas_api.RemoveLicense(full_file_name).status_code());
}

View File

@@ -0,0 +1,283 @@
// 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <vector>
#include "cas_util.h"
#include "mock_crypto_session.h"
#include "string_conversions.h"
#include "widevine_cas_session.h"
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;
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 MockEcmParser : public wvcas::EcmParser {
public:
MOCK_CONST_METHOD0(sequence_count, uint8_t());
MOCK_CONST_METHOD0(age_restriction, uint8_t());
MOCK_CONST_METHOD0(crypto_mode, wvcas::CryptoMode());
MOCK_CONST_METHOD0(rotation_enabled, bool());
MOCK_CONST_METHOD1(entitlement_key_id,
const std::vector<uint8_t>(wvcas::KeySlotId id));
MOCK_CONST_METHOD1(content_key_id,
const std::vector<uint8_t>(wvcas::KeySlotId id));
MOCK_CONST_METHOD1(wrapped_key_data,
const std::vector<uint8_t>(wvcas::KeySlotId id));
MOCK_CONST_METHOD1(wrapped_key_iv,
const std::vector<uint8_t>(wvcas::KeySlotId id));
MOCK_CONST_METHOD1(content_iv,
const std::vector<uint8_t>(wvcas::KeySlotId id));
};
class CasSessionTest : public ::testing::Test {
public:
CasSessionTest() {}
virtual ~CasSessionTest() {}
};
// Allow getEcmParser to return a mocked ecm.
class TestCasSession : public wvcas::WidevineCasSession {
public:
TestCasSession() {}
virtual ~TestCasSession() {}
std::unique_ptr<const wvcas::EcmParser> getEcmParser(
const wvcas::CasEcm& ecm) const;
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;
}
private:
uint8_t age_restriction_ = 0;
};
std::unique_ptr<const wvcas::EcmParser> TestCasSession::getEcmParser(
const wvcas::CasEcm& ecm) const {
std::unique_ptr<NiceMock<MockEcmParser>> mock_ecm_parser(
new NiceMock<MockEcmParser>);
ON_CALL(*mock_ecm_parser, sequence_count()).WillByDefault(Return(0));
ON_CALL(*mock_ecm_parser, age_restriction()).WillByDefault(Return(
age_restriction_));
ON_CALL(*mock_ecm_parser, crypto_mode())
.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));
return std::unique_ptr<const wvcas::EcmParser>(mock_ecm_parser.release());
}
TEST_F(CasSessionTest, processEcm) {
TestCasSession session;
std::shared_ptr<StrictMock<MockCryptoSession>> mock =
std::make_shared<StrictMock<MockCryptoSession>>();
uint32_t session_id;
EXPECT_CALL(*mock, CreateEntitledKeySession(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kEntitledKeySessionId),
Return(wvcas::CasStatusCode::kNoError)));
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.initialize(mock, &session_id).status_code());
EXPECT_EQ(session_id, kEntitledKeySessionId);
wvcas::CasEcm ecm(184);
EXPECT_CALL(*mock,
LoadCasECMKeys(session_id, IsValidKeyEvenSlotData(),
IsValidKeyOddSlotData()));
session.processEcm(ecm, 0);
EXPECT_CALL(*mock, RemoveEntitledKeySession(session_id));
}
TEST_F(CasSessionTest, parentalControl) {
TestCasSession session;
std::shared_ptr<StrictMock<MockCryptoSession>> mock =
std::make_shared<StrictMock<MockCryptoSession>>();
uint32_t session_id;
EXPECT_CALL(*mock, CreateEntitledKeySession(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kEntitledKeySessionId),
Return(wvcas::CasStatusCode::kNoError)));
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.initialize(mock, &session_id).status_code());
EXPECT_EQ(session_id, kEntitledKeySessionId);
EXPECT_CALL(*mock, LoadCasECMKeys(session_id, 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).status_code());
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.processEcm(ecm, 13).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).status_code());
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.processEcm(ecm, 10).status_code());
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kNoError,
session.processEcm(ecm, 13).status_code());
std::generate(ecm.begin(), ecm.end(), std::rand);
ASSERT_EQ(wvcas::CasStatusCode::kAccessDeniedByParentalControl,
session.processEcm(ecm, 3).status_code());
EXPECT_CALL(*mock, RemoveEntitledKeySession(session_id));
}

View File

@@ -0,0 +1,19 @@
// 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 "OEMCryptoCAS.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::LOG_INFO;
return RUN_ALL_TESTS();
}