diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 969d015a..f0b3205d 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -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); }; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index baaf2e04..b10592fc 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -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_; diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 725b18b5..ecd5020c 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -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& 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: diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index a438ebf7..23aec697 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -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 >* 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); }; diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index 79c8e1a4..677c73e5 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -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 loaded_keys_; + std::string provider_session_token_; // Used for certificate based licensing CdmKeyMessage key_request_; diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 48a0f937..ae5e6cb0 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -23,8 +23,8 @@ typedef uint32_t CryptoSessionId; typedef std::string CryptoKeyId; typedef std::map CdmAppParameterMap; typedef std::map CdmQueryMap; -typedef std::vector CdmSecureStops; -typedef std::vector CdmSecureStopReleaseMessage; +typedef std::vector CdmUsageInfo; +typedef std::string CdmUsageInfoReleaseMessage; typedef std::string CdmProvisioningRequest; typedef std::string CdmProvisioningResponse; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index cc54f868..e357ea92 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -6,6 +6,8 @@ #include #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 > 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(); } } diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 501766a4..566cb672 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -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 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 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 diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index 1b31d242..7d933ca6 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -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; } diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 82f01b9c..27dba904 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -155,7 +155,7 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) { return false; } - device_id->assign(reinterpret_cast(&id[0]), id_length); + device_id->assign(reinterpret_cast(&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& 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 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 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(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(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( + const_cast(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( + const_cast(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(message.data()); + const uint8_t* sig = reinterpret_cast(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"); diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index f79cc5e5..4a016ded 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -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(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(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 >* 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(data.size())) { - LOGW("DeviceFiles::StoreFile: write failed: %d %d", data.size(), bytes); + if (bytes != static_cast(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(data->size())) { + if (bytes != static_cast(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 diff --git a/libwvdrmengine/cdm/core/src/device_files.proto b/libwvdrmengine/cdm/core/src/device_files.proto index d561c831..7d8583e6 100644 --- a/libwvdrmengine/cdm/core/src/device_files.proto +++ b/libwvdrmengine/cdm/core/src/device_files.proto @@ -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 { diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 5909f2a2..fc36b2a4 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -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 diff --git a/libwvdrmengine/cdm/core/src/license_protocol.proto b/libwvdrmengine/cdm/core/src/license_protocol.proto index b6a9cd2c..6dec3bfd 100644 --- a/libwvdrmengine/cdm/core/src/license_protocol.proto +++ b/libwvdrmengine/cdm/core/src/license_protocol.proto @@ -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; diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index e64e3668..78ea5050 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -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 {}; +class DeviceFilesUsageInfoTest : public DeviceFilesTest, + public ::testing::WithParamInterface {}; + 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 > 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(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 diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index fc776bda..a1cd68ff 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -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 diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index ba4e4081..a8347253 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -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( diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 91e7687e..3c3867db 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -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 {}; + +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 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; diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index a6e295b6..ce230934 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -376,12 +376,12 @@ status_t WVDrmPlugin::provideProvisionResponse( } status_t WVDrmPlugin::getSecureStops(List >& 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 >& secureStops) { } status_t WVDrmPlugin::releaseSecureStops(const Vector& 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); } diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index 1f1391d6..08bfe47a 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -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 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);