365 lines
16 KiB
C++
365 lines
16 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright 2020 Google LLC.
|
|
//
|
|
// This software is licensed under the terms defined in the Widevine Master
|
|
// License Agreement. For a copy of this agreement, please contact
|
|
// widevine-licensing@google.com.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "common/security_profile_list.h"
|
|
|
|
#include "glog/logging.h"
|
|
#include "google/protobuf/util/message_differencer.h"
|
|
#include "testing/gmock.h"
|
|
#include "testing/gunit.h"
|
|
#include "absl/memory/memory.h"
|
|
#include "common/client_id_util.h"
|
|
#include "protos/public/device_common.pb.h"
|
|
#include "protos/public/device_security_profile_data.pb.h"
|
|
#include "protos/public/security_profile.pb.h"
|
|
|
|
namespace widevine {
|
|
namespace security_profile {
|
|
|
|
const char kMakeName[] = "company_name";
|
|
const char kMakeValue[] = "Google";
|
|
const char kModelName[] = "model_name";
|
|
const char kModelValue[] = "model1";
|
|
const char kDeviceNameValue[] = "TestDeviceName";
|
|
const char kProductNameValue[] = "TestProductName";
|
|
const char kBuildInfoValue[] = "TestBuildInfo";
|
|
const char kOemCryptoSecurityPatchLevelValue[] =
|
|
"TestOemCryptoSecurityPatchLevel";
|
|
const char kDefaultContentOwnerName[] = "Widevine";
|
|
const uint32_t kSystemId = 1234;
|
|
const uint32_t kSystemIdOverridden = 1567;
|
|
const uint32_t kResourceTierLow = 1;
|
|
const uint32_t kResourceTierMed = 2;
|
|
const uint32_t kResourceTierHigh = 3;
|
|
const uint64_t kStartTimeInSeconds = 5000;
|
|
const uint64_t kEndTimeInSeconds = 10000;
|
|
|
|
class SecurityProfileListTest : public ::testing::Test {
|
|
public:
|
|
SecurityProfileListTest() {}
|
|
~SecurityProfileListTest() override {}
|
|
|
|
void SetUp() override {
|
|
const uint32_t oemcrypto_12 = 12;
|
|
SecurityProfile profile;
|
|
std::string profile_namespace = "widevine";
|
|
profile_list_ = absl::make_unique<SecurityProfileList>(profile_namespace);
|
|
|
|
AddClientInfo(&client_id_, kMakeName, kMakeValue);
|
|
AddClientInfo(&client_id_, kModelName, kModelValue);
|
|
AddClientInfo(&client_id_, kModDrmDeviceName, kDeviceNameValue);
|
|
AddClientInfo(&client_id_, kModDrmProductName, kProductNameValue);
|
|
AddClientInfo(&client_id_, kModDrmBuildInfo, kBuildInfoValue);
|
|
AddClientInfo(&client_id_, kModDrmOemCryptoSecurityPatchLevel,
|
|
kOemCryptoSecurityPatchLevelValue);
|
|
client_id_.mutable_client_capabilities()->set_oem_crypto_api_version(
|
|
oemcrypto_12);
|
|
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
|
|
ClientCapabilities::HDCP_V2_2);
|
|
client_id_.mutable_client_capabilities()->set_resource_rating_tier(
|
|
kResourceTierHigh);
|
|
|
|
device_info_.set_security_level(ProvisionedDeviceInfo::LEVEL_1);
|
|
device_info_.set_system_id(kSystemId);
|
|
}
|
|
std::unique_ptr<SecurityProfileList> profile_list_;
|
|
ClientIdentification client_id_;
|
|
ProvisionedDeviceInfo device_info_;
|
|
};
|
|
|
|
TEST_F(SecurityProfileListTest, InsertProfile) {
|
|
// Insert test profile1 into the list.
|
|
SecurityProfileList profile_list("widevine-test");
|
|
SecurityProfile profile1;
|
|
profile1.set_name("profile1");
|
|
profile1.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_3);
|
|
EXPECT_TRUE(profile_list.InsertProfile(profile1));
|
|
// Verify the list still has one profile.
|
|
EXPECT_EQ(1, profile_list.NumProfiles());
|
|
// Should not allow insert if existing profile has the same name.
|
|
SecurityProfile profile2;
|
|
profile2.set_name(profile1.name());
|
|
profile2.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_3);
|
|
EXPECT_FALSE(profile_list.InsertProfile(profile2));
|
|
// Verify the list still has one profile.
|
|
EXPECT_EQ(1, profile_list.NumProfiles());
|
|
// Should allow insert since this profile has a different name.
|
|
profile2.set_name("profile2");
|
|
EXPECT_TRUE(profile_list.InsertProfile(profile2));
|
|
EXPECT_EQ(2, profile_list.NumProfiles());
|
|
}
|
|
|
|
TEST_F(SecurityProfileListTest, GetDrmInfo) {
|
|
SecurityProfile::DrmInfo drm_info;
|
|
DeviceModel* device_model = device_info_.add_model_info();
|
|
device_model->set_manufacturer(GetClientInfo(client_id_, kModDrmMake));
|
|
device_model->set_model_name(GetClientInfo(client_id_, kModDrmModel));
|
|
device_model->set_status(DeviceModel::MODEL_STATUS_VERIFIED);
|
|
const uint32_t model_launch_year = 2015;
|
|
device_model->set_model_year(model_launch_year);
|
|
ASSERT_TRUE(profile_list_->GetDrmInfo(client_id_, device_info_, &drm_info));
|
|
EXPECT_EQ(client_id_.client_capabilities().max_hdcp_version(),
|
|
drm_info.output().hdcp_version());
|
|
EXPECT_EQ(client_id_.client_capabilities().analog_output_capabilities(),
|
|
drm_info.output().analog_output_capabilities());
|
|
EXPECT_EQ(client_id_.client_capabilities().oem_crypto_api_version(),
|
|
drm_info.security().oemcrypto_api_version());
|
|
EXPECT_EQ(client_id_.client_capabilities().resource_rating_tier(),
|
|
drm_info.security().resource_rating_tier());
|
|
|
|
EXPECT_EQ(device_info_.security_level(),
|
|
drm_info.security().security_level());
|
|
EXPECT_EQ(device_info_.system_id(), drm_info.system_id());
|
|
|
|
EXPECT_EQ(kMakeValue, drm_info.request_model_info().manufacturer());
|
|
EXPECT_EQ(kModelValue, drm_info.request_model_info().model_name());
|
|
EXPECT_EQ(DeviceModel::MODEL_STATUS_VERIFIED,
|
|
drm_info.request_model_info().status());
|
|
EXPECT_EQ(model_launch_year, drm_info.request_model_info().model_year());
|
|
EXPECT_EQ(kDeviceNameValue, drm_info.client_info().device_name());
|
|
EXPECT_EQ(kProductNameValue,
|
|
drm_info.client_info().product_info().product_name());
|
|
EXPECT_EQ(kBuildInfoValue,
|
|
drm_info.client_info().product_info().build_info());
|
|
EXPECT_EQ(
|
|
kOemCryptoSecurityPatchLevelValue,
|
|
drm_info.client_info().product_info().oem_crypto_security_patch_level());
|
|
}
|
|
|
|
TEST_F(SecurityProfileListTest, QualifiedProfiles) {
|
|
SecurityProfile profile1;
|
|
profile1.set_name("profile1");
|
|
profile1.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_3);
|
|
profile1.mutable_min_output_requirements()->set_hdcp_version(
|
|
ClientCapabilities::HDCP_V1);
|
|
profile_list_->InsertProfile(profile1);
|
|
EXPECT_EQ(1, profile_list_->NumProfiles());
|
|
|
|
SecurityProfile profile2;
|
|
profile2.set_name("profile2");
|
|
profile2.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_1);
|
|
profile2.mutable_min_output_requirements()->set_hdcp_version(
|
|
ClientCapabilities::HDCP_V2);
|
|
profile_list_->InsertProfile(profile2);
|
|
EXPECT_EQ(2, profile_list_->NumProfiles());
|
|
|
|
// Both profiles should qualify based on client_info and device_info from the
|
|
// Setup function.
|
|
std::vector<std::string> qualified_profiles;
|
|
EXPECT_EQ(2, profile_list_->GetQualifiedProfiles(client_id_, device_info_,
|
|
&qualified_profiles));
|
|
EXPECT_NE(qualified_profiles.end(),
|
|
std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
|
profile1.name()));
|
|
EXPECT_NE(qualified_profiles.end(),
|
|
std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
|
profile2.name()));
|
|
|
|
// Reduce the DRM capabilities of the device so profile2 will not qualify.
|
|
client_id_.mutable_client_capabilities()->set_max_hdcp_version(
|
|
ClientCapabilities::HDCP_V1);
|
|
ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id_, device_info_,
|
|
&qualified_profiles));
|
|
EXPECT_NE(qualified_profiles.end(),
|
|
std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
|
profile1.name()));
|
|
}
|
|
|
|
TEST_F(SecurityProfileListTest, DeviceExceptionImpactOnQualifiedProfiles) {
|
|
std::vector<std::string> qualified_profiles;
|
|
// Add device_exceptions to a profile and check the qualified profile.
|
|
SecurityProfile profile1;
|
|
profile1.set_name("profile1");
|
|
profile1.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_3);
|
|
profile1.mutable_min_output_requirements()->set_hdcp_version(
|
|
ClientCapabilities::HDCP_V1);
|
|
// Add one system_id in device blocked list.
|
|
DeviceException* device_exception = profile1.add_device_exceptions();
|
|
device_exception->set_system_id(kSystemId);
|
|
device_exception->set_action(DeviceException::DEVICE_EXCEPTION_BLOCK);
|
|
device_exception = profile1.add_device_exceptions();
|
|
// Add another system_id in device allowable list.
|
|
device_exception->set_system_id(kSystemIdOverridden);
|
|
device_exception->set_action(DeviceException::DEVICE_EXCEPTION_ALLOW);
|
|
profile_list_->InsertProfile(profile1);
|
|
EXPECT_EQ(profile_list_->NumProfiles(), 1);
|
|
// System_id of the current device is listed in the device exceptions with
|
|
// DEVICE_EXCEPTION_BLOCK label. So this profile can't be used for it.
|
|
EXPECT_EQ(profile_list_->GetQualifiedProfiles(client_id_, device_info_,
|
|
&qualified_profiles),
|
|
0);
|
|
EXPECT_EQ(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
|
profile1.name()),
|
|
qualified_profiles.end());
|
|
|
|
// Reset the system id in the device info so that profile1 will be
|
|
// auto-qualified for the current device.
|
|
device_info_.set_system_id(kSystemIdOverridden);
|
|
ASSERT_EQ(profile_list_->GetQualifiedProfiles(client_id_, device_info_,
|
|
&qualified_profiles),
|
|
1);
|
|
EXPECT_NE(std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
|
profile1.name()),
|
|
qualified_profiles.end());
|
|
}
|
|
|
|
TEST_F(SecurityProfileListTest, GetQualifiedForInactiveDSPs) {
|
|
SecurityProfile profile1;
|
|
profile1.set_name("profile1");
|
|
profile1.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_3);
|
|
profile1.mutable_min_output_requirements()->set_hdcp_version(
|
|
ClientCapabilities::HDCP_V1);
|
|
profile1.set_owner("owner1");
|
|
int64_t current_time_seconds = time(nullptr);
|
|
// Set the start time one hour back.
|
|
profile1.mutable_control_time()->set_start_time_seconds(current_time_seconds -
|
|
3600);
|
|
// Set the end time one hour later.
|
|
profile1.mutable_control_time()->set_end_time_seconds(current_time_seconds +
|
|
3600);
|
|
|
|
profile_list_->InsertProfile(profile1);
|
|
ASSERT_EQ(1, profile_list_->NumProfiles());
|
|
std::vector<std::string> qualified_profiles;
|
|
ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id_, device_info_,
|
|
&qualified_profiles));
|
|
|
|
SecurityProfile profile2;
|
|
profile2.set_name("profile2");
|
|
profile2.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_1);
|
|
profile2.mutable_min_output_requirements()->set_hdcp_version(
|
|
ClientCapabilities::HDCP_V2);
|
|
// Set the start time one hour later.
|
|
profile2.mutable_control_time()->set_start_time_seconds(current_time_seconds +
|
|
3600);
|
|
// Set the end time two hours later.
|
|
profile2.mutable_control_time()->set_end_time_seconds(current_time_seconds +
|
|
7200);
|
|
|
|
profile_list_->InsertProfile(profile2);
|
|
ASSERT_EQ(2, profile_list_->NumProfiles());
|
|
qualified_profiles.clear();
|
|
ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id_, device_info_,
|
|
&qualified_profiles));
|
|
// Profile2 will be filtered out as it is an inactive profile.
|
|
EXPECT_NE(qualified_profiles.end(),
|
|
std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
|
profile1.name()));
|
|
|
|
SecurityProfile profile3;
|
|
profile3.set_name("profile3");
|
|
profile3.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_1);
|
|
profile3.mutable_min_output_requirements()->set_hdcp_version(
|
|
ClientCapabilities::HDCP_V2);
|
|
// Set the start time two hours back.
|
|
profile3.mutable_control_time()->set_start_time_seconds(current_time_seconds -
|
|
7200);
|
|
// Set the end time one hour back.
|
|
profile3.mutable_control_time()->set_end_time_seconds(current_time_seconds -
|
|
3600);
|
|
|
|
profile_list_->InsertProfile(profile3);
|
|
ASSERT_EQ(3, profile_list_->NumProfiles());
|
|
qualified_profiles.clear();
|
|
ASSERT_EQ(1, profile_list_->GetQualifiedProfiles(client_id_, device_info_,
|
|
&qualified_profiles));
|
|
// Profile3 will be filtered out as it is an expired profile.
|
|
EXPECT_NE(qualified_profiles.end(),
|
|
std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
|
profile1.name()));
|
|
|
|
SecurityProfile profile4;
|
|
profile4.set_name("profile4");
|
|
profile4.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_2);
|
|
// Set the start time two hours back.
|
|
profile4.mutable_control_time()->set_start_time_seconds(current_time_seconds -
|
|
7200);
|
|
// Set the end time to 0 (never expired).
|
|
profile4.mutable_control_time()->set_end_time_seconds(0);
|
|
profile_list_->InsertProfile(profile4);
|
|
ASSERT_EQ(4, profile_list_->NumProfiles());
|
|
qualified_profiles.clear();
|
|
ASSERT_EQ(2, profile_list_->GetQualifiedProfiles(client_id_, device_info_,
|
|
&qualified_profiles));
|
|
// Profile4 will be listed in the qualified profiles.
|
|
EXPECT_NE(qualified_profiles.end(),
|
|
std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
|
profile1.name()));
|
|
EXPECT_NE(qualified_profiles.end(),
|
|
std::find(qualified_profiles.begin(), qualified_profiles.end(),
|
|
profile4.name()));
|
|
}
|
|
|
|
TEST_F(SecurityProfileListTest, FindProfile) {
|
|
SecurityProfileList profile_list("widevine-test");
|
|
SecurityProfile profile1;
|
|
profile1.set_name("profile1");
|
|
profile1.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_3);
|
|
DeviceException* device_exception = profile1.add_device_exceptions();
|
|
device_exception->set_system_id(kSystemId);
|
|
device_exception->set_action(DeviceException::DEVICE_EXCEPTION_BLOCK);
|
|
device_exception = profile1.add_device_exceptions();
|
|
device_exception->set_system_id(kSystemIdOverridden);
|
|
device_exception->set_action(DeviceException::DEVICE_EXCEPTION_ALLOW);
|
|
|
|
EXPECT_EQ(kDefaultContentOwnerName, profile1.owner());
|
|
SecurityProfile profile2;
|
|
profile2.set_name("profile2");
|
|
profile2.mutable_min_security_requirements()->set_security_level(
|
|
ProvisionedDeviceInfo::LEVEL_3);
|
|
// Override the default owner name.
|
|
profile2.set_owner("owner2");
|
|
profile2.mutable_control_time()->set_start_time_seconds(kStartTimeInSeconds);
|
|
profile2.mutable_control_time()->set_end_time_seconds(kEndTimeInSeconds);
|
|
|
|
// Insert profiles 1 & 2.
|
|
EXPECT_TRUE(profile_list.InsertProfile(profile1));
|
|
EXPECT_TRUE(profile_list.InsertProfile(profile2));
|
|
EXPECT_EQ(2, profile_list.NumProfiles());
|
|
|
|
// Find the profile by its name.
|
|
SecurityProfile profile;
|
|
EXPECT_TRUE(profile_list.GetProfileByName(profile1.name(), &profile));
|
|
EXPECT_EQ(profile1.name(), profile.name());
|
|
EXPECT_EQ(profile1.level(), profile.level());
|
|
EXPECT_EQ(profile1.owner(), profile.owner());
|
|
// By default, security profile doesn't contain the control_dates field.
|
|
EXPECT_FALSE(profile1.has_control_time());
|
|
EXPECT_EQ(2, profile.device_exceptions().size());
|
|
EXPECT_EQ(kSystemId, profile.device_exceptions(0).system_id());
|
|
EXPECT_EQ(DeviceException::DEVICE_EXCEPTION_BLOCK,
|
|
profile.device_exceptions(0).action());
|
|
EXPECT_EQ(kSystemIdOverridden, profile.device_exceptions(1).system_id());
|
|
EXPECT_EQ(DeviceException::DEVICE_EXCEPTION_ALLOW,
|
|
profile.device_exceptions(1).action());
|
|
|
|
EXPECT_TRUE(profile_list.GetProfileByName(profile2.name(), &profile));
|
|
EXPECT_EQ(profile2.name(), profile.name());
|
|
EXPECT_EQ(profile2.level(), profile.level());
|
|
EXPECT_EQ(profile2.owner(), profile.owner());
|
|
EXPECT_TRUE(profile2.device_exceptions().empty());
|
|
EXPECT_EQ(profile2.control_time().start_time_seconds(), kStartTimeInSeconds);
|
|
EXPECT_EQ(profile2.control_time().end_time_seconds(), kEndTimeInSeconds);
|
|
|
|
EXPECT_FALSE(
|
|
profile_list.GetProfileByName("you-should-not-find-me", &profile));
|
|
}
|
|
|
|
} // namespace security_profile
|
|
} // namespace widevine
|