// 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_CRYPTO_SESSION_H_ #define WVCDM_CORE_CRYPTO_SESSION_H_ #include #include #include #include #include #include #include "OEMCryptoCENC.h" #include "crypto_wrapped_key.h" #include "key_session.h" #include "metrics_collections.h" #include "oemcrypto_adapter.h" #include "rw_lock.h" #include "timer_metric.h" #include "wv_cdm_types.h" #include "wv_class_utils.h" namespace wvcdm { class CryptoKey; class CryptoSessionFactory; class OtaKeyboxProvisioner; class CdmUsageTable; namespace okp { class SystemFallbackPolicy; } // namespace okp using CryptoKeyMap = std::map; // Crypto session utility functions used by KeySession implementations. void GenerateMacContext(const std::string& input_context, std::string* deriv_context); void GenerateEncryptContext(const std::string& input_context, std::string* deriv_context); size_t GetOffset(std::string message, std::string field); OEMCrypto_Substring GetSubstring(const std::string& message = "", const std::string& field = "", bool set_zero = false); OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode); class CryptoSession { public: using HdcpCapability = OEMCrypto_HDCP_Capability; enum UsageDurationStatus { kUsageDurationsInvalid = 0, kUsageDurationPlaybackNotBegun = 1, kUsageDurationsValid = 2, }; struct SupportedCertificateTypes { bool rsa_2048_bit; bool rsa_3072_bit; bool rsa_cast; bool ecc_secp256r1; bool ecc_secp384r1; bool ecc_secp521r1; }; // Creates an instance of CryptoSession with the given |crypto_metrics|. // |crypto_metrics| is owned by the caller, must NOT be null, and must // exist as long as the new CryptoSession exists. static CryptoSession* MakeCryptoSession( metrics::CryptoMetrics* crypto_metrics); static const char* HdcpCapabilityToString(HdcpCapability hdcp_level); CryptoSession() = delete; WVCDM_DISALLOW_COPY_AND_MOVE(CryptoSession); virtual ~CryptoSession(); // This method will try to terminate OEMCrypto if |session_size_| is 0. // A platform configured property |delay_oem_crypto_termination| will // determine if termination occurs immediately or after a delay. // If termination is delayed, a countdown mechanism is employed. // Call |TryTerminate| periodically until it no longer returns true. // To immediately terminate call |DisableDelayedTermination| before calling // |TryTerminate|. static bool TryTerminate(); static void DisableDelayedTermination(); virtual CdmResponseType GetProvisioningToken( RequestedSecurityLevel requested_security_level, std::string* token, std::string* additional_token); // Must be called after session is open. virtual CdmResponseType GetProvisioningToken(std::string* token, std::string* additional_token); virtual CdmResponseType GetProvisioning40TokenType( RequestedSecurityLevel requested_security_level, OEMCrypto_BCCType* bcc_type); // Must be called after session is open. virtual CdmResponseType GetProvisioning40TokenType( OEMCrypto_BCCType* bcc_type); virtual CdmResponseType GetProvisioning40TokenSignatureType( RequestedSecurityLevel requested_security_level, OEMCrypto_BCCSignatureType* bcc_signature_type); // Must be called after session is open. virtual CdmResponseType GetProvisioning40TokenSignatureType( OEMCrypto_BCCSignatureType* bcc_signature_type); virtual CdmClientTokenType GetPreProvisionTokenType() { return pre_provision_token_type_; } // Retrieves the key data portion of the OEMCrypto keybox. // Only valid for keybox-based based devices. // May return NEED_PROVISIONING if the device is keybox-based, but // OTA keybox provisioning is required. virtual CdmResponseType GetTokenFromKeybox( RequestedSecurityLevel requested_security_level, std::string* key_data); // Retrieves the public OEM certificate chain from OEMCrypto. // Only valid for OEM certificate-based based devices. virtual CdmResponseType GetTokenFromOemCert( RequestedSecurityLevel requested_security_level, std::string* oem_cert); // Retrieves the embedded public certificate from OEMCrypto. // Only valid for L3 devices with embedded (baked-in) certificates. virtual CdmResponseType GetTokenFromEmbeddedCertificate( RequestedSecurityLevel requested_security_level, std::string* token); // The overloaded methods with |requested_level| may be called // without a preceding call to Open. The other method must call Open first. virtual CdmSecurityLevel GetSecurityLevel(); virtual CdmSecurityLevel GetSecurityLevel( RequestedSecurityLevel requested_level); virtual bool GetApiVersion(uint32_t* version); virtual bool GetApiVersion(RequestedSecurityLevel requested_level, uint32_t* version); virtual bool GetApiMinorVersion(RequestedSecurityLevel requested_level, uint32_t* minor_version); // This method will return, for devices with a // * keybox: the 32 byte device ID from the keybox. // * OEM certificate: // - that implements |OEMCrypto_GetDeviceID|: the (1 to 64 byte) device ID. // - that does not implement |OEMCrypto_GetDeviceID|: the OEM public // certificate. virtual CdmResponseType GetInternalDeviceUniqueId(std::string* device_id); // This method will return, for devices with a // * keybox: the 32 byte device ID from the keybox. // * OEM certificate: // - that implements |OEMCrypto_GetDeviceID|: the (1 to 64 byte) device ID. // - that does not implement |OEMCrypto_GetDeviceID|: the 32 byte hash // of the OEM public certificate. virtual CdmResponseType GetExternalDeviceUniqueId(std::string* device_id); virtual CdmResponseType GetProvisioningId(std::string* provisioning_id); virtual uint8_t GetSecurityPatchLevel(); virtual bool GetCachedSystemId(uint32_t* system_id); // With provisioning 4.0, the system ID cannot reliably be found within // OEMCrypto. The system ID can be assigned to the CryptoSession instance // after the ID has been determined. virtual void SetSystemId(uint32_t system_id); virtual CdmResponseType Open() { return Open(kLevelDefault); } virtual CdmResponseType Open(RequestedSecurityLevel requested_security_level); virtual CdmResponseType MarkOfflineSession(); virtual void Close(); virtual bool IsOpen() { return open_; } virtual CryptoSessionId oec_session_id() { return oec_session_id_; } // All request/responses virtual const std::string& request_id() { return request_id_; } virtual CdmResponseType GenerateNonce(uint32_t* nonce); // License request/responses virtual CdmResponseType PrepareAndSignLicenseRequest( const std::string& message, std::string* core_message, std::string* signature, bool& should_specify_algorithm, OEMCrypto_SignatureHashAlgorithm& algorithm); virtual CdmResponseType UseSecondaryKey(bool dual_key); // V16 licenses. virtual CdmResponseType LoadLicense(const std::string& context, const std::string& session_key, const std::string& signed_message, const std::string& core_message, const std::string& signature, CdmLicenseKeyType key_type); // Renewal request/responses virtual CdmResponseType PrepareAndSignRenewalRequest( const std::string& message, std::string* core_message, std::string* signature); // V16 licenses. virtual CdmResponseType LoadRenewal(const std::string& signed_message, const std::string& core_message, const std::string& signature); // Entitled content Keys. virtual CdmResponseType LoadEntitledContentKeys( const std::vector& key_array); // Provisioning request/responses virtual CdmResponseType PrepareAndSignProvisioningRequest( const std::string& message, std::string* core_message, std::string* signature, bool& should_specify_algorithm, OEMCrypto_SignatureHashAlgorithm& algorithm); virtual CdmResponseType LoadProvisioning(const std::string& request, const std::string& signed_message, const std::string& core_message, const std::string& signature, std::string* wrapped_private_key); virtual CdmResponseType LoadProvisioningCast( const std::string& derivation_key, const std::string& request, const std::string& signed_message, const std::string& core_message, const std::string& signature, std::string* wrapped_private_key); virtual CdmResponseType LoadCertificatePrivateKey( const CryptoWrappedKey& private_key); virtual CdmResponseType GetBootCertificateChain( RequestedSecurityLevel requested_security_level, std::string* bcc, std::string* additional_signature); virtual CdmResponseType GetBootCertificateChain( std::string* bcc, std::string* additional_signature); virtual CdmResponseType GetBootCertificateChainSignatureType( RequestedSecurityLevel requested_security_level, OEMCrypto_BCCSignatureType* bcc_signature_type); virtual CdmResponseType GetBootCertificateChainSignatureType( OEMCrypto_BCCSignatureType* bcc_signature_type); virtual CdmResponseType GetDeviceInformation( RequestedSecurityLevel requested_security_level, std::string* device_info); virtual CdmResponseType GetDeviceSignedCsrPayload( RequestedSecurityLevel requested_security_level, const std::string& challenge, const std::string& device_info, std::string* signed_csr_payload); virtual CdmResponseType GenerateCertificateKeyPair( std::string* public_key, std::string* public_key_signature, std::string* wrapped_private_key, CryptoWrappedKey::Type* key_type); virtual CdmResponseType LoadOemCertificatePrivateKey( const CryptoWrappedKey& private_key); // Media data path virtual CdmResponseType Decrypt(const CdmDecryptionParametersV16& params); virtual bool IsAntiRollbackHwPresent(); // The overloaded methods with |security_level| may be called without a // preceding call to Open. The other methods must call Open first. virtual CdmResponseType GetHdcpCapabilities(HdcpCapability* current, HdcpCapability* max); virtual CdmResponseType GetHdcpCapabilities( RequestedSecurityLevel security_level, HdcpCapability* current, HdcpCapability* max); virtual bool GetResourceRatingTier(uint32_t* tier); virtual bool GetResourceRatingTier(RequestedSecurityLevel security_level, uint32_t* tier); virtual bool GetSupportedCertificateTypes(SupportedCertificateTypes* support); virtual CdmResponseType GetNumberOfOpenSessions( RequestedSecurityLevel security_level, size_t* count); virtual CdmResponseType GetMaxNumberOfSessions( RequestedSecurityLevel security_level, size_t* max); virtual CdmResponseType GetSrmVersion(uint16_t* srm_version); virtual bool GetBuildInformation(RequestedSecurityLevel security_level, std::string* info); virtual bool GetBuildInformation(std::string* info); virtual bool GetWatermarkingSupport(CdmWatermarkingSupport* support); virtual bool GetWatermarkingSupport( RequestedSecurityLevel requested_security_level, CdmWatermarkingSupport* support); virtual bool GetProductionReadiness(CdmProductionReadiness* readiness); virtual bool GetProductionReadiness( RequestedSecurityLevel requested_security_level, CdmProductionReadiness* readiness); virtual bool GetMaximumUsageTableEntries( RequestedSecurityLevel security_level, size_t* number_of_entries); virtual bool GetDecryptHashSupport(RequestedSecurityLevel security_level, uint32_t* hash_support); virtual CdmResponseType SetDecryptHash(uint32_t frame_number, const std::string& hash); virtual CdmResponseType GetDecryptHashError(std::string* error_string); virtual CdmResponseType GenericEncrypt(const std::string& in_buffer, const std::string& key_id, const std::string& iv, CdmEncryptionAlgorithm algorithm, std::string* out_buffer); virtual CdmResponseType GenericDecrypt(const std::string& in_buffer, const std::string& key_id, const std::string& iv, CdmEncryptionAlgorithm algorithm, std::string* out_buffer); virtual CdmResponseType GenericSign(const std::string& message, const std::string& key_id, CdmSigningAlgorithm algorithm, std::string* signature); virtual CdmResponseType GenericVerify(const std::string& message, const std::string& key_id, CdmSigningAlgorithm algorithm, const std::string& signature); // Usage table API related methods. // Used to manipulate the CDM managed usage table header & entries, // delegating calls to OEMCrypto. // Determines whether the OEMCrypto library supports usage table. // As of V16, the only valid type of support is usage table header + // usage entries. // The first method will use a cached value if present. virtual bool HasUsageTableSupport(bool* has_support); virtual bool HasUsageTableSupport(RequestedSecurityLevel security_level, bool* has_support); // Usage report. virtual CdmResponseType DeactivateUsageInformation( const std::string& provider_session_token); virtual CdmResponseType GenerateUsageReport( const std::string& provider_session_token, std::string* usage_report, UsageDurationStatus* usage_duration_status, int64_t* seconds_since_started, int64_t* seconds_since_last_played); // Usage table header. virtual CdmUsageTable* GetUsageTable() { return usage_table_; } // The following crypto methods do not require an open session to // complete the operations. virtual CdmResponseType CreateUsageTableHeader( RequestedSecurityLevel requested_security_level, UsageTableHeader* usage_table_header); virtual CdmResponseType LoadUsageTableHeader( RequestedSecurityLevel requested_security_level, const UsageTableHeader& usage_table_header); virtual CdmResponseType ShrinkUsageTableHeader( RequestedSecurityLevel requested_security_level, uint32_t new_entry_count, UsageTableHeader* usage_table_header); // Usage entry. virtual CdmResponseType CreateUsageEntry(UsageEntryIndex* entry_index); virtual CdmResponseType LoadUsageEntry(UsageEntryIndex entry_index, const UsageEntry& usage_entry); virtual CdmResponseType UpdateUsageEntry(UsageTableHeader* usage_table_header, UsageEntry* usage_entry); // Adjust usage entries in usage table header. virtual CdmResponseType MoveUsageEntry(UsageEntryIndex new_entry_index); virtual bool GetAnalogOutputCapabilities(bool* can_support_output, bool* can_disable_output, bool* can_support_cgms_a); virtual metrics::CryptoMetrics* GetCryptoMetrics() { return metrics_; } virtual CdmResponseType GetProvisioningMethod( RequestedSecurityLevel requested_security_level, CdmClientTokenType* token_type); // OTA Provisioning static bool needs_keybox_provisioning() { return needs_keybox_provisioning_; } // This tells the OEMCrypto adapter to ignore the next |count| keyboxes and // report that it needs provisioning instead. static CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count); // Returns a system-wide singleton instance of SystemFallbackPolicy // to be used for communicating OTA keybox provisioning state between // apps. Returns a null pointer if OTA provisioning is not supported, // or not required. static okp::SystemFallbackPolicy* GetOkpFallbackPolicy(); // Generates an OTA provisioning request. // This should only be called by an instance of OtaKeyboxProvisioner. virtual CdmResponseType PrepareOtaProvisioningRequest(bool use_test_key, std::string* request); // Loads an OTA provisioning response. // This should only be called by an instance of OtaKeyboxProvisioner. virtual CdmResponseType LoadOtaProvisioning(bool use_test_key, const std::string& response); // Cast specific generate signature method. virtual CdmResponseType GenerateRsaSignature(const std::string& message, std::string* signature, RSA_Padding_Scheme scheme); protected: // Creates an instance of CryptoSession with the given |crypto_metrics|. // |crypto_metrics| is owned by the caller, must NOT be null, and must // exist as long as the new CryptoSession exists. explicit CryptoSession(metrics::CryptoMetrics* crypto_metrics); int session_count() const { return session_count_; } // Cache api version and fallback policy. Call this once at initialization. void CacheVersion(); // Re-initialize for running tests with a test keybox. void ReinitializeForTest(); private: friend class CryptoSessionForTest; friend class CryptoSessionFactory; #if defined(UNIT_TEST) friend class CertificateProvisioningTest; friend class WvCdmTestBase; friend class CdmOtaKeyboxTest; #endif // The global factory method can be set to generate special crypto sessions // just for testing. These sessions will avoid nonce floods and will ask // OEMCrypto to use a test keybox. // Ownership of the object is transferred to CryptoSession. static void SetCryptoSessionFactory(CryptoSessionFactory* factory) { std::unique_lock auto_lock(factory_mutex_); factory_.reset(factory); } void Init(); // Will set up the CdmUsageTable for this session. This may require // creating a new CdmUsageTable if the global instance has not // been initialized. // Note: This function will lock the global static field lock in write mode. bool SetUpUsageTable(RequestedSecurityLevel requested_security_level); size_t GetMaxSubsampleRegionSize(); bool SetDestinationBufferType(); CdmResponseType SelectKey(const std::string& key_id, CdmCipherMode cipher_mode); // Retrieves the OEMCrypto usage table support for the specified // |requested_security_level|. // Caller should acquire the OEMCrypto read lock before calling. bool HasUsageTableSupportInternal( RequestedSecurityLevel requested_security_level, bool* has_support); // These methods fall back into each other in the order given, depending on // how much data they were given and how much data OEMCrypto can accept in one // call. OEMCryptoResult DecryptMultipleSamples( const std::vector& samples, CdmCipherMode cipher_mode, const OEMCrypto_CENCEncryptPatternDesc& pattern, bool is_any_subsample_protected); OEMCryptoResult DecryptSample(const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode, const OEMCrypto_CENCEncryptPatternDesc& pattern, bool is_any_subsample_protected); OEMCryptoResult LegacyDecrypt(const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode, const OEMCrypto_CENCEncryptPatternDesc& pattern, bool is_any_subsample_protected); OEMCryptoResult LegacyCopyBufferInChunks( const OEMCrypto_SampleDescription& sample, size_t max_chunk_size); OEMCryptoResult LegacyDecryptInChunks( const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode, const OEMCrypto_CENCEncryptPatternDesc& pattern, size_t max_chunk_size, bool is_any_subsample_protected); // These methods should be used to take the various CryptoSession mutexes in // preference to taking the mutexes directly. // // A lock should be taken on the Static Field Mutex before accessing // any of CryptoSession's non-atomic static fields with the exception // of the Usage Table Mutex. The Static Field Mutex can be taken as // a reader or as a writer, depending on how you will be accessing // the static fields. The Usage Table Mutex should be taken when // reading and writing to the static usage table fields (creating, // destroying or taking a pointer of the handles). The purpose of // having a separate mutex for usage table is due to the recursive // nature of initializing the global usage table. // // Before calling into OEMCrypto, code must take locks on the OEMCrypto Mutex // and/or the OEMCrypto Session Mutex. Which of them should be taken and how // depends on the OEMCrypto function being called; consult the OEMCrypto // specification's threading guarantees before making any calls. The OEMCrypto // specification defines several classes of functions for the purposes of // parallelism. The methods below lock the OEMCrypto Mutex and OEMCrypto // Session Mutex in the correct order and manner to fulfill the guarantees in // the specification. // // For this function class... | ...use this locking method // ------------------------------+--------------------------- // Initialization & Termination | WithOecWriteLock() // Property | WithOecReadLock() // Session Initialization | WithOecWriteLock() // Usage Table Header & Entries | WithOecWriteLock() // Session | WithOecSessionLock() // // Note that accessing |key_session_| often accesses the OEMCrypto session, so // WithOecSessionLock() should be used before accessing |key_session_| as // well. // // If a function needs to take a lock on both the Static Field Mutex and some // of the OEMCrypto mutexes simultaneously, it must *always* lock the Static // Field Mutex before the OEMCrypto mutexes. // // In general, all locks should only be held for the minimum time necessary // (e.g. a lock on the OEMCrypto mutexes should only be held for the duration // of a single call into OEMCrypto) unless there is a compelling argument // otherwise, such as making two calls into OEMCrypto immediately after each // other. template static auto WithStaticFieldWriteLock(const char* tag, Func body) -> decltype(body()); template static auto WithStaticFieldReadLock(const char* tag, Func body) -> decltype(body()); template static auto WithOecWriteLock(const char* tag, Func body) -> decltype(body()); template static auto WithOecReadLock(const char* tag, Func body) -> decltype(body()); template auto WithOecSessionLock(const char* tag, Func body) -> decltype(body()); static bool IsInitialized(); // The locking methods above should be used in preference to taking these // mutexes directly. If code takes these manually and needs to take more // than one, it must *always* take them in the order they are defined here. static wvutil::shared_mutex static_field_mutex_; static wvutil::shared_mutex oem_crypto_mutex_; std::mutex oem_crypto_session_mutex_; // Usage table mutex used only when performing write operations on // the static usage table pointers. static std::recursive_mutex usage_table_mutex_; static bool initialized_; static int session_count_; static int termination_counter_; static bool needs_keybox_provisioning_; enum CachedBooleanProperty { // Property has not yet been checked/cached. kBooleanUnset, // Property has been cached as false. kBooleanFalse, // Property has been cached as true. kBooleanTrue }; metrics::CryptoMetrics* metrics_; metrics::Timer life_span_; uint32_t system_id_; bool open_; CdmClientTokenType pre_provision_token_type_; std::string oem_token_; // Cached OEMCrypto Public Key bool update_usage_table_after_close_session_; CryptoSessionId oec_session_id_; std::unique_ptr key_session_; OEMCryptoBufferType destination_buffer_type_; bool is_destination_buffer_type_valid_; RequestedSecurityLevel requested_security_level_; // Open session-cached result of OEMCrypto_SupportsUsageTable(). CachedBooleanProperty has_usage_table_support_ = kBooleanUnset; CdmUsageTable* usage_table_ = nullptr; // These fields are protected by |usage_table_mutex_| and not // |static_field_mutex_|. static std::unique_ptr usage_table_l1_; static std::unique_ptr usage_table_l3_; std::string request_id_; static std::atomic request_id_index_source_; uint32_t api_version_; size_t max_subsample_region_size_; // Stores the most recent error code returned from a call to // OEMCrypto_DecryptCENC. This is used to reduce the total number of // error logs for decrypt calls, as there could be a large number of // same error code in sequence of each other. A value of // OEMCrypto_SUCCESS indicates that no error have yet occurred. OEMCryptoResult last_decrypt_error_ = OEMCrypto_SUCCESS; // Similar to |last_decrypt_error_|, but intended for calls to // SelectKey(). OEMCryptoResult last_select_key_error_ = OEMCrypto_SUCCESS; // In order to avoid creating a deadlock if instantiation needs to take any // of the CryptoSession static mutexes, |factory_| is protected by its own // mutex that is only used in the two functions that interact with it. static std::mutex factory_mutex_; static std::unique_ptr factory_; // A singleton instance of SystemFallbackPolicy. Only one will // be created for the system if OTA keybox provisioning is both // required and supported by L1. static std::unique_ptr okp_fallback_policy_l1_; }; // class CryptoSession class CryptoSessionFactory { public: WVCDM_DISALLOW_COPY_AND_MOVE(CryptoSessionFactory); virtual ~CryptoSessionFactory() {} virtual CryptoSession* MakeCryptoSession( metrics::CryptoMetrics* crypto_metrics); protected: friend class CryptoSession; CryptoSessionFactory() = default; }; // class CryptoSessionFactory } // namespace wvcdm #endif // WVCDM_CORE_CRYPTO_SESSION_H_