Enable usage reporting

[ Merge from Widevine CDM repo of
  https://widevine-internal-review.googlesource.com/#/c/10171/ and
  https://widevine-internal-review.googlesource.com/#/c/10172/ ]

Updated license_protocol.proto from constituent protos in google3

These changes make use of OEMCrypto v9 changes to support usage reporting.
Usage reporting may be enabled for streaming (by means of secure stops) and
offline playback by a provider session token specified in the license.

Changes include periodically updating usage information for relevant
sessions and reporting and releasing usage information as needed.

The CDM has removed all references to Secure Stops. This change
updates the Android API implementation to comply.

b/11987015

Change-Id: Ibb6f2ced4ef20ee349ca1ae6412ce686b2b5d085
This commit is contained in:
Rahul Frias
2014-05-17 09:31:41 -07:00
parent d68e1f8307
commit e56e58fbf5
20 changed files with 1573 additions and 261 deletions

View File

@@ -88,10 +88,9 @@ class CdmEngine {
std::string* cert,
std::string* wrapped_key);
// Secure stop related methods
CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
CdmResponseType ReleaseSecureStops(
const CdmSecureStopReleaseMessage& message);
// Usage related methods for streaming licenses
CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info);
CdmResponseType ReleaseUsageInfo(const CdmUsageInfoReleaseMessage& message);
// Decryption and key related methods
// Accept encrypted buffer and return decrypted data.
@@ -126,6 +125,9 @@ class CdmEngine {
CdmReleaseKeySetMap release_key_sets_;
CertificateProvisioning cert_provisioning_;
SecurityLevel cert_provisioning_requested_security_level_;
CdmSession* usage_session_;
int64_t last_usage_information_update_time;
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
};

View File

