// 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_LICENSE_H_ #define WVCDM_CORE_LICENSE_H_ #include #include #include "initialization_data.h" #include "license_protocol.pb.h" #include "service_certificate.h" #include "wv_cdm_types.h" #include "wv_class_utils.h" namespace wvutil { class Clock; } // namespace wvutil namespace wvcdm { class CdmSession; class CryptoKey; class CryptoSession; class InitializationData; class PolicyEngine; class CdmLicense { public: using PsshEntitledKey = video_widevine::WidevinePsshData::EntitledKey; CdmLicense() = delete; WVCDM_DISALLOW_COPY_AND_MOVE(CdmLicense); explicit CdmLicense(const CdmSessionId& session_id); virtual ~CdmLicense(); virtual bool Init(bool use_privacy_mode, const std::string& signed_service_certificate, CryptoSession* session, PolicyEngine* policy_engine); // == Accessors == virtual bool is_offline() const { return is_offline_; } virtual std::string provider_session_token() { return provider_session_token_; } virtual bool HasInitData() { return static_cast(stored_init_data_); } virtual const video_widevine::VersionInfo& GetServiceVersion() { return latest_service_version_; } virtual bool IsKeyLoaded(const KeyId& key_id); // == Service Certificate API == // Override the currently-installed service certificate with a new service // certificate. virtual CdmResponseType SetServiceCertificate( const std::string& signed_service_certificate); // == License Restoring API == // Restores an offline license for continued use. // // Parameters: // |client_token| // Client's DRM certificate used as the "token" when // authenticating the device and client with the license // server. // |license_request| // Original license request generated by this device. // Expected to be a serialized SignedMessage, with type // LICENSE_REQUEST. // |license_response| // Original license response generated by the license // server, associated with the |license_response|. // Expected to be a serialized SignedMessage, with type // LICENSE. // |license_renewal_response| // Last renewal response received from the renewal license // server. // Expected to be a serialized SignedMessage, with type // LICENSE. // |playback_start_time|, |last_playback_time|, |grace_period_end_time| // License timers from the CDM, not necessarily secure timers. // // Important Results: // NO_ERROR // Successfully restored license for playback. virtual CdmResponseType RestoreOfflineLicense( const std::string& client_token, const CdmKeyMessage& license_request, const CdmKeyResponse& license_response, const CdmKeyResponse& license_renewal_response, int64_t playback_start_time, int64_t last_playback_time, int64_t grace_period_end_time, CdmSession* cdm_session); // Restores an offline license for release. // // License is loaded into OEMCrypto with the intention of // generating a release request. // Attempting playback will cause undefined behavior. // // Parameters: // |client_token| // Client's DRM certificate used as the "token" when // authenticating the device and client with the license // server. // |license_request| // Original license request generated by this device. // Expected to be a serialized SignedMessage, with type // LICENSE_REQUEST. // |license_response| // Original license response generated by the license // server, associated with the |license_response|. // Expected to be a serialized SignedMessage, with type // LICENSE. // // Important Results: // NO_ERROR // Successfully restored license for release. virtual CdmResponseType RestoreLicenseForRelease( const std::string& client_token, const CdmKeyMessage& license_request, const CdmKeyResponse& license_response); // == Request/Response API == // Generates a license or service certificate request. // // If the license does not have a service certificate, then the // first time calling this method on a newly initialized instance // will generate a service certificate request. // // Parameters: // |init_data| // App provided initialization data. Should be of a supported // format. // |client_token| // Client's DRM certificate used as the "token" when // authenticating the device and client with the license // server. // |license_type| // Type of license to be requested, used in the generation // of the content ID. // |app_parameters| // Optional key-value pair of parameters to be inserted into // the client ID of the license request. // Note: Certain key's are reserved for Widevine use and // cannot be specified by the app. // |signed_request| (out) // Serialized license request to be sent to the server. // Only set if generating license is successful. // This message is a serialized SignedMessage, with type // NEW. // |server_url| (out) // Server URL, never specified by the CDM for new licenses. // Legacy field for older revisions. // // Important Results: // KEY_MESSAGE // Successfully generated a request. // PRIVACY_MODE_ERROR_1 // Privacy mode is enabled, but no service certificate has // been provided, and requesting service certificates are // forbidden. // GENERATE_SIGNATURE_ERROR (legacy name) // OEMCrypto was unable to sign the request. virtual CdmResponseType PrepareKeyRequest( const InitializationData& init_data, const std::string& client_token, CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request, std::string* server_url); // Generates a license renewal or release request. // // For license renewals (|is_renewal| = true), will attempt to // generate a renewal request using the policies specified in the // original license. Playback will continue to be allowed so // long as the policy timers have not expired. // // For license release (|is_renewal| = false), will attempt to // generate a release request using the policies specified in the // original license. Playback will be halted, and not allowed to // resume. For licenses with a usage entry, OEMCrypto will enforce // playback halting, and a usage report will be generated to be // sent with the release. // // Parameters: // |is_renewal| // Flag to indicate whether the generated request is a // renewal (true) or a release (false). // |app_parameters| // Optional key-value pair of parameters to be inserted into // the client ID of the license request. // Note: Certain key's are reserved for Widevine use and // cannot be specified by the app. // |cdm_session| // Handle to the calling CdmSession to allow for usage // reporting. // |signed_request| (out) // Serialized renewal or release request to be sent to the // server. Only set if generating license is successful. // This message is a serialized SignedMessage, with type // RENEWAL or RELEASE. // |server_url| (out) // Server URL to be used for sending renewals. The value // provided is extracted from the license policy received // in the original license response. // // Important Results: // KEY_MESSAGE // Successfully generated a renewal or release request. // LICENSE_RENEWAL_PROHIBITED // License policy forbids license renewals. // GENERATE_SIGNATURE_ERROR (legacy name) // OEMCrypto was unable to sign the request. virtual CdmResponseType PrepareKeyUpdateRequest( bool is_renewal, const CdmAppParameterMap& app_parameters, CdmSession* cdm_session, CdmKeyMessage* signed_request, std::string* server_url); // Parses and loads license or service certificate response, // or handles license errors. // // For license response, the content of the license is parsed; // extracting and verifying keys, initializing the license's // policy engine, and updating other policy rules. If license // contains entitlement keys, changes the license type // to entitlement. // // For service certificate response, the service certificate // is parsed and loaded into the |service_certificate_| // field for next call to generate request. // // For error response, parses the error message, producing logs // and returning an appropriate error. // // Parameters: // |license_response| // Serialized license response. // Expected to be a serialized SignedMessage, with type // LICENSE. // // Important Results: // KEY_ADDED // Successfully loaded license. // NEED_KEY // Successfully loaded service certificate, and signals // to the app that another request is required to generate // a license request. // NO_CONTENT_KEY // License was received, but did not contain any content // or operator session keys. // LOAD_LICENSE_ERROR // OEMCrypto rejected the license, see logs for details. virtual CdmResponseType HandleKeyResponse( const CdmKeyResponse& license_response) { return HandleKeyResponseInternal(/* is_restore = */ false, license_response); } // Parses and loads renewal response, or handles license // errors. // // For license renewal response, the content of the renewal is // parsed; updating policy timers and other license policy // variables. // // For error response, parses the error message, producing logs // and returning an appropriate error. // // Parameters: // |license_response| // Serialized license renewal response. // Expected to be a serialized SignedMessage, with type // LICENSE. // // Important Results: // KEY_ADDED // Successfully loaded renewal. // NO_CONTENT_KEY // License was received, but did not contain any content // or operator session keys. // LOAD_LICENSE_ERROR // OEMCrypto rejected the license, see logs for details. virtual CdmResponseType HandleKeyUpdateResponse( bool is_renewal, const CdmKeyResponse& license_response) { return HandleKeyUpdateResponseInternal(is_renewal, /* is_restore = */ false, license_response); } // Parses and loads new embedded keys. // // Updates the license's key session with new entitled keys // for which the license has entitlement keys for. Storing // those keys internally as new content keys. // // Used exclusively for entitlement licenses. // // Parameters: // |init_data| // Initialization data containing entitled keys within // the PSSH data. // // Important Results: // KEY_ADDED // Successfully loaded new entitled keys. virtual CdmResponseType HandleEmbeddedKeyData( const InitializationData& init_data); // == Utilities == // Utility method for extracting the provider session token // from the license response. static bool ExtractProviderSessionToken( const CdmKeyResponse& license_response, std::string* provider_session_token); // == Test Accessors == // Testing only. Caller retains ownership of pointers. void set_crypto_session(CryptoSession* crypto_session) { crypto_session_ = crypto_session; } void set_policy_engine(PolicyEngine* policy_engine) { policy_engine_ = policy_engine; } protected: // Test Constructor. // CdmLicense takes ownership of the clock. CdmLicense(const CdmSessionId& session_id, wvutil::Clock* clock); // Inserts an entitlement key ID from test data. void InsertEntitlementKeyIdForTest(const KeyId& entitlement_key_id) { entitlement_key_ids_.insert(entitlement_key_id); } CdmResponseType HandleNewEntitledKeysForTest( const std::vector& packaged_entitled_keys) { return HandleNewEntitledKeysInternal(packaged_entitled_keys); } private: // == Internal Request/Response API == // Prepare to reload a key update message. Some special code is needed to work // around b/166010609. // TODO(b/166007195): Remove this. CdmResponseType PrepareKeyUpdateReload(CdmSession* cdm_session); // Used internally to handle license responses for both newly acquired // licenses and for restoring of offline licenses. CdmResponseType HandleKeyResponseInternal( bool is_restore, const CdmKeyResponse& license_response); // Used internally to handle renewal responses for both newly acquired // renewals and for restoring of offline licenses which had received // renewals. CdmResponseType HandleKeyUpdateResponseInternal( bool is_renewal, bool is_restore, const CdmKeyResponse& license_response); // Used internally to handle license error responses for both // license requests and renewal requests. Maps the error code // from the license protocol to a CDM error code. CdmResponseType HandleKeyErrorResponse( const video_widevine::SignedMessage& signed_message); // Used internally to load the content license into OEMCrypto // and update the license data if successful. CdmResponseType HandleContentKeyResponse( bool is_restore, const std::string& session_key, const std::string& msg, const std::string& core_message, const std::string& signature, const std::vector& license_keys, const video_widevine::License& license); // Loads the entitlement keys in |license_keys| into // the crypto session. // // In addition, it also extracts content keys from // |request_entitled_keys_| and loads them for use. CdmResponseType HandleEntitlementKeyResponse( bool is_restore, const std::string& session_key, const std::string& msg, const std::string& core_message, const std::string& signature, const std::vector& license_keys, const video_widevine::License& license); // Used internally to load the entitlement keys from // outside the license into the CryptoSession. CdmResponseType HandleNewEntitledKeysInternal( const std::vector& packaged_entitled_keys); // == Internal Utilities == CdmResponseType PrepareClientId( const CdmAppParameterMap& app_parameters, const std::string& provider_client_token, video_widevine::LicenseRequest* license_request); CdmResponseType PrepareContentId( const InitializationData& init_data, CdmLicenseType license_type, const std::string& request_id, video_widevine::LicenseRequest* license_request); template bool SetTypeAndId(CdmLicenseType license_type, const std::string& request_id, T* content_id); // == Creation-time Variables == // CDM session ID associated with this license. CdmLicense should // only be associated with a single CDM session. const CdmSessionId session_id_; // Internal clock used to get REE system-time. // For production, uses the default Clock implementation; // for testing, uses a MockClock. std::unique_ptr clock_; // == Initialization-time Variables == // Flag to indicate that the CdmLicense has been initialized // correctly via a call to Init(). bool initialized_ = false; // License's crypto session, owned by the CdmSession which owns // this instance. CryptoSession* crypto_session_ = nullptr; // License's policy engine, owned by the CdmSession which owns // this instance. PolicyEngine* policy_engine_ = nullptr; // The flag |use_privacy_mode_| is used to determine whether // the client identification field should be encrypted when // generating license / renewal request. bool use_privacy_mode_ = false; // The service certificate used to encrypt client // identification. Must be initialized if |use_privacy_mode_| // is true. ServiceCertificate service_certificate_; // Assume the latest, and downgrade later. // May be downgraded based on the OEMCrypto level, or the // license response. video_widevine::ProtocolVersion protocol_version_ = video_widevine::VERSION_2_2; // == License Request/Response variables == // Device-side token used to authenticate the license request // with the license server. // Always contains a serialized DRM certificate associated // with the client. std::string client_token_; // Contains the initialization data that was provided by the app // when making the first request. // Used for certain devices which may perform a service certificate // request. The app will only provided the license init data on // the first request, but may not provide initialization data on // the follow up request. std::unique_ptr stored_init_data_; // The nonce used in the original license request. uint32_t license_nonce_ = 0; // Serialized LicenseRequest proto. // Either originates internally from a license request generated // since the opening of this session; or provided while restoring // of a license. CdmKeyMessage license_request_; // These are extracted from the PSSH from the initialization data. // They are used to load entitled content keys after the license // has been received. It is also used in updating the key status // info. // Used exclusively for entitlement key licensing. // These entitled keys are extracted at license request time. std::vector request_entitled_keys_; // Content-provider token for identifying a client's device across // multiple sessions/licenses. std::string provider_client_token_; // Content-provider token for identifying a client's session for // a single persistent license, and potentially across multiple // restores. std::string provider_session_token_; // Flag to indicate that a renewal request requires a client ID. // This is specified in the license. bool renew_with_client_id_ = false; // Server URL which the client app should use when requesting // a renewal. std::string renewal_server_url_; // This is the latest version info extracted from the SignedMessage // when loading a received license. video_widevine::VersionInfo latest_service_version_; // == License Life-Time Variables == // A license is assumed to be content type until the license // response changes that. CdmLicenseKeyType license_key_type_ = kLicenseKeyTypeContent; // Flag to indicate that the license is offline. // A license is assumed to be streaming/online until a response is // received. This flag is determined by the license // type (OFFLINE) and the policy's "can persist" flag. bool is_offline_ = false; // A list of content keys (either basic content keys, or entitled // content keys) which have been received. // Note: for entitlement licenses, this is updated when new entitled // content keys are received. std::set content_key_ids_; // A list of entitlement key IDs from the license. Used to filter // out the any entitled keys tied to an entitlement key which are // not specified in the license response. std::set entitlement_key_ids_; }; // class CdmLicense } // namespace wvcdm #endif // WVCDM_CORE_LICENSE_H_