Allow Secure Stops to be queried and deleted by application ID

This CL changes the WVDrmPlugin so that an application can segregate
its secure stops from those of other applications by setting an
application ID.

This CL is a merge of the following Widevine CLs:
https://widevine-internal-review.googlesource.com/#/c/11565/
Add getSecureStop by ssid

https://widevine-internal-review.googlesource.com/#/c/11572
Add getSecureStop by SSID and releaseAllSecureStops by app id.

https://widevine-internal-review.googlesource.com/#/c/11564/
Store Usage Info by App Id (device_file stubs)

https://widevine-internal-review.googlesource.com/#/c/11563/
Add application id to StoreUsageInfo.

https://widevine-internal-review.googlesource.com/#/c/11561/
Added Application ID to PropertySet for secure stop.

bug: 18053197
bug: 18076411
Change-Id: I5444baf67ba1b960dee2dc958bced8de82ab70a3
This commit is contained in:
Fred Gylys-Colwell
2014-11-05 17:39:44 -08:00
parent b3650a9661
commit 20191d996c
19 changed files with 421 additions and 54 deletions

View File

@@ -19,6 +19,7 @@ class CdmClientPropertySet {
virtual bool is_session_sharing_enabled() const = 0;
virtual uint32_t session_sharing_id() const = 0;
virtual void set_session_sharing_id(uint32_t id) = 0;
virtual const std::string& app_id() const = 0;
};
} // namespace wvcdm

View File