@@ -28,6 +28,8 @@ class CdmSession {
CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id,
const CdmLicenseType license_type);
CdmResponseType RestoreUsageSession(const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response);
void set_key_system(const CdmKeySystem& ksystem) { key_system_ = ksystem; }
const CdmKeySystem& key_system() { return key_system_; }
@@ -89,6 +91,12 @@ class CdmSession {
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
SecurityLevel GetRequestedSecurityLevel();
CdmSecurityLevel GetSecurityLevel();
CdmResponseType UpdateUsageInformation();
bool is_usage_update_needed() { return is_usage_update_needed_; }
void reset_is_usage_update_needed() { is_usage_update_needed_ = false; }
private:
@@ -96,7 +104,9 @@ class CdmSession {
CdmSessionId GenerateSessionId();
bool GenerateKeySetId(CdmKeySetId* key_set_id);
CdmResponseType StoreLicense();
bool StoreLicense(DeviceFiles::LicenseState state);
bool DeleteLicense();
// instance variables
const CdmSessionId session_id_;
@@ -106,13 +116,16 @@ class CdmSession {
PolicyEngine policy_engine_;
bool license_received_;
bool reinitialize_session_;
bool is_offline_;
bool is_release_;
bool is_usage_update_needed_;
CdmLicenseType license_type_;
// information useful for offline and usage scenarios
CdmKeyMessage key_request_;
CdmKeyResponse key_response_;
// license type offline related information
CdmInitData offline_init_data_;
CdmKeyMessage offline_key_request_;
CdmKeyResponse offline_key_response_;
CdmKeyMessage offline_key_renewal_request_;
CdmKeyResponse offline_key_renewal_response_;
std::string offline_release_server_url_;

View File

@@ -45,7 +45,8 @@ class CryptoSession {
const std::string& signature,
const std::string& mac_key_iv,
const std::string& mac_key,
int num_keys, const CryptoKey* key_array);
const std::vector<CryptoKey>& key_array,
const std::string& provider_session_token);
bool LoadCertificatePrivateKey(std::string& wrapped_key);
bool RefreshKeys(const std::string& message, const std::string& signature,
int num_keys, const CryptoKey* key_array);
@@ -63,6 +64,15 @@ class CryptoSession {
// Media data path
CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
CdmResponseType UpdateUsageInformation();
CdmResponseType GenerateUsageReport(
const std::string& provider_session_token,
std::string* usage_report);
CdmResponseType ReleaseUsageInformation(
const std::string& message,
const std::string& signature,
const std::string& provider_session_token);
bool GetRandom(size_t data_length, uint8_t* random_data);
private:

View File

@@ -18,10 +18,10 @@ class DeviceFiles {
} LicenseState;
DeviceFiles(): file_(NULL), security_level_(kSecurityLevelUninitialized),
initialized_(false) {}
virtual ~DeviceFiles() {}
initialized_(false), test_file_(false) {}
virtual ~DeviceFiles();
virtual bool Init(const File* handle, CdmSecurityLevel security_level);
virtual bool Init(CdmSecurityLevel security_level);
virtual bool StoreCertificate(const std::string& certificate,
const std::string& wrapped_private_key);
@@ -49,14 +49,24 @@ class DeviceFiles {
virtual bool DeleteAllLicenses();
virtual bool LicenseExists(const std::string& key_set_id);
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();
virtual bool RetrieveUsageInfo(
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
// For testing only
static std::string GetCertificateFileName();
static std::string GetLicenseFileNameExtension();
static std::string GetUsageInfoFileName();
void SetTestFile(File* file);
protected:
bool Hash(const std::string& data, std::string* hash);
bool StoreFile(const char* name, const std::string& data);
bool RetrieveFile(const char* name, std::string* data);
bool StoreFile(const char* name, const std::string& serialized_file);
bool RetrieveFile(const char* name, std::string* serialized_file);
private:
// Certificate and offline licenses are now stored in security
@@ -68,6 +78,8 @@ class DeviceFiles {
CdmSecurityLevel security_level_;
bool initialized_;
bool test_file_;
CORE_DISALLOW_COPY_AND_ASSIGN(DeviceFiles);
};

View File

@@ -40,12 +40,16 @@ class CdmLicense {
CdmResponseType HandleKeyUpdateResponse(
bool is_renewal, const CdmKeyResponse& license_response);
bool RestoreOfflineLicense(CdmKeyMessage& license_request,
CdmKeyResponse& license_response,
CdmKeyResponse& license_renewal_response);
bool RestoreOfflineLicense(const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response);
bool RestoreUsageLicense(const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response);
bool HasInitData() { return !stored_init_data_.empty(); }
bool IsKeyLoaded(const KeyId& key_id);
std::string provider_session_token() { return provider_session_token_; }
private:
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
std::string* server_url);
@@ -67,6 +71,7 @@ class CdmLicense {
std::string stored_init_data_;
bool initialized_;
std::set<KeyId> loaded_keys_;
std::string provider_session_token_;
// Used for certificate based licensing
CdmKeyMessage key_request_;

View File

@@ -23,8 +23,8 @@ typedef uint32_t CryptoSessionId;
typedef std::string CryptoKeyId;
typedef std::map<std::string, std::string> CdmAppParameterMap;
typedef std::map<std::string, std::string> CdmQueryMap;
typedef std::vector<std::string> CdmSecureStops;
typedef std::vector<uint8_t> CdmSecureStopReleaseMessage;
typedef std::vector<std::string> CdmUsageInfo;
typedef std::string CdmUsageInfoReleaseMessage;
typedef std::string CdmProvisioningRequest;
typedef std::string CdmProvisioningResponse;

View File

@@ -6,6 +6,8 @@
#include <sstream>
#include "cdm_session.h"
#include "clock.h"
#include "device_files.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
@@ -14,16 +16,26 @@
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
namespace {
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
const size_t kMinNoncesPerSession = 4;
} // unnamed namespace
namespace wvcdm {
CdmEngine::CdmEngine()
: cert_provisioning_requested_security_level_(kLevelDefault) {
: cert_provisioning_requested_security_level_(kLevelDefault),
usage_session_(NULL),
last_usage_information_update_time(0) {
Properties::Init();
}
CdmEngine::~CdmEngine() {
CancelSessions();
if (NULL != usage_session_)
delete usage_session_;
CdmSessionMap::iterator i(sessions_.begin());
for (; i != sessions_.end(); ++i)
delete i->second;
@@ -482,15 +494,73 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
wrapped_key);
}
CdmResponseType CdmEngine::GetSecureStops(
CdmSecureStops* secure_stops) {
// TODO(edwinwong, rfrias): add implementation
return NO_ERROR;
CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
if (NULL == usage_session_) {
usage_session_ = new CdmSession(NULL);
}
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: unable to initialize device files");
return status;
}
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
if (!handle.RetrieveUsageInfo(&license_info)) {
LOGE("CdmEngine::GetUsageInfo: unable to read usage information");
return UNKNOWN_ERROR;
}
if (0 == license_info.size()) {
usage_info->resize(0);
return NO_ERROR;
}
std::string server_url;
// rate limit secure stop messages based on minimum nonce
// table size per session
usage_info->resize(license_info.size() >= kMinNoncesPerSession - 1
? kMinNoncesPerSession - 1
: license_info.size());
for (size_t i = 0; i < usage_info->size(); ++i) {
status = usage_session_->RestoreUsageSession(license_info[i].first,
license_info[i].second);
if (KEY_ADDED != status) {
LOGE("CdmEngine::GetUsageInfo: restore usage session error: %ld",
status);
usage_info->clear();
return status;
}
status = usage_session_->GenerateReleaseRequest(&(*usage_info)[i],
&server_url);
if (KEY_MESSAGE != status) {
LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld",
status);
usage_info->clear();
return status;
}
}
return KEY_MESSAGE;
}
CdmResponseType CdmEngine::ReleaseSecureStops(
const CdmSecureStopReleaseMessage& message) {
// TODO(edwinwong, rfrias): add implementation
CdmResponseType CdmEngine::ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message) {
if (NULL == usage_session_) {
LOGE("CdmEngine::ReleaseUsageInfo: cdm session not initialized");
return UNKNOWN_ERROR;
}
CdmResponseType status = usage_session_->ReleaseKey(message);
if (NO_ERROR != status) {
LOGE("CdmEngine::ReleaseUsageInfo: release key error: %ld", status);
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
@@ -615,9 +685,31 @@ bool CdmEngine::CancelSessions() {
}
void CdmEngine::OnTimerEvent() {
Clock clock;
uint64_t current_time = clock.GetCurrentTime();
bool update_usage_information = false;
if (current_time - last_usage_information_update_time >
kUpdateUsageInformationPeriod) {
update_usage_information = true;
last_usage_information_update_time = current_time;
}
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter != sessions_.end(); ++iter) {
iter->second->OnTimerEvent();
if (update_usage_information && iter->second->is_usage_update_needed()) {
// usage is updated for all sessions so this needs to be
// called only once per update usage information period
CdmResponseType status = iter->second->UpdateUsageInformation();
if (NO_ERROR != status) {
LOGW("Update usage information failed: %u", status);
} else {
update_usage_information = false;
}
}
iter->second->reset_is_usage_update_needed();
}
}

View File

@@ -31,7 +31,9 @@ CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set)
crypto_session_(NULL),
license_received_(false),
reinitialize_session_(false),
license_type_(kLicenseTypeStreaming),
is_offline_(false),
is_release_(false),
is_usage_update_needed_(false),
is_certificate_loaded_(false) {
if (cdm_client_property_set) {
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
@@ -48,9 +50,8 @@ CdmResponseType CdmSession::Init() {
std::string token;
if (Properties::use_certificates_as_identification()) {
File file;
DeviceFiles handle;
if (!handle.Init(&file, session.get()->GetSecurityLevel()) ||
if (!handle.Init(session.get()->GetSecurityLevel()) ||
!handle.RetrieveCertificate(&token, &wrapped_key_)) {
return NEED_PROVISIONING;
}
@@ -72,15 +73,14 @@ CdmResponseType CdmSession::RestoreOfflineSession(
key_set_id_ = key_set_id;
// Retrieve license information from persistent store
File file;
DeviceFiles handle;
if (!handle.Init(&file, crypto_session_->GetSecurityLevel()))
if (!handle.Init(crypto_session_->GetSecurityLevel()))
return UNKNOWN_ERROR;
DeviceFiles::LicenseState license_state;
if (!handle.RetrieveLicense(key_set_id, &license_state, &offline_init_data_,
&offline_key_request_, &offline_key_response_,
&key_request_, &key_response_,
&offline_key_renewal_request_,
&offline_key_renewal_response_,
&offline_release_server_url_)) {
@@ -103,14 +103,39 @@ CdmResponseType CdmSession::RestoreOfflineSession(
}
}
if (!license_parser_.RestoreOfflineLicense(offline_key_request_,
offline_key_response_,
if (!license_parser_.RestoreOfflineLicense(key_request_, key_response_,
offline_key_renewal_response_)) {
return UNKNOWN_ERROR;
}
license_received_ = true;
license_type_ = license_type;
is_offline_ = true;
is_release_ = license_type == kLicenseTypeRelease;
return KEY_ADDED;
}
CdmResponseType CdmSession::RestoreUsageSession(
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response) {
key_request_ = key_request;
key_response_ = key_response;
if (Properties::use_certificates_as_identification()) {
if (is_certificate_loaded_ ||
crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
is_certificate_loaded_ = true;
} else {
return NEED_PROVISIONING;
}
}
if (!license_parser_.RestoreUsageLicense(key_request_, key_response_)) {
return UNKNOWN_ERROR;
}
license_received_ = true;
is_offline_ = false;
is_release_ = true;
return KEY_ADDED;
}
@@ -143,9 +168,17 @@ CdmResponseType CdmSession::GenerateKeyRequest(
return UNKNOWN_ERROR;
}
license_type_ = license_type;
switch (license_type) {
case kLicenseTypeStreaming: is_offline_ = false; break;
case kLicenseTypeOffline: is_offline_ = true; break;
case kLicenseTypeRelease: is_release_ = true; break;
default:
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
license_type);
return UNKNOWN_ERROR;
}
if (license_type_ == kLicenseTypeRelease) {
if (is_release_) {
return GenerateReleaseRequest(key_request, server_url);
} else if (license_received_) { // renewal
return Properties::require_explicit_renew_request()
@@ -178,9 +211,9 @@ CdmResponseType CdmSession::GenerateKeyRequest(
return KEY_ERROR;
}
if (license_type_ == kLicenseTypeOffline) {
key_request_ = *key_request;
if (is_offline_) {
offline_init_data_ = init_data.data();
offline_key_request_ = *key_request;
offline_release_server_url_ = *server_url;
}
@@ -201,7 +234,7 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
return UNKNOWN_ERROR;
}
if (license_type_ == kLicenseTypeRelease) {
if (is_release_) {
return ReleaseKey(key_response);
} else if (license_received_) { // renewal
return Properties::require_explicit_renew_request()
@@ -213,25 +246,11 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
if (sts != KEY_ADDED) return sts;
license_received_ = true;
key_response_ = key_response;
if (license_type_ == kLicenseTypeOffline) {
offline_key_response_ = key_response;
if (!GenerateKeySetId(&key_set_id_)) {
LOGE("CdmSession::AddKey: Unable to generate key set Id");
return UNKNOWN_ERROR;
}
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::AddKey: Unable to store license");
CdmResponseType sts = Init();
if (sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Reinitialization failed");
return sts;
}
key_set_id_.clear();
return UNKNOWN_ERROR;
}
if (is_offline_ || !license_parser_.provider_session_token().empty()) {
sts = StoreLicense();
if (sts != NO_ERROR) return sts;
}
*key_set_id = key_set_id_;
@@ -314,6 +333,14 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
return NEED_KEY;
}
}
if (NO_ERROR == status) {
if (!is_usage_update_needed_) {
is_usage_update_needed_ =
!license_parser_.provider_session_token().empty();
}
}
return status;
}
@@ -327,7 +354,7 @@ CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
return KEY_ERROR;
}
if (license_type_ == kLicenseTypeOffline) {
if (is_offline_) {
offline_key_renewal_request_ = *key_request;
}
return KEY_MESSAGE;
@@ -339,7 +366,7 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
license_parser_.HandleKeyUpdateResponse(true, key_response);
if (sts != KEY_ADDED) return sts;
if (license_type_ == kLicenseTypeOffline) {
if (is_offline_) {
offline_key_renewal_response_ = key_response;
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) return UNKNOWN_ERROR;
}
@@ -348,23 +375,28 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
std::string* server_url) {
if (license_parser_.PrepareKeyUpdateRequest(false, key_request, server_url)) {
// Mark license as being released
if (StoreLicense(DeviceFiles::kLicenseStateReleasing)) return KEY_MESSAGE;
is_release_ = true;
if (!license_parser_.PrepareKeyUpdateRequest(false, key_request, server_url))
return UNKNOWN_ERROR;
if (is_offline_) { // Mark license as being released
if (!StoreLicense(DeviceFiles::kLicenseStateReleasing))
return UNKNOWN_ERROR;
}
return UNKNOWN_ERROR;
return KEY_MESSAGE;
}
// ReleaseKey() - Accept release response and release license.
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
CdmResponseType sts =
license_parser_.HandleKeyUpdateResponse(false, key_response);
File file;
DeviceFiles handle;
if (handle.Init(&file, crypto_session_->GetSecurityLevel()))
handle.DeleteLicense(key_set_id_);
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false,
key_response);
if (NO_ERROR != sts)
return sts;
return sts;
if (is_offline_ || !license_parser_.provider_session_token().empty()) {
DeleteLicense();
}
return NO_ERROR;
}
bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
@@ -387,9 +419,8 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
std::vector<uint8_t> random_data(
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
File file;
DeviceFiles handle;
if (!handle.Init(&file, crypto_session_->GetSecurityLevel()))
if (!handle.Init(crypto_session_->GetSecurityLevel()))
return false;
while (key_set_id->empty()) {
@@ -406,18 +437,75 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
return true;
}
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
File file;
CdmResponseType CdmSession::StoreLicense() {
if (is_offline_) {
if (!GenerateKeySetId(&key_set_id_)) {
LOGE("CdmSession::StoreLicense: Unable to generate key set Id");
return UNKNOWN_ERROR;
}
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::StoreLicense: Unable to store license");
CdmResponseType sts = Init();
if (sts != NO_ERROR) {
LOGW("CdmSession::StoreLicense: Reinitialization failed");
return sts;
}
key_set_id_.clear();
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
std::string provider_session_token = license_parser_.provider_session_token();
if (provider_session_token.empty()) {
LOGE("CdmSession::StoreLicense: No provider session token and not offline");
return UNKNOWN_ERROR;
}
DeviceFiles handle;
if (!handle.Init(&file, crypto_session_->GetSecurityLevel()))
if (!handle.Init(crypto_session_->GetSecurityLevel())) {
LOGE("CdmSession::StoreLicense: Unable to initialize device files");
return UNKNOWN_ERROR;
}
if (!handle.StoreUsageInfo(provider_session_token, key_request_,
key_response_)) {
LOGE("CdmSession::StoreLicense: Unable to store usage info");
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
DeviceFiles handle;
if (!handle.Init(crypto_session_->GetSecurityLevel()))
return false;
return handle.StoreLicense(
key_set_id_, state, offline_init_data_, offline_key_request_,
offline_key_response_, offline_key_renewal_request_,
key_set_id_, state, offline_init_data_, key_request_,
key_response_, offline_key_renewal_request_,
offline_key_renewal_response_, offline_release_server_url_);
}
bool CdmSession::DeleteLicense() {
if (!is_offline_ && license_parser_.provider_session_token().empty())
return false;
DeviceFiles handle;
if (!handle.Init(crypto_session_->GetSecurityLevel())) {
LOGE("CdmSession::DeleteLicense: Unable to initialize device files");
return false;
}
if (is_offline_)
return handle.DeleteLicense(key_set_id_);
else
return handle.DeleteUsageInfo(
license_parser_.provider_session_token());
}
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
std::pair<CdmEventListenerIter, bool> result = listeners_.insert(listener);
return result.second;
@@ -459,4 +547,15 @@ SecurityLevel CdmSession::GetRequestedSecurityLevel() {
return kLevelDefault;
}
CdmSecurityLevel CdmSession::GetSecurityLevel() {
if (NULL == crypto_session_.get())
return kSecurityLevelUninitialized;
return crypto_session_.get()->GetSecurityLevel();
}
CdmResponseType CdmSession::UpdateUsageInformation() {
return crypto_session_->UpdateUsageInformation();
}
} // namespace wvcdm

View File

@@ -103,7 +103,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
switch (cert_type) {
case kCertificateWidevine:
options->set_certificate_type(
video_widevine_server::sdk::ProvisioningOptions_CertificateType_RSA_WIDEVINE);
video_widevine_server::sdk::
ProvisioningOptions_CertificateType_WIDEVINE_DRM);
break;
case kCertificateX509:
options->set_certificate_type(
@@ -262,9 +263,8 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
const std::string& device_certificate =
provisioning_response.device_certificate();
File file;
DeviceFiles handle;
if (!handle.Init(&file, crypto_session_.GetSecurityLevel())) {
if (!handle.Init(crypto_session_.GetSecurityLevel())) {
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
return UNKNOWN_ERROR;
}

View File

@@ -155,7 +155,7 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
return false;
}
device_id->assign(reinterpret_cast<char *>(&id[0]), id_length);
device_id->assign(reinterpret_cast<char*>(&id[0]), id_length);
return true;
}
@@ -340,12 +340,11 @@ size_t CryptoSession::GetOffset(std::string message, std::string field) {
return pos;
}
CdmResponseType CryptoSession::LoadKeys(const std::string& message,
const std::string& signature,
const std::string& mac_key_iv,
const std::string& mac_key,
int num_keys,
const CryptoKey* key_array) {
CdmResponseType CryptoSession::LoadKeys(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token) {
LOGV("CryptoSession::LoadKeys: Lock");
AutoLock auto_lock(crypto_lock_);
@@ -358,10 +357,10 @@ CdmResponseType CryptoSession::LoadKeys(const std::string& message,
} else {
LOGV("CryptoSession::LoadKeys: enc_mac_key not set");
}
std::vector<OEMCrypto_KeyObject> load_key_array(num_keys);
for (int i = 0; i < num_keys; ++i) {
const CryptoKey* ki = &key_array[i];
OEMCrypto_KeyObject* ko = &load_key_array[i];
std::vector<OEMCrypto_KeyObject> load_keys(keys.size());
for (size_t i = 0; i < keys.size(); ++i) {
const CryptoKey* ki = &keys[i];
OEMCrypto_KeyObject* ko = &load_keys[i];
ko->key_id = msg + GetOffset(message, ki->key_id());
ko->key_id_length = ki->key_id().length();
ko->key_data_iv = msg + GetOffset(message, ki->key_data_iv());
@@ -377,11 +376,17 @@ CdmResponseType CryptoSession::LoadKeys(const std::string& message,
ko->key_control = NULL;
}
}
uint8_t* pst = NULL;
if (!provider_session_token.empty()) {
pst =
const_cast<uint8_t*>(msg) + GetOffset(message, provider_session_token);
}
LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts = OEMCrypto_LoadKeys(
oec_session_id_, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
enc_mac_key_iv, enc_mac_key, num_keys, &load_key_array[0], NULL, 0);
enc_mac_key_iv, enc_mac_key, keys.size(), &load_keys[0], pst,
provider_session_token.length());
if (OEMCrypto_SUCCESS == sts) {
return KEY_ADDED;
@@ -613,6 +618,84 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
}
}
CdmResponseType CryptoSession::UpdateUsageInformation() {
return (OEMCrypto_UpdateUsageTable() == OEMCrypto_SUCCESS) ? NO_ERROR
: UNKNOWN_ERROR;
}
CdmResponseType CryptoSession::GenerateUsageReport(
const std::string& provider_session_token, std::string* usage_report) {
LOGV("GenerateUsageReport: id=%ld", (uint32_t)oec_session_id_);
if (NULL == usage_report) {
LOGE("usage_report parameter is null");
return UNKNOWN_ERROR;
}
AutoLock auto_lock(crypto_lock_);
uint8_t* pst = reinterpret_cast<uint8_t*>(
const_cast<char*>(provider_session_token.data()));
OEMCryptoResult status =
OEMCrypto_DeactivateUsageEntry(pst, provider_session_token.length());
if (OEMCrypto_SUCCESS != status) {
LOGE("CryptoSession::GenerateUsageReport: Deactivate Usage Entry error=%ld",
status);
return UNKNOWN_ERROR;
}
size_t usage_length = 0;
status = OEMCrypto_ReportUsage(oec_session_id_, pst,
provider_session_token.length(), NULL,
&usage_length);
if (OEMCrypto_ERROR_SHORT_BUFFER != status) {
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld", status);
return UNKNOWN_ERROR;
}
usage_report->resize(usage_length);
OEMCrypto_PST_Report* report = reinterpret_cast<OEMCrypto_PST_Report*>(
const_cast<char*>(usage_report->data()));
status = OEMCrypto_ReportUsage(oec_session_id_, pst,
provider_session_token.length(), report,
&usage_length);
if (OEMCrypto_SUCCESS != status) {
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld", status);
return UNKNOWN_ERROR;
}
if (usage_length < usage_report->length()) {
usage_report->resize(usage_length);
}
return NO_ERROR;
}
CdmResponseType CryptoSession::ReleaseUsageInformation(
const std::string& message, const std::string& signature,
const std::string& provider_session_token) {
LOGV("ReleaseUsageInformation: id=%ld", (uint32_t)oec_session_id_);
AutoLock auto_lock(crypto_lock_);
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* sig = reinterpret_cast<const uint8_t*>(signature.data());
const uint8_t* pst = msg + GetOffset(message, provider_session_token);
OEMCryptoResult status = OEMCrypto_DeleteUsageEntry(
oec_session_id_, pst, provider_session_token.length(), msg,
message.length(), sig, signature.length());
if (OEMCrypto_SUCCESS != status) {
LOGE("CryptoSession::ReleaseUsageInformation: Report Usage error=%ld",
status);
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
bool CryptoSession::GenerateNonce(uint32_t* nonce) {
if (!nonce) {
LOGE("input parameter is null");

View File

@@ -10,6 +10,7 @@
#include "log.h"
#include "openssl/sha.h"
#include "properties.h"
#include "string_conversions.h"
// Protobuf generated classes.
using video_widevine_client::sdk::DeviceCertificate;
@@ -17,9 +18,12 @@ using video_widevine_client::sdk::HashedFile;
using video_widevine_client::sdk::License;
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
using video_widevine_client::sdk::License_LicenseState_RELEASING;
using video_widevine_client::sdk::UsageInfo;
using video_widevine_client::sdk::UsageInfo_ProviderSession;
namespace {
const char kCertificateFileName[] = "cert.bin";
const char kUsageInfoFileName[] = "usage.bin";
const char kLicenseFileNameExt[] = ".lic";
const char kWildcard[] = "*";
const char kDirectoryDelimiter = '/';
@@ -27,15 +31,15 @@ const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"};
size_t kSecurityLevelPathCompatibilityExclusionListSize =
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
} // namespace
} // unnamed namespace
namespace wvcdm {
bool DeviceFiles::Init(const File* handle, CdmSecurityLevel security_level) {
if (handle == NULL) {
LOGW("DeviceFiles::Init: Invalid file handle parameter");
return false;
}
DeviceFiles::~DeviceFiles() {
if (!file_ && !test_file_) delete file_;
}
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
switch (security_level) {
case kSecurityLevelL1:
case kSecurityLevelL2:
@@ -45,7 +49,7 @@ bool DeviceFiles::Init(const File* handle, CdmSecurityLevel security_level) {
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
return false;
}
file_ = const_cast<File*>(handle);
file_ = new File();
security_level_ = security_level;
initialized_ = true;
return true;
@@ -68,24 +72,10 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
device_certificate->set_certificate(certificate);
device_certificate->set_wrapped_private_key(wrapped_private_key);
std::string serialized_string;
file.SerializeToString(&serialized_string);
std::string serialized_file;
file.SerializeToString(&serialized_file);
// calculate SHA hash
std::string hash;
if (!Hash(serialized_string, &hash)) {
LOGW("DeviceFiles::StoreCertificate: Hash computation failed");
return false;
}
// Fill in hashed file data
HashedFile hashed_file;
hashed_file.set_file(serialized_string);
hashed_file.set_hash(hash);
hashed_file.SerializeToString(&serialized_string);
return StoreFile(kCertificateFileName, serialized_string);
return StoreFile(kCertificateFileName, serialized_file);
}
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
@@ -99,29 +89,12 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
SecurityLevelPathBackwardCompatibility();
}
std::string serialized_hashed_file;
if (!RetrieveFile(kCertificateFileName, &serialized_hashed_file))
std::string serialized_file;
if (!RetrieveFile(kCertificateFileName, &serialized_file))
return false;
HashedFile hashed_file;
if (!hashed_file.ParseFromString(serialized_hashed_file)) {
LOGW("DeviceFiles::RetrieveCertificate: Unable to parse hash file");
return false;
}
std::string hash;
if (!Hash(hashed_file.file(), &hash)) {
LOGW("DeviceFiles::RetrieveCertificate: Hash computation failed");
return false;
}
if (hash.compare(hashed_file.hash())) {
LOGW("DeviceFiles::RetrieveCertificate: Hash mismatch");
return false;
}
video_widevine_client::sdk::File file;
if (!file.ParseFromString(hashed_file.file())) {
if (!file.ParseFromString(serialized_file)) {
LOGW("DeviceFiles::RetrieveCertificate: Unable to parse file");
return false;
}
@@ -187,25 +160,11 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
license->set_renewal(license_renewal);
license->set_release_server_url(release_server_url);
std::string serialized_string;
file.SerializeToString(&serialized_string);
// calculate SHA hash
std::string hash;
if (!Hash(serialized_string, &hash)) {
LOGW("DeviceFiles::StoreLicense: Hash computation failed");
return false;
}
// File in hashed file data
HashedFile hashed_file;
hashed_file.set_file(serialized_string);
hashed_file.set_hash(hash);
hashed_file.SerializeToString(&serialized_string);
std::string serialized_file;
file.SerializeToString(&serialized_file);
std::string file_name = key_set_id + kLicenseFileNameExt;
return StoreFile(file_name.c_str(), serialized_string);
return StoreFile(file_name.c_str(), serialized_file);
}
bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
@@ -220,29 +179,12 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
return false;
}
std::string serialized_hashed_file;
std::string serialized_file;
std::string file_name = key_set_id + kLicenseFileNameExt;
if (!RetrieveFile(file_name.c_str(), &serialized_hashed_file)) return false;
HashedFile hashed_file;
if (!hashed_file.ParseFromString(serialized_hashed_file)) {
LOGW("DeviceFiles::RetrieveLicense: Unable to parse hash file");
return false;
}
std::string hash;
if (!Hash(hashed_file.file(), &hash)) {
LOGW("DeviceFiles::RetrieveLicense: Hash computation failed");
return false;
}
if (hash.compare(hashed_file.hash())) {
LOGW("DeviceFiles::RetrieveLicense: Hash mismatch");
return false;
}
if (!RetrieveFile(file_name.c_str(), &serialized_file)) return false;
video_widevine_client::sdk::File file;
if (!file.ParseFromString(hashed_file.file())) {
if (!file.ParseFromString(serialized_file)) {
LOGW("DeviceFiles::RetrieveLicense: Unable to parse file");
return false;
}
@@ -352,6 +294,142 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
return file_->Exists(path);
}
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
return false;
}
std::string serialized_file;
video_widevine_client::sdk::File file;
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) {
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
} else {
if (!file.ParseFromString(serialized_file)) {
LOGW("DeviceFiles::StoreUsageInfo: Unable to parse file");
return false;
}
}
UsageInfo* usage_info = file.mutable_usage_info();
UsageInfo_ProviderSession* provider_session = usage_info->add_sessions();
provider_session->set_token(provider_session_token.data(),
provider_session_token.size());
provider_session->set_license_request(key_request.data(),
key_request.size());
provider_session->set_license(key_response.data(),
key_response.size());
file.SerializeToString(&serialized_file);
return StoreFile(kUsageInfoFileName, serialized_file);
}
bool DeviceFiles::DeleteUsageInfo(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;
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
LOGW("DeviceFiles::DeleteUsageInfo: Unable to parse file");
return false;
}
UsageInfo* updated_info = file.mutable_usage_info();
UsageInfo info(*(const_cast<const UsageInfo*>(updated_info)));
updated_info->clear_sessions();
bool found = false;
for (int i = 0; i < info.sessions_size(); ++i) {
if (info.sessions(i).token().compare(provider_session_token) == 0) {
found = true;
} else {
updated_info->add_sessions()->set_token(info.sessions(i).token());
updated_info->add_sessions()->set_license_request(
info.sessions(i).license_request());
updated_info->add_sessions()->set_license(info.sessions(i).license());
}
}
if (!found) {
LOGW("DeviceFiles::DeleteUsageInfo: Unable to find provider session "
"token: %s", b2a_hex(provider_session_token).c_str());
return false;
}
file.SerializeToString(&serialized_file);
return StoreFile(kUsageInfoFileName, serialized_file);
}
bool DeviceFiles::DeleteUsageInfo() {
if (!initialized_) {
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::DeleteUsageInfo: Unable to get base path");
return false;
}
path.append(kUsageInfoFileName);
return file_->Remove(path);
}
bool DeviceFiles::RetrieveUsageInfo(std::vector<
std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
if (NULL == usage_info) {
LOGW("DeviceFiles::RetrieveUsageInfo: license destination not "
"provided");
return false;
}
std::string serialized_file;
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
return false;
}
path += kUsageInfoFileName;
if (!file_->Exists(path) || 0 == file_->FileSize(path)) {
usage_info->resize(0);
return true;
}
return false;
}
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
return false;
}
usage_info->resize(file.usage_info().sessions_size());
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
(*usage_info)[i] =
std::make_pair(file.usage_info().sessions(i).license_request(),
file.usage_info().sessions(i).license());
}
return true;
}
bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
if (!hash) return false;
@@ -363,7 +441,8 @@ bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
return true;
}
bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
bool DeviceFiles::StoreFile(const char* name,
const std::string& serialized_file) {
if (!file_) {
LOGW("DeviceFiles::StoreFile: Invalid file handle");
return false;
@@ -374,6 +453,21 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
return false;
}
// calculate SHA hash
std::string hash;
if (!Hash(serialized_file, &hash)) {
LOGW("DeviceFiles::StoreFile: Hash computation failed");
return false;
}
// Fill in hashed file data
HashedFile hash_file;
hash_file.set_file(serialized_file);
hash_file.set_hash(hash);
std::string serialized_hash_file;
hash_file.SerializeToString(&serialized_hash_file);
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path");
@@ -391,19 +485,24 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
return false;
}
ssize_t bytes = file_->Write(data.data(), data.size());
ssize_t bytes = file_->Write(serialized_hash_file.data(),
serialized_hash_file.size());
file_->Close();
if (bytes != static_cast<ssize_t>(data.size())) {
LOGW("DeviceFiles::StoreFile: write failed: %d %d", data.size(), bytes);
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
LOGW("DeviceFiles::StoreFile: write failed: (actual: %d, expected: %d)",
bytes,
serialized_hash_file.size());
return false;
}
LOGV("DeviceFiles::StoreFile: success: %s (%db)", path.c_str(), data.size());
LOGV("DeviceFiles::StoreFile: success: %s (%db)",
path.c_str(),
serialized_hash_file.size());
return true;
}
bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
bool DeviceFiles::RetrieveFile(const char* name, std::string* serialized_file) {
if (!file_) {
LOGW("DeviceFiles::RetrieveFile: Invalid file handle");
return false;
@@ -414,8 +513,8 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
return false;
}
if (!data) {
LOGW("DeviceFiles::RetrieveFile: Unspecified data parameter");
if (!serialized_file) {
LOGW("DeviceFiles::RetrieveFile: Unspecified serialized_file parameter");
return false;
}
@@ -434,7 +533,7 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
ssize_t bytes = file_->FileSize(path);
if (bytes <= 0) {
LOGW("DeviceFiles::RetrieveFile: File size invalid: %d", path.c_str());
LOGW("DeviceFiles::RetrieveFile: File size invalid: %s", path.c_str());
return false;
}
@@ -442,17 +541,37 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
return false;
}
data->resize(bytes);
bytes = file_->Read(&(*data)[0], data->size());
std::string serialized_hash_file;
serialized_hash_file.resize(bytes);
bytes = file_->Read(&serialized_hash_file[0], serialized_hash_file.size());
file_->Close();
if (bytes != static_cast<ssize_t>(data->size())) {
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
LOGW("DeviceFiles::RetrieveFile: read failed");
return false;
}
LOGV("DeviceFiles::RetrieveFile: success: %s (%db)", path.c_str(),
data->size());
serialized_hash_file.size());
HashedFile hash_file;
if (!hash_file.ParseFromString(serialized_hash_file)) {
LOGW("DeviceFiles::RetrieveFile: Unable to parse hash file");
return false;
}
std::string hash;
if (!Hash(hash_file.file(), &hash)) {
LOGW("DeviceFiles::RetrieveFile: Hash computation failed");
return false;
}
if (hash.compare(hash_file.hash())) {
LOGW("DeviceFiles::RetrieveFile: Hash mismatch");
return false;
}
*serialized_file = hash_file.file();
return true;
}
@@ -528,4 +647,14 @@ std::string DeviceFiles::GetLicenseFileNameExtension() {
return kLicenseFileNameExt;
}
std::string DeviceFiles::GetUsageInfoFileName() {
return kUsageInfoFileName;
}
void DeviceFiles::SetTestFile(File* file) {
if (file_) delete file_;
file_ = file;
test_file_ = true;
}
} // namespace wvcdm

View File

@@ -33,10 +33,21 @@ message License {
optional bytes release_server_url = 7;
}
message UsageInfo {
message ProviderSession {
optional bytes token = 1;
optional bytes license_request = 2;
optional bytes license = 3;
}
repeated ProviderSession sessions = 1;
}
message File {
enum FileType {
DEVICE_CERTIFICATE = 1;
LICENSE = 2;
USAGE_INFO = 2;
}
enum FileVersion {
@@ -47,6 +58,7 @@ message File {
optional FileVersion version = 2 [default = VERSION_1];
optional DeviceCertificate device_certificate = 3;
optional License license = 4;
optional UsageInfo usage_info = 5;
}
message HashedFile {

View File

@@ -6,6 +6,7 @@
#include "crypto_key.h"
#include "crypto_session.h"
#include "device_files.h"
#include "log.h"
#include "policy_engine.h"
#include "properties.h"
@@ -81,15 +82,16 @@ using video_widevine_server::sdk::ClientIdentification;
using video_widevine_server::sdk::ClientIdentification_NameValue;
using video_widevine_server::sdk::DeviceCertificate;
using video_widevine_server::sdk::EncryptedClientIdentification;
using video_widevine_server::sdk::License;
using video_widevine_server::sdk::License_KeyContainer;
using video_widevine_server::sdk::LicenseError;
using video_widevine_server::sdk::LicenseIdentification;
using video_widevine_server::sdk::LicenseRequest;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_WebM;
using video_widevine_server::sdk::
LicenseRequest_ContentIdentification_ExistingLicense;
using video_widevine_server::sdk::License;
using video_widevine_server::sdk::License_KeyContainer;
using video_widevine_server::sdk::LicenseError;
using video_widevine_server::sdk::SignedDeviceCertificate;
using video_widevine_server::sdk::SignedMessage;
@@ -424,7 +426,19 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
license_request.mutable_content_id()->mutable_license();
current_license->mutable_license_id()->CopyFrom(policy_engine_->license_id());
LicenseIdentification license_id = policy_engine_->license_id();
current_license->mutable_license_id()->CopyFrom(license_id);
if (!is_renewal) {
if (license_id.has_provider_session_token()) {
std::string usage_report;
if (!session_->GenerateUsageReport(license_id.provider_session_token(),
&usage_report)) {
return false;
}
current_license->set_session_usage_table_entry(usage_report);
}
}
// Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response.
@@ -488,7 +502,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
break;
case SignedMessage::SERVICE_CERTIFICATE:
return CdmLicense::HandleServiceCertificateResponse(signed_response);
case SignedMessage::ERROR:
case SignedMessage::ERROR_RESPONSE:
return HandleKeyErrorResponse(signed_response);
default:
LOGE("CdmLicense::HandleKeyResponse: unrecognized signed message type: %d"
@@ -546,6 +560,10 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return KEY_ERROR;
}
std::string provider_session_token;
if (license.id().has_provider_session_token())
provider_session_token_ = license.id().provider_session_token();
if (license.policy().has_renewal_server_url()) {
server_url_ = license.policy().renewal_server_url();
}
@@ -559,8 +577,8 @@ CdmResponseType CdmLicense::HandleKeyResponse(
signed_response.signature(),
mac_key_iv,
mac_key,
key_array.size(),
&key_array[0]);
key_array,
provider_session_token);
if (KEY_ADDED == resp) {
loaded_keys_.clear();
@@ -590,7 +608,7 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
return KEY_ERROR;
}
if (signed_response.type() == SignedMessage::ERROR) {
if (signed_response.type() == SignedMessage::ERROR_RESPONSE) {
return HandleKeyErrorResponse(signed_response);
}
@@ -618,6 +636,14 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
server_url_ = license.policy().renewal_server_url();
}
}
else {
if (license.id().has_provider_session_token()) {
provider_session_token_ = license.id().provider_session_token();
session_->ReleaseUsageInformation(signed_response.msg(),
signed_response.signature(),
provider_session_token_);
}
}
// TODO(kqyang, jfore, gmorgan): change UpdateLicense function signature to
// be able to return true/false to accept/reject the license. (Pending code
@@ -637,8 +663,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
}
bool CdmLicense::RestoreOfflineLicense(
CdmKeyMessage& license_request, CdmKeyResponse& license_response,
CdmKeyResponse& license_renewal_response) {
const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response) {
if (license_request.empty() || license_response.empty()) {
LOGE(
@@ -681,6 +708,77 @@ bool CdmLicense::RestoreOfflineLicense(
return true;
}
bool CdmLicense::RestoreUsageLicense(const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response) {
if (license_request.empty() || license_response.empty()) {
LOGE(
"CdmLicense::RestoreUsageLicense: key_request or response empty: %u %u",
license_request.size(), license_response.size());
return false;
}
SignedMessage signed_request;
if (!signed_request.ParseFromString(license_request)) {
LOGE("CdmLicense::RestoreUsageLicense: license_request parse failed");
return false;
}
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
LOGE(
"CdmLicense::RestoreUsageLicense: license request type: expected = %d,"
" actual = %d",
SignedMessage::LICENSE_REQUEST, signed_request.type());
return false;
}
key_request_ = signed_request.msg();
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
LOGE("CdmLicense::RestoreUsageLicense: unable to parse signed license"
" response");
return false;
}
if (SignedMessage::LICENSE != signed_response.type()) {
LOGE("CdmLicense::RestoreUsageLicense: unrecognized signed message type: %d"
, signed_response.type());
return false;
}
if (Properties::use_certificates_as_identification()) {
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::RestoreUsageLicense: no session keys present");
return false;
}
if (!session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return false;
} else {
if (!session_->GenerateDerivedKeys(key_request_)) return false;
}
if (!signed_response.has_signature()) {
LOGE("CdmLicense::RestoreUsageLicense: license response is not signed");
return false;
}
License license;
if (!license.ParseFromString(signed_response.msg())) {
LOGE("CdmLicense::RestoreUsageLicense: unable to parse license response");
return false;
}
policy_engine_->SetLicense(license);
return true;
}
bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end();
}
bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
std::string* server_url) {
if (!initialized_) {
@@ -804,8 +902,4 @@ bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
return true;
}
bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end();
}
} // namespace wvcdm

View File

@@ -27,6 +27,7 @@ message LicenseIdentification {
optional bytes purchase_id = 3;
optional LicenseType type = 4;
optional int32 version = 5;
optional bytes provider_session_token = 6;
}
message License {
@@ -126,6 +127,8 @@ message License {
HDCP_NONE = 0;
HDCP_V1 = 1;
HDCP_V2 = 2;
HDCP_V2_1 = 3;
HDCP_V2_2 = 4;
}
optional HDCP hdcp = 1 [default = HDCP_NONE];
@@ -139,6 +142,15 @@ message License {
optional CGMS cgms_flags = 2 [default = CGMS_NONE];
}
message VideoResolutionConstraint {
// Minimum and maximum video resolutions in the range (height x width).
optional uint32 min_resolution_pixels = 1;
optional uint32 max_resolution_pixels = 2;
// Optional output protection requirements for this range. If not
// specified, the OutputProtection in the KeyContainer applies.
optional OutputProtection required_protection = 3;
}
message OperatorSessionKeyPermissions {
// Permissions/key usage flags for operator service keys
// (type = OPERATOR_SESSION).
@@ -157,12 +169,20 @@ message License {
optional OutputProtection requested_protection = 7;
optional KeyControl key_control = 8;
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
// Optional video resolution constraints. If the video resolution of the
// content being decrypted/decoded falls within one of the specified ranges,
// the optional required_protections may be applied. Otherwise an error will
// be reported.
repeated VideoResolutionConstraint video_resolution_constraints = 10;
}
optional LicenseIdentification id = 1;
optional Policy policy = 2;
repeated KeyContainer key = 3;
optional int64 license_start_time = 4;
optional bool remote_attestation_verified = 5 [default = false];
// Client token generated by the content provider. Optional.
optional bytes provider_client_token = 6;
}
enum ProtocolVersion {
@@ -187,6 +207,8 @@ message LicenseRequest {
message ExistingLicense {
optional LicenseIdentification license_id = 1;
optional int64 seconds_since_started = 2;
optional int64 seconds_since_last_played = 3;
optional bytes session_usage_table_entry = 4;
}
// Exactly one of these must be present.
@@ -233,11 +255,22 @@ message LicenseError {
optional Error error_code = 1;
}
message RemoteAttestation {
// Encrypted ClientIdentification message containing the device remote
// attestation certificate. Required.
optional EncryptedClientIdentification certificate = 1;
// Bytes of salt which were added to the remote attestation challenge prior to
// signing it. Required.
optional bytes salt = 2;
// Signed remote attestation challenge + salt. Required.
optional bytes signature = 3;
}
message SignedMessage {
enum MessageType {
LICENSE_REQUEST = 1;
LICENSE = 2;
ERROR = 3;
ERROR_RESPONSE = 3;
SERVICE_CERTIFICATE_REQUEST = 4;
SERVICE_CERTIFICATE = 5;
}
@@ -246,10 +279,20 @@ message SignedMessage {
optional bytes msg = 2;
optional bytes signature = 3;
optional bytes session_key = 4;
// Remote attestation data which will be present in the initial license
// request for ChromeOS client devices operating in verified mode. Remote
// attestation challenge data is |msg| field above. Optional.
optional RemoteAttestation remote_attestation = 5;
}
// This message is used to pass optional data on initial license issuance.
message SessionInit {
enum ReplayControl {
NO_SESSION_USAGE = 0;
NONCE_REQUIRED_AND_NEW_SESSION_USAGE = 1;
NONCE_REQUIRED_OR_EXISTING_SESSION_USAGE = 2;
}
optional bytes session_id = 1;
optional bytes purchase_id = 2;
// master_signing_key should be 128 bits in length.
@@ -258,6 +301,19 @@ message SessionInit {
// (server || client) HMAC-SHA256 keys.
optional bytes signing_key = 4;
optional int64 license_start_time = 5;
// Client token for the session. This session is for use by the license
// provider, and is akin to a client cookie. It will be copied to
// License::provider_client_token, and sent back by the client in
// ClientIdentification::provider_client_token in all license requests
// thereafter.
optional bytes provider_client_token = 6;
// Session token for the session. This token is for use by the license
// provider, and is akin to a session cookie. It will be copied to
// LicenseIdentfication::provider_session_token, and sent back in all
// license renewal and release requests for the session thereafter.
optional bytes provider_session_token = 7;
// Replay control indicator which will be encoded into V9+ KeyControl blocks.
optional ReplayControl replay_control = 8 [default = NO_SESSION_USAGE];
}
// This message is used by the server to preserve and restore session state.
@@ -280,7 +336,7 @@ message SessionState {
// in the case of X509 certificates, the certificate authority to use.
message ProvisioningOptions {
enum CertificateType {
RSA_WIDEVINE = 0; // Default. The original certificate type.
WIDEVINE_DRM = 0; // Default. The original certificate type.
X509 = 1; // X.509 certificate.
}
@@ -336,6 +392,7 @@ message ClientIdentification {
enum TokenType {
KEYBOX = 0;
DEVICE_CERTIFICATE = 1;
REMOTE_ATTESTATION_CERTIFICATE = 2;
}
message NameValue {
@@ -343,12 +400,36 @@ message ClientIdentification {
optional string value = 2;
}
// Capabilities which not all clients may support. Used for the license
// exchange protocol only.
message ClientCapabilities {
enum HdcpVersion {
HDCP_NONE = 0;
HDCP_V1 = 1;
HDCP_V2 = 2;
HDCP_V2_1 = 3;
HDCP_V2_2 = 4;
}
optional bool client_token = 1 [default = false];
optional bool session_token = 2 [default = false];
optional bool video_resolution_constraints = 3 [default = false];
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
}
// Type of factory-provisioned device root of trust. Optional.
optional TokenType type = 1 [default = KEYBOX];
// Factory-provisioned device root of trust. Required.
optional bytes token = 2;
// Optional client information name/value pairs.
repeated NameValue client_info = 3;
// Client token generated by the content provider. Optional.
optional bytes provider_client_token = 4;
// Number of licenses received by the client to which the token above belongs.
// Only present if client_token is specified.
optional uint32 license_counter = 5;
// List of non-baseline client capabilities.
optional ClientCapabilities client_capabilities = 6;
}
// EncryptedClientIdentification message used to hold ClientIdentification
@@ -359,16 +440,16 @@ message EncryptedClientIdentification {
optional string service_id = 1;
// Serial number for the service certificate for which ClientIdentification is
// encrypted.
optional string service_certificate_serial_number = 2;
// Serialized ClientIdentification message, encrypted with the privacy key
// using AES-128-CBC with PKCS#5 padding.
optional bytes service_certificate_serial_number = 2;
// Serialized ClientIdentification message, encrypted with the privacy key using
// AES-128-CBC with PKCS#5 padding.
optional bytes encrypted_client_id = 3;
// Initialization vector needed to decrypt encrypted_client_id.
optional bytes encrypted_client_id_iv = 4;
// AES-128 privacy key, encrytped with the service public public key using
// RSA-OAEP.
optional bytes encrypted_privacy_key = 5;
};
}
// ----------------------------------------------------------------------------
// device_certificate.proto
@@ -400,9 +481,10 @@ message DeviceCertificate {
// Widevine system ID for the device. Required for intermediate and
// user device certificates.
optional uint32 system_id = 5;
// True if the certificate corresponds to a test (non production) device or
// service. Optional.
optional bool test_device = 6 [default = false];
// Deprecated field, which used to indicate whether the device was a test
// (non-production) device. The test_device field in ProvisionedDeviceInfo
// below should be observed instead.
optional bool test_device_deprecated = 6 [deprecated = true];
// Service identifier (web origin) for the service which owns the certificate.
// Required for service certificates.
optional string service_id = 7;

View File

@@ -26,7 +26,10 @@ using ::testing::StrEq;
namespace {
const uint32_t kCertificateLen = 700;
const uint32_t kWrappedKeyLen = 500;
const uint32_t kProtobufEstimatedLen = 75;
const uint32_t kProtobufEstimatedOverhead = 75;
const uint32_t kLicenseRequestLen = 300;
const uint32_t kLicenseLen = 500;
const uint32_t kProviderSessionTokenLen = 128;
const std::string kTestCertificate =
"124B035F3D256A656F0E505A085E7A6C482B61035E0C4A540F7803137F4C3B45206B7F33"
"347F4D7A005E56400F0955011F4E07072D0D46781817460974326A516E3944385760280E"
@@ -881,8 +884,7 @@ LicenseInfo license_update_test_data[] = {
"D30B08B2C4673551293A2E68747470733A2F2F746573742E676F6F676C65"
"2E636F6D2F6C6963656E73652F47657443656E634C6963656E736512200A"
"1C78D0E574D0827C3AE78A05EEC90BAC31D10686EC19EB0599F75B2D1AB4"
"C5"
)},
"C5")},
// license being released. all fields are identical except for license
// state and hashed file data
{"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "",
@@ -981,6 +983,287 @@ LicenseInfo license_update_test_data[] = {
"7186A244EF561E3B07DC459BC681A0798B180667EA448327F6BBBD30212A"
"49")}};
struct UsageInfo {
std::string provider_session_token;
std::string license_request;
std::string license;
std::string file_data;
};
UsageInfo kUsageInfoTestData[] = {
{"", "", "", // 0 usage info records
wvcdm::a2bs_hex(
"0A06080210012A00122095053501C5FA405B7EF01DA94685C6B20CB36493"
"A9CF1653B720E2BEA3B77929")},
{// 1 usage info record
wvcdm::a2bs_hex(
"924B035FBDA56AE5EF0ED05A08DE7AECC8ABE1835E0C4A548F7803937F4C3B4520EB7"
"F3334FFCDFA00DE56408F09D5019FCE87072D0DC6789817468974B2EA51EE3944B8D7"
"E0A88E4F16EBB80F03BD845231A01E6146841CBAEF0134DCD9300DB2D92732992C0F2"
"310D8E386FB31C67B9477010DEF9D99C4272589572A26A17E"),
wvcdm::a2bs_hex(
"1E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315DB32B6D3FDD1A8E8A09"
"4174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E154BC1548FC40EC70927"
"75531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6BE37645E6800F053B1D"
"A9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D344A419C0F0034A1B5"
"F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410F218E52A853AD214FD"
"05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3BCBAE42DF9EA65E42E"
"151827086EADE71C138B972CC3992CF9ADA944C063816352ED8658D3FA07BE0F32239"
"E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E510445294F44E511BD9B1A"
"F19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E"),
wvcdm::a2bs_hex(
"40FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C029F"
"D2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B037"
"72BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA991C8"
"BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C592436C8"
"B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2EFC6"
"780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A660"
"2B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F4C3"
"5FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24B06"
"53A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6374"
"D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6FB1A"
"C91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB8904A5"
"999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267ACA2"
"E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82989"
"C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A771"
"F561B165987E552824B0C914E708E425C3"),
wvcdm::a2bs_hex(
"0AB307080210012AAC070AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE"
"1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D"
"0DC6789817468974B2EA51EE3944B8D7E0A88E4F16EBB80F03BD845231A01E6146841"
"CBAEF0134DCD9300DB2D92732992C0F2310D8E386FB31C67B9477010DEF9D99C42725"
"89572A26A17E12AC021E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315D"
"B32B6D3FDD1A8E8A094174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E15"
"4BC1548FC40EC7092775531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6B"
"E37645E6800F053B1DA9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D"
"344A419C0F0034A1B5F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410"
"F218E52A853AD214FD05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3"
"BCBAE42DF9EA65E42E151827086EADE71C138B972CC3992CF9ADA944C063816352ED8"
"658D3FA07BE0F32239E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E5104"
"45294F44E511BD9B1AF19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E1AF"
"40340FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C0"
"29FD2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B"
"03772BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA99"
"1C8BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C59243"
"6C8B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2E"
"FC6780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A"
"6602B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F"
"4C35FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24"
"B0653A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6"
"374D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6F"
"B1AC91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB890"
"4A5999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267A"
"CA2E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82"
"989C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A"
"771F561B165987E552824B0C914E708E425C3122051C8F84C5713500997DC5B325BAE"
"D208B224DFAEB2B034E58046A62F503FED6E")},
{// 2 usage info records
wvcdm::a2bs_hex(
"7290396E183156BDF830B7BF31BA762CB2675528C9004FD24A61DAFB587ABCF1D36F8"
"7795EE0B3DA0B425616A66C82349B2E3BB8841C1335536865F919ED2AE671487B608B"
"21A362D888E0AB4F7AB7175B82F108617C3503F175435788AECAF7FFBFE76995D93CD"
"79424A843A247A8D8A6054A5B5404C9C057AACAD91A203229"),
wvcdm::a2bs_hex(
"3478A2D76DEB90BE713B03A11037EA7C305D1AF65099E3F2B92C4D4443A8F481C1177"
"DEF0A3CB49BA5F1448A10AF1207AD2D361B4A1F961B4B1F215B76A9A5005B414EF45E"
"AFBCF2636ABFC01413B27DD11871103579F8C041A799E22888D9ADB798E92A5E29BC4"
"6DECBC90991C65FE151C49F18068C1B65D0E90A9ECDA9248B87C120D5FD8EC81D4D36"
"B529FB2DAD39E0D39578B13B158E2B07C752D86F1A9D8160C93930C1F4F9E1D0D8E2C"
"5AB308732EB27722A6BF8BE852624C2BE3E4FE85819B89BEBA6535FCFBE85FA63A57B"
"D0FBAF284C64FFD97A146B76B3F37B576FC091C03E2222FBD24C2211344B7E2417EFC"
"36C4A54DCCC460CF810E7EA8AC6386D6AB567C819FED88A22CE55EF9BBE62C2CBC7AE"
"EDE5E5A69FF3472418CE2F4514496C59D26E72F3BFE0131F"),
wvcdm::a2bs_hex(
"C45FDCB3296A0EBE24FF381E027E6E2EF1AC289C67D3B858330669A81E8131583D2F1"
"40FD64615BDED0ED8316ABFD9C7E887433E1CAA6EA8E0C4F87ADB2A7FC3CF6FF87A7F"
"02AFF03BF5DB640AD8DDB572C41532E673618DCD8C33EF2BFE4E25EE821DF7D742B09"
"90398543B16EFCDBB03C6327B79D3664CED442E894020F4410ECC178C92AAEDFE39DC"
"563AC226FE9E0EF22E1C896C4F2835CDFDCD50B6C4DBA2B27A3B65DE3963D0A5F6E44"
"2A3C32008AB9D1ACBE4F366990EB43F8EE213B71E98DA090282680ABDD649BECA8970"
"0764561379F1DD23490CE967632ECA349AF8E1CBFA1F3A4F39F453614C8FFB5A17975"
"6243CB1FDB515834229BC64917C47A2F2E1116FAAC13368015312C31FD41215106469"
"BEE77D0EF2FE10CF645B3E82902EAF53A676933D0EC433949C1833BE52E76602CC3E4"
"E784C002E20624BCE0F38F9CBC478439899DA7F15554D0ACADEC140C00C8FA8FC9886"
"2D9933938781B30CB9C76899B3A48DBF170DDA0A18ED37D77F048ABBC85CB19469638"
"C2A32AA3180CF3943BD6B8C5CB26F2EA70868F18B0707C882054141086997A1AE5B70"
"9D4D0AA2B358990F244BA76C8E40791D29A0C63C9EF620B97FDFFA9B671E5A65AFCC1"
"C94CAACE0443E9D91F14028935BEA3988831BEBBFD3EB7C3A5AC9605B3534712A0912"
"4345ACB09665E357E58946871BC140D365"),
wvcdm::a2bs_hex(
"0ADF0E080210012AD80E0AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE"
"1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D"
"0DC6789817468974B2EA51EE3944B8D7E0A88E4F16EBB80F03BD845231A01E6146841"
"CBAEF0134DCD9300DB2D92732992C0F2310D8E386FB31C67B9477010DEF9D99C42725"
"89572A26A17E12AC021E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315D"
"B32B6D3FDD1A8E8A094174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E15"
"4BC1548FC40EC7092775531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6B"
"E37645E6800F053B1DA9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D"
"344A419C0F0034A1B5F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410"
"F218E52A853AD214FD05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3"
"BCBAE42DF9EA65E42E151827086EADE71C138B972CC3992CF9ADA944C063816352ED8"
"658D3FA07BE0F32239E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E5104"
"45294F44E511BD9B1AF19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E1AF"
"40340FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C0"
"29FD2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B"
"03772BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA99"
"1C8BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C59243"
"6C8B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2E"
"FC6780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A"
"6602B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F"
"4C35FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24"
"B0653A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6"
"374D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6F"
"B1AC91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB890"
"4A5999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267A"
"CA2E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82"
"989C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A"
"771F561B165987E552824B0C914E708E425C30AA9070A80017290396E183156BDF830"
"B7BF31BA762CB2675528C9004FD24A61DAFB587ABCF1D36F87795EE0B3DA0B425616A"
"66C82349B2E3BB8841C1335536865F919ED2AE671487B608B21A362D888E0AB4F7AB7"
"175B82F108617C3503F175435788AECAF7FFBFE76995D93CD79424A843A247A8D8A60"
"54A5B5404C9C057AACAD91A20322912AC023478A2D76DEB90BE713B03A11037EA7C30"
"5D1AF65099E3F2B92C4D4443A8F481C1177DEF0A3CB49BA5F1448A10AF1207AD2D361"
"B4A1F961B4B1F215B76A9A5005B414EF45EAFBCF2636ABFC01413B27DD11871103579"
"F8C041A799E22888D9ADB798E92A5E29BC46DECBC90991C65FE151C49F18068C1B65D"
"0E90A9ECDA9248B87C120D5FD8EC81D4D36B529FB2DAD39E0D39578B13B158E2B07C7"
"52D86F1A9D8160C93930C1F4F9E1D0D8E2C5AB308732EB27722A6BF8BE852624C2BE3"
"E4FE85819B89BEBA6535FCFBE85FA63A57BD0FBAF284C64FFD97A146B76B3F37B576F"
"C091C03E2222FBD24C2211344B7E2417EFC36C4A54DCCC460CF810E7EA8AC6386D6AB"
"567C819FED88A22CE55EF9BBE62C2CBC7AEEDE5E5A69FF3472418CE2F4514496C59D2"
"6E72F3BFE0131F1AF403C45FDCB3296A0EBE24FF381E027E6E2EF1AC289C67D3B8583"
"30669A81E8131583D2F140FD64615BDED0ED8316ABFD9C7E887433E1CAA6EA8E0C4F8"
"7ADB2A7FC3CF6FF87A7F02AFF03BF5DB640AD8DDB572C41532E673618DCD8C33EF2BF"
"E4E25EE821DF7D742B0990398543B16EFCDBB03C6327B79D3664CED442E894020F441"
"0ECC178C92AAEDFE39DC563AC226FE9E0EF22E1C896C4F2835CDFDCD50B6C4DBA2B27"
"A3B65DE3963D0A5F6E442A3C32008AB9D1ACBE4F366990EB43F8EE213B71E98DA0902"
"82680ABDD649BECA89700764561379F1DD23490CE967632ECA349AF8E1CBFA1F3A4F3"
"9F453614C8FFB5A179756243CB1FDB515834229BC64917C47A2F2E1116FAAC1336801"
"5312C31FD41215106469BEE77D0EF2FE10CF645B3E82902EAF53A676933D0EC433949"
"C1833BE52E76602CC3E4E784C002E20624BCE0F38F9CBC478439899DA7F15554D0ACA"
"DEC140C00C8FA8FC98862D9933938781B30CB9C76899B3A48DBF170DDA0A18ED37D77"
"F048ABBC85CB19469638C2A32AA3180CF3943BD6B8C5CB26F2EA70868F18B0707C882"
"054141086997A1AE5B709D4D0AA2B358990F244BA76C8E40791D29A0C63C9EF620B97"
"FDFFA9B671E5A65AFCC1C94CAACE0443E9D91F14028935BEA3988831BEBBFD3EB7C3A"
"5AC9605B3534712A09124345ACB09665E357E58946871BC140D3651220464E4A1BB23"
"1A5B0287888B34CA0A8CF5396EB2B8313377DC5ED5C41A9B389A9")},
{// 3 usage info records
wvcdm::a2bs_hex(
"983358221FB8DBF892047F00AA661F217EEC4E7A1626E8F98E025509E4D65A685E7D9"
"B169B98B16934F6E43E0E0E854A3FA9EB8E9A9D08E9D9B3A6C766AA44F7C655879BA2"
"DF5F38732FB7EDCA66D8C13A855B15E32CC9389B7DD119BA1F2417825FF1F52970F8E"
"985D34DD353D2AC8B24267353E5B8406C098427C4559A90CC"),
wvcdm::a2bs_hex(
"483EAC68243092009D06FAB41DB594ACB22E068C9524810758ECFF8BAB7E1B1ACA988"
"C3987023F01EFEC11529C7326279742E805E755A08EBBD9AA322F305805BE1166AB45"
"CB156FB0A9E6734371F4028707EE01CF2FB08465707E7E5613DD90D74B0D02536E26C"
"F1261CDDA8713943F3620ECC54095C76F8CD3CE31948C3CC0C9EB5582A4D087A54B39"
"1B4CDCBC98E35830B5932F6CF8D16427EF115CFF0A99499513702DD54C758E53248BB"
"5D195F2A2DD1DB18F97562F1F9034E223CEDB1E09ED1B0FE26089C20ED43B5D87B51F"
"6FC6C9F86255FBF70DF233F2665D604355BF9740A3B755521102E0B485C5CCCA607A9"
"A1BEB757BEDEF12327C637D17D6401E3756719F99BBE69B9CE4C8E47C2AC771F35A8E"
"E3FC4D58B2B2269CF85728E4DA7231BC8F0FD7C50E2A1EE9"),
wvcdm::a2bs_hex(
"5826D3A95F78879292612BCE06D845D64285CD45A7EAA6C87A9DBC3290B0B6AC95315"
"809F8CC7938768F9BD342C62CD4CE055866394489D955247CB0535001D50EFF4FEDF0"
"9501C58569B1EB9AA2305A113A5F4D4524AD34148A2DC48D2F522937F44A57FC76F57"
"EB1D4819C438EA42C7F8974FC7D2FE61CAAB3E1F27172FE6B8675DF4CCF1329A6EFB3"
"1F686FB0DC0F8B552D78970708D50C82ADBE333B585F6DE5A0D01D106F8232EB9ED45"
"42A2DC5AA031CC44652E8A42EDCA5AB08B0B5CA61A922E69A119E556F6014642522EA"
"1550F6D6E63EB25ACC03A4DD3F22F4686ED525F994FABA87629AF5939C16BA68C0F09"
"3EFE033CD319180BF69FCB72AC5123EBCB9DCF1AF00F0A68E31FF5B18FA8CFF3DFBB7"
"DA45413799105D67FA78217710D2F6C33394DD4088100013295FF43CF0598E6FE5C05"
"F03417CCD031F01CF63BECD444C750DF198345F155AB2B2AB94394A3C0C0AE05E386D"
"E6CC565AE82398BD0E377D6ABE103B9D5E84582C3772584B759891FC4B121A113370E"
"2DF5372DD81FB6358C64B0F6EB8F26193CA119E4D9D3D38036FA450EE2047CB2CE265"
"0FF37DF85BE23D58C17379FEC08DC0648236A107AE66178EEBF78F05F3B898424FA02"
"668B51F838AFA90D367B5CB425372D8CC3790BEA8AFB8795251FA09340D85A7F0B003"
"134C838F08BB1054D18404C3F69130700E"),
wvcdm::a2bs_hex(
"0A8B16080210012A84160AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE"
"1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D"
"0DC6789817468974B2EA51EE3944B8D7E0A88E4F16EBB80F03BD845231A01E6146841"
"CBAEF0134DCD9300DB2D92732992C0F2310D8E386FB31C67B9477010DEF9D99C42725"
"89572A26A17E12AC021E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315D"
"B32B6D3FDD1A8E8A094174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E15"
"4BC1548FC40EC7092775531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6B"
"E37645E6800F053B1DA9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D"
"344A419C0F0034A1B5F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410"
"F218E52A853AD214FD05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3"
"BCBAE42DF9EA65E42E151827086EADE71C138B972CC3992CF9ADA944C063816352ED8"
"658D3FA07BE0F32239E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E5104"
"45294F44E511BD9B1AF19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E1AF"
"40340FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C0"
"29FD2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B"
"03772BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA99"
"1C8BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C59243"
"6C8B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2E"
"FC6780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A"
"6602B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F"
"4C35FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24"
"B0653A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6"
"374D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6F"
"B1AC91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB890"
"4A5999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267A"
"CA2E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82"
"989C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A"
"771F561B165987E552824B0C914E708E425C30AA9070A80017290396E183156BDF830"
"B7BF31BA762CB2675528C9004FD24A61DAFB587ABCF1D36F87795EE0B3DA0B425616A"
"66C82349B2E3BB8841C1335536865F919ED2AE671487B608B21A362D888E0AB4F7AB7"
"175B82F108617C3503F175435788AECAF7FFBFE76995D93CD79424A843A247A8D8A60"
"54A5B5404C9C057AACAD91A20322912AC023478A2D76DEB90BE713B03A11037EA7C30"
"5D1AF65099E3F2B92C4D4443A8F481C1177DEF0A3CB49BA5F1448A10AF1207AD2D361"
"B4A1F961B4B1F215B76A9A5005B414EF45EAFBCF2636ABFC01413B27DD11871103579"
"F8C041A799E22888D9ADB798E92A5E29BC46DECBC90991C65FE151C49F18068C1B65D"
"0E90A9ECDA9248B87C120D5FD8EC81D4D36B529FB2DAD39E0D39578B13B158E2B07C7"
"52D86F1A9D8160C93930C1F4F9E1D0D8E2C5AB308732EB27722A6BF8BE852624C2BE3"
"E4FE85819B89BEBA6535FCFBE85FA63A57BD0FBAF284C64FFD97A146B76B3F37B576F"
"C091C03E2222FBD24C2211344B7E2417EFC36C4A54DCCC460CF810E7EA8AC6386D6AB"
"567C819FED88A22CE55EF9BBE62C2CBC7AEEDE5E5A69FF3472418CE2F4514496C59D2"
"6E72F3BFE0131F1AF403C45FDCB3296A0EBE24FF381E027E6E2EF1AC289C67D3B8583"
"30669A81E8131583D2F140FD64615BDED0ED8316ABFD9C7E887433E1CAA6EA8E0C4F8"
"7ADB2A7FC3CF6FF87A7F02AFF03BF5DB640AD8DDB572C41532E673618DCD8C33EF2BF"
"E4E25EE821DF7D742B0990398543B16EFCDBB03C6327B79D3664CED442E894020F441"
"0ECC178C92AAEDFE39DC563AC226FE9E0EF22E1C896C4F2835CDFDCD50B6C4DBA2B27"
"A3B65DE3963D0A5F6E442A3C32008AB9D1ACBE4F366990EB43F8EE213B71E98DA0902"
"82680ABDD649BECA89700764561379F1DD23490CE967632ECA349AF8E1CBFA1F3A4F3"
"9F453614C8FFB5A179756243CB1FDB515834229BC64917C47A2F2E1116FAAC1336801"
"5312C31FD41215106469BEE77D0EF2FE10CF645B3E82902EAF53A676933D0EC433949"
"C1833BE52E76602CC3E4E784C002E20624BCE0F38F9CBC478439899DA7F15554D0ACA"
"DEC140C00C8FA8FC98862D9933938781B30CB9C76899B3A48DBF170DDA0A18ED37D77"
"F048ABBC85CB19469638C2A32AA3180CF3943BD6B8C5CB26F2EA70868F18B0707C882"
"054141086997A1AE5B709D4D0AA2B358990F244BA76C8E40791D29A0C63C9EF620B97"
"FDFFA9B671E5A65AFCC1C94CAACE0443E9D91F14028935BEA3988831BEBBFD3EB7C3A"
"5AC9605B3534712A09124345ACB09665E357E58946871BC140D3650AA9070A8001983"
"358221FB8DBF892047F00AA661F217EEC4E7A1626E8F98E025509E4D65A685E7D9B16"
"9B98B16934F6E43E0E0E854A3FA9EB8E9A9D08E9D9B3A6C766AA44F7C655879BA2DF5"
"F38732FB7EDCA66D8C13A855B15E32CC9389B7DD119BA1F2417825FF1F52970F8E985"
"D34DD353D2AC8B24267353E5B8406C098427C4559A90CC12AC02483EAC68243092009"
"D06FAB41DB594ACB22E068C9524810758ECFF8BAB7E1B1ACA988C3987023F01EFEC11"
"529C7326279742E805E755A08EBBD9AA322F305805BE1166AB45CB156FB0A9E673437"
"1F4028707EE01CF2FB08465707E7E5613DD90D74B0D02536E26CF1261CDDA8713943F"
"3620ECC54095C76F8CD3CE31948C3CC0C9EB5582A4D087A54B391B4CDCBC98E35830B"
"5932F6CF8D16427EF115CFF0A99499513702DD54C758E53248BB5D195F2A2DD1DB18F"
"97562F1F9034E223CEDB1E09ED1B0FE26089C20ED43B5D87B51F6FC6C9F86255FBF70"
"DF233F2665D604355BF9740A3B755521102E0B485C5CCCA607A9A1BEB757BEDEF1232"
"7C637D17D6401E3756719F99BBE69B9CE4C8E47C2AC771F35A8EE3FC4D58B2B2269CF"
"85728E4DA7231BC8F0FD7C50E2A1EE91AF4035826D3A95F78879292612BCE06D845D6"
"4285CD45A7EAA6C87A9DBC3290B0B6AC95315809F8CC7938768F9BD342C62CD4CE055"
"866394489D955247CB0535001D50EFF4FEDF09501C58569B1EB9AA2305A113A5F4D45"
"24AD34148A2DC48D2F522937F44A57FC76F57EB1D4819C438EA42C7F8974FC7D2FE61"
"CAAB3E1F27172FE6B8675DF4CCF1329A6EFB31F686FB0DC0F8B552D78970708D50C82"
"ADBE333B585F6DE5A0D01D106F8232EB9ED4542A2DC5AA031CC44652E8A42EDCA5AB0"
"8B0B5CA61A922E69A119E556F6014642522EA1550F6D6E63EB25ACC03A4DD3F22F468"
"6ED525F994FABA87629AF5939C16BA68C0F093EFE033CD319180BF69FCB72AC5123EB"
"CB9DCF1AF00F0A68E31FF5B18FA8CFF3DFBB7DA45413799105D67FA78217710D2F6C3"
"3394DD4088100013295FF43CF0598E6FE5C05F03417CCD031F01CF63BECD444C750DF"
"198345F155AB2B2AB94394A3C0C0AE05E386DE6CC565AE82398BD0E377D6ABE103B9D"
"5E84582C3772584B759891FC4B121A113370E2DF5372DD81FB6358C64B0F6EB8F2619"
"3CA119E4D9D3D38036FA450EE2047CB2CE2650FF37DF85BE23D58C17379FEC08DC064"
"8236A107AE66178EEBF78F05F3B898424FA02668B51F838AFA90D367B5CB425372D8C"
"C3790BEA8AFB8795251FA09340D85A7F0B003134C838F08BB1054D18404C3F6913070"
"0E12202FF1FBA9926A24A1F79970EC427DDF87B4421488F7952499BC33CEB282D9E48"
"A")}};
} // namespace
class MockFile : public File {
@@ -1033,6 +1316,9 @@ class DeviceFilesSecurityLevelTest
: public DeviceFilesTest,
public ::testing::WithParamInterface<CdmSecurityLevel> {};
class DeviceFilesUsageInfoTest : public DeviceFilesTest,
public ::testing::WithParamInterface<int> {};
MATCHER(IsCreateFileFlagSet, "") { return File::kCreate & arg; }
MATCHER(IsBinaryFileFlagSet, "") { return File::kBinary & arg; }
MATCHER_P(IsStrEq, str, "") {
@@ -1040,18 +1326,29 @@ MATCHER_P(IsStrEq, str, "") {
// as well as pointer to data but that will introduce a dependency on tr1
return memcmp(arg, str.c_str(), str.size()) == 0;
}
MATCHER_P2(Contains, str1, str2, "") {
MATCHER_P3(Contains, str1, str2, size, "") {
// 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() + kProtobufEstimatedLen);
std::string data(
arg, size + str1.size() + str2.size() + kProtobufEstimatedOverhead);
return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos);
}
MATCHER_P4(Contains, str1, str2, str3, size, "") {
// 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, size + str1.size() + str2.size() + str3.size() +
kProtobufEstimatedOverhead);
return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos &&
data.find(str3) != std::string::npos);
}
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() + kProtobufEstimatedLen);
str5.size() + str6.size() +
kProtobufEstimatedOverhead);
return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos &&
data.find(str3) != std::string::npos &&
@@ -1080,14 +1377,15 @@ TEST_P(DeviceFilesStoreTest, StoreCertificate) {
EXPECT_CALL(file, Open(StrEq(device_certificate_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key),
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0),
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, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
}
@@ -1112,7 +1410,8 @@ TEST_F(DeviceFilesTest, ReadCertificate) {
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
std::string certificate, wrapped_private_key;
ASSERT_TRUE(
@@ -1141,14 +1440,15 @@ TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
EXPECT_CALL(file, Open(StrEq(device_certificate_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key),
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0),
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.Init(security_level));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
}
@@ -1188,7 +1488,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreLicense(
license_test_data[license_num].key_set_id,
license_test_data[license_num].license_state,
@@ -1206,7 +1507,8 @@ INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest,
TEST_F(DeviceFilesTest, StoreLicenses) {
MockFile file;
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.Times(kNumberOfLicenses).WillRepeatedly(Return(true));
.Times(kNumberOfLicenses)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
for (size_t i = 0; i < kNumberOfLicenses; ++i) {
@@ -1231,7 +1533,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
for (size_t i = 0; i < kNumberOfLicenses; i++) {
EXPECT_TRUE(device_files.StoreLicense(
license_test_data[i].key_set_id, license_test_data[i].license_state,
@@ -1266,7 +1569,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
DeviceFiles::LicenseState license_state;
CdmInitData pssh_data;
CdmKeyMessage key_request;
@@ -1318,12 +1622,11 @@ TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
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, 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();
new_path =
base_path + security_dirs[i] + DeviceFiles::GetCertificateFileName();
EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path)))
.WillOnce(Return(true));
}
@@ -1357,7 +1660,8 @@ TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
Properties::Init();
std::string certificate, wrapped_private_key;
@@ -1371,12 +1675,14 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
license_update_test_data[0].key_set_id +
DeviceFiles::GetLicenseFileNameExtension();
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))).Times(2)
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Open(StrEq(license_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.Times(2).WillRepeatedly(Return(true));
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, Write(IsStrEq(license_update_test_data[0].file_data),
Eq(license_update_test_data[0].file_data.size())))
.WillOnce(ReturnArg<1>());
@@ -1387,7 +1693,8 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreLicense(
license_update_test_data[0].key_set_id,
license_update_test_data[0].license_state,
@@ -1417,7 +1724,9 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
size_t size = license_test_data[0].file_data.size();
EXPECT_CALL(file, Exists(StrEq(license_path))).Times(2).WillOnce(Return(true))
EXPECT_CALL(file, Exists(StrEq(license_path)))
.Times(2)
.WillOnce(Return(true))
.WillOnce(Return(false));
EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size));
EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet()))
@@ -1431,7 +1740,8 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
DeviceFiles::LicenseState license_state;
CdmInitData pssh_data;
CdmKeyMessage key_request;
@@ -1455,4 +1765,154 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
EXPECT_FALSE(device_files.LicenseExists(license_test_data[0].key_set_id));
}
TEST_P(DeviceFilesUsageInfoTest, Read) {
MockFile file;
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
int index = GetParam();
std::string data;
if (index >= 0) {
data = kUsageInfoTestData[index].file_data;
}
if (index >= 0) {
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(true));
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.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);
} else {
EXPECT_CALL(file, Exists(StrEq(path))).Times(2).WillRepeatedly(
Return(false));
EXPECT_CALL(file, FileSize(_)).Times(0);
EXPECT_CALL(file, Open(_, _)).Times(0);
EXPECT_CALL(file, Close()).Times(0);
}
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
ASSERT_TRUE(device_files.RetrieveUsageInfo(&license_info));
if (index >= 0) {
EXPECT_EQ(index, license_info.size());
for (size_t i = 0; i < license_info.size(); ++i) {
bool found = false;
for (size_t j = 0; j <= static_cast<size_t>(index); ++j) {
if ((license_info[i].first.compare(
kUsageInfoTestData[j].license_request) == 0) &&
(license_info[i].second.compare(kUsageInfoTestData[j].license) ==
0)) {
found = true;
}
}
EXPECT_TRUE(found);
}
} else {
EXPECT_EQ(0, license_info.size());
}
}
TEST_P(DeviceFilesUsageInfoTest, Store) {
MockFile file;
std::string pst(GenerateRandomData(kProviderSessionTokenLen));
std::string license_request(GenerateRandomData(kLicenseRequestLen));
std::string license(GenerateRandomData(kLicenseLen));
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
int index = GetParam();
std::string data;
if (index >= 0) {
data = kUsageInfoTestData[index].file_data;
}
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(index >= 0));
if (index >= 0) {
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.Times(2)
.WillRepeatedly(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(2);
} else {
EXPECT_CALL(file, FileSize(_)).Times(0);
EXPECT_CALL(file, Open(_, _)).Times(1).WillOnce(Return(true));
EXPECT_CALL(file, Close()).Times(1);
}
EXPECT_CALL(file, Write(Contains(pst, license_request, license, data.size()),
Gt(pst.size() + license_request.size() +
license.size()))).WillOnce(ReturnArg<1>());
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
ASSERT_TRUE(device_files.StoreUsageInfo(pst, license_request, license));
}
TEST_P(DeviceFilesUsageInfoTest, Delete) {
MockFile file;
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
int index = GetParam();
if (index < 0) return;
std::string data, pst, prev_data, prev_pst, prev_license;
if (index >= 0) {
data = kUsageInfoTestData[index].file_data;
if (index >= 1) {
pst = kUsageInfoTestData[index].provider_session_token;
prev_data = kUsageInfoTestData[index - 1].file_data;
prev_pst = kUsageInfoTestData[index - 1].provider_session_token;
prev_license = kUsageInfoTestData[index - 1].license;
}
}
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(index >= 0));
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
if (index >= 1) {
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, Write(Contains(prev_pst, prev_license, prev_data.size()),
Gt(prev_pst.size() + prev_license.size())))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Close()).Times(2);
} else {
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.WillOnce(Return(true));
EXPECT_CALL(file, Write(_, _)).Times(0);
EXPECT_CALL(file, Close()).Times(1);
}
EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))).WillOnce(DoAll(
SetArrayArgument<0>(data.begin(), data.end()), Return(data.size())));
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
if (index >= 1) {
ASSERT_TRUE(device_files.DeleteUsageInfo(pst));
} else {
ASSERT_FALSE(device_files.DeleteUsageInfo(pst));
}
}
INSTANTIATE_TEST_CASE_P(UsageInfo, DeviceFilesUsageInfoTest,
::testing::Values(-1, 0, 1, 2, 3));
} // namespace wvcdm

