From b039f31b27be07614a26d3951abe8d12ada9a043 Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Wed, 12 Oct 2022 18:10:13 -0700 Subject: [PATCH] Core CDM: Removed secure stop support. [ Merge of http://go/wvgerrit/158721 ] This CL removes support for secure stop / usage info sessions from the CDM engine and CDM session. APIs for related to secure stop operations will return NOT_IMPLEMENTED_ERROR. New secure stop licenses will be rejected by the CDM when added. Bug: 242289743 Test: run_x86_64_tests request_license_test Change-Id: I30cd47e580d63014e001c903382a28238746f6d4 --- libwvdrmengine/cdm/core/include/cdm_engine.h | 43 +- libwvdrmengine/cdm/core/include/cdm_session.h | 34 +- libwvdrmengine/cdm/core/src/cdm_engine.cpp | 496 ++---------------- libwvdrmengine/cdm/core/src/cdm_session.cpp | 217 ++------ libwvdrmengine/cdm/core/src/license.cpp | 4 +- .../cdm/core/test/cdm_session_unittest.cpp | 5 +- 6 files changed, 139 insertions(+), 660 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 2bc0a7a8..bd355d68 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -127,10 +127,10 @@ class CdmEngine { // stored offline data associated with the session. virtual CdmResponseType RemoveKeys(const CdmSessionId& session_id); - // This method removes all offline data associated with the session, such as - // offline keys and usage info. It should be used with care, as it deletes the - // info immediately and without using a release message, so the server is not - // able to receive usage info or track releases for offline licenses. + // This method removes all offline data associated with the session. It + // should be used with care, as it deletes the info immediately and + // without using a release message, so the server is not able to + // receive usage info or track releases for offline licenses. virtual CdmResponseType RemoveLicense(const CdmSessionId& session_id); // Construct valid renewal request for the current session keys. @@ -235,38 +235,32 @@ class CdmEngine { virtual CdmResponseType RemoveOfflineLicense(const std::string& key_set_id, CdmSecurityLevel security_level); - // Usage related methods for streaming licenses - // Retrieve a random usage info from the list of all usage infos for this app - // id. If |error_detail| is not null, an additional error code may be provided - // in the event of an error. + // Usage related methods for streaming licenses with persistent usage + // information (deprecated). virtual CdmResponseType GetUsageInfo(const std::string& app_id, int* error_detail, CdmUsageInfo* usage_info); - // Retrieve usage info whose PST is specified by |ssid| - // If |error_detail| is not null, an additional error code may be provided - // in the event of an error. + // Retrieve usage info whose PST is specified by |ssid| (deprecated). virtual CdmResponseType GetUsageInfo(const std::string& app_id, const CdmSecureStopId& ssid, int* error_detail, CdmUsageInfo* usage_info); - // Retrieve usage info for a given security level and whose - // PST is specified by |ssid|. - // If |error_detail| is not null, an additional error code may be provided - // in the event of an error. + // Retrieve usage info for a given security level and whose PST is + // specified by |ssid| (deprecated). virtual CdmResponseType GetUsageInfo(const std::string& app_id, const CdmSecureStopId& ssid, RequestedSecurityLevel security_level, int* error_detail, CdmUsageInfo* usage_info); - // Remove all usage records for the current origin. + // Remove all usage records for the current origin (deprecated). virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id, CdmSecurityLevel security_level); // Remove all usage records for the current origin. Span all - // security levels. + // security levels (deprecated). virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id); virtual CdmResponseType RemoveUsageInfo( @@ -406,9 +400,6 @@ class CdmEngine { CdmSessionId* session_id); bool ValidateKeySystem(const CdmKeySystem& key_system); - CdmResponseType GetUsageInfo(const std::string& app_id, - RequestedSecurityLevel requested_security_level, - int* error_detail, CdmUsageInfo* usage_info); void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); @@ -443,13 +434,11 @@ class CdmEngine { std::string spoid_; uint32_t user_id_; - // Usage related variables - // Used to isolate a single active usage information license. Loading, - // creating or releasing a different usage licenses through the engine - // API will release the handle to previously active secure stop license. - std::unique_ptr usage_session_; - std::unique_ptr usage_property_set_; - int64_t last_usage_information_update_time_; + // Tracks the time of the last update to usage info across all + // actively managed CDM sessions with loaded usage entries. Used + // to periodically update a usage entry associated with an active + // license. + int64_t last_usage_info_update_time_ = 0; // Protect release_key_sets_ from non-thread-safe operations. std::mutex release_key_sets_lock_; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index c98c8030..d551dbd2 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -75,12 +75,6 @@ class CdmSession { virtual CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id, CdmLicenseType license_type, int* error_detail); - // Restores an usage session from the provided |usage_data|. - // The |error_detail| will be filled with an internal error code. The - // |error_detail| may be a CdmResponseType or other error code type. It is - // only suitable for additional logging or debugging. - virtual CdmResponseType RestoreUsageSession( - const DeviceFiles::CdmUsageData& usage_data, int* error_detail); virtual const CdmSessionId& session_id() { return session_id_; } virtual const CdmKeySetId& key_set_id() { return key_set_id_; } @@ -150,7 +144,7 @@ class CdmSession { virtual bool is_initial_usage_update() { return is_initial_usage_update_; } virtual bool is_usage_update_needed() { return is_usage_update_needed_; } - virtual void reset_usage_flags() { + virtual void ResetUsageFlags() { is_initial_usage_update_ = false; is_usage_update_needed_ = false; } @@ -159,12 +153,19 @@ class CdmSession { virtual bool is_offline() { return is_offline_; } virtual bool is_temporary() { return is_temporary_; } virtual bool license_received() { return license_received_; } - virtual bool has_provider_session_token() { - return (license_parser_ && - license_parser_->provider_session_token().size() > 0); + + virtual bool HasUsageEntry() { + // The PST is only set if a usage entry has been loaded. + return provider_session_token_.size() > 0; + } + virtual const std::string& provider_session_token() { + return provider_session_token_; + } + void SetProviderSessionTokenForTest(const std::string& pst) { + provider_session_token_ = pst; } - virtual bool supports_usage_info() const { + virtual bool SupportsUsageEntries() const { return usage_table_header_ != nullptr; } @@ -243,8 +244,6 @@ class CdmSession { bool StoreLicense(CdmOfflineLicenseState state, int* error_detail); - bool UpdateUsageInfo(); - CdmResponseType GenerateKeyRequestInternal( const InitializationData& init_data, CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request); @@ -305,14 +304,15 @@ class CdmSession { bool last_decrypt_failed_ = false; // Usage related flags and data - bool is_initial_usage_update_; - bool is_usage_update_needed_; + bool is_initial_usage_update_ = true; + bool is_usage_update_needed_ = false; // Only assign |usage_table_header_| if capable of supporting usage // information. UsageTableHeader* usage_table_header_ = nullptr; - uint32_t usage_entry_number_; + uint32_t usage_entry_number_ = 0; CdmUsageEntry usage_entry_; - std::string usage_provider_session_token_; + // This PST should only be set if the session has a usage entry. + std::string provider_session_token_; // information useful for offline and usage scenarios CdmKeyMessage key_request_; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 726763c8..a5a8c451 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -31,8 +31,8 @@ namespace wvcdm { namespace { -const uint64_t kReleaseSessionTimeToLive = 60; // seconds -const uint32_t kUpdateUsageInformationPeriod = 60; // seconds +constexpr uint64_t kReleaseSessionTimeToLive = 60; // seconds +constexpr uint32_t kUpdateUsageInfoPeriod = 60; // seconds } // namespace class UsagePropertySet : public CdmClientPropertySet { @@ -67,16 +67,12 @@ CdmEngine::CdmEngine(wvutil::FileSystem* file_system, : metrics_(metrics), cert_provisioning_(), file_system_(file_system), - spoid_(EMPTY_SPOID), - usage_session_(), - usage_property_set_(), - last_usage_information_update_time_(0) { + spoid_(EMPTY_SPOID) { assert(file_system); Properties::Init(); } CdmEngine::~CdmEngine() { - usage_session_.reset(); std::unique_lock lock(session_map_lock_); session_map_.Terminate(); } @@ -415,8 +411,7 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id, } if (key_set_id != nullptr) { - if ((session->is_offline() || session->has_provider_session_token()) && - !license_type_release) { + if ((session->is_offline()) && !license_type_release) { *key_set_id = session->key_set_id(); LOGI("key_set_id = %s", IdPtrToString(key_set_id)); } else { @@ -1284,50 +1279,21 @@ CdmResponseType CdmEngine::ListStoredLicenses( CdmResponseType CdmEngine::ListUsageIds( const std::string& app_id, CdmSecurityLevel security_level, - std::vector* ksids, - std::vector* provider_session_tokens) { - if (!ksids && !provider_session_tokens) { - LOGE("Outputs |ksids| and |provider_session_tokens| are null"); - return INVALID_PARAMETERS_ENG_23; - } - if (security_level == kSecurityLevelL1 && OkpIsInFallbackMode()) { - LOGD("OKP fallback to L3"); - security_level = kSecurityLevelL3; - } - DeviceFiles handle(file_system_); - if (!handle.Init(security_level)) { - LOGE("Unable to initialize device files"); - return LIST_USAGE_ERROR_1; - } - if (!handle.ListUsageIds(app_id, ksids, provider_session_tokens)) { - LOGE("Failed: app_id = %s, security_level = %s", IdToString(app_id), - CdmSecurityLevelToString(security_level)); - return LIST_USAGE_ERROR_2; - } - return NO_ERROR; + std::vector* /* ksids */, + std::vector* /* provider_session_tokens */) { + LOGI("app_id = %s, security_level = %s", IdToString(app_id), + CdmSecurityLevelToString(security_level)); + LOGW("API not supported"); + return NOT_IMPLEMENTED_ERROR; } CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id, CdmSecurityLevel security_level, const std::string& key_set_id) { - LOGI("app_id = %s, key_set_id = %s", IdToString(app_id), - IdToString(key_set_id)); - if (security_level == kSecurityLevelL1 && OkpIsInFallbackMode()) { - LOGD("OKP fallback to L3"); - security_level = kSecurityLevelL3; - } - DeviceFiles handle(file_system_); - if (!handle.Init(security_level)) { - LOGE("Unable to initialize device files"); - return DELETE_USAGE_ERROR_1; - } - std::string provider_session_token; - if (!handle.GetProviderSessionToken(app_id, key_set_id, - &provider_session_token)) { - LOGE("GetProviderSessionToken failed"); - return DELETE_USAGE_ERROR_2; - } - return RemoveUsageInfo(app_id, provider_session_token); + LOGI("app_id = %s, security_level = %s, key_set_id = %s", IdToString(app_id), + CdmSecurityLevelToString(security_level), IdToString(key_set_id)); + LOGW("API not supported"); + return NOT_IMPLEMENTED_ERROR; } CdmResponseType CdmEngine::GetOfflineLicenseState( @@ -1397,7 +1363,7 @@ CdmResponseType CdmEngine::RemoveOfflineLicense( } } else if (sts == LICENSE_USAGE_ENTRY_MISSING) { // It is possible that the CDM is tracking a key set ID, but has - // removed the usage information associated with it. In this case, + // removed the usage entry associated with it. In this case, // it will no longer be possible to load the license for release; // and the file should simply be deleted. LOGW("License usage entry is missing, deleting license file"); @@ -1415,415 +1381,66 @@ CdmResponseType CdmEngine::RemoveOfflineLicense( CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, const CdmSecureStopId& ssid, - int* error_detail, - CdmUsageInfo* usage_info) { - // Try to find usage info at the default security level. If the - // security level is unprovisioned or we are unable to find it, - // try L3. - CdmResponseType status = - GetUsageInfo(app_id, ssid, kLevelDefault, error_detail, usage_info); - switch (status) { - case NEED_PROVISIONING: - case GET_USAGE_INFO_ERROR_1: - case GET_USAGE_INFO_ERROR_2: - case USAGE_INFO_NOT_FOUND: - status = GetUsageInfo(app_id, ssid, kLevel3, error_detail, usage_info); - return status; - default: - return status; - } + int* /* error_detail */, + CdmUsageInfo* /* usage_info */) { + LOGI("app_id = %s, ssid = %s", IdToString(app_id), IdToString(ssid)); + LOGE("API not supported"); + return NOT_IMPLEMENTED_ERROR; } CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, const CdmSecureStopId& ssid, RequestedSecurityLevel security_level, - int* error_detail, - CdmUsageInfo* usage_info) { - LOGI("app_id = %s, ssid = %s", IdToString(app_id), IdToString(ssid)); - if (!usage_property_set_) { - usage_property_set_.reset(new UsagePropertySet()); - } - if (usage_info == nullptr) { - LOGE("Output |usage_info| is null"); - return PARAMETER_NULL; - } - usage_property_set_->set_security_level(security_level); - usage_property_set_->set_app_id(app_id); - usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession())); - CdmResponseType status = usage_session_->Init(usage_property_set_.get()); - if (NO_ERROR != status) { - LOGE("Session init error: status = %d", static_cast(status)); - return status; - } - DeviceFiles handle(file_system_); - if (!handle.Init(usage_session_->GetSecurityLevel())) { - LOGE("Device file init error"); - return GET_USAGE_INFO_ERROR_1; - } - - DeviceFiles::CdmUsageData usage_data; - if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), ssid, - &usage_data)) { - usage_property_set_->set_security_level(kLevel3); - usage_property_set_->set_app_id(app_id); - usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession())); - status = usage_session_->Init(usage_property_set_.get()); - if (NO_ERROR != status) { - LOGE("Session init error: status = %d", static_cast(status)); - return status; - } - if (!handle.Reset(usage_session_->GetSecurityLevel())) { - LOGE("Device file init error"); - return GET_USAGE_INFO_ERROR_2; - } - if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), - ssid, &usage_data)) { - // No entry found for that ssid. - return USAGE_INFO_NOT_FOUND; - } - } - - status = usage_session_->RestoreUsageSession(usage_data, error_detail); - - if (KEY_ADDED != status) { - LOGE("RestoreUsageSession failed: status = %d", static_cast(status)); - usage_info->clear(); - return status; - } - - CdmKeyRequest request; - status = usage_session_->GenerateReleaseRequest(&request); - - usage_info->clear(); - usage_info->push_back(request.message); - - if (KEY_MESSAGE != status) { - LOGE("GenerateReleaseRequest failed: status = %d", - static_cast(status)); - usage_info->clear(); - return status; - } - - return KEY_MESSAGE; + int* /* error_detail */, + CdmUsageInfo* /* usage_info */) { + LOGI("app_id = %s, ssid = %s, security_level = %s", IdToString(app_id), + IdToString(ssid), RequestedSecurityLevelToString(security_level)); + LOGE("API not supported"); + return NOT_IMPLEMENTED_ERROR; } CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, - int* error_detail, - CdmUsageInfo* usage_info) { + int* /* error_detail */, + CdmUsageInfo* /* usage_info */) { LOGI("app_id = %s", IdToString(app_id)); - if (usage_info == nullptr) { - LOGE("Output |usage_info| is null"); - return PARAMETER_NULL; - } - // Return a random usage report from a random security level - RequestedSecurityLevel security_level = - wvutil::CdmRandom::RandomBool() ? kLevelDefault : kLevel3; - CdmResponseType status = UNKNOWN_ERROR; - do { - status = GetUsageInfo(app_id, security_level, error_detail, usage_info); - if (KEY_MESSAGE == status && !usage_info->empty()) { - return status; - } - } while (KEY_CANCELED == status); - - security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3; - do { - status = GetUsageInfo(app_id, security_level, error_detail, usage_info); - if (NEED_PROVISIONING == status) - return NO_ERROR; // Valid scenario that one of the security - // levels has not been provisioned - } while (KEY_CANCELED == status); - return status; -} - -CdmResponseType CdmEngine::GetUsageInfo( - const std::string& app_id, RequestedSecurityLevel requested_security_level, - int* error_detail, CdmUsageInfo* usage_info) { - LOGI("app_id = %s, security_level = %s", IdToString(app_id), - RequestedSecurityLevelToString(requested_security_level)); - if (usage_info == nullptr) { - LOGE("Output |usage_info| is null"); - return PARAMETER_NULL; - } - if (requested_security_level == kLevelDefault && OkpIsInFallbackMode()) { - LOGD("OKP fallback to L3"); - requested_security_level = kLevel3; - } - if (!usage_property_set_) { - usage_property_set_.reset(new UsagePropertySet()); - } - usage_property_set_->set_security_level(requested_security_level); - usage_property_set_->set_app_id(app_id); - - usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession())); - - CdmResponseType status = usage_session_->Init(usage_property_set_.get()); - if (NO_ERROR != status) { - LOGE("Session init error"); - return status; - } - - DeviceFiles handle(file_system_); - if (!handle.Init(usage_session_->GetSecurityLevel())) { - LOGE("Unable to initialize device files"); - return GET_USAGE_INFO_ERROR_3; - } - - std::vector usage_data; - if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), - &usage_data)) { - LOGE("Unable to read usage information"); - return GET_USAGE_INFO_ERROR_4; - } - - if (usage_data.empty()) { - usage_info->clear(); - return NO_ERROR; - } - - const size_t index = wvutil::CdmRandom::RandomInRange(usage_data.size() - 1); - status = usage_session_->RestoreUsageSession(usage_data[index], error_detail); - if (KEY_ADDED != status) { - // TODO(b/141704872): Make multiple attempts. - LOGE("RestoreUsageSession failed: index = %zu, status = %d", index, - static_cast(status)); - usage_info->clear(); - return status; - } - - CdmKeyRequest request; - status = usage_session_->GenerateReleaseRequest(&request); - - usage_info->clear(); - usage_info->push_back(request.message); - - switch (status) { - case KEY_MESSAGE: - break; - case KEY_CANCELED: // usage information not present in - usage_session_->DeleteLicenseFile(); // OEMCrypto, delete and try again - usage_info->clear(); - break; - default: - LOGE("GenerateReleaseRequest failed: status = %d", - static_cast(status)); - usage_info->clear(); - break; - } - return status; + LOGE("API not supported"); + return NOT_IMPLEMENTED_ERROR; } CdmResponseType CdmEngine::RemoveAllUsageInfo( const std::string& app_id, CdmSecurityLevel cdm_security_level) { LOGI("app_id = %s, security_level = %s", IdToString(app_id), CdmSecurityLevelToString(cdm_security_level)); - if (!usage_property_set_) { - usage_property_set_.reset(new UsagePropertySet()); - } - usage_property_set_->set_app_id(app_id); - - CdmResponseType status = NO_ERROR; - DeviceFiles handle(file_system_); - if (handle.Init(cdm_security_level)) { - const RequestedSecurityLevel security_level = - cdm_security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault; - usage_property_set_->set_security_level(security_level); - usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession())); - usage_session_->Init(usage_property_set_.get()); - - if (usage_session_->supports_usage_info()) { - std::vector usage_data; - // Retrieve all usage information but delete only one before - // refetching. This is because deleting the usage entry - // might cause other entries to be shifted and information updated. - do { - if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), - &usage_data)) { - LOGW("Failed to retrieve usage info"); - break; - } - - if (usage_data.empty()) break; - - CdmResponseType res = - usage_session_->DeleteUsageEntry(usage_data[0].usage_entry_number); - - if (res != NO_ERROR) { - LOGW("Failed to delete usage entry: status = %d", - static_cast(res)); - break; - } - - if (!handle.DeleteUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), - usage_data[0].provider_session_token)) { - LOGW("Failed to delete usage info"); - break; - } - } while (!usage_data.empty()); - - std::vector provider_session_tokens; - if (!handle.DeleteAllUsageInfoForApp( - DeviceFiles::GetUsageInfoFileName(app_id), - &provider_session_tokens)) { - status = REMOVE_ALL_USAGE_INFO_ERROR_5; - } - } - } - usage_session_.reset(); - return status; + LOGE("API not supported"); + return NOT_IMPLEMENTED_ERROR; } CdmResponseType CdmEngine::RemoveAllUsageInfo(const std::string& app_id) { LOGI("app_id = %s", IdToString(app_id)); - const CdmResponseType status_l1 = - RemoveAllUsageInfo(app_id, kSecurityLevelL1); - const CdmResponseType status_l3 = - RemoveAllUsageInfo(app_id, kSecurityLevelL3); - // Prioritizing L1 status. - if (status_l1 != NO_ERROR) { - return status_l1; - } - return status_l3; + LOGE("API not supported"); + return NOT_IMPLEMENTED_ERROR; } CdmResponseType CdmEngine::RemoveUsageInfo( const std::string& app_id, const CdmSecureStopId& provider_session_token) { LOGI("app_id = %s, pst = %s", IdToString(app_id), IdToString(provider_session_token)); - if (!usage_property_set_) { - usage_property_set_.reset(new UsagePropertySet()); - } - usage_property_set_->set_app_id(app_id); - - CdmResponseType status = NO_ERROR; - for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) { - DeviceFiles handle(file_system_); - if (handle.Init(static_cast(j))) { - RequestedSecurityLevel security_level = - static_cast(j) == kSecurityLevelL3 ? kLevel3 - : kLevelDefault; - usage_property_set_->set_security_level(security_level); - usage_session_.reset( - new CdmSession(file_system_, metrics_->AddSession())); - usage_session_->Init(usage_property_set_.get()); - - CdmKeyMessage license_request; - CdmKeyResponse license_response; - CdmUsageEntry usage_entry; - uint32_t usage_entry_number; - std::string drm_certificate; - CryptoWrappedKey wrapped_private_key; - - if (!handle.RetrieveUsageInfo( - DeviceFiles::GetUsageInfoFileName(app_id), provider_session_token, - &license_request, &license_response, &usage_entry, - &usage_entry_number, &drm_certificate, &wrapped_private_key)) { - // Try other security level - continue; - } - - if (usage_session_->supports_usage_info()) { - status = usage_session_->DeleteUsageEntry(usage_entry_number); - if (!handle.DeleteUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), - provider_session_token)) { - status = REMOVE_USAGE_INFO_ERROR_1; - } - usage_session_.reset(); - return status; - } - } else { - LOGE("Failed to initialize L%d device files", j); - status = REMOVE_USAGE_INFO_ERROR_2; - } - } - usage_session_.reset(); - return REMOVE_USAGE_INFO_ERROR_3; + LOGE("API not supported"); + return NOT_IMPLEMENTED_ERROR; } CdmResponseType CdmEngine::ReleaseUsageInfo( const CdmUsageInfoReleaseMessage& message) { LOGI("message_size = %zu", message.size()); - if (!usage_session_) { - LOGE("Usage session not initialized"); - return RELEASE_USAGE_INFO_ERROR; - } - const CdmResponseType status = usage_session_->ReleaseKey(message); - usage_session_.reset(); - if (NO_ERROR != status) { - LOGE("ReleaseKey failed: status = %d", status); - } - return status; + LOGE("API not supported"); + return NOT_IMPLEMENTED_ERROR; } -CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id, - CdmKeyMessage* release_message) { +CdmResponseType CdmEngine::LoadUsageSession( + const CdmKeySetId& key_set_id, CdmKeyMessage* /* release_message */) { LOGI("key_set_id = %s", IdToString(key_set_id)); - // This method is currently only used by the CE CDM, in which all session IDs - // are key set IDs. - assert(Properties::AlwaysUseKeySetIds()); - if (key_set_id.empty()) { - LOGE("Invalid key set ID"); - return EMPTY_KEYSET_ID_ENG_5; - } - if (release_message == nullptr) { - LOGE("Output |release_message| is null"); - return PARAMETER_NULL; - } - - std::shared_ptr session; - if (!session_map_.FindSession(key_set_id, &session)) { - LOGE("Session not found: key_set_id = %s", IdToString(key_set_id)); - return SESSION_NOT_FOUND_11; - } - - DeviceFiles handle(file_system_); - if (!handle.Init(session->GetSecurityLevel())) { - LOGE("Unable to initialize device files"); - return LOAD_USAGE_INFO_FILE_ERROR; - } - - std::string app_id; - session->GetApplicationId(&app_id); - - DeviceFiles::CdmUsageData usage_data; - if (!handle.RetrieveUsageInfoByKeySetId( - DeviceFiles::GetUsageInfoFileName(app_id), key_set_id, - &(usage_data.provider_session_token), &(usage_data.license_request), - &(usage_data.license), &(usage_data.usage_entry), - &(usage_data.usage_entry_number), &(usage_data.drm_certificate), - &(usage_data.wrapped_private_key))) { - LOGE("Unable to find usage information"); - return LOAD_USAGE_INFO_MISSING; - } - - int error_detail = NO_ERROR; - usage_data.key_set_id = key_set_id; - CdmResponseType status = - session->RestoreUsageSession(usage_data, &error_detail); - session->GetMetrics()->cdm_session_restore_usage_session_.Increment( - status, error_detail); - if (KEY_ADDED != status) { - LOGE("Restore failed: key_set_id = %s, status = %d", IdToString(key_set_id), - static_cast(status)); - return status; - } - - CdmKeyRequest request; - status = session->GenerateReleaseRequest(&request); - *release_message = std::move(request.message); - switch (status) { - case KEY_MESSAGE: - break; - case KEY_CANCELED: - // usage information not present in OEMCrypto, delete and try again - session->DeleteLicenseFile(); - break; - default: - LOGE("GenerateReleaseRequest failed: status = %d", - static_cast(status)); - break; - } - return status; + LOGE("API not supported"); + return NOT_IMPLEMENTED_ERROR; } CdmResponseType CdmEngine::DecryptV16( @@ -2072,6 +1689,7 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id, bool CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width, uint32_t height) { + std::unique_lock lock(session_map_lock_); std::shared_ptr session; if (session_map_.FindSession(session_id, &session)) { session->NotifyResolution(width, height); @@ -2088,10 +1706,9 @@ void CdmEngine::OnTimerEvent() { wvutil::Clock clock; const uint64_t current_time = clock.GetCurrentTime(); bool usage_update_period_expired = false; - if (current_time - last_usage_information_update_time_ > - kUpdateUsageInformationPeriod) { + if (current_time - last_usage_info_update_time_ > kUpdateUsageInfoPeriod) { usage_update_period_expired = true; - last_usage_information_update_time_ = current_time; + last_usage_info_update_time_ = current_time; } bool is_initial_usage_update = false; @@ -2101,27 +1718,24 @@ void CdmEngine::OnTimerEvent() { CdmSessionList sessions; session_map_.GetSessionList(sessions); - while (!sessions.empty()) { - is_initial_usage_update = is_initial_usage_update || - sessions.front()->is_initial_usage_update(); + for (auto& session : sessions) { + is_initial_usage_update = + is_initial_usage_update || session->is_initial_usage_update(); is_usage_update_needed = - is_usage_update_needed || sessions.front()->is_usage_update_needed(); - - sessions.front()->OnTimerEvent(usage_update_period_expired); - sessions.pop_front(); + is_usage_update_needed || session->is_usage_update_needed(); + session->OnTimerEvent(usage_update_period_expired); } if (is_usage_update_needed && (usage_update_period_expired || is_initial_usage_update)) { // Session list may have changed. Rebuild. + sessions.clear(); session_map_.GetSessionList(sessions); - for (CdmSessionList::iterator iter = sessions.begin(); - iter != sessions.end(); ++iter) { - (*iter)->reset_usage_flags(); - if ((*iter)->supports_usage_info() && - (*iter)->has_provider_session_token()) { - (*iter)->UpdateUsageEntryInformation(); + for (auto& session : sessions) { + session->ResetUsageFlags(); + if (session->SupportsUsageEntries() && session->HasUsageEntry()) { + session->UpdateUsageEntryInformation(); } } } diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 87659180..a1bc5203 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -78,10 +78,6 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system, is_temporary_(false), security_level_(kSecurityLevelUninitialized), requested_security_level_(kLevelDefault), - is_initial_usage_update_(true), - is_usage_update_needed_(false), - usage_table_header_(nullptr), - usage_entry_number_(0), mock_license_parser_in_use_(false), mock_policy_engine_in_use_(false) { assert(metrics_); // metrics_ must not be null. @@ -91,7 +87,7 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system, } CdmSession::~CdmSession() { - if (has_provider_session_token() && supports_usage_info() && !is_release_) { + if (HasUsageEntry() && !is_release_) { UpdateUsageEntryInformation(); } @@ -269,9 +265,9 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id, return GET_RELEASED_LICENSE_ERROR; } - std::string provider_session_token; bool sign_fake_request = false; // TODO(b/169483174): remove this variable. - if (supports_usage_info()) { + if (SupportsUsageEntries()) { + std::string provider_session_token; if (!license_parser_->ExtractProviderSessionToken( key_response_, &provider_session_token)) { provider_session_token.clear(); @@ -288,6 +284,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id, IdToString(key_set_id)); return USAGE_ENTRY_ALREADY_LOADED; } + provider_session_token_ = std::move(provider_session_token); if (sts != NO_ERROR) { LOGE("Failed to load usage entry: status = %d", static_cast(sts)); return sts; @@ -296,6 +293,10 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id, } else { sign_fake_request = true; // TODO(b/169483174): remove this block. } + // Must be set before updating the usage entry. CdmLicense will attempt + // to update the usage info when restoring. + is_offline_ = true; + // TODO(b/169483174): remove this code in v17. For OEMCrypto v16, an offline // license would not work because the rental clock in OEMCrypto is only // started when the license request is signed. We will sign a fake license @@ -332,7 +333,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id, } } - if (!provider_session_token.empty() && supports_usage_info()) { + if (HasUsageEntry()) { CdmResponseType sts = usage_table_header_->UpdateEntry( usage_entry_number_, crypto_session_.get(), &usage_entry_); if (sts != NO_ERROR) { @@ -340,74 +341,16 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id, return sts; } if (!StoreLicense(license_data.state, error_detail)) { - LOGW("Unable to save updated usage info"); + LOGW("Unable to save updated license"); } } license_received_ = true; - is_offline_ = true; is_release_ = license_type == kLicenseTypeRelease; has_license_been_restored_ = true; return KEY_ADDED; } -CdmResponseType CdmSession::RestoreUsageSession( - const DeviceFiles::CdmUsageData& usage_data, int* error_detail) { - if (!initialized_) { - LOGE("CDM session not initialized"); - return NOT_INITIALIZED_ERROR; - } - if (!key_set_id_.empty()) { - file_handle_->UnreserveLicenseId(key_set_id_); - } - key_set_id_ = usage_data.key_set_id; - key_request_ = usage_data.license_request; - key_response_ = usage_data.license; - usage_entry_ = usage_data.usage_entry; - usage_entry_number_ = usage_data.usage_entry_number; - usage_provider_session_token_ = usage_data.provider_session_token; - - CdmResponseType status = LoadPrivateOrLegacyKey( - usage_data.drm_certificate, usage_data.wrapped_private_key); - if (status != NO_ERROR) return status; - - CdmResponseType sts = NO_ERROR; - if (supports_usage_info()) { - sts = usage_table_header_->LoadEntry(crypto_session_.get(), usage_entry_, - usage_entry_number_); - crypto_metrics_->usage_table_header_load_entry_.Increment(sts); - if (sts != NO_ERROR) { - LOGE("Failed to load usage entry: status = %d", static_cast(sts)); - return sts; - } - } - - sts = license_parser_->RestoreLicenseForRelease(usage_data.drm_certificate, - key_request_, key_response_); - - if (sts != NO_ERROR) { - SetErrorDetail(error_detail, sts); - return RELEASE_LICENSE_ERROR_2; - } - - if (supports_usage_info()) { - sts = usage_table_header_->UpdateEntry( - usage_entry_number_, crypto_session_.get(), &usage_entry_); - if (sts != NO_ERROR) { - LOGE("Failed to update usage entry: status = %d", static_cast(sts)); - return sts; - } - if (!UpdateUsageInfo()) { - LOGW("Unable to save updated usage info"); - } - } - - license_received_ = true; - is_offline_ = false; - is_release_ = true; - return KEY_ADDED; -} - // This is a thin wrapper that initiates the latency metric. CdmResponseType CdmSession::GenerateKeyRequest( const InitializationData& init_data, CdmLicenseType license_type, @@ -532,19 +475,21 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) { // to see if it has a provider session token. If so a new entry needs // to be created. CdmResponseType sts; - std::string provider_session_token; - if (supports_usage_info()) { + if (SupportsUsageEntries()) { + std::string provider_session_token; if (license_parser_->ExtractProviderSessionToken(key_response, &provider_session_token) && !provider_session_token.empty()) { - std::string app_id; - GetApplicationId(&app_id); + if (!is_offline_) { + LOGE("CDM does not support secure stop licenses"); + return ADD_KEY_ERROR; + } sts = usage_table_header_->AddEntry( - crypto_session_.get(), is_offline_, key_set_id_, - DeviceFiles::GetUsageInfoFileName(app_id), key_response, - &usage_entry_number_); + crypto_session_.get(), /* is_persistent */ true, key_set_id_, + /* usage_info_filename */ "", key_response, &usage_entry_number_); crypto_metrics_->usage_table_header_add_entry_.Increment(sts); if (sts != NO_ERROR) return sts; + provider_session_token_ = std::move(provider_session_token); } } sts = @@ -557,13 +502,14 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) { version_info.license_service_version()); // Update or invalidate entry if usage table header+entries are supported - if (!provider_session_token.empty() && supports_usage_info()) { + if (HasUsageEntry()) { if (sts != KEY_ADDED) { const CdmResponseType invalidate_sts = usage_table_header_->InvalidateEntry( usage_entry_number_, true, file_handle_.get(), crypto_metrics_); crypto_metrics_->usage_table_header_delete_entry_.Increment( invalidate_sts); + provider_session_token_.clear(); if (invalidate_sts != NO_ERROR) { LOGW("Invalidate usage entry failed: status = %d", static_cast(invalidate_sts)); @@ -580,15 +526,11 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) { IdToString(license_parser_->provider_session_token()), license_parser_->provider_session_token().size()); - if ((is_offline_ || has_provider_session_token()) && !is_temporary_) { - if (has_provider_session_token() && supports_usage_info()) { + if (is_offline_ && !is_temporary_) { + if (HasUsageEntry()) { usage_table_header_->UpdateEntry(usage_entry_number_, crypto_session_.get(), &usage_entry_); } - - if (!is_offline_) - usage_provider_session_token_ = license_parser_->provider_session_token(); - sts = StoreLicense(); if (sts != NO_ERROR) return sts; } @@ -700,7 +642,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParametersV16& params) { } has_decrypted_since_last_report_ = true; if (!is_usage_update_needed_) { - is_usage_update_needed_ = has_provider_session_token(); + is_usage_update_needed_ = HasUsageEntry(); } last_decrypt_failed_ = false; } else { @@ -786,7 +728,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyRequest* key_request) { if (KEY_MESSAGE != status) return status; - if (has_provider_session_token() && supports_usage_info()) { + if (HasUsageEntry()) { status = usage_table_header_->UpdateEntry( usage_entry_number_, crypto_session_.get(), &usage_entry_); @@ -799,10 +741,6 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyRequest* key_request) { if (is_offline_) { // Mark license as being released if (!StoreLicense(kLicenseStateReleasing, nullptr)) return RELEASE_KEY_REQUEST_ERROR; - } else if (!usage_provider_session_token_.empty()) { - if (supports_usage_info()) { - if (!UpdateUsageInfo()) return RELEASE_USAGE_INFO_FAILED; - } } key_request_type_ = key_request->type; @@ -833,7 +771,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) { LOGE("CDM session not initialized"); return NOT_INITIALIZED_ERROR; } - if (!supports_usage_info()) { + if (!SupportsUsageEntries()) { LOGE("Cannot delete entry, usage table not supported"); return INCORRECT_USAGE_SUPPORT_TYPE_1; } @@ -862,6 +800,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) { sts = usage_table_header_->InvalidateEntry( usage_entry_number, true, file_handle_.get(), crypto_metrics_); crypto_metrics_->usage_table_header_delete_entry_.Increment(sts); + provider_session_token_.clear(); return sts; } @@ -908,54 +847,23 @@ bool CdmSession::GenerateKeySetId(bool atsc_mode_enabled, } CdmResponseType CdmSession::StoreLicense() { - if (is_temporary_) { + if (is_temporary_ || !is_offline_) { LOGE("Session type prohibits storage"); return STORAGE_PROHIBITED; } - - if (is_offline_) { - if (key_set_id_.empty()) { - LOGE("No key set ID"); - return EMPTY_KEYSET_ID; - } - - if (!license_parser_->is_offline()) { - LOGE("License policy prohibits storage"); - return OFFLINE_LICENSE_PROHIBITED; - } - - if (!StoreLicense(kLicenseStateActive, nullptr)) { - LOGE("Unable to store license"); - return STORE_LICENSE_ERROR_1; - } - return NO_ERROR; - } // if (is_offline_) - - std::string provider_session_token = - license_parser_->provider_session_token(); - if (provider_session_token.empty()) { - LOGE("No provider session token and not offline"); - return STORE_LICENSE_ERROR_2; + if (key_set_id_.empty()) { + LOGE("No key set ID"); + return EMPTY_KEYSET_ID; } - std::string app_id; - GetApplicationId(&app_id); - if (!file_handle_->StoreUsageInfo( - provider_session_token, key_request_, key_response_, - DeviceFiles::GetUsageInfoFileName(app_id), key_set_id_, usage_entry_, - usage_entry_number_, drm_certificate_, wrapped_private_key_)) { - LOGE("Unable to store usage info"); - // Usage info file is corrupt. Delete current usage entry and file. - if (supports_usage_info()) { - DeleteUsageEntry(usage_entry_number_); - } else { - LOGW("Cannot store, usage table not supported"); - } - std::vector provider_session_tokens; - file_handle_->DeleteAllUsageInfoForApp( - DeviceFiles::GetUsageInfoFileName(app_id), &provider_session_tokens); + if (!license_parser_->is_offline()) { + LOGE("License policy prohibits storage"); + return OFFLINE_LICENSE_PROHIBITED; + } - return STORE_USAGE_INFO_ERROR; + if (!StoreLicense(kLicenseStateActive, nullptr)) { + LOGE("Unable to store license"); + return STORE_LICENSE_ERROR_1; } return NO_ERROR; } @@ -999,8 +907,8 @@ CdmResponseType CdmSession::RemoveKeys() { } CdmResponseType CdmSession::RemoveLicense() { - if (is_offline_ || has_provider_session_token()) { - if (has_provider_session_token() && supports_usage_info()) { + if (is_offline_) { + if (HasUsageEntry()) { DeleteUsageEntry(usage_entry_number_); } DeleteLicenseFile(); @@ -1009,17 +917,8 @@ CdmResponseType CdmSession::RemoveLicense() { } bool CdmSession::DeleteLicenseFile() { - if (!is_offline_ && !has_provider_session_token()) return false; - - if (is_offline_) { - return file_handle_->DeleteLicense(key_set_id_); - } else { - std::string app_id; - GetApplicationId(&app_id); - return file_handle_->DeleteUsageInfo( - DeviceFiles::GetUsageInfoFileName(app_id), - license_parser_->provider_session_token()); - } + if (!is_offline_) return false; + return file_handle_->DeleteLicense(key_set_id_); } void CdmSession::NotifyResolution(uint32_t width, uint32_t height) { @@ -1050,16 +949,14 @@ void CdmSession::GetApplicationId(std::string* app_id) { } CdmResponseType CdmSession::UpdateUsageEntryInformation() { - if (!has_provider_session_token() || !supports_usage_info()) { - LOGE("Unexpected state: usage_support = %s, PST present = %s, ", - supports_usage_info() ? "true" : "false", - has_provider_session_token() ? "yes" : "no"); + if (!HasUsageEntry()) { + LOGE("Session does not have a usage entry"); return INCORRECT_USAGE_SUPPORT_TYPE_2; } CdmResponseType sts = NO_ERROR; // TODO(blueeyes): Add measurements to all UpdateEntry calls in a way that - // allos us to isolate this particular use case within + // allows us to isolate this particular use case within // UpdateUsageEntryInformation. M_TIME(sts = usage_table_header_->UpdateEntry( usage_entry_number_, crypto_session_.get(), &usage_entry_), @@ -1067,11 +964,8 @@ CdmResponseType CdmSession::UpdateUsageEntryInformation() { if (sts != NO_ERROR) return sts; - if (is_offline_) - StoreLicense(is_release_ ? kLicenseStateReleasing : kLicenseStateActive, - nullptr); - else if (!usage_provider_session_token_.empty()) - UpdateUsageInfo(); + StoreLicense(is_release_ ? kLicenseStateReleasing : kLicenseStateActive, + nullptr); return NO_ERROR; } @@ -1138,23 +1032,6 @@ CdmResponseType CdmSession::GetDecryptHashError(std::string* error_string) { return crypto_session_->GetDecryptHashError(error_string); } -bool CdmSession::UpdateUsageInfo() { - std::string app_id; - GetApplicationId(&app_id); - - DeviceFiles::CdmUsageData usage_data; - usage_data.provider_session_token = usage_provider_session_token_; - usage_data.license_request = key_request_; - usage_data.license = key_response_; - usage_data.key_set_id = key_set_id_; - usage_data.usage_entry = usage_entry_; - usage_data.usage_entry_number = usage_entry_number_; - - return file_handle_->UpdateUsageInfo( - DeviceFiles::GetUsageInfoFileName(app_id), usage_provider_session_token_, - usage_data); -} - void CdmSession::UpdateRequestLatencyTiming(CdmResponseType sts) { if (sts == KEY_ADDED && license_request_latency_.IsStarted()) { metrics_->cdm_session_license_request_latency_ms_.Record( diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index d701fa5a..c43d670f 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -452,7 +452,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest( } // TODO(rfrias): Refactor to avoid needing to call CdmSession - if (cdm_session && cdm_session->supports_usage_info()) { + if (cdm_session && cdm_session->SupportsUsageEntries()) { const CdmResponseType status = cdm_session->UpdateUsageEntryInformation(); if (NO_ERROR != status) return status; } @@ -796,7 +796,7 @@ CdmResponseType CdmLicense::RestoreOfflineLicense( } if (!provider_session_token_.empty()) { - if (cdm_session && cdm_session->supports_usage_info()) { + if (cdm_session && cdm_session->SupportsUsageEntries()) { const CdmResponseType status = cdm_session->UpdateUsageEntryInformation(); if (NO_ERROR != status) return sts; } diff --git a/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp index 5ec877dc..d22f9c83 100644 --- a/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp @@ -303,13 +303,12 @@ TEST_F(CdmSessionTest, UpdateUsageEntry) { // Set up mocks and expectations for the UpdateUsageEntryInformation call. EXPECT_CALL(*crypto_session_, HasUsageInfoSupport(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(true), Return(true))); - EXPECT_CALL(*license_parser_, provider_session_token()) - .WillRepeatedly(Return("Mock provider session token")); EXPECT_CALL(usage_table_header_, UpdateEntry(_, NotNull(), NotNull())) .WillRepeatedly(Return(NO_ERROR)); EXPECT_EQ(NO_ERROR, cdm_session_->Init(nullptr)); - EXPECT_TRUE(cdm_session_->supports_usage_info()); + cdm_session_->SetProviderSessionTokenForTest("Mock provider session token"); + EXPECT_TRUE(cdm_session_->SupportsUsageEntries()); EXPECT_EQ(NO_ERROR, cdm_session_->UpdateUsageEntryInformation()); // Verify the UsageEntry metric is set.