From db41502f8668439705ffb8e203ebafd0bf3e6443 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Thu, 22 Aug 2013 09:37:18 -0700 Subject: [PATCH] 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 --- .../cdm/core/include/device_files.h | 2 + libwvdrmengine/cdm/core/include/file_store.h | 2 + libwvdrmengine/cdm/core/include/properties.h | 8 ++ libwvdrmengine/cdm/core/src/device_files.cpp | 65 +++++++++ libwvdrmengine/cdm/core/src/properties.cpp | 15 ++ .../cdm/core/test/device_files_unittest.cpp | 128 +++++++++++++++++- .../cdm/core/test/file_store_unittest.cpp | 64 +++++++++ .../cdm/include/properties_configuration.h | 4 + libwvdrmengine/cdm/src/file_store.cpp | 76 ++++++++++- .../cdm/test/request_license_test.cpp | 103 ++++++++++++++ 10 files changed, 455 insertions(+), 12 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 86c55c0e..929675ce 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -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_; diff --git a/libwvdrmengine/cdm/core/include/file_store.h b/libwvdrmengine/cdm/core/include/file_store.h index 143a4768..a41f1131 100644 --- a/libwvdrmengine/cdm/core/include/file_store.h +++ b/libwvdrmengine/cdm/core/include/file_store.h @@ -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* 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); diff --git a/libwvdrmengine/cdm/core/include/properties.h b/libwvdrmengine/cdm/core/include/properties.h index 49fed6a8..3a08055f 100644 --- a/libwvdrmengine/cdm/core/include/properties.h +++ b/libwvdrmengine/cdm/core/include/properties.h @@ -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* dirs); static const std::string GetSecurityLevel(const CdmSessionId& session_id); static const std::vector 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 session_property_set_; CORE_DISALLOW_COPY_AND_ASSIGN(Properties); diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index 1baf8d20..04234ef0 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -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 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 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; } diff --git a/libwvdrmengine/cdm/core/src/properties.cpp b/libwvdrmengine/cdm/core/src/properties.cpp index ffc56411..352c0951 100644 --- a/libwvdrmengine/cdm/core/src/properties.cpp +++ b/libwvdrmengine/cdm/core/src/properties.cpp @@ -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 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* 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 diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 087d09c7..196519f9 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -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*)); 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 {}; +class DeviceFilesSecurityLevelTest + : public DeviceFilesTest, + public ::testing::WithParamInterface {}; + 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 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 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_ + diff --git a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp index 8faac410..3331dad0 100644 --- a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp @@ -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 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 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 diff --git a/libwvdrmengine/cdm/include/properties_configuration.h b/libwvdrmengine/cdm/include/properties_configuration.h index 58af5936..2343e243 100644 --- a/libwvdrmengine/cdm/include/properties_configuration.h +++ b/libwvdrmengine/cdm/include/properties_configuration.h @@ -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_ diff --git a/libwvdrmengine/cdm/src/file_store.cpp b/libwvdrmengine/cdm/src/file_store.cpp index bd7fd357..9a59632b 100644 --- a/libwvdrmengine/cdm/src/file_store.cpp +++ b/libwvdrmengine/cdm/src/file_store.cpp @@ -6,8 +6,10 @@ #include #include -#include +#include +#include #include +#include #include #include @@ -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* 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) { diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index b7a4caf4..9f2b00b1 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -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 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(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 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(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_);