Backward compatibility for licenses and certificates

Certificates and offline licenses are stored in security level
specific directories in klp. When devices transition from jb-mr2,
their persistent information has to be ported to these directories.

bug:10366036

Merge of https://widevine-internal-review.googlesource.com/#/c/7310/
from the widevine CDM repo

Change-Id: I70b4a79dc5b69bda7fc3a4b92fdcde7ef8b41836
This commit is contained in:
Jeff Tinker
2013-08-22 09:37:18 -07:00
parent 2fa6b63292
commit db41502f86
10 changed files with 455 additions and 12 deletions

View File

@@ -19,6 +19,7 @@ using ::testing::HasSubstr;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::ReturnArg;
using ::testing::SetArgPointee;
using ::testing::SetArrayArgument;
using ::testing::StrEq;
@@ -987,6 +988,8 @@ class MockFile : public File {
MOCK_METHOD1(Exists, bool(const std::string&));
MOCK_METHOD1(Remove, bool(const std::string&));
MOCK_METHOD2(Copy, bool(const std::string&, const std::string&));
MOCK_METHOD2(List, bool(const std::string&, std::vector<std::string>*));
MOCK_METHOD1(CreateDirectory, bool(const std::string));
MOCK_METHOD1(IsDirectory, bool(const std::string&));
MOCK_METHOD1(IsRegularFile, bool(const std::string&));
@@ -1021,6 +1024,10 @@ class DeviceFilesTest : public ::testing::Test {
class DeviceFilesStoreTest : public DeviceFilesTest,
public ::testing::WithParamInterface<bool> {};
class DeviceFilesSecurityLevelTest
: public DeviceFilesTest,
public ::testing::WithParamInterface<CdmSecurityLevel> {};
MATCHER(IsCreateFileFlagSet, "") { return File::kCreate & arg; }
MATCHER(IsBinaryFileFlagSet, "") { return File::kBinary & arg; }
MATCHER_P(IsStrEq, str, "") {
@@ -1031,7 +1038,7 @@ MATCHER_P(IsStrEq, str, "") {
MATCHER_P2(Contains, str1, str2, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
std::string data(arg, str1.size() + str2.size() + 500);
std::string data(arg, str1.size() + str2.size() + 75);
return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos);
}
@@ -1039,12 +1046,12 @@ MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
std::string data(arg, str1.size() + str2.size() + str3.size() + str4.size() +
str5.size() + str6.size() + 500);
str5.size() + str6.size() + 75);
return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos,
data.find(str3) != std::string::npos,
data.find(str4) != std::string::npos,
data.find(str5) != std::string::npos,
data.find(str2) != std::string::npos &&
data.find(str3) != std::string::npos &&
data.find(str4) != std::string::npos &&
data.find(str5) != std::string::npos &&
data.find(str6) != std::string::npos);
}
@@ -1109,6 +1116,40 @@ TEST_F(DeviceFilesTest, ReadCertificate) {
EXPECT_EQ(kTestWrappedPrivateKey, b2a_hex(wrapped_private_key));
}
TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
MockFile file;
std::string certificate(GenerateRandomData(kCertificateLen));
std::string wrapped_private_key(GenerateRandomData(kWrappedKeyLen));
CdmSecurityLevel security_level = GetParam();
std::string device_base_path;
ASSERT_TRUE(
Properties::GetDeviceFilesBasePath(security_level, &device_base_path));
std::string device_certificate_path =
device_base_path + DeviceFiles::GetCertificateFileName();
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path)))
.WillOnce(Return(false));
EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path)))
.WillOnce(Return(true));
EXPECT_CALL(file, Open(StrEq(device_certificate_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key),
Gt(certificate.size() + wrapped_private_key.size())))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, security_level));
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
}
INSTANTIATE_TEST_CASE_P(SecurityLevel, DeviceFilesSecurityLevelTest,
::testing::Values(kSecurityLevelL1, kSecurityLevelL3));
TEST_P(DeviceFilesStoreTest, StoreLicense) {
MockFile file;
size_t license_num = 0;
@@ -1244,6 +1285,81 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
}
}
TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
MockFile file;
std::vector<std::string> security_dirs;
EXPECT_TRUE(Properties::GetSecurityLevelDirectories(&security_dirs));
size_t pos = std::string::npos;
for (size_t i = 0; i < security_dirs.size(); ++i) {
pos = device_base_path_.rfind(security_dirs[i]);
if (std::string::npos != pos) break;
}
EXPECT_NE(std::string::npos, pos);
std::string base_path(device_base_path_, 0, pos);
std::vector<std::string> old_files;
std::string new_path;
for (size_t i = 0; i < security_dirs.size(); ++i) {
old_files.push_back(security_dirs[i]);
new_path = base_path + security_dirs[i];
EXPECT_CALL(file, IsRegularFile(StrEq(new_path))).WillOnce(Return(false));
EXPECT_CALL(file, Exists(StrEq(new_path)))
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(StrEq(new_path))).WillOnce(Return(true));
}
std::string old_path = base_path + DeviceFiles::GetCertificateFileName();
old_files.push_back(DeviceFiles::GetCertificateFileName());
EXPECT_CALL(file, IsRegularFile(StrEq(old_path)))
.WillOnce(Return(true));
EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true));
for (size_t i = 0; i < security_dirs.size(); ++i) {
new_path = base_path + security_dirs[i] +
DeviceFiles::GetCertificateFileName();
EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path)))
.WillOnce(Return(true));
}
for (size_t j = 0; j < kNumberOfLicenses; ++j) {
std::string file_name = license_test_data[j].key_set_id +
DeviceFiles::GetLicenseFileNameExtension();
old_path = base_path + file_name;
old_files.push_back(file_name);
EXPECT_CALL(file, IsRegularFile(StrEq(old_path))).WillOnce(Return(true));
EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true));
for (size_t i = 0; i < security_dirs.size(); ++i) {
new_path = base_path + security_dirs[i] + file_name;
EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path)))
.WillOnce(Return(true));
}
}
EXPECT_CALL(file, List(StrEq(base_path), NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(old_files), Return(true)));
std::string data = a2bs_hex(kTestCertificateFileData);
new_path = device_base_path_ + DeviceFiles::GetCertificateFileName();
EXPECT_CALL(file, Exists(StrEq(new_path))).WillOnce(Return(true));
EXPECT_CALL(file, FileSize(_)).WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(_, _)).WillOnce(Return(true));
EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))).WillOnce(DoAll(
SetArrayArgument<0>(data.begin(), data.end()), Return(data.size())));
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
Properties::Init();
std::string certificate, wrapped_private_key;
ASSERT_TRUE(
device_files.RetrieveCertificate(&certificate, &wrapped_private_key));
}
TEST_F(DeviceFilesTest, UpdateLicenseState) {
MockFile file;
std::string license_path = device_base_path_ +

View File

@@ -7,6 +7,7 @@
#include "test_vectors.h"
namespace {
const std::string kTestDirName = "test";
const std::string kTestFileName = "test.txt";
const std::string kTestFileName2 = "test2.txt";
const std::string kTestFileNameExt = ".txt";
@@ -199,4 +200,67 @@ TEST_F(FileTest, WriteReadBinaryFile) {
EXPECT_EQ(write_data, read_data);
}
TEST_F(FileTest, CopyFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
std::string write_data = GenerateRandomData(600);
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
std::string path_copy = test_vectors::kTestDir + kTestFileName2;
EXPECT_FALSE(file.Exists(path_copy));
EXPECT_TRUE(file.Copy(path, path_copy));
std::string read_data;
read_data.resize(file.FileSize(path_copy));
File rd_file;
EXPECT_TRUE(rd_file.Open(path_copy, File::kReadOnly));
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
rd_file.Close();
EXPECT_EQ(write_data, read_data);
EXPECT_EQ(file.FileSize(path), file.FileSize(path_copy));
}
TEST_F(FileTest, ListEmptyDirectory) {
std::vector<std::string> files;
File file;
EXPECT_TRUE(file.List(test_vectors::kTestDir, &files));
EXPECT_EQ(0u, files.size());
}
TEST_F(FileTest, ListFiles) {
File file;
std::string path = test_vectors::kTestDir + kTestDirName;
EXPECT_TRUE(file.CreateDirectory(path));
path = test_vectors::kTestDir + kTestFileName;
std::string write_data = GenerateRandomData(600);
EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(file.Write(write_data.data(), write_data.size()));
file.Close();
EXPECT_TRUE(file.Exists(path));
path = test_vectors::kTestDir + kTestFileName2;
write_data = GenerateRandomData(600);
EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(file.Write(write_data.data(), write_data.size()));
file.Close();
EXPECT_TRUE(file.Exists(path));
std::vector<std::string> files;
EXPECT_TRUE(file.List(test_vectors::kTestDir, &files));
EXPECT_EQ(3u, files.size());
for (size_t i = 0; i < files.size(); ++i) {
EXPECT_TRUE(files[i].compare(kTestDirName) == 0 ||
files[i].compare(kTestFileName) == 0 ||
files[i].compare(kTestFileName2) == 0);
}
}
} // namespace wvcdm