502 lines
24 KiB
C++
502 lines
24 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine License
|
|
// Agreement.
|
|
|
|
#ifndef WVCDM_CORE_CDM_ENGINE_H_
|
|
#define WVCDM_CORE_CDM_ENGINE_H_
|
|
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "cdm_session_map.h"
|
|
#include "certificate_provisioning.h"
|
|
#include "clock.h"
|
|
#include "crypto_session.h"
|
|
#include "disallow_copy_and_assign.h"
|
|
#include "file_store.h"
|
|
#include "initialization_data.h"
|
|
#include "metrics_collections.h"
|
|
#include "oemcrypto_adapter.h"
|
|
#include "service_certificate.h"
|
|
#include "wv_cdm_constants.h"
|
|
#include "wv_cdm_types.h"
|
|
|
|
namespace wvcdm {
|
|
class CdmClientPropertySet;
|
|
class CdmEngineFactory;
|
|
class CdmSession;
|
|
class CryptoEngine;
|
|
class OtaKeyboxProvisioner;
|
|
class UsagePropertySet;
|
|
class WvCdmEventListener;
|
|
|
|
// Keep expiration time for each key set
|
|
using CdmReleaseKeySetMap =
|
|
std::map<CdmKeySetId, std::pair<CdmSessionId, int64_t> >;
|
|
|
|
class CdmEngine {
|
|
public:
|
|
virtual ~CdmEngine();
|
|
|
|
// Session related methods
|
|
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
|
CdmClientPropertySet* property_set,
|
|
const CdmSessionId& forced_session_id,
|
|
WvCdmEventListener* event_listener);
|
|
|
|
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
|
CdmClientPropertySet* property_set,
|
|
WvCdmEventListener* event_listener,
|
|
CdmSessionId* session_id);
|
|
|
|
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
|
|
|
virtual bool IsOpenSession(const CdmSessionId& session_id);
|
|
|
|
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id,
|
|
CdmClientPropertySet* property_set,
|
|
WvCdmEventListener* event_listener);
|
|
|
|
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
|
|
|
|
// License related methods
|
|
|
|
// Construct a valid license request. The arguments are used as follows:
|
|
// session_id: The Session ID of the session the request is being generated
|
|
// for. This is ignored for license release requests.
|
|
// key_set_id: The Key Set ID of the key set the request is being generated
|
|
// for. This is ignored except for license release requests.
|
|
// init_data: The initialization data from the media file, which is used to
|
|
// build the key request. This is ignored for release and renewal
|
|
// requests.
|
|
// license_type: The type of license being requested. Never ignored.
|
|
// app_parameters: Additional, application-specific parameters that factor
|
|
// into the request generation. This is ignored for release
|
|
// and renewal requests.
|
|
// Certain app parameter keys are reserved for CDM
|
|
// device identification on the license server. These
|
|
// parameters will be overwritten by the CDM request
|
|
// generator.
|
|
// key_request: This must be non-null and point to a CdmKeyRequest. The
|
|
// message field will be filled with the key request, the
|
|
// type field will be filled with the key request type,
|
|
// whether it is an initial request, renewal request,
|
|
// release request, etc. The url field will be filled with
|
|
// the default URL (if one is known) to send this key
|
|
// request to.
|
|
virtual CdmResponseType GenerateKeyRequest(
|
|
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
|
const InitializationData& init_data, const CdmLicenseType license_type,
|
|
CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request);
|
|
// This API may
|
|
// (a) accept license response, extract key info and load keys.
|
|
// (b) accept a renewal response and update license policy information.
|
|
// (c) accept a release response and release an offline license or secure
|
|
// stop.
|
|
// (d) accept a service certificate and cache that information for the
|
|
// lifetime of the session.
|
|
//
|
|
// |session_id| identifies the session that generated the request and can
|
|
// process the response. Should be empty if a release response.
|
|
// |key_data| is the license, renewal, release response or service
|
|
// certificate response.
|
|
// |license_type| must not be null. If the result is KEY_ADDED, this out
|
|
// parameter indicates the type of license contained in
|
|
// key_data. For any other return code, no value is provided.
|
|
// |key_set_id| should be non-null and specified if license release.
|
|
// If offline license or streaming license associated with
|
|
// a secure stop, |key_set_id| should be non-null and will
|
|
// be filled in on return. Use the |key_set_id| with
|
|
// RestoreKeys (to reload offline session) or
|
|
// GenerateKeyRequest (to release offline session/secure stop).
|
|
// |key_set_id| will be cleared if release or streaming
|
|
// (not associated with a secure stop).
|
|
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
|
const CdmKeyResponse& key_data,
|
|
CdmLicenseType* license_type,
|
|
CdmKeySetId* key_set_id);
|
|
|
|
virtual CdmResponseType RestoreKey(const CdmSessionId& session_id,
|
|
const CdmKeySetId& key_set_id);
|
|
|
|
// This method releases the crypto resources and policy resources associated
|
|
// with the given session. This renders the session largely useless. It is
|
|
// preferable to close the session outright. This method does not delete any
|
|
// 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.
|
|
virtual CdmResponseType RemoveLicense(const CdmSessionId& session_id);
|
|
|
|
// Construct valid renewal request for the current session keys.
|
|
virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
|
|
CdmKeyRequest* key_request);
|
|
|
|
// Accept renewal response and update key info.
|
|
virtual CdmResponseType RenewKey(const CdmSessionId& session_id,
|
|
const CdmKeyResponse& key_data);
|
|
|
|
// Change the service certificate installed in a given session.
|
|
virtual CdmResponseType SetSessionServiceCertificate(
|
|
const CdmSessionId& session_id, const std::string& service_certificate);
|
|
|
|
// Query system information
|
|
virtual CdmResponseType QueryStatus(RequestedSecurityLevel security_level,
|
|
const std::string& query_token,
|
|
std::string* query_response);
|
|
|
|
// Query session information
|
|
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,
|
|
CdmQueryMap* query_response);
|
|
virtual bool IsReleaseSession(const CdmSessionId& session_id);
|
|
virtual bool IsOfflineSession(const CdmSessionId& session_id);
|
|
|
|
// Query license information
|
|
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
|
|
CdmQueryMap* query_response);
|
|
|
|
// Query security level support
|
|
static bool IsSecurityLevelSupported(CdmSecurityLevel level);
|
|
|
|
// Query the types of usage permitted for the specified key.
|
|
virtual CdmResponseType QueryKeyAllowedUsage(const CdmSessionId& session_id,
|
|
const std::string& key_id,
|
|
CdmKeyAllowedUsage* key_usage);
|
|
|
|
// Query the types of usage permitted for the specified key.
|
|
// Apply the query across all sessions. If the key is found in more than
|
|
// one session, return the allowed usage settings only if the usage settings
|
|
// are identical for each instance of the key. Otherwise, clear the settings
|
|
// and return KEY_CONFLICT_1.
|
|
virtual CdmResponseType QueryKeyAllowedUsage(const std::string& key_id,
|
|
CdmKeyAllowedUsage* key_usage);
|
|
|
|
// Query OEMCrypto session ID
|
|
virtual CdmResponseType QueryOemCryptoSessionId(
|
|
const CdmSessionId& session_id, CdmQueryMap* query_response);
|
|
|
|
// Generate and return a valid provisioning request.
|
|
virtual CdmResponseType GetProvisioningRequest(
|
|
CdmCertificateType cert_type, const std::string& cert_authority,
|
|
const std::string& service_certificate,
|
|
RequestedSecurityLevel requested_security_level,
|
|
CdmProvisioningRequest* request, std::string* default_url);
|
|
|
|
// Verify and process a provisioning response.
|
|
virtual CdmResponseType HandleProvisioningResponse(
|
|
const CdmProvisioningResponse& response,
|
|
RequestedSecurityLevel requested_security_level, std::string* cert,
|
|
std::string* wrapped_key);
|
|
|
|
// Return true if there is a device certificate on the current
|
|
// (origin-specific) file system.
|
|
virtual bool IsProvisioned(CdmSecurityLevel security_level);
|
|
|
|
// Retrieves the current provisioning status based on whether a DRM
|
|
// certificate or an OEM certificate (in provisioning 4) exists the current
|
|
// (origin-specific) file system.
|
|
virtual CdmProvisioningStatus GetProvisioningStatus(
|
|
CdmSecurityLevel security_level);
|
|
|
|
// Remove device DRM certificate from the current (origin-specific) file
|
|
// system. This will force the device to reprovision itself.
|
|
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
|
|
|
|
// Return the list of key_set_ids stored on the current (origin-specific)
|
|
// file system.
|
|
virtual CdmResponseType ListStoredLicenses(
|
|
CdmSecurityLevel security_level, std::vector<std::string>* key_set_ids);
|
|
|
|
// Return the list of IDs associated with usage records for the
|
|
// current (origin-specific) file system. At least one parameter
|
|
// |ksids| or |provider_session_tokens| needs to be supplied.
|
|
virtual CdmResponseType ListUsageIds(
|
|
const std::string& app_id, CdmSecurityLevel security_level,
|
|
std::vector<std::string>* ksids,
|
|
std::vector<std::string>* provider_session_tokens);
|
|
|
|
// Delete the usage record for the given key_set_id. This removes the
|
|
// usage record in the file system and the OEMCrypto usage record.
|
|
virtual CdmResponseType DeleteUsageRecord(const std::string& app_id,
|
|
CdmSecurityLevel security_level,
|
|
const std::string& key_set_id);
|
|
|
|
// Get offline license status: active, release or unknown
|
|
virtual CdmResponseType GetOfflineLicenseState(
|
|
const std::string& key_set_id, CdmSecurityLevel security_level,
|
|
CdmOfflineLicenseState* license_state);
|
|
|
|
// Remove offline license with the given key_set_id
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id,
|
|
CdmSecurityLevel security_level);
|
|
|
|
// Remove all usage records for the current origin. Span all
|
|
// security levels.
|
|
virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id);
|
|
|
|
virtual CdmResponseType RemoveUsageInfo(
|
|
const std::string& app_id, const CdmSecureStopId& secure_stop_id);
|
|
|
|
virtual CdmResponseType ReleaseUsageInfo(
|
|
const CdmUsageInfoReleaseMessage& message);
|
|
virtual CdmResponseType LoadUsageSession(const CdmKeySetId& key_set_id,
|
|
CdmKeyMessage* release_message);
|
|
|
|
// Decryption and key related methods
|
|
// Accept encrypted buffer and return decrypted data.
|
|
virtual CdmResponseType DecryptV16(
|
|
const CdmSessionId& session_id,
|
|
const CdmDecryptionParametersV16& parameters);
|
|
|
|
// Generic crypto operations - provides basic crypto operations that an
|
|
// application can use outside of content stream processing
|
|
|
|
// Encrypts a buffer of app-level data.
|
|
virtual CdmResponseType GenericEncrypt(const std::string& session_id,
|
|
const std::string& in_buffer,
|
|
const std::string& key_id,
|
|
const std::string& iv,
|
|
CdmEncryptionAlgorithm algorithm,
|
|
std::string* out_buffer);
|
|
|
|
// Decrypts a buffer of app-level data.
|
|
virtual CdmResponseType GenericDecrypt(const std::string& session_id,
|
|
const std::string& in_buffer,
|
|
const std::string& key_id,
|
|
const std::string& iv,
|
|
CdmEncryptionAlgorithm algorithm,
|
|
std::string* out_buffer);
|
|
|
|
// Computes the signature for a message.
|
|
virtual CdmResponseType GenericSign(const std::string& session_id,
|
|
const std::string& message,
|
|
const std::string& key_id,
|
|
CdmSigningAlgorithm algorithm,
|
|
std::string* signature);
|
|
|
|
// Verifies the signature on a buffer of app-level data.
|
|
virtual CdmResponseType GenericVerify(const std::string& session_id,
|
|
const std::string& message,
|
|
const std::string& key_id,
|
|
CdmSigningAlgorithm algorithm,
|
|
const std::string& signature);
|
|
|
|
virtual size_t SessionSize() const { return session_map_.Size(); }
|
|
|
|
// This tells the OEMCrypto adapter to ignore the next |count| keyboxes and
|
|
// report that it needs provisioning instead.
|
|
static CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count) {
|
|
return CryptoSession::SetDebugIgnoreKeyboxCount(count);
|
|
}
|
|
|
|
// This tells the OEMCrypto adapter to allow the device to continue with a
|
|
// test keybox. Otherwise, the keybox is reported as invalid.
|
|
static CdmResponseType SetAllowTestKeybox(bool allow) {
|
|
return CryptoSession::SetAllowTestKeybox(allow);
|
|
}
|
|
|
|
static CdmResponseType ParseDecryptHashString(const std::string& hash_string,
|
|
CdmSessionId* id,
|
|
uint32_t* frame_number,
|
|
std::string* hash);
|
|
virtual CdmResponseType SetDecryptHash(const CdmSessionId& session_id,
|
|
uint32_t frame_number,
|
|
const std::string& hash);
|
|
virtual CdmResponseType GetDecryptHashError(const CdmSessionId& session_id,
|
|
std::string* hash_error_string);
|
|
|
|
// Is the key known to any session?
|
|
virtual bool IsKeyLoaded(const KeyId& key_id);
|
|
virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
|
|
|
|
// Used for notifying the Max-Res Engine of resolution changes.
|
|
// Return false if no match is found for session_id.
|
|
virtual bool NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
|
uint32_t height);
|
|
|
|
// Timer expiration method. This method is not re-entrant -- there can be
|
|
// only one timer.
|
|
// This method triggers appropriate event callbacks from |event_listener_|,
|
|
// which is assumed to be asynchronous -- i.e. an event should be dispatched
|
|
// to another thread which does the actual work. In particular, if a
|
|
// synchronous listener calls OpenSession or CloseSession, the thread will
|
|
// dead lock.
|
|
virtual void OnTimerEvent();
|
|
|
|
// Fills the |engine_metrics| parameter with the current snapshot of metrics
|
|
// data. Returns true if the metrics data is populated, false otherwise.
|
|
// |engine_metrics| is owned by the caller and must not be null.
|
|
// The CdmEngine implementation is a placeholder. Just return false.
|
|
virtual bool GetMetricsSnapshot(drm_metrics::WvCdmMetrics* /* metrics */) {
|
|
return false;
|
|
}
|
|
|
|
virtual CdmResponseType ValidateServiceCertificate(const std::string& cert);
|
|
|
|
// Setter and getter for the |app_package_name| identifier for this instance
|
|
// of the CdmEngine. This is used to identify the package name.
|
|
virtual void SetAppPackageName(const std::string& app_package_name) {
|
|
app_package_name_ = app_package_name;
|
|
}
|
|
virtual const std::string& GetAppPackageName() { return app_package_name_; }
|
|
virtual void SetSpoid(const std::string& spoid) { spoid_ = spoid; }
|
|
virtual CdmResponseType SetPlaybackId(const CdmSessionId& session_id,
|
|
const std::string& playback_id);
|
|
|
|
virtual void SetUserId(uint32_t user_id) { user_id_ = user_id; }
|
|
virtual uint32_t GetUserId() const { return user_id_; }
|
|
|
|
// Changes the rules used for calculating the fallback duration
|
|
// when OTA keybox provisioning fails.
|
|
// Default rules use fallback duration measured in days, with exponential
|
|
// backoff.
|
|
// Fast rules use fallback durations of a few seconds, without exponential
|
|
// backoff.
|
|
// This method has no effect if OTA keybox is not required.
|
|
virtual void SetDefaultOtaKeyboxFallbackDurationRules();
|
|
virtual void SetFastOtaKeyboxFallbackDurationRules();
|
|
|
|
// A signing method specifically used by Cast.
|
|
// This method should not be used otherwise.
|
|
virtual CdmResponseType SignRsa(const std::string& wrapped_key,
|
|
const std::string& message,
|
|
std::string* signature,
|
|
RSA_Padding_Scheme padding_scheme);
|
|
|
|
protected:
|
|
friend class CdmEngineFactory;
|
|
|
|
CdmEngine(wvutil::FileSystem* file_system,
|
|
std::shared_ptr<metrics::EngineMetrics> metrics);
|
|
|
|
private:
|
|
// private methods
|
|
CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
|
CdmClientPropertySet* property_set,
|
|
WvCdmEventListener* event_listener,
|
|
const CdmSessionId* forced_session_id,
|
|
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);
|
|
|
|
std::string MapHdcpVersion(CryptoSession::HdcpCapability version);
|
|
|
|
void CloseExpiredReleaseSessions();
|
|
|
|
// Returns "true" if |okp_provisioner_| should be checked.
|
|
bool OkpCheck();
|
|
// Returns "true" if CdmEngine should always fallback to L3.
|
|
bool OkpIsInFallbackMode();
|
|
void OkpTriggerFallback();
|
|
void OkpCleanUp();
|
|
|
|
// instance variables
|
|
|
|
/*
|
|
* The metrics group must be the first variable declared to ensure
|
|
* that it is the last member destroyed so that no child members
|
|
* try to use a reference to it after it is destroyed. This will
|
|
* ensure that all data has been properly recorded in the group before
|
|
* it is published.
|
|
*/
|
|
std::shared_ptr<metrics::EngineMetrics> metrics_;
|
|
std::string app_package_name_;
|
|
|
|
CdmSessionMap session_map_;
|
|
CdmReleaseKeySetMap release_key_sets_;
|
|
std::unique_ptr<CertificateProvisioning> cert_provisioning_;
|
|
// Lock must be acquired before using |cert_provisioning_|.
|
|
std::mutex cert_provisioning_mutex_;
|
|
wvutil::FileSystem* file_system_;
|
|
wvutil::Clock clock_;
|
|
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<CdmSession> usage_session_;
|
|
std::unique_ptr<UsagePropertySet> usage_property_set_;
|
|
int64_t last_usage_information_update_time_;
|
|
|
|
// Protect release_key_sets_ from non-thread-safe operations.
|
|
std::mutex release_key_sets_lock_;
|
|
|
|
// TODO(b/124471172): Replace with two sets of locks, one to protect
|
|
// the CdmSessionMap and a per-session lock to control access to
|
|
// session usage/destruction.
|
|
// Locks the session map |session_map_| and session usage/destruction
|
|
// between session management calls (OpenSession, CloseSession, etc),
|
|
// periodic timer calls (OnTimerEvent), and calls to Decrypt.
|
|
// The layer above the CDM implementation is expected to handle thread
|
|
// synchronization to make sure other functions that access sessions do not
|
|
// occur simultaneously with OpenSession or CloseSession.
|
|
// This mutex must be recursive because it is sometimes held while callbacks
|
|
// occur that may subsequently call back into CdmEngine.
|
|
std::recursive_mutex session_map_lock_;
|
|
|
|
// OTA Keybox Provisioning (OKP)
|
|
// Engine should check for the OKP status of the device before opening
|
|
// sessions or generating DRM cert provisioning requests.
|
|
bool okp_initialized_ = false;
|
|
// If OKP is required, then the engine should create an instance
|
|
// of |okp_provisioner_|. If the instance exists, it should be used
|
|
// for GetProvisionRequest, ProvideProvisionRequest, and
|
|
// OpenSession when requested with default security level.
|
|
std::unique_ptr<OtaKeyboxProvisioner> okp_provisioner_;
|
|
// Should the engine need to fallback, this flag should be set to
|
|
// true and |okp_provisioner_| should be cleared. All follow-up
|
|
// requests from the app with security level default should use L3.
|
|
bool okp_fallback_ = false;
|
|
// To prevent race conditions around the engine's OKP state, this mutex
|
|
// should be locked before the use of any of the |okp_*| variables.
|
|
std::mutex okp_mutex_;
|
|
|
|
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
|
|
};
|
|
|
|
} // namespace wvcdm
|
|
|
|
#endif // WVCDM_CORE_CDM_ENGINE_H_
|