@@ -93,7 +93,16 @@ class CdmEngine {
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
// Usage related methods for streaming licenses
virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info);
// Retrieve a random usage info from the list of all usage infos for this app
// id.
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
CdmUsageInfo* usage_info);
// Retrieve the usage info for the specified pst.
// Returns UNKNOWN_ERROR if no usage info was found.
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid,
CdmUsageInfo* usage_info);
virtual CdmResponseType ReleaseAllUsageInfo(const std::string& app_id);
virtual CdmResponseType ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message);
@@ -120,7 +129,8 @@ class CdmEngine {
private:
// private methods
bool ValidateKeySystem(const CdmKeySystem& key_system);
CdmResponseType GetUsageInfo(SecurityLevel requested_security_level,
CdmResponseType GetUsageInfo(const std::string& app_id,
SecurityLevel requested_security_level,
CdmUsageInfo* usage_info);
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);

View File

@@ -83,9 +83,11 @@ class CdmSession {
virtual void OnTimerEvent(bool update_usage);
virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
virtual void GetApplicationId(std::string* app_id);
virtual SecurityLevel GetRequestedSecurityLevel();
virtual CdmSecurityLevel GetSecurityLevel();
virtual CdmResponseType DeleteUsageInformation(const std::string& app_id);
virtual CdmResponseType UpdateUsageInformation();
virtual bool is_initial_usage_update() { return is_initial_usage_update_; }
@@ -120,6 +122,7 @@ class CdmSession {
bool is_offline_;
bool is_release_;
CdmSecurityLevel security_level_;
std::string app_id_;
// decryption and usage flags
bool is_initial_decryption_;

View File

@@ -62,11 +62,22 @@ class DeviceFiles {
virtual bool StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response);
virtual bool DeleteUsageInfo(const std::string& provider_session_token);
virtual bool DeleteUsageInfo();
const CdmKeyResponse& key_response,
const std::string& app_id);
virtual bool DeleteUsageInfo(const std::string& app_id,
const std::string& provider_session_token);
virtual bool DeleteAllUsageInfoForApp(const std::string& app_id);
// Retrieve one usage info from the file. Subsequent calls will retrieve
// subsequent entries in the table for this app_id.
virtual bool RetrieveUsageInfo(
const std::string& app_id,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
// Retrieve the usage info entry specified by |provider_session_token|.
// Returns false if the entry could not be found.
virtual bool RetrieveUsageInfo(const std::string& app_id,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response);
private:
bool StoreFile(const char* name, const std::string& serialized_file);
@@ -80,7 +91,7 @@ class DeviceFiles {
// For testing only:
static std::string GetCertificateFileName();
static std::string GetLicenseFileNameExtension();
static std::string GetUsageInfoFileName();
static std::string GetUsageInfoFileName(const std::string& app_id);
void SetTestFile(File* file);
#if defined(UNIT_TEST)
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);

View File

@@ -53,6 +53,8 @@ class Properties {
static bool GetFactoryKeyboxPath(std::string* keybox);
static bool GetOEMCryptoPath(std::string* library_name);
static bool GetSecurityLevelDirectories(std::vector<std::string>* dirs);
static bool GetApplicationId(const CdmSessionId& session_id,
std::string* app_id);
static bool GetSecurityLevel(const CdmSessionId& session_id,
std::string* security_level);
static bool GetServiceCertificate(const CdmSessionId& session_id,

View File

@@ -15,6 +15,7 @@ typedef std::string CdmInitData;
typedef std::string CdmKeyMessage;
typedef std::string CdmKeyResponse;
typedef std::string KeyId;
typedef std::string CdmSecureStopId;
typedef std::string CdmSessionId;
typedef std::string CdmKeySetId;
typedef std::string RequestId;

View File

@@ -43,7 +43,11 @@ class UsagePropertySet : public CdmClientPropertySet {
virtual void set_session_sharing_id(uint32_t id) {
id; // noop to suppress warning
}
virtual const std::string& app_id() const { return app_id_; }
void set_app_id(const std::string& appId) { app_id_ = appId; }
private:
std::string app_id_;
std::string security_level_;
const std::string empty_;
};
@@ -582,29 +586,95 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
return status;
}
CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid,
CdmUsageInfo* usage_info) {
if (NULL == usage_property_set_.get()) {
usage_property_set_.reset(new UsagePropertySet());
}
usage_property_set_->set_security_level(kLevelDefault);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(usage_property_set_.get()));
CdmResponseType status = usage_session_->Init();
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
return status;
}
DeviceFiles handle;
if (!handle.Init(usage_session_->GetSecurityLevel())) {
LOGE("CdmEngine::GetUsageInfo: device file init error");
return UNKNOWN_ERROR;
}
CdmKeyMessage license_request;
CdmKeyResponse license_response;
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
&license_response)) {
usage_property_set_->set_security_level(kLevel3);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(usage_property_set_.get()));
status = usage_session_->Init();
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
return status;
}
if (!handle.Reset(usage_session_->GetSecurityLevel())) {
LOGE("CdmEngine::GetUsageInfo: device file init error");
return UNKNOWN_ERROR;
}
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
&license_response)) {
// No entry found for that ssid.
return UNKNOWN_ERROR;
}
}
std::string server_url;
usage_info->resize(1);
status =
usage_session_->RestoreUsageSession(license_request, license_response);
if (KEY_ADDED != status) {
LOGE("CdmEngine::GetUsageInfo: restore usage session error %d", status);
usage_info->clear();
return status;
}
status =
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
if (KEY_MESSAGE != status) {
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d", status);
usage_info->clear();
return status;
}
return KEY_MESSAGE;
}
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
CdmUsageInfo* usage_info) {
// Return a random usage report from a random security level
SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3;
CdmResponseType status = GetUsageInfo(security_level, usage_info);
CdmResponseType status = GetUsageInfo(app_id, security_level, usage_info);
if (KEY_MESSAGE == status && !usage_info->empty())
return status;
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
status = GetUsageInfo(security_level, usage_info);
status = GetUsageInfo(app_id, security_level, usage_info);
if (NEED_PROVISIONING == status)
return NO_ERROR; // Valid scenario that one of the security
// levels has not been provisioned
return status;
}
CdmResponseType CdmEngine::GetUsageInfo(
SecurityLevel requested_security_level,
CdmUsageInfo* usage_info) {
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
SecurityLevel requested_security_level,
CdmUsageInfo* usage_info) {
if (NULL == usage_property_set_.get()) {
usage_property_set_.reset(new UsagePropertySet());
}
usage_property_set_->set_security_level(requested_security_level);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(usage_property_set_.get()));
@@ -617,11 +687,11 @@ CdmResponseType CdmEngine::GetUsageInfo(
DeviceFiles handle;
if (!handle.Init(usage_session_->GetSecurityLevel())) {
LOGE("CdmEngine::GetUsageInfo: unable to initialize device files");
return status;
return UNKNOWN_ERROR;
}
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
if (!handle.RetrieveUsageInfo(&license_info)) {
if (!handle.RetrieveUsageInfo(app_id, &license_info)) {
LOGE("CdmEngine::GetUsageInfo: unable to read usage information");
return UNKNOWN_ERROR;
}
@@ -655,6 +725,23 @@ CdmResponseType CdmEngine::GetUsageInfo(
return KEY_MESSAGE;
}
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
CdmResponseType status = NO_ERROR;
DeviceFiles handle[kSecurityLevelUnknown - kSecurityLevelL1];
for (int i = 0, j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++i, ++j) {
if (handle[i].Init(static_cast<CdmSecurityLevel>(j))) {
if (!handle[i].DeleteAllUsageInfoForApp(app_id)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete L%d secure stops", j);
status = UNKNOWN_ERROR;
}
} else {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to initialize L%d device files", j);
status = UNKNOWN_ERROR;
}
}
return status;
}
CdmResponseType CdmEngine::ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message) {
if (NULL == usage_session_.get()) {

View File

@@ -81,6 +81,7 @@ void CdmSession::Create(
}
security_level_ = GetRequestedSecurityLevel() == kLevel3
? kSecurityLevelL3 : GetSecurityLevel();
app_id_.clear();
}
CdmSession::~CdmSession() { Properties::RemoveSessionPropertySet(session_id_); }
@@ -503,8 +504,10 @@ CdmResponseType CdmSession::StoreLicense() {
return UNKNOWN_ERROR;
}
std::string app_id;
GetApplicationId(&app_id);
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
key_response_)) {
key_response_, app_id)) {
LOGE("CdmSession::StoreLicense: Unable to store usage info");
return UNKNOWN_ERROR;
}
@@ -532,11 +535,14 @@ bool CdmSession::DeleteLicense() {
return false;
}
if (is_offline_)
if (is_offline_) {
return file_handle_->DeleteLicense(key_set_id_);
else
} else {
std::string app_id;
GetApplicationId(&app_id);
return file_handle_->DeleteUsageInfo(
license_parser_->provider_session_token());
app_id, license_parser_->provider_session_token());
}
}
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
@@ -579,6 +585,12 @@ void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
}
}
void CdmSession::GetApplicationId(std::string* app_id) {
if (app_id && !Properties::GetApplicationId(session_id_, app_id)) {
*app_id = "";
}
}
SecurityLevel CdmSession::GetRequestedSecurityLevel() {
std::string security_level;
if (Properties::GetSecurityLevel(session_id_, &security_level) &&
@@ -596,6 +608,20 @@ CdmSecurityLevel CdmSession::GetSecurityLevel() {
return crypto_session_.get()->GetSecurityLevel();
}
CdmResponseType CdmSession::DeleteUsageInformation(const std::string& app_id) {
if (!file_handle_->Reset(security_level_)) {
LOGE("CdmSession::StoreLicense: Unable to initialize device files");
return UNKNOWN_ERROR;
}
if (file_handle_->DeleteAllUsageInfoForApp(app_id)) {
return NO_ERROR;
} else {
LOGE("CdmSession::DeleteUsageInformation: failed");
return UNKNOWN_ERROR;
}
}
CdmResponseType CdmSession::UpdateUsageInformation() {
return crypto_session_->UpdateUsageInformation();
}

View File

@@ -8,6 +8,7 @@
# define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
#else
# include <openssl/sha.h>
#include <openssl/md5.h>
#endif
#include <cstring>
@@ -31,7 +32,8 @@ using video_widevine_client::sdk::UsageInfo_ProviderSession;
namespace {
const char kCertificateFileName[] = "cert.bin";
const char kUsageInfoFileName[] = "usage.bin";
const char kUsageInfoFileNamePrefix[] = "usage";
const char kUsageInfoFileNameExt[] = ".bin";
const char kLicenseFileNameExt[] = ".lic";
const char kWildcard[] = "*";
const char kDirectoryDelimiter = '/';
@@ -329,7 +331,8 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response) {
const CdmKeyResponse& key_response,
const std::string& app_id) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
return false;
@@ -337,7 +340,8 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
std::string serialized_file;
video_widevine_client::sdk::File file;
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) {
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveFile(file_name.c_str(), &serialized_file)) {
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
} else {
@@ -358,17 +362,19 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
key_response.size());
file.SerializeToString(&serialized_file);
return StoreFile(kUsageInfoFileName, serialized_file);
return StoreFile(file_name.c_str(), serialized_file);
}
bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
const std::string& provider_session_token) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
return false;
}
std::string serialized_file;
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) return false;
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveFile(file_name.c_str(), &serialized_file)) return false;
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
@@ -400,27 +406,29 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
sessions->RemoveLast();
file.SerializeToString(&serialized_file);
return StoreFile(kUsageInfoFileName, serialized_file);
return StoreFile(file_name.c_str(), serialized_file);
}
bool DeviceFiles::DeleteUsageInfo() {
bool DeviceFiles::DeleteAllUsageInfoForApp(const std::string& app_id) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::DeleteUsageInfo: Unable to get base path");
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to get base path");
return false;
}
path.append(kUsageInfoFileName);
std::string file_name = GetUsageInfoFileName(app_id);
path.append(file_name);
return file_->Remove(path);
}
bool DeviceFiles::RetrieveUsageInfo(std::vector<
std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
bool DeviceFiles::RetrieveUsageInfo(
const std::string &app_id,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
@@ -433,13 +441,14 @@ bool DeviceFiles::RetrieveUsageInfo(std::vector<
}
std::string serialized_file;
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) {
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveFile(file_name.c_str(), &serialized_file)) {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
return false;
}
path += kUsageInfoFileName;
path += file_name;
if (!file_->Exists(path) || 0 == file_->FileSize(path)) {
usage_info->resize(0);
@@ -465,6 +474,42 @@ bool DeviceFiles::RetrieveUsageInfo(std::vector<
return true;
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
std::string serialized_file;
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveFile(file_name.c_str(), &serialized_file)) return false;
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
return false;
}
int index = 0;
bool found = false;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).token().compare(
provider_session_token) == 0) {
found = true;
break;
}
}
if (!found) {
return false;
}
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
return true;
}
bool DeviceFiles::StoreFile(const char* name,
const std::string& serialized_file) {
if (!file_.get()) {
@@ -671,8 +716,15 @@ std::string DeviceFiles::GetLicenseFileNameExtension() {
return kLicenseFileNameExt;
}
std::string DeviceFiles::GetUsageInfoFileName() {
return kUsageInfoFileName;
std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
if (app_id == "") return std::string(kUsageInfoFileNamePrefix)
+ std::string(kUsageInfoFileNameExt);
std::vector<uint8_t> hash(MD5_DIGEST_LENGTH);
const unsigned char* input =
reinterpret_cast<const unsigned char*>(app_id.data());
MD5(input, app_id.size(), &hash[0]);
return std::string(kUsageInfoFileNamePrefix) + wvcdm::Base64SafeEncode(hash) +
std::string(kUsageInfoFileNameExt);
}
void DeviceFiles::SetTestFile(File* file) {

View File

@@ -60,6 +60,17 @@ const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
return NULL;
}
bool Properties::GetApplicationId(const CdmSessionId& session_id,
std::string* app_id) {
const CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);
if (NULL == property_set) {
return false;
}
*app_id = property_set->app_id();
return true;
}
bool Properties::GetSecurityLevel(const CdmSessionId& session_id,
std::string* security_level) {
const CdmClientPropertySet* property_set =

View File

@@ -1747,7 +1747,8 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
TEST_P(DeviceFilesUsageInfoTest, Read) {
MockFile file;
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
std::string app_id; // TODO(fredgc): expand tests.
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id);
int index = GetParam();
std::string data;
@@ -1777,7 +1778,7 @@ TEST_P(DeviceFilesUsageInfoTest, Read) {
device_files.SetTestFile(&file);
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
ASSERT_TRUE(device_files.RetrieveUsageInfo(&license_info));
ASSERT_TRUE(device_files.RetrieveUsageInfo(app_id, &license_info));
if (index >= 0) {
EXPECT_EQ(index, license_info.size());
for (size_t i = 0; i < license_info.size(); ++i) {
@@ -1799,10 +1800,11 @@ TEST_P(DeviceFilesUsageInfoTest, Read) {
TEST_P(DeviceFilesUsageInfoTest, Store) {
MockFile file;
std::string app_id; // TODO(fredgc): expand tests.
std::string pst(GenerateRandomData(kProviderSessionTokenLen));
std::string license_request(GenerateRandomData(kLicenseRequestLen));
std::string license(GenerateRandomData(kLicenseLen));
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id);
int index = GetParam();
std::string data;
@@ -1836,12 +1838,13 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
ASSERT_TRUE(device_files.StoreUsageInfo(pst, license_request, license));
ASSERT_TRUE(device_files.StoreUsageInfo(pst, license_request, license, app_id));
}
TEST_P(DeviceFilesUsageInfoTest, Delete) {
MockFile file;
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
std::string app_id; // TODO(fredgc): expand tests.
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id);
int index = GetParam();
if (index < 0) return;
@@ -1886,9 +1889,9 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) {
device_files.SetTestFile(&file);
if (index >= 1) {
ASSERT_TRUE(device_files.DeleteUsageInfo(pst));
ASSERT_TRUE(device_files.DeleteUsageInfo(app_id, pst));
} else {
ASSERT_FALSE(device_files.DeleteUsageInfo(pst));
ASSERT_FALSE(device_files.DeleteUsageInfo(app_id, pst));
}
}