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:
@@ -59,6 +59,8 @@ class DeviceFiles {
|
||||
bool RetrieveFile(const char* name, std::string* data);
|
||||
|
||||
private:
|
||||
virtual void SecurityLevelPathBackwardCompatibility();
|
||||
|
||||
File* file_;
|
||||
CdmSecurityLevel security_level_;
|
||||
bool initialized_;
|
||||
|
||||
@@ -31,6 +31,8 @@ class File {
|
||||
|
||||
virtual bool Exists(const std::string& file_path);
|
||||
virtual bool Remove(const std::string& file_path);
|
||||
virtual bool Copy(const std::string& old_path, const std::string& new_path);
|
||||
virtual bool List(const std::string& path, std::vector<std::string>* files);
|
||||
virtual bool CreateDirectory(const std::string dir_path);
|
||||
virtual bool IsDirectory(const std::string& dir_path);
|
||||
virtual bool IsRegularFile(const std::string& file_path);
|
||||
|
||||
@@ -48,6 +48,9 @@ class Properties {
|
||||
static inline bool decrypt_with_empty_session_support() {
|
||||
return decrypt_with_empty_session_support_;
|
||||
}
|
||||
static inline bool security_level_path_backward_compatibility_support() {
|
||||
return security_level_path_backward_compatibility_support_;
|
||||
}
|
||||
static bool GetCompanyName(std::string* company_name);
|
||||
static bool GetModelName(std::string* model_name);
|
||||
static bool GetArchitectureName(std::string* arch_name);
|
||||
@@ -58,6 +61,7 @@ class Properties {
|
||||
std::string* base_path);
|
||||
static bool GetFactoryKeyboxPath(std::string* keybox);
|
||||
static bool GetOEMCryptoPath(std::string* library_name);
|
||||
static bool GetSecurityLevelDirectories(std::vector<std::string>* dirs);
|
||||
static const std::string GetSecurityLevel(const CdmSessionId& session_id);
|
||||
static const std::vector<uint8_t> GetServiceCertificate(
|
||||
const CdmSessionId& session_id);
|
||||
@@ -95,6 +99,9 @@ class Properties {
|
||||
static void set_decrypt_with_empty_session_support(bool flag) {
|
||||
decrypt_with_empty_session_support_ = flag;
|
||||
}
|
||||
static void set_security_level_path_backward_compatibility_support(bool flag) {
|
||||
security_level_path_backward_compatibility_support_ = flag;
|
||||
}
|
||||
|
||||
static bool begin_license_usage_when_received_;
|
||||
static bool require_explicit_renew_request_;
|
||||
@@ -104,6 +111,7 @@ class Properties {
|
||||
static bool use_certificates_as_identification_;
|
||||
static bool extract_pssh_data_;
|
||||
static bool decrypt_with_empty_session_support_;
|
||||
static bool security_level_path_backward_compatibility_support_;
|
||||
static scoped_ptr<CdmClientPropertySetMap> session_property_set_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(Properties);
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace {
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
const char kLicenseFileNameExt[] = ".lic";
|
||||
const char kWildcard[] = "*";
|
||||
const char kPathDelimiter[] = "/";
|
||||
const char *kSecurityLevelPathCompatibilityExclusionList[] = { "ay64.dat" };
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -90,6 +92,10 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Properties::security_level_path_backward_compatibility_support()) {
|
||||
SecurityLevelPathBackwardCompatibility();
|
||||
}
|
||||
|
||||
std::string serialized_hashed_file;
|
||||
if (!RetrieveFile(kCertificateFileName, &serialized_hashed_file))
|
||||
return false;
|
||||
@@ -448,6 +454,65 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
|
||||
"get base path");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> security_dirs;
|
||||
if (!Properties::GetSecurityLevelDirectories(&security_dirs)) {
|
||||
LOGW("DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
|
||||
"get security directories");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pos = std::string::npos;
|
||||
for (size_t i = 0; i < security_dirs.size(); ++i) {
|
||||
pos = path.rfind(security_dirs[i]);
|
||||
if (std::string::npos != pos)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
LOGV("DeviceFiles::SecurityLevelPathBackwardCompatibility: Security level "
|
||||
"specific path not found. Check properties?");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string from_dir(path, 0, pos);
|
||||
|
||||
std::vector<std::string> files;
|
||||
file_->List(from_dir, &files);
|
||||
|
||||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
std::string from = from_dir + files[i];
|
||||
bool exclude = false;
|
||||
for (size_t j = 0;
|
||||
j < sizeof(kSecurityLevelPathCompatibilityExclusionList) /
|
||||
sizeof(const char*);
|
||||
j++) {
|
||||
if (files[i].compare(kSecurityLevelPathCompatibilityExclusionList[j]) == 0) {
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exclude) continue;
|
||||
if (!file_->IsRegularFile(from)) continue;
|
||||
|
||||
for (size_t j = 0; j < security_dirs.size(); ++j) {
|
||||
std::string to_dir = from_dir + security_dirs[j];
|
||||
if (!file_->Exists(to_dir))
|
||||
file_->CreateDirectory(to_dir);
|
||||
std::string to = to_dir + files[i];
|
||||
file_->Copy(from, to);
|
||||
}
|
||||
file_->Remove(from);
|
||||
}
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetCertificateFileName() {
|
||||
return kCertificateFileName;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include "properties_configuration.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
const char *kSecurityLevelDirs[] = { "L1/", "L3/" };
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
bool Properties::begin_license_usage_when_received_;
|
||||
bool Properties::require_explicit_renew_request_;
|
||||
@@ -13,6 +17,7 @@ bool Properties::oem_crypto_use_userspace_buffers_;
|
||||
bool Properties::use_certificates_as_identification_;
|
||||
bool Properties::extract_pssh_data_;
|
||||
bool Properties::decrypt_with_empty_session_support_;
|
||||
bool Properties::security_level_path_backward_compatibility_support_;
|
||||
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
|
||||
|
||||
void Properties::Init() {
|
||||
@@ -25,6 +30,7 @@ void Properties::Init() {
|
||||
kPropertyUseCertificatesAsIdentification;
|
||||
extract_pssh_data_ = kExtractPsshData;
|
||||
decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport;
|
||||
security_level_path_backward_compatibility_support_ = kSecurityLevelPathBackwardCompatibilitySupport;
|
||||
session_property_set_.reset(new CdmClientPropertySetMap());
|
||||
}
|
||||
|
||||
@@ -93,4 +99,13 @@ bool Properties::UsePrivacyMode(const CdmSessionId& session_id) {
|
||||
}
|
||||
return property_set->use_privacy_mode();
|
||||
}
|
||||
|
||||
bool Properties::GetSecurityLevelDirectories(std::vector<std::string>* dirs) {
|
||||
dirs->resize(sizeof(kSecurityLevelDirs)/sizeof(const char*));
|
||||
for (size_t i = 0; i < dirs->size(); ++i) {
|
||||
(*dirs)[i] = kSecurityLevelDirs[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -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_ +
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -35,6 +35,10 @@ const bool kExtractPsshData = true;
|
||||
// function will try to find out the session_id from the key_id.
|
||||
const bool kDecryptWithEmptySessionSupport = false;
|
||||
|
||||
// If true, device files will be moved to the directory specified by
|
||||
// Properties::GetDeviceFilesBasePath
|
||||
const bool kSecurityLevelPathBackwardCompatibilitySupport = true;
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -175,18 +177,80 @@ bool File::Remove(const std::string& path) {
|
||||
}
|
||||
}
|
||||
|
||||
bool File::Copy(const std::string& src, const std::string& dest) {
|
||||
struct stat stat_buf;
|
||||
if (stat(src.c_str(), &stat_buf)) {
|
||||
LOGV("File::Copy: file %s does not exist: %d", src.c_str(), errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd_src = open(src.c_str(), O_RDONLY);
|
||||
if (fd_src < 0) {
|
||||
LOGV("File::Copy: unable to open file %s: %d", src.c_str(), errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd_dest = open(dest.c_str(), O_WRONLY|O_CREAT, stat_buf.st_mode);
|
||||
if (fd_dest < 0) {
|
||||
LOGV("File::Copy: unable to open file %s: %d", dest.c_str(), errno);
|
||||
close(fd_src);
|
||||
return false;
|
||||
}
|
||||
|
||||
off_t offset = 0;
|
||||
bool sts = true;
|
||||
if (sendfile(fd_dest, fd_src, &offset, stat_buf.st_size) < 0) {
|
||||
LOGV("File::Copy: unable to copy %s to %s: %d", src.c_str(), dest.c_str(),
|
||||
errno);
|
||||
sts = false;
|
||||
}
|
||||
|
||||
close(fd_src);
|
||||
close(fd_dest);
|
||||
return sts;
|
||||
}
|
||||
|
||||
bool File::List(const std::string& path, std::vector<std::string>* files) {
|
||||
if (NULL == files) {
|
||||
LOGV("File::List: files destination not provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Exists(path)) {
|
||||
LOGV("File::List: path %s does not exist: %d", path.c_str(), errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
DIR* dir;
|
||||
if ((dir = opendir(path.c_str())) == NULL) {
|
||||
LOGW("File::List: unable to open directory %s: %d", path.c_str(), errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, kCurrentDirectory) &&
|
||||
(strcmp(entry->d_name, kParentDirectory))) {
|
||||
files->push_back(entry->d_name);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::CreateDirectory(std::string path) {
|
||||
size_t size = path.size();
|
||||
if ((size == 1) && (path[0] == '/')) return true;
|
||||
if ((size == 1) && (path[0] == kPathDelimiter[0])) return true;
|
||||
|
||||
if (size <= 1) return false;
|
||||
|
||||
if (path.at(size - 1) == '/') {
|
||||
if (path.at(size - 1) == kPathDelimiter[0]) {
|
||||
--size;
|
||||
path.resize(size);
|
||||
}
|
||||
|
||||
size_t pos = path.find('/', 1);
|
||||
size_t pos = path.find(kPathDelimiter[0], 1);
|
||||
while (pos < size) {
|
||||
path.at(pos) = '\0';
|
||||
if (mkdir(path.c_str(), 0775) != 0) {
|
||||
@@ -195,8 +259,8 @@ bool File::CreateDirectory(std::string path) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
path.at(pos) = '/';
|
||||
pos = path.find('/', pos + 1);
|
||||
path.at(pos) = kPathDelimiter[0];
|
||||
pos = path.find(kPathDelimiter[0], pos + 1);
|
||||
}
|
||||
if (mkdir(path.c_str(), 0775) != 0) {
|
||||
if (errno != EEXIST) {
|
||||
|
||||
@@ -730,6 +730,109 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) {
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) {
|
||||
CdmQueryMap query_info;
|
||||
CdmQueryMap::iterator itr;
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryStatus(&query_info));
|
||||
itr = query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
|
||||
ASSERT_TRUE(itr != query_info.end());
|
||||
EXPECT_EQ(2u, itr->second.size());
|
||||
EXPECT_TRUE(itr->second.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0 ||
|
||||
itr->second.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0);
|
||||
|
||||
CdmSecurityLevel security_level =
|
||||
(itr->second.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0)
|
||||
? kSecurityLevelL1
|
||||
: kSecurityLevelL3;
|
||||
|
||||
std::string base_path;
|
||||
EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &base_path));
|
||||
|
||||
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 = base_path.rfind(security_dirs[i]);
|
||||
if (std::string::npos != pos)
|
||||
break;
|
||||
}
|
||||
|
||||
EXPECT_NE(std::string::npos, pos);
|
||||
std::string old_base_path(base_path, 0, pos);
|
||||
File file;
|
||||
file.Remove(old_base_path);
|
||||
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
std::string provisioning_server_url;
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||
&key_msg_, &provisioning_server_url));
|
||||
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||
std::string response =
|
||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
CdmKeySetId key_set_id = key_set_id_;
|
||||
EXPECT_FALSE(key_set_id_.empty());
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
std::vector<std::string> files;
|
||||
EXPECT_TRUE(file.List(base_path, &files));
|
||||
EXPECT_TRUE(2u == files.size() || 3u == files.size());
|
||||
|
||||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
std::string from = base_path + files[i];
|
||||
if (file.IsRegularFile(from)) {
|
||||
std::string to = old_base_path + files[i];
|
||||
EXPECT_TRUE(file.Copy(from, to));
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(file.Remove(base_path));
|
||||
|
||||
// Setup complete to earlier version (non-security level based) path.
|
||||
// Restore persistent license, retrieve L1, L3 streaming licenses to verify
|
||||
session_id_.clear();
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
TestWvCdmClientPropertySet property_set;
|
||||
property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
|
||||
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
decryptor_.OpenSession(g_key_system, &property_set, &session_id_));
|
||||
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
std::string server_url;
|
||||
EXPECT_EQ(wvcdm::NEED_PROVISIONING,
|
||||
decryptor_.GenerateKeyRequest(session_id_, key_set_id, g_key_id,
|
||||
kLicenseTypeStreaming, app_parameters,
|
||||
&key_msg_, &server_url));
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
decryptor_.GetProvisioningRequest(&key_msg_,
|
||||
&provisioning_server_url));
|
||||
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
|
||||
response =
|
||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
||||
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
|
||||
&session_id_));
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_P(WvCdmDecryptionTest, DecryptionTest) {
|
||||
SubSampleInfo* data = GetParam();
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
|
||||
Reference in New Issue
Block a user