View File

@@ -82,9 +82,9 @@ class WvContentDecryptionModule : public TimerHandler {
std::string* wrapped_key);
// Secure stop related methods
virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
virtual CdmResponseType ReleaseSecureStops(
const CdmSecureStopReleaseMessage& message);
virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info);
virtual CdmResponseType ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message);
// Accept encrypted buffer and decrypt data.
// Decryption parameters that need to be specified are

View File

@@ -149,14 +149,14 @@ CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse(
return cdm_engine_->HandleProvisioningResponse(response, cert, wrapped_key);
}
CdmResponseType WvContentDecryptionModule::GetSecureStops(
CdmSecureStops* secure_stops) {
return cdm_engine_->GetSecureStops(secure_stops);
CdmResponseType WvContentDecryptionModule::GetUsageInfo(
CdmUsageInfo* usage_info) {
return cdm_engine_->GetUsageInfo(usage_info);
}
CdmResponseType WvContentDecryptionModule::ReleaseSecureStops(
const CdmSecureStopReleaseMessage& message) {
return cdm_engine_->ReleaseSecureStops(message);
CdmResponseType WvContentDecryptionModule::ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message) {
return cdm_engine_->ReleaseUsageInfo(message);
}
CdmResponseType WvContentDecryptionModule::Decrypt(

View File

@@ -236,6 +236,114 @@ SubSampleInfo single_encrypted_sub_sample_icp = {
"58b938c2e3ca4c2ce48942da97f9e45797f2c074ac6004734e93784a48af6160"),
wvcdm::a2b_hex("4cca615fc013102892f91efee936639b"), 0};
SubSampleInfo usage_info_sub_samples_icp[] = {
{true, 1, true, true, false,
wvcdm::a2bs_hex("E82DDD1D07CBB31CDD31EBAAE0894609"),
wvcdm::a2b_hex(
"fe8670a01c86906c056b4bf85ad278464c4eb79c60de1da8480e66e78561350e"
"a25ae19a001f834c43aaeadf900b3c5a6745e885a4d1d1ae5bafac08dc1d60e5"
"f3465da303909ec4b09023490471f670b615d77db844192854fdab52b7806203"
"89b374594bbb6a2f2fcc31036d7cb8a3f80c0e27637b58a7650028fbf2470d68"
"1bbd77934af165d215ef325c74438c9d99a20fc628792db28c05ed5deff7d9d4"
"dba02ddb6cf11dc6e78cb5200940af9a2321c3a7c4c79be67b54a744dae1209c"
"fa02fc250ce18d30c7da9c3a4a6c9619bf8561a42ff1e55a7b14fa3c8de69196"
"c2b8e3ff672fc37003b479da5d567b7199917dbe5aa402890ebb066bce140b33"),
wvcdm::a2b_hex(
"d08733bd0ef671f467906b50ff8322091400f86fd6f016fea2b86e33923775b3"
"ebb4c8c6f3ba8b78dd200a74d3872a40264ab99e1d422e4f819abb7f249114aa"
"b334420b37c86ce81938615ab9d3a6b2de8db545cd88e35091031e73016fb386"
"1b754298329b52dbe483de3a532277815e659f3e05e89257333225b933d92e15"
"ef2deff287a192d2c8fc942a29a5f3a1d54440ac6385de7b34bb650b889e4ae9"
"58c957b5f5ff268f445c0a6b825fcad55290cb7b5c9814bc4c72984dcf4c8fd7"
"5f511c173b2e0a3163b18a1eac58539e5c188aeb0751b946ad4dcd08ea777a7f"
"37326df26fa509343faa98dff667629f557873f1284903202e451227ef465a62"),
wvcdm::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0},
{true, 1, true, true, false,
wvcdm::a2bs_hex("04B2B456405CAD7170BE848CA75AA2DF"),
wvcdm::a2b_hex(
"ccb68f97b03e7af1a9e208d91655ba645cc5a5d454f3cb7c3d621e98a7592d90"
"4ff7023555f0e99bcf3918948f4fca7a430faf17d7d67268d81d8096d7c48809"
"c14220e634680fbe0c760571dd86a1905835035f4a238c2d7f17bd1363b113c1"
"91782aebb77a1064142a68b59ecdcc6990ed4244082464d91dbfe09e08744b2f"
"d1e850a008acbbe129fbd050c8dc1b28cb8cc2c1e2d920ea458f74809297b513"
"85307b481cbb81d6759385ee782d6c0e101c20ca1937cfd0d6e024da1a0f718a"
"fb7c4ff3df1ca87e67602d28168233cc2448d44b79f405d4c6e67eb88d705050"
"2a806cb986423e3b0e7a97738e1d1d143b4f5f926a4e2f37c7fbe65f56d5b690"),
wvcdm::a2b_hex(
"fa35aa1f5e5d7b958880d5eed9cc1bb81d36ebd04c0250a8c752ea5f413bbdcf"
"3785790c8dba7a0b21c71346bb7f946a9b71c0d2fe87d2e2fab14e35ee8400e7"
"097a7d2d9a25b468e848e8dee2388f890967516c7dab96db4713c7855f717aed"
"2ae9c2895baaa636e4a610ab26b35d771d62397ba40d78694dab70dcbdfa91c3"
"6af79ad6b6ebb479b4a5fbc242a8574ebe6717f0813fbd6f726ce2af4d522e66"
"b36c940fce519c913db56a6372c3636b10c0149b4cd97e74c576765b533abdc2"
"729f1470dd7f9a60d3572dcc9839582a4606ee17eaced39797daef8f885d3f8f"
"e14877ae530451c4242bbc3934f85a5bb71b363351894f881896471cfeaf68b2"),
wvcdm::a2b_hex("4a59e3e5f3e4f7e2f494ad09c12a9e4c"), 0},
{true, 1, true, true, false,
wvcdm::a2bs_hex("3AE243D83B93B3311A1D777FF5FBE01A"),
wvcdm::a2b_hex(
"934997779aa1aeb45d6ba8845f13786575d0adf85a5e93674d9597f8d4286ed7"
"dcce02f306e502bbd9f1cadf502f354038ca921276d158d911bdf3171d335b18"
"0ae0f9abece16ff31ee263228354f724da2f3723b19caa38ea02bd6563b01208"
"fb5bf57854ac0fe38d5883197ef90324b2721ff20fdcf9a53819515e6daa096e"
"70f6f5c1d29a4a13dafd127e2e1f761ea0e28fd451607552ecbaef5da3c780bc"
"aaf2667b4cc4f858f01d480cac9e32c3fbb5705e5d2adcceebefc2535c117208"
"e65f604799fc3d7223e16908550f287a4bea687008cb0064cf14d3aeedb8c705"
"09ebc5c2b8b5315f43c04d78d2f55f4b32c7d33e157114362106395cc0bb6d93"),
wvcdm::a2b_hex(
"2dd54eee1307753508e1f250d637044d6e8f5abf057dab73e9e95f83910e4efc"
"191c9bac63950f13fd51833dd94a4d03f2b64fb5c721970c418fe53fa6f74ad5"
"a6e16477a35c7aa6e28909b069cd25770ef80da20918fc30fe95fd5c87fd3522"
"1649de17ca2c7b3dc31f936f0cbdf97c7b1c15de3a86b279dc4b4de64943914a"
"99734556c4b7a1a0b022c1933cb0786068fc18d49fed2f2b49f3ac6d01c32d07"
"92175ce2844eaf9064e6a3fcffade038d690cbed81659351163a22432f0d0545"
"037e1c805d8e92a1272b4196ad0ce22f26bb80063137a8e454d3b97e2414283d"
"ed0716cd8bceb80cf59166a217006bd147c51b04dfb183088ce3f51e9b9f759e"),
wvcdm::a2b_hex("b358ab21ac90455bbf60490daad457e3"), 0},
{true, 1, true, true, false,
wvcdm::a2bs_hex("5292104C011418973C31235953FC8205"),
wvcdm::a2b_hex(
"d433adfd3d892d5c3e7d54ab0218ee0712400a920d4b71d553912451169f9b79"
"3f103260cf04c34f6a5944bb96da79946a62bdbcd804ca28b17656338edfa700"
"5c090f2750663a026fd15a0b0e448adbbfd53f613ea3993d9fd504421b575f28"
"12020bb8cca0ce333eabee0403df9f410c6473d7673d6991caab6ea2ece8f743"
"5a3ca049fa00c96c9b7c47e3073d25d08f23b47ffc509c48a81a2f98c9ec8a1d"
"e41764c14a5010df8b4692e8612a45bf0645601d4910119e6268ca4f6d8016a8"
"3d933d53f44243674b522bae43043c068c8cae43f0ac224198de71315b3a6f82"
"c1b523bbdcdb3e9f162c308684dd17e364b448ed0e90b0e496b8cf633a982708"),
wvcdm::a2b_hex(
"5efb5e5b913785e9935e67e763b8ff29a6687ac6c18d5a7e16951beb704f9c95"
"f081ca28f54c3e237fb5a7b0444e9a3e17da91e5cf2c0a8f009a873fb079c339"
"81b0ebc565b2c56d983ee33686fa5057c9891e246b67bb6950400acb06d5ae50"
"0e61a7e9289ea67ec2e88e8d0cc3c494fd996e93270e9b264a21818987e969c5"
"1e2955c5a53202e5aec1e2c906e1c006325112eb5c33ee37d0c07ea97d80c17f"
"d56e0efcf40c8c98981a86c18a159f05d851891236c124641d4584c49ccd7478"
"4f328a9cacae0f945238d98741b2969fe258903e85f963daba7168f05c18b09f"
"660dae18de41b1c49769cd38e24b135c37a65b69533f5c7d085898faedfbed5d"),
wvcdm::a2b_hex("cef7e8aaa6ec1154cb68a988f7c9e803"), 0},
{true, 1, true, true, false,
wvcdm::a2bs_hex("D7C01C2F868AE314BCB893E4E9C6AC75"),
wvcdm::a2b_hex(
"fa5d28677721de488ffc209321529728c77bc338accd45ccc98ab2063fc8c373"
"48c7698534175d72bf185690d19474d08c4fd4ed4eb46d858633f05337d70e92"
"03f7ee6bec0f7003bdf6fa665ba172855a51a82da406348ba651a2f62888c30a"
"7b4e1355bb94a9ff5c458f397c9a09e5d7785b286ef83142ddad324cc74e1929"
"60ad1c34c425cdefbedcb62ca9b21ac4f3df7f5922e263cb7798de54b622ab3f"
"64a0dd6ee1e40be6ecc857e657994ecac02ccfafc9036f382d7dbdf35c903356"
"40b7c9db088143060b24f24b21c4a7c2faeb3d308e57c5a75955fd704cfe4dee"
"71a4a7d823102b90eddded795ca6eb36282d777db8cfd783e50e5c2a816ee9ed"),
wvcdm::a2b_hex(
"d5db2f50c0f5a39414ddfa5129c2c641836a8c6312b26a210c996988e0c768d5"
"9a3adff117293b52b0653c0d6e22589edda804fb8caa7442362fe4caf9053b6a"
"2a34896399259a188f0c805de54b091a7eabff098b28d54584c01dd83301e4ca"
"a01b226c4541af1592d4440e103eb55bbd08c471efb0856ec9ced43211fc3325"
"3d402dff0d15f40833dd71259a8d40d527659ef3e5f9fd0826c9471dddb17e1e"
"fab916abc957fb07d7eac4a368ac92a8fb16d995613af47303034ee57b59b1d7"
"101aa031f5586b2f6b4c74372c4d7306db02509b5924d52c46a270f427743a85"
"614f080d83f3b15cbc6600ddda43adff5d2941da13ebe49d80fd0cea5025412b"),
wvcdm::a2b_hex("964c2dfda920357c668308d52d33c652"), 0}
};
// License duration + fudge factor
const uint32_t kSingleEncryptedSubSampleIcpLicenseDurationExpiration = 5 + 2;
@@ -253,6 +361,17 @@ SessionSharingSubSampleInfo session_sharing_sub_samples[] = {
{ &single_encrypted_sub_sample, true }
};
struct UsageInfoSubSampleInfo {
SubSampleInfo* sub_sample;
uint32_t usage_info;
};
UsageInfoSubSampleInfo usage_info_sub_sample_info[] = {
{ &usage_info_sub_samples_icp[0], 1 },
{ &usage_info_sub_samples_icp[0], 3 },
{ &usage_info_sub_samples_icp[0], 5 }
};
} // namespace
namespace wvcdm {
@@ -408,6 +527,33 @@ class WvCdmRequestLicenseTest : public testing::Test {
return message;
}
// Post a request and extract the signed provisioning message from
// the HTTP response.
std::string GetUsageInfoResponse(const std::string& server_url,
const std::string& client_auth,
const std::string& usage_info_request) {
// Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth, g_port,
g_use_secure_transfer, g_use_chunked_transfer);
if (!url_request.is_connected()) {
return "";
}
url_request.PostRequest(usage_info_request);
std::string message;
int resp_bytes = url_request.GetResponse(&message);
int status_code = url_request.GetStatusCode(message);
EXPECT_EQ(kHttpOk, status_code);
std::string usage_info;
if (kHttpOk == status_code) {
LicenseRequest license;
license.GetDrmMessage(message, usage_info);
LOGV("HTTP response body: (%u bytes)", usage_info.size());
}
return usage_info;
}
void VerifyKeyRequestResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data, bool is_renewal) {
@@ -586,7 +732,8 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
File file;
DeviceFiles handle;
EXPECT_TRUE(handle.Init(&file, kSecurityLevelL3));
EXPECT_TRUE(handle.Init(kSecurityLevelL3));
handle.SetTestFile(&file);
EXPECT_TRUE(handle.DeleteAllFiles());
EXPECT_EQ(NEED_PROVISIONING,
@@ -766,6 +913,80 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) {
decryptor_.CloseSession(session_id_);
}
class WvCdmUsageInfoTest
: public WvCdmRequestLicenseTest,
public ::testing::WithParamInterface<UsageInfoSubSampleInfo*> {};
TEST_P(WvCdmUsageInfoTest, DISABLED_UsageInfo) {
File file;
DeviceFiles handle;
std::string level = GetSecurityLevel(NULL).c_str();
CdmSecurityLevel security_level = kSecurityLevelUninitialized;
if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0) {
security_level = kSecurityLevelL1;
} else if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0) {
security_level = kSecurityLevelL3;
} else {
EXPECT_TRUE(false);
}
EXPECT_TRUE(handle.Init(security_level));
handle.SetTestFile(&file);
EXPECT_TRUE(handle.DeleteUsageInfo());
UsageInfoSubSampleInfo* usage_info_data = GetParam();
for (size_t i = 0; i < usage_info_data->usage_info; ++i) {
SubSampleInfo* data = usage_info_data->sub_sample + i;
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
std::string key_id = a2bs_hex(
"000000427073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
"08011a0d7769646576696e655f74657374220f73" // pssh data
"747265616d696e675f636c6970");
char ch = 0x33 + i;
key_id.append(1, ch);
GenerateKeyRequest(g_key_system, key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, data->key_id,
false);
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
&data->iv, data->block_offset, &decrypt_buffer[0]);
decryption_parameters.is_encrypted = data->is_encrypted;
decryption_parameters.is_secure = data->is_secure;
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data->validate_key_id,
decryption_parameters));
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
decrypt_buffer.begin()));
decryptor_.CloseSession(session_id_);
}
uint32_t num_usage_info = 0;
CdmUsageInfo usage_info;
CdmUsageInfoReleaseMessage release_msg;
CdmResponseType status = decryptor_.GetUsageInfo(&usage_info);
EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status);
while (usage_info.size() > 0) {
for (size_t i = 0; i < usage_info.size(); ++i) {
release_msg = GetUsageInfoResponse(g_license_server, g_client_auth,
usage_info[i]);
EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg));
}
status = decryptor_.GetUsageInfo(&usage_info);
EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status);
}
}
INSTANTIATE_TEST_CASE_P(
Cdm, WvCdmUsageInfoTest,
::testing::Values(&usage_info_sub_sample_info[0],
&usage_info_sub_sample_info[1],
&usage_info_sub_sample_info[2]));
TEST_F(WvCdmRequestLicenseTest, QueryUnmodifiedSessionStatus) {
// Test that the global value is returned when no properties are modifying it.
CdmQueryMap system_query_info;

View File

@@ -376,12 +376,12 @@ status_t WVDrmPlugin::provideProvisionResponse(
}
status_t WVDrmPlugin::getSecureStops(List<Vector<uint8_t> >& secureStops) {
CdmSecureStops cdmSecureStops;
CdmResponseType res = mCDM->GetSecureStops(&cdmSecureStops);
CdmUsageInfo cdmUsageInfo;
CdmResponseType res = mCDM->GetUsageInfo(&cdmUsageInfo);
if (isCdmResponseTypeSuccess(res)) {
secureStops.clear();
for (CdmSecureStops::const_iterator iter = cdmSecureStops.begin();
iter != cdmSecureStops.end();
for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin();
iter != cdmUsageInfo.end();
++iter) {
const string& cdmStop = *iter;
@@ -396,10 +396,8 @@ status_t WVDrmPlugin::getSecureStops(List<Vector<uint8_t> >& secureStops) {
}
status_t WVDrmPlugin::releaseSecureStops(const Vector<uint8_t>& ssRelease) {
CdmSecureStopReleaseMessage cdmMessage(ssRelease.begin(), ssRelease.end());
CdmResponseType res = mCDM->ReleaseSecureStops(cdmMessage);
CdmUsageInfoReleaseMessage cdmMessage(ssRelease.begin(), ssRelease.end());
CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage);
return mapCdmResponseType(res);
}

View File

@@ -64,10 +64,10 @@ class MockCDM : public WvContentDecryptionModule {
MOCK_METHOD3(HandleProvisioningResponse,
CdmResponseType(CdmProvisioningResponse&, std::string*, std::string*));
MOCK_METHOD1(GetSecureStops, CdmResponseType(CdmSecureStops*));
MOCK_METHOD1(GetUsageInfo, CdmResponseType(CdmUsageInfo*));
MOCK_METHOD1(ReleaseSecureStops,
CdmResponseType(const CdmSecureStopReleaseMessage&));
MOCK_METHOD1(ReleaseUsageInfo,
CdmResponseType(const CdmUsageInfoReleaseMessage&));
MOCK_METHOD2(AttachEventListener, bool(const CdmSessionId&,
WvCdmEventListener*));
@@ -568,12 +568,12 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) {
}
fclose(fp);
CdmSecureStops cdmStops;
CdmUsageInfo cdmStops;
for (uint32_t i = 0; i < kStopCount; ++i) {
cdmStops.push_back(string(stopsRaw[i], stopsRaw[i] + kStopSize));
}
EXPECT_CALL(cdm, GetSecureStops(_))
EXPECT_CALL(cdm, GetUsageInfo(_))
.WillOnce(DoAll(SetArgPointee<0>(cdmStops),
Return(wvcdm::NO_ERROR)));
@@ -610,8 +610,8 @@ TEST_F(WVDrmPluginTest, ReleasesSecureStops) {
Vector<uint8_t> message;
message.appendArray(messageRaw, kMessageSize);
EXPECT_CALL(cdm, ReleaseSecureStops(ElementsAreArray(messageRaw,
kMessageSize)))
EXPECT_CALL(cdm, ReleaseUsageInfo(ElementsAreArray(messageRaw,
kMessageSize)))
.Times(1);
status_t res = plugin.releaseSecureStops(message);