diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index 73dbfc1c..9ebd5972 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -32,8 +32,10 @@ LOCAL_SRC_FILES := \ $(CORE_SRC_DIR)/cdm_session_map.cpp \ $(CORE_SRC_DIR)/certificate_provisioning.cpp \ $(CORE_SRC_DIR)/client_identification.cpp \ + $(CORE_SRC_DIR)/content_key_session.cpp \ $(CORE_SRC_DIR)/crypto_session.cpp \ $(CORE_SRC_DIR)/device_files.cpp \ + $(CORE_SRC_DIR)/entitlement_key_session.cpp \ $(CORE_SRC_DIR)/initialization_data.cpp \ $(CORE_SRC_DIR)/license.cpp \ $(CORE_SRC_DIR)/license_key_status.cpp \ @@ -41,6 +43,7 @@ LOCAL_SRC_FILES := \ $(CORE_SRC_DIR)/policy_engine.cpp \ $(CORE_SRC_DIR)/privacy_crypto_boringssl.cpp \ $(CORE_SRC_DIR)/service_certificate.cpp \ + $(CORE_SRC_DIR)/sublicense_key_session.cpp \ $(CORE_SRC_DIR)/usage_table_header.cpp \ $(SRC_DIR)/wv_content_decryption_module.cpp \ $(METRICS_SRC_DIR)/counter_metric.cpp \ diff --git a/libwvdrmengine/cdm/core/include/content_key_session.h b/libwvdrmengine/cdm/core/include/content_key_session.h new file mode 100644 index 00000000..9d51f2c4 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/content_key_session.h @@ -0,0 +1,63 @@ +#ifndef WVCDM_CORE_CONTENT_KEY_SESSSION_H_ +#define WVCDM_CORE_CONTENT_KEY_SESSSION_H_ + +#include "key_session.h" +#include "timer_metric.h" + +namespace wvcdm { + +class ContentKeySession : public KeySession { + public: + ContentKeySession(CryptoSessionId oec_session_id, + metrics::CryptoMetrics* metrics) + : KeySession(metrics), oec_session_id_(oec_session_id) {} + virtual ~ContentKeySession() {} + + KeySessionType Type() { return kDefault; } + + // Generate Derived Keys for ContentKeySession + bool GenerateDerivedKeys(const std::string& message); + + // Generate Derived Keys (from session key) for ContentKeySession + bool GenerateDerivedKeys(const std::string& message, + const std::string& session_key); + + // Load Keys for ContentKeySession + OEMCryptoResult LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, + CdmCipherMode* cipher_mode, + const std::string& srm_requirement); + + OEMCryptoResult LoadEntitledContentKeys(const std::vector& keys) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Select Key for ContentKeySession + OEMCryptoResult SelectKey(const std::string& key_id, + CdmCipherMode cipher_mode); + + // Decrypt for ContentKeySession + OEMCryptoResult Decrypt(const CdmDecryptionParameters& params, + OEMCrypto_DestBufferDesc& buffer_descriptor, + OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor); + + protected: + OEMCryptoResult LoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, CdmCipherMode* cipher_mode, + const std::string& srm_requirement, OEMCrypto_LicenseType license_type); + CryptoSessionId oec_session_id_; + + private: + KeyId cached_key_id_; +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_CONTENT_KEY_SESSSION_H_ \ No newline at end of file diff --git a/libwvdrmengine/cdm/core/include/crypto_key.h b/libwvdrmengine/cdm/core/include/crypto_key.h index 0da57d56..8f08e751 100644 --- a/libwvdrmengine/cdm/core/include/crypto_key.h +++ b/libwvdrmengine/cdm/core/include/crypto_key.h @@ -19,6 +19,7 @@ class CryptoKey { const std::string& key_control_iv() const { return key_control_iv_; } const std::string& sub_session_key_id() const {return sub_session_key_id_;} const std::string& sub_session_key() const {return sub_session_key_;} + const std::string& entitlement_key_id() const {return entitlement_key_id_;} const std::string& track_label() const { return track_label_; } CdmCipherMode cipher_mode() const { return cipher_mode_; } void set_key_id(const std::string& key_id) { key_id_ = key_id; } @@ -40,6 +41,9 @@ class CryptoKey { void set_track_label(const std::string& track_label) { track_label_ = track_label; } + void set_entitlement_key_id(const std::string& entitlement_key_id) { + entitlement_key_id_ = entitlement_key_id; + } bool HasKeyControl() const { return key_control_.size() >= 16; } @@ -52,6 +56,7 @@ class CryptoKey { std::string sub_session_key_id_; std::string track_label_; std::string sub_session_key_; + std::string entitlement_key_id_; CdmCipherMode cipher_mode_; }; diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 82619b16..35f169a3 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -8,10 +8,10 @@ #include #include "OEMCryptoCENC.h" +#include "key_session.h" #include "lock.h" #include "metrics_collections.h" #include "oemcrypto_adapter.h" -#include "OEMCryptoCENC.h" #include "scoped_ptr.h" #include "timer_metric.h" #include "wv_cdm_types.h" @@ -22,37 +22,13 @@ class CryptoKey; class UsageTableHeader; typedef std::map CryptoKeyMap; -typedef std::map SubLicenseSessionMap; -class KeySession { - protected: - KeySession(metrics::CryptoMetrics* metrics) : metrics_(metrics) {} - - public: - typedef enum { kDefault, kSubLicense } KeySessionType; - virtual ~KeySession() {} - virtual KeySessionType Type() = 0; - virtual bool GenerateDerivedKeys(const std::string& message) = 0; - virtual bool GenerateDerivedKeys(const std::string& message, - const std::string& session_key) = 0; - virtual OEMCryptoResult LoadKeys(const std::string& message, - const std::string& signature, - const std::string& mac_key_iv, - const std::string& mac_key, - const std::vector& keys, - const std::string& provider_session_token, - CdmCipherMode* cipher_mode, - const std::string& srm_requirement) = 0; - virtual OEMCryptoResult SelectKey(const std::string& key_id, - CdmCipherMode cipher_mode) = 0; - virtual OEMCryptoResult Decrypt( - const CdmDecryptionParameters& params, - OEMCrypto_DestBufferDesc& buffer_descriptor, - OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) = 0; - - protected: - metrics::CryptoMetrics* metrics_; -}; +// 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); class CryptoSession { public: @@ -100,13 +76,15 @@ class CryptoSession { bool is_provisioning, std::string* signature); virtual bool PrepareRenewalRequest(const std::string& message, std::string* signature); - virtual CdmResponseType LoadKeys(const std::string& message, - const std::string& signature, - const std::string& mac_key_iv, - const std::string& mac_key, - const std::vector& key_array, - const std::string& provider_session_token, - const std::string& srm_requirement); + virtual CdmResponseType LoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& key_array, + const std::string& provider_session_token, + const std::string& srm_requirement, + CdmLicenseKeyType key_type); + virtual CdmResponseType LoadEntitledContentKeys( + const std::vector& key_array); virtual bool LoadCertificatePrivateKey(std::string& wrapped_key); virtual bool RefreshKeys(const std::string& message, const std::string& signature, int num_keys, @@ -192,19 +170,17 @@ class CryptoSession { virtual CdmResponseType LoadUsageEntry(uint32_t entry_number, const CdmUsageEntry& usage_entry); virtual CdmResponseType UpdateUsageEntry( - CdmUsageTableHeader* usage_table_header, - CdmUsageEntry* usage_entry); + CdmUsageTableHeader* usage_table_header, CdmUsageEntry* usage_entry); virtual CdmResponseType ShrinkUsageTableHeader( uint32_t new_entry_count, CdmUsageTableHeader* usage_table_header); virtual CdmResponseType MoveUsageEntry(uint32_t new_entry_number); - virtual bool CreateOldUsageEntry( - uint64_t time_since_license_received, - uint64_t time_since_first_decrypt, - uint64_t time_since_last_decrypt, - UsageDurationStatus status, - const std::string& server_mac_key, - const std::string& client_mac_key, - const std::string& provider_session_token); + virtual bool CreateOldUsageEntry(uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + UsageDurationStatus status, + const std::string& server_mac_key, + const std::string& client_mac_key, + const std::string& provider_session_token); virtual CdmResponseType CopyOldUsageEntry( const std::string& provider_session_token); virtual bool GetAnalogOutputCapabilities(bool* can_support_output, @@ -235,15 +211,19 @@ class CryptoSession { bool SetDestinationBufferType(); - bool RewrapDeviceRSAKey( - const std::string& message, const std::string& signature, - const std::string& nonce, const std::string& enc_rsa_key, - const std::string& rsa_key_iv, std::string* wrapped_rsa_key); + bool RewrapDeviceRSAKey(const std::string& message, + const std::string& signature, + const std::string& nonce, + const std::string& enc_rsa_key, + const std::string& rsa_key_iv, + std::string* wrapped_rsa_key); - bool RewrapDeviceRSAKey30( - const std::string& message, const std::string& nonce, - const std::string& private_key, const std::string& iv, - const std::string& wrapping_key, std::string* wrapped_private_key); + bool RewrapDeviceRSAKey30(const std::string& message, + const std::string& nonce, + const std::string& private_key, + const std::string& iv, + const std::string& wrapping_key, + std::string* wrapped_private_key); CdmResponseType SelectKey(const std::string& key_id, CdmCipherMode cipher_mode); diff --git a/libwvdrmengine/cdm/core/include/entitlement_key_session.h b/libwvdrmengine/cdm/core/include/entitlement_key_session.h new file mode 100644 index 00000000..7cdad259 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/entitlement_key_session.h @@ -0,0 +1,34 @@ +#ifndef WVCDM_CORE_ENTITLEMENT_KEY_SESSSION_H_ +#define WVCDM_CORE_ENTITLEMENT_KEY_SESSSION_H_ + +#include "content_key_session.h" +#include "key_session.h" + +namespace wvcdm { + +class EntitlementKeySession : public ContentKeySession { + public: + EntitlementKeySession(CryptoSessionId oec_session_id, + metrics::CryptoMetrics* metrics); + virtual ~EntitlementKeySession() {} + + KeySessionType Type() { return kEntitlement; } + + // Load Keys for ContentKeySession + OEMCryptoResult LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, + CdmCipherMode* cipher_mode, + const std::string& srm_requirement); + OEMCryptoResult LoadEntitledContentKeys(const std::vector& keys); + + private: + std::vector keys_; +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_ENTITLEMENT_KEY_SESSSION_H_ \ No newline at end of file diff --git a/libwvdrmengine/cdm/core/include/initialization_data.h b/libwvdrmengine/cdm/core/include/initialization_data.h index 53e8402f..ce26ea14 100644 --- a/libwvdrmengine/cdm/core/include/initialization_data.h +++ b/libwvdrmengine/cdm/core/include/initialization_data.h @@ -28,8 +28,10 @@ class InitializationData { const CdmInitData& data() const { return data_; } std::vector hls_iv() const { return hls_iv_; } CdmHlsMethod hls_method() const { return hls_method_; } - std::vector ExtractEmbeddedKeys() const; - const std::string ExtractGroupMasterKeyId() const; + // TODO(jfore): Perhaps this should be a generic structure with the ids for + // any type of licensing? + std::vector ExtractSublicenseKeys() const; + std::vector ExtractWrappedKeys() const; private: // Parse a blob of multiple concatenated PSSH atoms to extract the first diff --git a/libwvdrmengine/cdm/core/include/key_session.h b/libwvdrmengine/cdm/core/include/key_session.h new file mode 100644 index 00000000..d8c3c737 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/key_session.h @@ -0,0 +1,46 @@ +#ifndef WVCDM_CORE_KEY_SESSSION_H_ +#define WVCDM_CORE_KEY_SESSSION_H_ + +#include "metrics_collections.h" + +namespace wvcdm { + +class CryptoKey; + +class KeySession { + protected: + KeySession(metrics::CryptoMetrics* metrics) : metrics_(metrics) {} + + public: + typedef enum { kDefault, kSubLicense, kEntitlement } KeySessionType; + virtual ~KeySession() {} + virtual KeySessionType Type() = 0; + virtual bool GenerateDerivedKeys(const std::string& message) = 0; + virtual bool GenerateDerivedKeys(const std::string& message, + const std::string& session_key) = 0; + virtual OEMCryptoResult LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, + CdmCipherMode* cipher_mode, + const std::string& srm_requirement) = 0; + virtual OEMCryptoResult LoadEntitledContentKeys( + const std::vector& keys) = 0; + virtual OEMCryptoResult SelectKey(const std::string& key_id, + CdmCipherMode cipher_mode) = 0; + virtual OEMCryptoResult Decrypt( + const CdmDecryptionParameters& params, + OEMCrypto_DestBufferDesc& buffer_descriptor, + OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) = 0; + + protected: + metrics::CryptoMetrics* metrics_; +}; + +typedef std::map SubLicenseSessionMap; + +} // namespace wvcdm + +#endif // WVCDM_CORE_KEY_SESSSION_H_ \ No newline at end of file diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index dc02859d..ad86c0f7 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -29,11 +29,11 @@ class CdmLicense { CdmLicense(const CdmSessionId& session_id); virtual ~CdmLicense(); - virtual bool Init( - const std::string& client_token, CdmClientTokenType client_token_type, - const std::string& device_id, bool use_privacy_mode, - const std::string& signed_service_certificate, CryptoSession* session, - PolicyEngine* policy_engine); + virtual bool Init(const std::string& client_token, + CdmClientTokenType client_token_type, + const std::string& device_id, bool use_privacy_mode, + const std::string& signed_service_certificate, + CryptoSession* session, PolicyEngine* policy_engine); virtual CdmResponseType PrepareKeyRequest( const InitializationData& init_data, CdmLicenseType license_type, @@ -64,16 +64,13 @@ class CdmLicense { return provider_session_token_; } - virtual bool is_offline() { - return is_offline_; - } + virtual bool is_offline() { return is_offline_; } static bool ExtractProviderSessionToken( const CdmKeyResponse& license_response, std::string* provider_session_token); private: - CdmResponseType HandleKeyErrorResponse( const video_widevine::SignedMessage& signed_message); @@ -86,9 +83,24 @@ class CdmLicense { const std::string& request_id, video_widevine::LicenseRequest* license_request); + CdmResponseType HandleContentKeyResponse( + const std::string& msg, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& key_array, + const video_widevine::License& license); + + // HandleEntitlementKeyResponse loads the entitlement keys in |key_array| into + // the crypto session. In addition, it also extracts content keys from + // |wrapped_keys_| and loads them for use. + CdmResponseType HandleEntitlementKeyResponse( + const std::string& msg, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& key_array, + const video_widevine::License& license); + template - bool SetTypeAndId(CdmLicenseType license_type, - const std::string& request_id, T* content_id); + bool SetTypeAndId(CdmLicenseType license_type, const std::string& request_id, + T* content_id); CryptoSession* crypto_session_; PolicyEngine* policy_engine_; @@ -121,6 +133,12 @@ class CdmLicense { // sub session keys we may have received in a license response. These keys // may be used to support key rotation. std::vector sub_session_key_array_; + + // For entitlement key licensing. This holds the keys from the init_data. + // These keys are extracted from the pssh when we generate a license request. + // It is used to load content keys after we have received a license and + // entitelement keys. It is also used in updating the key status info. + std::vector wrapped_keys_; #if defined(UNIT_TEST) friend class CdmLicenseTest; #endif diff --git a/libwvdrmengine/cdm/core/include/sublicense_key_session.h b/libwvdrmengine/cdm/core/include/sublicense_key_session.h new file mode 100644 index 00000000..1367882f --- /dev/null +++ b/libwvdrmengine/cdm/core/include/sublicense_key_session.h @@ -0,0 +1,95 @@ +#ifndef WVCDM_CORE_SUBLICENSE_KEY_SESSSION_H_ +#define WVCDM_CORE_SUBLICENSE_KEY_SESSSION_H_ + +#include "crypto_key.h" +#include "key_session.h" + +namespace wvcdm { + +class SubLicenseKeySession : public KeySession { + typedef enum { + kInitializing, + kInitialLicenseLoaded, + kInitialLicenseFailed, + } SubLicenseState; + + public: + SubLicenseKeySession(SubLicenseSessionMap& sub_license_oec_sessions, + metrics::CryptoMetrics* metrics, + const std::string& wrapped_private_device_key, + SecurityLevel requested_security_level, + const std::string& group_id); + + virtual ~SubLicenseKeySession(); + + KeySessionType Type() { return kSubLicense; } + + // This version of GenerateDerivedKeys is for devices using keyboxes. It is + // not supported using sub licenses. + bool GenerateDerivedKeys(const std::string&) { return false; } + + // GenerateDerivedKeys is called for each open oemcrypto session and is only + // called once. + bool GenerateDerivedKeys(const std::string& message, + const std::string& session_key); + + // Load the keys in |keys|. The initial keys are saved for key rotation. + OEMCryptoResult LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, + CdmCipherMode* cipher_mode, + const std::string& srm_requirement); + + OEMCryptoResult LoadEntitledContentKeys(const std::vector& keys) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Each oemcrypto session contains a single key. Find the right sub session + // and save it's id as the selected oemcrypto session. + OEMCryptoResult SelectKey(const std::string& key_id, + CdmCipherMode cipher_mode); + + // Decrypt performs the decryption using the selected oemcrypto session. + // TODO(jfore): Support DecryptInChunks. + OEMCryptoResult Decrypt(const CdmDecryptionParameters& params, + OEMCrypto_DestBufferDesc& buffer_descriptor, + OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor); + + private: + // Destroy each open oemcrypto session and relace them with new ones. + OEMCryptoResult ResetCryptoSessions(); + + // DoLoadKeys loads a single key into each oemcrypto session. + OEMCryptoResult DoLoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, + CdmCipherMode* cipher_mode, + const std::string& srm_requirement); + + // DoLoadKeys loads a single key into each oemcrypto session. + OEMCryptoResult DoSubLicenseLoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const CryptoKey& key, const std::string& provider_session_token, + CdmCipherMode*, const std::string& srm_requirement); + + SubLicenseState state_; + std::string cached_sub_session_key_id_; + std::string wrapped_private_device_key_; + std::string message_; + std::string session_key_; + std::vector keys_; + SubLicenseSessionMap& sub_license_oec_sessions_; + SecurityLevel requested_security_level_; + KeyId group_id_; +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_SUBLICENSE_KEY_SESSSION_H_ \ No newline at end of file diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index a313b279..1ebce62c 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -345,6 +345,11 @@ enum CdmLicenseType { kLicenseTypeSubSession }; +enum CdmLicenseKeyType { + kLicenseKeyTypeContent, + kLicenseKeyTypeEntitlement +}; + enum SecurityLevel { kLevelDefault, kLevel3 diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 15b25bee..7a8813e1 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -382,11 +382,11 @@ CdmResponseType CdmSession::GenerateKeyRequest( } std::vector embedded_key_data = - init_data.ExtractEmbeddedKeys(); + init_data.ExtractSublicenseKeys(); for (size_t i = 0; i < embedded_key_data.size(); ++i) { CdmResponseType sts = crypto_session_->AddSubSession( embedded_key_data[i].sub_session_key_id(), - init_data.ExtractGroupMasterKeyId()); + embedded_key_data[i].group_id()); if (NO_ERROR != sts) { LOGE("CdmSession::GenerateKeyRequest: Unable to generate sub session"); return sts; diff --git a/libwvdrmengine/cdm/core/src/content_key_session.cpp b/libwvdrmengine/cdm/core/src/content_key_session.cpp new file mode 100644 index 00000000..5075fd9c --- /dev/null +++ b/libwvdrmengine/cdm/core/src/content_key_session.cpp @@ -0,0 +1,174 @@ +#include "content_key_session.h" +#include "crypto_key.h" +#include "crypto_session.h" +#include "log.h" +#include "wv_cdm_constants.h" + +namespace wvcdm { + +// Generate Derived Keys for ContentKeySession +bool ContentKeySession::GenerateDerivedKeys(const std::string& message) { + std::string mac_deriv_message; + std::string enc_deriv_message; + GenerateMacContext(message, &mac_deriv_message); + GenerateEncryptContext(message, &enc_deriv_message); + + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); + OEMCryptoResult sts; + M_TIME(sts = OEMCrypto_GenerateDerivedKeys( + oec_session_id_, + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, oemcrypto_generate_derived_keys_, sts); + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts); + return false; + } + + return true; +} + +// Generate Derived Keys (from session key) for ContentKeySession +bool ContentKeySession::GenerateDerivedKeys(const std::string& message, + const std::string& session_key) { + std::string mac_deriv_message; + std::string enc_deriv_message; + GenerateMacContext(message, &mac_deriv_message); + GenerateEncryptContext(message, &enc_deriv_message); + + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DeriveKeysFromSessionKey( + oec_session_id_, reinterpret_cast(session_key.data()), + session_key.size(), + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, oemcrypto_derive_keys_from_session_key_, sts); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts); + return false; + } + + return true; +} + +// Load Keys for ContentKeySession +OEMCryptoResult ContentKeySession::LoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { + return LoadKeys(message, signature, mac_key_iv, mac_key, keys, + provider_session_token, cipher_mode, srm_requirement, + OEMCrypto_ContentLicense); +} + +// Select Key for ContentKeySession +OEMCryptoResult ContentKeySession::SelectKey(const std::string& key_id, + CdmCipherMode cipher_mode) { + // Crypto session lock already locked. + if (!cached_key_id_.empty() && cached_key_id_ == key_id) { + // Already using the desired key. + return OEMCrypto_SUCCESS; + } + + cached_key_id_ = key_id; + + const uint8_t* key_id_string = + reinterpret_cast(cached_key_id_.data()); + + OEMCryptoResult sts; + M_TIME(sts = OEMCrypto_SelectKey( + oec_session_id_, key_id_string, cached_key_id_.size(), + static_cast(cipher_mode)), + metrics_, oemcrypto_select_key_, sts); + + if (OEMCrypto_SUCCESS != sts) { + cached_key_id_.clear(); + } + return sts; +} + +// Decrypt for ContentKeySession +OEMCryptoResult ContentKeySession::Decrypt( + const CdmDecryptionParameters& params, + OEMCrypto_DestBufferDesc& buffer_descriptor, + OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) { + OEMCryptoResult sts; + M_TIME(sts = OEMCrypto_DecryptCENC( + oec_session_id_, params.encrypt_buffer, params.encrypt_length, + params.is_encrypted, &(*params.iv).front(), params.block_offset, + &buffer_descriptor, &pattern_descriptor, params.subsample_flags), + metrics_, oemcrypto_decrypt_cenc_, sts, + metrics::Pow2Bucket(params.encrypt_length)); + return sts; +} + +OEMCryptoResult ContentKeySession::LoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, CdmCipherMode* cipher_mode, + const std::string& srm_requirement, OEMCrypto_LicenseType license_type) { + const uint8_t* msg = reinterpret_cast(message.data()); + const uint8_t* enc_mac_key = NULL; + const uint8_t* enc_mac_key_iv = NULL; + if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { + enc_mac_key = msg + GetOffset(message, mac_key); + enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); + } else { + LOGV("ContentKeySession::LoadKeys: enc_mac_key not set"); + } + std::vector load_keys(keys.size()); + for (size_t i = 0; i < keys.size(); ++i) { + const CryptoKey* ki = &keys[i]; + OEMCrypto_KeyObject* ko = &load_keys[i]; + ko->key_id = msg + GetOffset(message, ki->key_id()); + ko->key_id_length = ki->key_id().length(); + ko->key_data_iv = msg + GetOffset(message, ki->key_data_iv()); + ko->key_data = msg + GetOffset(message, ki->key_data()); + ko->key_data_length = ki->key_data().length(); + if (ki->HasKeyControl()) { + ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv()); + ko->key_control = msg + GetOffset(message, ki->key_control()); + } else { + LOGE("For key %d: XXX key has no control block. size=%d", i, + ki->key_control().size()); + ko->key_control_iv = NULL; + ko->key_control = NULL; + } + // TODO(jfore): Is returning the cipher needed. If not drop this. + *cipher_mode = ki->cipher_mode(); + } + + uint8_t* pst = NULL; + if (!provider_session_token.empty()) { + pst = + const_cast(msg) + GetOffset(message, provider_session_token); + } + + uint8_t* srm_req = NULL; + if (!srm_requirement.empty()) { + srm_req = const_cast(msg) + GetOffset(message, srm_requirement); + } + + LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_LoadKeys( + oec_session_id_, msg, message.size(), + reinterpret_cast(signature.data()), signature.size(), + enc_mac_key_iv, enc_mac_key, keys.size(), &load_keys[0], pst, + provider_session_token.length(), srm_req, license_type), + metrics_, oemcrypto_load_keys_, sts); + return sts; +} + +} // namespace wvcdm \ No newline at end of file diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 942e201f..acda18e3 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -11,7 +11,9 @@ #include #include +#include "content_key_session.h" #include "crypto_key.h" +#include "entitlement_key_session.h" #include "log.h" #include "openssl/asn1.h" #include "openssl/sha.h" @@ -19,6 +21,7 @@ #include "properties.h" #include "pst_report.h" #include "string_conversions.h" +#include "sublicense_key_session.h" #include "usage_table_header.h" #include "wv_cdm_constants.h" @@ -34,6 +37,46 @@ std::string EncodeUint32(unsigned int u) { return s; } +const uint32_t kRsaSignatureLength = 256; +const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB +const size_t kEstimatedInitialUsageTableHeader = 40; +const size_t kOemCryptoApiVersionSupportsBigUsageTables = 13; + +// Constants and utility objects relating to OEM Certificates +const int kExtensionOidSize = 64; +const char* const kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1"; + +// Helpers for working with BoringSSL +template +class boringssl_ptr { + public: + explicit boringssl_ptr(T* p = NULL) : ptr_(p) {} + ~boringssl_ptr() { + if (ptr_) func(ptr_); + } + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + private: + T* ptr_; + CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr); +}; + +void DeleteX509Stack(STACK_OF(X509)* stack) { + sk_X509_pop_free(stack, X509_free); +} + +} + +namespace wvcdm { +Lock CryptoSession::crypto_lock_; +bool CryptoSession::initialized_ = false; +int CryptoSession::session_count_ = 0; +uint64_t CryptoSession::request_id_index_ = 0; +UsageTableHeader* CryptoSession::usage_table_header_l1_ = NULL; +UsageTableHeader* CryptoSession::usage_table_header_l3_ = NULL; + size_t GetOffset(std::string message, std::string field) { size_t pos = message.find(field); if (pos == std::string::npos) { @@ -77,607 +120,12 @@ void GenerateEncryptContext(const std::string& input_context, deriv_context->append(EncodeUint32(kEncryptionKeySizeBits)); } -OEMCryptoCipherMode ToOEMCryptoCipherMode(wvcdm::CdmCipherMode cipher_mode) { - return (cipher_mode == wvcdm::kCipherModeCtr) ? OEMCrypto_CipherMode_CTR - : OEMCrypto_CipherMode_CBC; +OEMCrypto_LicenseType OEMCryptoLicenseType(CdmLicenseKeyType cdm_license_type) { + return cdm_license_type == kLicenseKeyTypeContent + ? OEMCrypto_ContentLicense + : OEMCrypto_EntitlementLicense; } -const uint32_t kRsaSignatureLength = 256; -const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB -const size_t kEstimatedInitialUsageTableHeader = 40; -const size_t kOemCryptoApiVersionSupportsBigUsageTables = 13; - -// Constants and utility objects relating to OEM Certificates -const int kExtensionOidSize = 64; -const char* const kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1"; - -// Helpers for working with BoringSSL -template -class boringssl_ptr { - public: - explicit boringssl_ptr(T* p = NULL) : ptr_(p) {} - ~boringssl_ptr() { - if (ptr_) func(ptr_); - } - T& operator*() const { return *ptr_; } - T* operator->() const { return ptr_; } - T* get() const { return ptr_; } - - private: - T* ptr_; - CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr); -}; - -void DeleteX509Stack(STACK_OF(X509)* stack) { - sk_X509_pop_free(stack, X509_free); -} - -} - -namespace wvcdm { - -Lock CryptoSession::crypto_lock_; -bool CryptoSession::initialized_ = false; -int CryptoSession::session_count_ = 0; -uint64_t CryptoSession::request_id_index_ = 0; -UsageTableHeader* CryptoSession::usage_table_header_l1_ = NULL; -UsageTableHeader* CryptoSession::usage_table_header_l3_ = NULL; - -// TODO(jfore, rfrias, gmorgan): This object should be split off into it's own -// module or refactored out. -class DefaultKeySession : public KeySession { - public: - DefaultKeySession(CryptoSessionId oec_session_id, - metrics::CryptoMetrics* metrics) - : KeySession(metrics), oec_session_id_(oec_session_id) {} - virtual ~DefaultKeySession() {} - - KeySessionType Type() { return kDefault; } - - // Generate Derived Keys for DefaultKeySession - bool GenerateDerivedKeys(const std::string& message) { - std::string mac_deriv_message; - std::string enc_deriv_message; - GenerateMacContext(message, &mac_deriv_message); - GenerateEncryptContext(message, &enc_deriv_message); - - LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); - OEMCryptoResult sts; - M_TIME(sts = OEMCrypto_GenerateDerivedKeys( - oec_session_id_, - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()), - metrics_, oemcrypto_generate_derived_keys_, sts); - if (OEMCrypto_SUCCESS != sts) { - LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts); - return false; - } - - return true; - } - - // Generate Derived Keys (from session key) for DefaultKeySession - bool GenerateDerivedKeys(const std::string& message, - const std::string& session_key) { - std::string mac_deriv_message; - std::string enc_deriv_message; - GenerateMacContext(message, &mac_deriv_message); - GenerateEncryptContext(message, &enc_deriv_message); - - LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); - OEMCryptoResult sts; - M_TIME(sts = OEMCrypto_DeriveKeysFromSessionKey( - oec_session_id_, - reinterpret_cast(session_key.data()), - session_key.size(), - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()), - metrics_, oemcrypto_derive_keys_from_session_key_, sts); - - if (OEMCrypto_SUCCESS != sts) { - LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", - sts); - return false; - } - - return true; - } - - // Load Keys for DefaultKeySession - OEMCryptoResult LoadKeys(const std::string& message, - const std::string& signature, - const std::string& mac_key_iv, - const std::string& mac_key, - const std::vector& keys, - const std::string& provider_session_token, - CdmCipherMode* cipher_mode, - const std::string& srm_requirement) { - const uint8_t* msg = reinterpret_cast(message.data()); - const uint8_t* enc_mac_key = NULL; - const uint8_t* enc_mac_key_iv = NULL; - if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { - enc_mac_key = msg + GetOffset(message, mac_key); - enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); - } else { - LOGV("DefaultKeySession::LoadKeys: enc_mac_key not set"); - } - std::vector load_keys(keys.size()); - for (size_t i = 0; i < keys.size(); ++i) { - const CryptoKey* ki = &keys[i]; - OEMCrypto_KeyObject_V13* ko = &load_keys[i]; - ko->key_id = msg + GetOffset(message, ki->key_id()); - ko->key_id_length = ki->key_id().length(); - ko->key_data_iv = msg + GetOffset(message, ki->key_data_iv()); - ko->key_data = msg + GetOffset(message, ki->key_data()); - ko->key_data_length = ki->key_data().length(); - if (ki->HasKeyControl()) { - ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv()); - ko->key_control = msg + GetOffset(message, ki->key_control()); - } else { - LOGE("For key %d: XXX key has no control block. size=%d", i, - ki->key_control().size()); - ko->key_control_iv = NULL; - ko->key_control = NULL; - } - *cipher_mode = ki->cipher_mode(); - } - - uint8_t* pst = NULL; - if (!provider_session_token.empty()) { - pst = const_cast(msg) + - GetOffset(message, provider_session_token); - } - - uint8_t* srm_req = NULL; - if (!srm_requirement.empty()) { - srm_req = const_cast(msg) + GetOffset(message, srm_requirement); - } - - LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_); - OEMCryptoResult sts; - M_TIME(sts = OEMCrypto_LoadKeys_Back_Compat( - oec_session_id_, msg, message.size(), - reinterpret_cast(signature.data()), - signature.size(), enc_mac_key_iv, enc_mac_key, keys.size(), - &load_keys[0], pst, provider_session_token.length(), srm_req, - OEMCrypto_ContentLicense), - metrics_, oemcrypto_load_keys_, sts); - return sts; - } - - // Select Key for DefaultKeySession - OEMCryptoResult SelectKey(const std::string& key_id, - CdmCipherMode cipher_mode) { - // Crypto session lock already locked. - if (!cached_key_id_.empty() && cached_key_id_ == key_id) { - // Already using the desired key. - return OEMCrypto_SUCCESS; - } - - cached_key_id_ = key_id; - - const uint8_t* key_id_string = - reinterpret_cast(cached_key_id_.data()); - - OEMCryptoResult sts; - M_TIME(sts = OEMCrypto_SelectKey(oec_session_id_, key_id_string, - cached_key_id_.size(), - ToOEMCryptoCipherMode(cipher_mode)), - metrics_, oemcrypto_select_key_, sts); - - if (OEMCrypto_SUCCESS != sts) { - cached_key_id_.clear(); - } - return sts; - } - - // Decrypt for DefaultKeySession - OEMCryptoResult Decrypt( - const CdmDecryptionParameters& params, - OEMCrypto_DestBufferDesc& buffer_descriptor, - OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) { - OEMCryptoResult sts; - M_TIME(sts = OEMCrypto_DecryptCENC( - oec_session_id_, params.encrypt_buffer, params.encrypt_length, - params.is_encrypted, &(*params.iv).front(), params.block_offset, - &buffer_descriptor, &pattern_descriptor, params.subsample_flags), - metrics_, oemcrypto_decrypt_cenc_, sts, - metrics::Pow2Bucket(params.encrypt_length)); - return sts; - } - - private: - CryptoSessionId oec_session_id_; - KeyId cached_key_id_; -}; - -// TODO(jfore, rfrias, gmorgan): This object should be split off into it's own -// module or refactored out. -class SubLicenseKeySession : public KeySession { - typedef enum { - kInitializing, - kInitialLicenseLoaded, - kInitialLicenseFailed, - } SubLicenseState; - - public: - SubLicenseKeySession(SubLicenseSessionMap& sub_license_oec_sessions, - metrics::CryptoMetrics* metrics, - const std::string& wrapped_private_device_key, - SecurityLevel requested_security_level, - const std::string& group_master_key_id) - : KeySession(metrics), - state_(kInitializing), - wrapped_private_device_key_(wrapped_private_device_key), - sub_license_oec_sessions_(sub_license_oec_sessions), - requested_security_level_(requested_security_level), - group_master_key_id_(group_master_key_id) {} - - virtual ~SubLicenseKeySession() { - for (SubLicenseSessionMap::iterator oec_session = - sub_license_oec_sessions_.begin(); - oec_session != sub_license_oec_sessions_.end(); oec_session++) { - metrics_->oemcrypto_close_session_.Increment( - OEMCrypto_CloseSession(oec_session->second)); - } - sub_license_oec_sessions_.clear(); - } - - KeySessionType Type() { return kSubLicense; } - - // This version of GenerateDerivedKeys is for devices using keyboxes. It is - // not supported using sub licenses. - bool GenerateDerivedKeys(const std::string&) { return false; } - - // GenerateDerivedKeys is called for each open oemcrypto session and is only - // called once. - bool GenerateDerivedKeys(const std::string& message, - const std::string& session_key) { - std::string mac_deriv_message; - std::string enc_deriv_message; - GenerateMacContext(message, &mac_deriv_message); - GenerateEncryptContext(message, &enc_deriv_message); - - for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin(); - it != sub_license_oec_sessions_.end(); it++) { - LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second); - OEMCryptoResult sts; - M_TIME( - sts = OEMCrypto_DeriveKeysFromSessionKey( - it->second, reinterpret_cast(session_key.data()), - session_key.size(), - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()), - metrics_, oemcrypto_derive_keys_from_session_key_, sts); - - if (OEMCrypto_SUCCESS != sts) { - LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", - sts); - return false; - } - } - - return true; - } - - // Load the keys in |keys|. The initial keys are saved for key rotation. - OEMCryptoResult LoadKeys(const std::string& message, - const std::string& signature, - const std::string& mac_key_iv, - const std::string& mac_key, - const std::vector& keys, - const std::string& provider_session_token, - CdmCipherMode* cipher_mode, - const std::string& srm_requirement) { - if (state_ == kInitializing) { - state_ = kInitialLicenseLoaded; - keys_ = keys; - OEMCryptoResult sts = - DoLoadKeys(message, signature, mac_key_iv, mac_key, keys, - provider_session_token, cipher_mode, srm_requirement); - if (OEMCrypto_SUCCESS != sts) { - state_ = kInitialLicenseFailed; - } - return sts; - } - return DoSubLicenseLoadKeys(message, signature, mac_key_iv, mac_key, - keys[0], provider_session_token, cipher_mode, - srm_requirement); - } - - // Each oemcrypto session contains a single key. Find the right sub session - // and save it's id as the selected oemcrypto session. - OEMCryptoResult SelectKey(const std::string& key_id, - CdmCipherMode cipher_mode) { - for (size_t i = 0; i < keys_.size(); ++i) { - if (keys_[i].key_id() == key_id) { - cached_sub_session_key_id_ = keys_[i].sub_session_key_id(); - if (keys_[i].cipher_mode() != cipher_mode) { - SubLicenseSessionMap::iterator it = - sub_license_oec_sessions_.find(cached_sub_session_key_id_); - if (it == sub_license_oec_sessions_.end()) { - return OEMCrypto_ERROR_INVALID_SESSION; - } - - OEMCryptoResult status = OEMCrypto_SUCCESS; - M_TIME(status = OEMCrypto_SelectKey( - it->second, - reinterpret_cast(keys_[i].key_id().data()), - keys_[i].key_id().size(), - static_cast(cipher_mode)), - metrics_, oemcrypto_select_key_, status); - if (OEMCrypto_SUCCESS != status) { - return status; - } - keys_[i].set_cipher_mode(cipher_mode); - } - } - } - return OEMCrypto_SUCCESS; - } - - // Decrypt performs the decryption using the selected oemcrypto session. - // TODO(jfore): Support DecryptInChunks. - OEMCryptoResult Decrypt( - const CdmDecryptionParameters& params, - OEMCrypto_DestBufferDesc& buffer_descriptor, - OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) { - SubLicenseSessionMap::iterator it = - sub_license_oec_sessions_.find(cached_sub_session_key_id_); - if (it == sub_license_oec_sessions_.end()) { - return OEMCrypto_ERROR_INVALID_SESSION; - } - OEMCryptoResult sts; - M_TIME(sts = OEMCrypto_DecryptCENC( - it->second, params.encrypt_buffer, params.encrypt_length, - params.is_encrypted, &(*params.iv).front(), params.block_offset, - &buffer_descriptor, &pattern_descriptor, params.subsample_flags), - metrics_, oemcrypto_decrypt_cenc_, sts, - metrics::Pow2Bucket(params.encrypt_length)); - return sts; - } - - private: - // Destroy each open oemcrypto session and relace them with new ones. - OEMCryptoResult ResetCryptoSessions() { - for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin(); - it != sub_license_oec_sessions_.end(); it++) { - OEMCryptoResult sts = OEMCrypto_CloseSession(it->second); - metrics_->oemcrypto_close_session_.Increment(sts); - if (OEMCrypto_SUCCESS != sts) { - return sts; - } - sts = OEMCrypto_OpenSession(&it->second, requested_security_level_); - if (OEMCrypto_SUCCESS != sts) { - return sts; - } - M_TIME(sts = OEMCrypto_LoadDeviceRSAKey( - it->second, - reinterpret_cast( - wrapped_private_device_key_.data()), - wrapped_private_device_key_.size()), - metrics_, oemcrypto_load_device_rsa_key_, sts); - if (OEMCrypto_SUCCESS != sts) { - return sts; - } - } - return OEMCrypto_SUCCESS; - } - - // DoLoadKeys loads a single key into each oemcrypto session. - OEMCryptoResult DoLoadKeys(const std::string& message, - const std::string& signature, - const std::string& mac_key_iv, - const std::string& mac_key, - const std::vector& keys, - const std::string& provider_session_token, - CdmCipherMode* cipher_mode, - const std::string& srm_requirement) { - const uint8_t* msg = reinterpret_cast(message.data()); - const uint8_t* enc_mac_key = NULL; - const uint8_t* enc_mac_key_iv = NULL; - if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { - enc_mac_key = msg + GetOffset(message, mac_key); - enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); - } else { - LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); - } - - uint8_t* pst = NULL; - if (!provider_session_token.empty()) { - pst = const_cast(msg) + - GetOffset(message, provider_session_token); - } - - uint8_t* srm_req = NULL; - if (!srm_requirement.empty()) { - srm_req = const_cast(msg) + GetOffset(message, srm_requirement); - } - - for (size_t i = 0; i < keys.size(); i++) { - OEMCrypto_KeyObject_V13 key_object; - const CryptoKey& key_data = keys[i]; - key_object.key_id = msg + GetOffset(message, key_data.key_id()); - key_object.key_id_length = key_data.key_id().length(); - key_object.key_data_iv = msg + GetOffset(message, key_data.key_data_iv()); - key_object.key_data = msg + GetOffset(message, key_data.key_data()); - key_object.key_data_length = key_data.key_data().length(); - if (key_data.HasKeyControl()) { - key_object.key_control_iv = - msg + GetOffset(message, key_data.key_control_iv()); - key_object.key_control = - msg + GetOffset(message, key_data.key_control()); - } else { - LOGE("For key %s: XXX key has no control block. size=%d", - key_data.key_id().c_str(), key_data.key_control().size()); - key_object.key_control_iv = NULL; - key_object.key_control = NULL; - } - *cipher_mode = key_data.cipher_mode(); - - SubLicenseSessionMap::iterator oec_session_id = - sub_license_oec_sessions_.find(key_data.sub_session_key_id()); - if (oec_session_id == sub_license_oec_sessions_.end()) { - LOGE("CryptoSession::LoadKeys: Unrecognized sub session %s", - key_data.sub_session_key_id().c_str()); - return OEMCrypto_ERROR_INVALID_SESSION; - } - - OEMCryptoResult sts; - M_TIME(sts = OEMCrypto_LoadKeys_Back_Compat( - oec_session_id->second, msg, message.size(), - reinterpret_cast(signature.data()), - signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, - pst, provider_session_token.length(), srm_req, - OEMCrypto_ContentLicense), - metrics_, oemcrypto_load_keys_, sts); - - if (sts != OEMCrypto_SUCCESS) { - return sts; - } - - M_TIME(sts = OEMCrypto_SelectKey( - oec_session_id->second, - reinterpret_cast(key_data.key_id().data()), - key_data.key_id().size(), - static_cast(key_data.cipher_mode())), - metrics_, oemcrypto_select_key_, sts); - - if (sts != OEMCrypto_SUCCESS) { - return sts; - } - } - keys_ = keys; - return OEMCrypto_SUCCESS; - } - - // DoLoadKeys loads a single key into each oemcrypto session. - OEMCryptoResult DoSubLicenseLoadKeys( - const std::string& message, const std::string& signature, - const std::string& mac_key_iv, const std::string& mac_key, - const CryptoKey& key, const std::string& provider_session_token, - CdmCipherMode*, const std::string& srm_requirement) { - SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.end(); - size_t key_index = 0; - for (; key_index < keys_.size(); key_index++) { - if (keys_[key_index].track_label() == key.track_label()) { - it = sub_license_oec_sessions_.find( - keys_[key_index].sub_session_key_id()); - CryptoKey tmp = key; - tmp.set_sub_session_key_id(keys_[key_index].sub_session_key_id()); - tmp.set_sub_session_key(keys_[key_index].sub_session_key()); - keys_[key_index] = tmp; - break; - } - } - if (it == sub_license_oec_sessions_.end()) { - return OEMCrypto_SUCCESS; - } - - LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second); - - std::string mac_deriv_message; - std::string enc_deriv_message; - GenerateMacContext(group_master_key_id_ + key.track_label(), - &mac_deriv_message); - GenerateEncryptContext(group_master_key_id_ + key.track_label(), - &enc_deriv_message); - - const uint8_t* msg = reinterpret_cast(message.data()); - const uint8_t* enc_mac_key = NULL; - const uint8_t* enc_mac_key_iv = NULL; - if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { - enc_mac_key = msg + GetOffset(message, mac_key); - enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); - } else { - LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); - } - - uint8_t* pst = NULL; - if (!provider_session_token.empty()) { - pst = const_cast(msg) + - GetOffset(message, provider_session_token); - } - - uint8_t* srm_req = NULL; - if (!srm_requirement.empty()) { - srm_req = const_cast(msg) + GetOffset(message, srm_requirement); - } - - OEMCryptoResult sts; - const std::string& sub_session_key = keys_[key_index].sub_session_key(); - LOGE("ssksize = %d", sub_session_key.size()); - - M_TIME(sts = OEMCrypto_DeriveKeysFromSessionKey( - it->second, - reinterpret_cast(sub_session_key.data()), - sub_session_key.size(), - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()), - metrics_, oemcrypto_derive_keys_from_session_key_, sts); - - if (OEMCrypto_SUCCESS != sts) { - LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", - sts); - return sts; - } - - OEMCrypto_KeyObject_V13 key_object; - key_object.key_id = msg + GetOffset(message, keys_[key_index].key_id()); - key_object.key_id_length = keys_[key_index].key_id().length(); - key_object.key_data_iv = - msg + GetOffset(message, keys_[key_index].key_data_iv()); - key_object.key_data = msg + GetOffset(message, keys_[key_index].key_data()); - key_object.key_data_length = keys_[key_index].key_data().length(); - if (key.HasKeyControl()) { - key_object.key_control_iv = - msg + GetOffset(message, keys_[key_index].key_control_iv()); - key_object.key_control = - msg + GetOffset(message, keys_[key_index].key_control()); - } - - M_TIME( - sts = OEMCrypto_LoadKeys_Back_Compat( - it->second, msg, message.size(), - reinterpret_cast(signature.data()), - signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, pst, - provider_session_token.length(), srm_req, OEMCrypto_ContentLicense), - metrics_, oemcrypto_load_keys_, sts); - - if (sts != OEMCrypto_SUCCESS) { - return sts; - } - - M_TIME( - sts = OEMCrypto_SelectKey( - it->second, - reinterpret_cast(keys_[key_index].key_id().data()), - keys_[key_index].key_id().size(), - static_cast(keys_[key_index].cipher_mode())), - metrics_, oemcrypto_select_key_, sts); - - return sts; - } - - SubLicenseState state_; - std::string cached_sub_session_key_id_; - std::string wrapped_private_device_key_; - std::string message_; - std::string session_key_; - std::vector keys_; - SubLicenseSessionMap& sub_license_oec_sessions_; - SecurityLevel requested_security_level_; - KeyId group_master_key_id_; -}; - CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics) : metrics_(metrics), system_id_(-1), @@ -1251,7 +699,7 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { } // TODO(gmorgan, jfore): resolve handling of usage records in sublicenses - key_session_.reset(new DefaultKeySession(oec_session_id_, metrics_)); + key_session_.reset(new ContentKeySession(oec_session_id_, metrics_)); return NO_ERROR; } @@ -1338,10 +786,15 @@ CdmResponseType CryptoSession::LoadKeys( const std::string& mac_key_iv, const std::string& mac_key, const std::vector& keys, const std::string& provider_session_token, - const std::string& srm_requirement) { + const std::string& srm_requirement, CdmLicenseKeyType key_type) { LOGV("CryptoSession::LoadKeys: Lock"); AutoLock auto_lock(crypto_lock_); + if (key_type == kLicenseKeyTypeEntitlement && + key_session_->Type() != KeySession::kEntitlement) { + key_session_.reset(new EntitlementKeySession(oec_session_id_, metrics_)); + } + LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_); OEMCryptoResult sts = key_session_->LoadKeys( message, signature, mac_key_iv, mac_key, keys, provider_session_token, @@ -1374,6 +827,13 @@ CdmResponseType CryptoSession::LoadKeys( return result; } +CdmResponseType CryptoSession::LoadEntitledContentKeys( + const std::vector& key_array) { + // TODO(jfore): Handle and return errors. + /*OEMCryptoResult status =*/ key_session_->LoadEntitledContentKeys(key_array); + return KEY_ADDED; +} + bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { LOGV("CryptoSession::LoadCertificatePrivateKey: Lock"); AutoLock auto_lock(crypto_lock_); diff --git a/libwvdrmengine/cdm/core/src/entitlement_key_session.cpp b/libwvdrmengine/cdm/core/src/entitlement_key_session.cpp new file mode 100644 index 00000000..1db42ef0 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/entitlement_key_session.cpp @@ -0,0 +1,54 @@ +#include "entitlement_key_session.h" + +#include "crypto_key.h" + +namespace wvcdm { +EntitlementKeySession::EntitlementKeySession(CryptoSessionId oec_session_id, + metrics::CryptoMetrics* metrics) + : ContentKeySession(oec_session_id, metrics) {} + +OEMCryptoResult EntitlementKeySession::LoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { + keys_.resize(keys.size()); + return ContentKeySession::LoadKeys( + message, signature, mac_key_iv, mac_key, keys, provider_session_token, + cipher_mode, srm_requirement, OEMCrypto_EntitlementLicense); +} + +OEMCryptoResult EntitlementKeySession::LoadEntitledContentKeys( + const std::vector& keys) { + // The array |keys| contains new content keys, plus entitlement key ids for + // those content keys. + std::vector entitlements; + entitlements.resize(keys.size()); + + for (size_t i = 0; i < keys.size(); ++i) { + entitlements[i].entitlement_key_id = + reinterpret_cast(keys[i].entitlement_key_id().data()); + entitlements[i].entitlement_key_id_length = + keys[i].entitlement_key_id().size(); + + entitlements[i].content_key_id = + reinterpret_cast(keys[i].key_id().data()); + entitlements[i].content_key_id_length = keys[i].key_id().size(); + + entitlements[i].content_key_data_iv = + reinterpret_cast(keys[i].key_data_iv().data()); + + entitlements[i].content_key_data = + reinterpret_cast(keys[i].key_data().data()); + entitlements[i].content_key_data_length = keys[i].key_data().size(); + } + + OEMCryptoResult result = OEMCrypto_SUCCESS; + M_TIME(result = OEMCrypto_LoadEntitledContentKeys( + oec_session_id_, entitlements.size(), &entitlements[0]), + metrics_, oemcrypto_load_entitled_keys_, result); + return result; +} + +} // namespace wvcdm \ No newline at end of file diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp index a4748a59..cfa94722 100644 --- a/libwvdrmengine/cdm/core/src/initialization_data.cpp +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -67,7 +67,7 @@ InitializationData::InitializationData(const std::string& type, // Parse the pssh data and return the embedded key data if it exists. std::vector -InitializationData::ExtractEmbeddedKeys() const { +InitializationData::ExtractSublicenseKeys() const { std::vector keys; WidevinePsshData cenc_header; if (!is_cenc_ || !cenc_header.ParseFromString(data_) || @@ -81,6 +81,20 @@ InitializationData::ExtractEmbeddedKeys() const { return keys; } +std::vector InitializationData::ExtractWrappedKeys() + const { + std::vector keys; + WidevinePsshData cenc_header; + if (!is_cenc_ || !cenc_header.ParseFromString(data_) || + cenc_header.entitled_keys().size() == 0) + return keys; + keys.reserve(cenc_header.entitled_keys().size()); + for (int i = 0; i < cenc_header.entitled_keys().size(); ++i) { + keys.push_back(cenc_header.entitled_keys(i)); + } + return keys; +} + // Parse a blob of multiple concatenated PSSH atoms to extract the first // Widevine PSSH. bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data, @@ -483,7 +497,7 @@ bool InitializationData::ConstructWidevineInitData( // have not yet been pushed to production. Set until then. cenc_header.set_algorithm(WidevinePsshData_Algorithm_AESCTR); for (size_t i = 0; i < key_ids.size(); ++i) { - cenc_header.add_key_id(key_ids[i]); + cenc_header.add_key_ids(key_ids[i]); } cenc_header.set_provider(provider); cenc_header.set_content_id(content_id); @@ -589,19 +603,4 @@ std::vector InitializationData::ExtractKeyFormatVersions( return versions; } -// Extract the key id of the group master key used to generate sublicense data. -// Returns an empty string if not defined. -const std::string InitializationData::ExtractGroupMasterKeyId() const { - if (!is_cenc_) { - return ""; - } - - WidevinePsshData cenc_header; - if (!cenc_header.ParseFromString(data_)) { - return ""; - } - - return cenc_header.group_master_key_id(); -} - } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 11037939..a9f1fdc1 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -72,6 +72,54 @@ static std::vector ExtractSubSessionKeys(const License& license) { return key_array; } +static std::vector ExtractEntitlementKeys(const License& license) { + std::vector key_array; + + // Extract sub session key(s) + for (int i = 0; i < license.key_size(); ++i) { + CryptoKey key; + switch (license.key(i).type()) { + case License_KeyContainer::ENTITLEMENT: { + key.set_key_data(license.key(i).key()); + key.set_key_data_iv(license.key(i).iv()); + key.set_key_id(license.key(i).id()); + key.set_track_label(license.key(i).track_label()); + if (license.key(i).has_key_control()) { + key.set_key_control(license.key(i).key_control().key_control_block()); + key.set_key_control_iv(license.key(i).key_control().iv()); + } + uint32_t four_cc = kFourCcCenc; + if (license.has_protection_scheme()) { + four_cc = license.protection_scheme(); + } + key.set_track_label(license.key(i).track_label()); + switch (four_cc) { + // b/30713238: Android N assumed that the "protection scheme" Four + // CC code, after being extracted from the protobuf, was host byte + // order dependent. Later versions do not assume this, and thus, + // for backwards compatibility, must support both byte orders. + case kFourCcCbc1: + case kFourCcCbcs: + case kFourCcLittleEndianCbc1: + case kFourCcLittleEndianCbcs: + key.set_cipher_mode(kCipherModeCbc); + break; + default: + key.set_cipher_mode(kCipherModeCtr); + break; + } + key_array.push_back(key); + } break; + + default: + // Ignore all but ENTITLEMENT key types. + break; + } + } + + return key_array; +} + static std::vector ExtractContentKeys(const License& license) { std::vector key_array; @@ -239,6 +287,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest( return PrepareKeyRequest(restored_init_data, license_type, app_parameters, signed_request, server_url); } + wrapped_keys_ = init_data.ExtractWrappedKeys(); if (!init_data.is_supported()) { LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)", init_data.type().c_str()); @@ -306,7 +355,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest( // Prepare the request for any embedded keys that may exist in the // initialization data. std::vector embedded_key_data = - init_data.ExtractEmbeddedKeys(); + init_data.ExtractSublicenseKeys(); for (size_t i = 0; i < embedded_key_data.size(); ++i) { bool exists = false; if (!crypto_session_->GenerateSubSessionNonce( @@ -575,8 +624,15 @@ CdmResponseType CdmLicense::HandleKeyResponse( } } - std::vector key_array = ExtractContentKeys(license); - if (!key_array.size()) { + CdmLicenseKeyType key_type = kLicenseKeyTypeEntitlement; + std::vector key_array = ExtractEntitlementKeys(license); + if (key_array.empty()) { + key_array = ExtractContentKeys(license); + key_type = kLicenseKeyTypeContent; + } else if (wrapped_keys_.empty()) { + key_array.clear(); + } + if (key_array.empty()) { LOGE("CdmLicense::HandleKeyResponse : No content keys."); return NO_CONTENT_KEY; } @@ -601,17 +657,15 @@ CdmResponseType CdmLicense::HandleKeyResponse( renew_with_client_id_ = license.policy().always_include_client_id(); } - CdmResponseType resp = crypto_session_->LoadKeys( - signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key, - key_array, provider_session_token_, license.srm_requirement()); - - if (KEY_ADDED == resp) { - loaded_keys_.clear(); - for (std::vector::iterator it = key_array.begin(); - it != key_array.end(); ++it) { - loaded_keys_.insert(it->key_id()); - } - policy_engine_->SetLicense(license); + CdmResponseType resp = NO_CONTENT_KEY; + if (kLicenseKeyTypeEntitlement == key_type) { + resp = HandleEntitlementKeyResponse(signed_response.msg(), + signed_response.signature(), mac_key_iv, + mac_key, key_array, license); + } else if (kLicenseKeyTypeContent == key_type) { + resp = HandleContentKeyResponse(signed_response.msg(), + signed_response.signature(), mac_key_iv, + mac_key, key_array, license); } return resp; } @@ -699,7 +753,7 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse( CdmResponseType CdmLicense::HandleSubLicense( const InitializationData& init_data) { std::vector subkeys = - init_data.ExtractEmbeddedKeys(); + init_data.ExtractSublicenseKeys(); std::set loaded_keys; // Build a license with the rotated keys. License license; @@ -732,7 +786,7 @@ CdmResponseType CdmLicense::HandleSubLicense( //TODO: passing empty cipher_mode and srm_req params - OK? CdmResponseType result = crypto_session_->LoadKeys( sm.msg(), sm.signature(), std::string(), std::string(), keys, - std::string(), std::string()); + std::string(), std::string(), kLicenseKeyTypeContent); if (result != KEY_ADDED) { LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d", result); @@ -1049,6 +1103,101 @@ CdmResponseType CdmLicense::PrepareContentId( return NO_ERROR; } +CdmResponseType CdmLicense::HandleContentKeyResponse( + const std::string& msg, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& key_array, + const video_widevine::License& license) { + if (key_array.empty()) { + LOGE("CdmLicense::HandleKeyResponse : No content keys."); + return NO_CONTENT_KEY; + } + CdmResponseType resp = crypto_session_->LoadKeys( + msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_, + license.srm_requirement(), kLicenseKeyTypeContent); + + if (KEY_ADDED == resp) { + loaded_keys_.clear(); + for (std::vector::const_iterator it = key_array.begin(); + it != key_array.end(); ++it) { + loaded_keys_.insert(it->key_id()); + } + policy_engine_->SetLicense(license); + } + return resp; +} + +CdmResponseType CdmLicense::HandleEntitlementKeyResponse( + const std::string& msg, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& key_array, + const video_widevine::License& license) { + if (key_array.empty() || wrapped_keys_.empty()) { + LOGE("CdmLicense::HandleKeyResponse : No content keys."); + return NO_CONTENT_KEY; + } + CdmResponseType resp = crypto_session_->LoadKeys( + msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_, + license.srm_requirement(), kLicenseKeyTypeEntitlement); + if (KEY_ADDED != resp) { + return resp; + } + + std::vector entitled_key_array; + entitled_key_array.reserve(key_array.size()); + + for (std::vector::iterator wk = + wrapped_keys_.begin(); + wk != wrapped_keys_.end(); wk++) { + for (std::vector::const_iterator key = key_array.begin(); + key != key_array.end(); key++) { + if (wk->wrapping_key_id() == key->key_id()) { + entitled_key_array.resize(entitled_key_array.size() + 1); + CryptoKey& this_entry = entitled_key_array.back(); + this_entry.set_key_id(wk->key_id()); + this_entry.set_key_data(wk->wrapped_key()); + this_entry.set_key_data_iv(wk->wrapping_iv()); + this_entry.set_entitlement_key_id(wk->wrapping_key_id()); + } + } + } + + resp = crypto_session_->LoadEntitledContentKeys(entitled_key_array); + if (KEY_ADDED == resp) { + loaded_keys_.clear(); + for (std::vector::const_iterator it = + wrapped_keys_.begin(); + it != wrapped_keys_.end(); ++it) { + loaded_keys_.insert(it->key_id()); + } + + // TODO(jfore): Move the information to build this "license" to the + // entitlement key session. It is used to update the policy engine and + // key status when using entitlement licenses. It may become unnecessary + // if policy manager ius changed to allow setting keys from the wrapped + // keys from init_data. + video_widevine::License entitled_license; + entitled_license.mutable_policy()->CopyFrom(license.policy()); + entitled_license.mutable_id()->CopyFrom(license.id()); + entitled_license.mutable_key()->CopyFrom(license.key()); + entitled_license.set_license_start_time(license.license_start_time()); + for (size_t i = 0; i < wrapped_keys_.size(); ++i) { + for (int x = 0; x < entitled_license.key().size(); ++x) { + if (entitled_license.key(x).id() == + wrapped_keys_[i].wrapping_key_id()) { + video_widevine::License::KeyContainer* kc = + entitled_license.mutable_key(x); + kc->set_type(video_widevine::License::KeyContainer::CONTENT); + kc->set_key(wrapped_keys_[i].wrapped_key()); + kc->set_id(wrapped_keys_[i].key_id()); + } + } + } + policy_engine_->SetLicense(entitled_license); + } + return resp; +} + template bool CdmLicense::SetTypeAndId(CdmLicenseType license_type, const std::string& request_id, T* content_id) { diff --git a/libwvdrmengine/cdm/core/src/license_protocol.proto b/libwvdrmengine/cdm/core/src/license_protocol.proto index 400814cf..580b4d0f 100644 --- a/libwvdrmengine/cdm/core/src/license_protocol.proto +++ b/libwvdrmengine/cdm/core/src/license_protocol.proto @@ -102,6 +102,7 @@ message License { KEY_CONTROL = 3; OPERATOR_SESSION = 4; SUB_SESSION = 5; + ENTITLEMENT = 6; } // The SecurityLevel enumeration allows the server to communicate the level @@ -729,28 +730,54 @@ message SubLicense { // proto. Internally the message field will contain a serialized KeyContainer // holding a single content key. optional bytes key_msg = 2; + + // TODO(jfore): There is some uncertainty about including the current group in + // a license. This may change. + // Byte string that identifies the group to which this this content + // belongs. + optional bytes group_id = 13; +} + +// Container for keys which are wrapped using an entitlement key from a master +// license. +message WrappedKey { +// ID of the wrapped key. Required. + optional bytes key_id = 1; + // ID of wrapping key. Required. + optional bytes wrapping_key_id = 2; + // IV used to wrap the key. Required. + optional bytes wrapping_iv = 3; + // Encrypted entitled key. Wrapped with the entitlement key and IV, using + // AES-256-CBC with PKCS#7 padding. Required. + optional bytes wrapped_key = 4; } message WidevinePsshData { +// Superceded by protection_scheme. enum Algorithm { UNENCRYPTED = 0; AESCTR = 1; }; - // Replaced with protection_scheme. - optional Algorithm algorithm = 1; - repeated bytes key_id = 2; + + optional Algorithm algorithm = 1 [deprecated = true]; + + // Key IDentifier(s). This field is mutually exclusive with content_id, below. + // Only One or the other, but at least one must be present. + repeated bytes key_ids = 2; // Content provider name. - optional string provider = 3; + optional string provider = 3 [deprecated = true]; // A content identifier, specified by content provider. + // This field is mutually exclusive with key_ids, above. Only + // one or the other, but at least one must be present. optional bytes content_id = 4; // Track type. Acceptable values are SD, HD and AUDIO. Used to differentiate // content keys used by an asset. // No longer adding track_type to the PSSH since the Widevine license server // will return keys for all allowed track types in a single license. - optional string track_type_deprecated = 5; + optional string track_type = 5 [deprecated = true]; // The name of a registered policy to be used for this asset. optional string policy = 6 [deprecated=true]; @@ -760,16 +787,16 @@ message WidevinePsshData { // Optional protected context for group content. The grouped_license is a // serialized SignedMessage. - optional bytes grouped_license = 8; + optional bytes grouped_license = 8 [deprecated = true]; // Protection scheme identifying the encryption algorithm. The protection // scheme is represented as a uint32 value. The uint32 contains 4 bytes each // representing a single ascii character in one of the 4CC protection scheme - // values. + // values. To be soon deprecated in favor of signaling from content. // 'cenc' (AES-CTR) protection_scheme = 0x63656E63, // 'cbc1' (AES-CBC) protection_scheme = 0x63626331, - // 'cens' (AES-CTR subsample) protection_scheme = 0x63656E73, - // 'cbcs' (AES-CBC subsample) protection_scheme = 0x63626373. + // 'cens' (AES-CTR pattern encryption) protection_scheme = 0x63656E73, + // 'cbcs' (AES-CBC pattern encryption) protection_scheme = 0x63626373. optional uint32 protection_scheme = 9; // Optional. For media using key rotation, this represents the duration @@ -779,9 +806,15 @@ message WidevinePsshData { // Required when using content keys that are embedded in content. repeated SubLicense sub_licenses = 11; - // Key ID used to identify the group master key License Server is supposed - // to use to generate group license. - optional string group_master_key_id = 12; + // IDs of the groups to which the content belongs. A group is a set of + // content IDs. A particular piece of content may belong to multiple groups. + repeated bytes group_ids = 12; + + // Copy/copies of the content key used to decrypt the media stream in which + // the PSSH box is embedded, each wrapped with a different entitlement key. + // May be repeated if using group entitlement keys. Optional, used for content + // key rotation. + repeated WrappedKey entitled_keys = 13; } // Signed device certificate definition. diff --git a/libwvdrmengine/cdm/core/src/sublicense_key_session.cpp b/libwvdrmengine/cdm/core/src/sublicense_key_session.cpp new file mode 100644 index 00000000..57357d05 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/sublicense_key_session.cpp @@ -0,0 +1,351 @@ +#include "sublicense_key_session.h" + +#include "crypto_session.h" +#include "log.h" +#include "wv_cdm_constants.h" + +namespace wvcdm { + +SubLicenseKeySession::SubLicenseKeySession( + SubLicenseSessionMap& sub_license_oec_sessions, + metrics::CryptoMetrics* metrics, + const std::string& wrapped_private_device_key, + SecurityLevel requested_security_level, + const std::string& group_id) + : KeySession(metrics), + state_(kInitializing), + wrapped_private_device_key_(wrapped_private_device_key), + sub_license_oec_sessions_(sub_license_oec_sessions), + requested_security_level_(requested_security_level), + group_id_(group_id) {} + +SubLicenseKeySession::~SubLicenseKeySession() { + for (SubLicenseSessionMap::iterator oec_session = + sub_license_oec_sessions_.begin(); + oec_session != sub_license_oec_sessions_.end(); oec_session++) { + metrics_->oemcrypto_close_session_.Increment( + OEMCrypto_CloseSession(oec_session->second)); + } + sub_license_oec_sessions_.clear(); +} + +// GenerateDerivedKeys is called for each open oemcrypto session and is only +// called once. +bool SubLicenseKeySession::GenerateDerivedKeys(const std::string& message, + const std::string& session_key) { + std::string mac_deriv_message; + std::string enc_deriv_message; + GenerateMacContext(message, &mac_deriv_message); + GenerateEncryptContext(message, &enc_deriv_message); + + for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin(); + it != sub_license_oec_sessions_.end(); it++) { + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second); + OEMCryptoResult sts; + M_TIME(sts = OEMCrypto_DeriveKeysFromSessionKey( + it->second, reinterpret_cast(session_key.data()), + session_key.size(), + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, oemcrypto_derive_keys_from_session_key_, sts); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", + sts); + return false; + } + } + + return true; +} + +OEMCryptoResult SubLicenseKeySession::LoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { + if (state_ == kInitializing) { + state_ = kInitialLicenseLoaded; + keys_ = keys; + OEMCryptoResult sts = + DoLoadKeys(message, signature, mac_key_iv, mac_key, keys, + provider_session_token, cipher_mode, srm_requirement); + if (OEMCrypto_SUCCESS != sts) { + state_ = kInitialLicenseFailed; + } + return sts; + } + return DoSubLicenseLoadKeys(message, signature, mac_key_iv, mac_key, keys[0], + provider_session_token, cipher_mode, + srm_requirement); +} + +OEMCryptoResult SubLicenseKeySession::SelectKey(const std::string& key_id, + CdmCipherMode cipher_mode) { + for (size_t i = 0; i < keys_.size(); ++i) { + if (keys_[i].key_id() == key_id) { + cached_sub_session_key_id_ = keys_[i].sub_session_key_id(); + if (keys_[i].cipher_mode() != cipher_mode) { + SubLicenseSessionMap::iterator it = + sub_license_oec_sessions_.find(cached_sub_session_key_id_); + if (it == sub_license_oec_sessions_.end()) { + return OEMCrypto_ERROR_INVALID_SESSION; + } + + OEMCryptoResult status = OEMCrypto_SUCCESS; + M_TIME(status = OEMCrypto_SelectKey( + it->second, + reinterpret_cast(keys_[i].key_id().data()), + keys_[i].key_id().size(), + static_cast(cipher_mode)), + metrics_, oemcrypto_select_key_, status); + if (OEMCrypto_SUCCESS != status) { + return status; + } + keys_[i].set_cipher_mode(cipher_mode); + } + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SubLicenseKeySession::Decrypt( + const CdmDecryptionParameters& params, + OEMCrypto_DestBufferDesc& buffer_descriptor, + OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) { + SubLicenseSessionMap::iterator it = + sub_license_oec_sessions_.find(cached_sub_session_key_id_); + if (it == sub_license_oec_sessions_.end()) { + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoResult sts; + M_TIME(sts = OEMCrypto_DecryptCENC( + it->second, params.encrypt_buffer, params.encrypt_length, + params.is_encrypted, &(*params.iv).front(), params.block_offset, + &buffer_descriptor, &pattern_descriptor, params.subsample_flags), + metrics_, oemcrypto_decrypt_cenc_, sts, + metrics::Pow2Bucket(params.encrypt_length)); + return sts; +} + +OEMCryptoResult SubLicenseKeySession::ResetCryptoSessions() { + for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin(); + it != sub_license_oec_sessions_.end(); it++) { + OEMCryptoResult sts = OEMCrypto_CloseSession(it->second); + metrics_->oemcrypto_close_session_.Increment(sts); + if (OEMCrypto_SUCCESS != sts) { + return sts; + } + sts = OEMCrypto_OpenSession(&it->second, requested_security_level_); + if (OEMCrypto_SUCCESS != sts) { + return sts; + } + M_TIME(sts = OEMCrypto_LoadDeviceRSAKey( + it->second, + reinterpret_cast( + wrapped_private_device_key_.data()), + wrapped_private_device_key_.size()), + metrics_, oemcrypto_load_device_rsa_key_, sts); + if (OEMCrypto_SUCCESS != sts) { + return sts; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SubLicenseKeySession::DoLoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { + const uint8_t* msg = reinterpret_cast(message.data()); + const uint8_t* enc_mac_key = NULL; + const uint8_t* enc_mac_key_iv = NULL; + if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { + enc_mac_key = msg + GetOffset(message, mac_key); + enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); + } else { + LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); + } + + uint8_t* pst = NULL; + if (!provider_session_token.empty()) { + pst = + const_cast(msg) + GetOffset(message, provider_session_token); + } + + uint8_t* srm_req = NULL; + if (!srm_requirement.empty()) { + srm_req = const_cast(msg) + GetOffset(message, srm_requirement); + } + + for (size_t i = 0; i < keys.size(); i++) { + OEMCrypto_KeyObject key_object; + const CryptoKey& key_data = keys[i]; + key_object.key_id = msg + GetOffset(message, key_data.key_id()); + key_object.key_id_length = key_data.key_id().length(); + key_object.key_data_iv = msg + GetOffset(message, key_data.key_data_iv()); + key_object.key_data = msg + GetOffset(message, key_data.key_data()); + key_object.key_data_length = key_data.key_data().length(); + if (key_data.HasKeyControl()) { + key_object.key_control_iv = + msg + GetOffset(message, key_data.key_control_iv()); + key_object.key_control = msg + GetOffset(message, key_data.key_control()); + } else { + LOGE("For key %s: XXX key has no control block. size=%d", + key_data.key_id().c_str(), key_data.key_control().size()); + key_object.key_control_iv = NULL; + key_object.key_control = NULL; + } + // TODO(jfore): Does returning the cipher mode serve any purpose? + // If not drop. + *cipher_mode = key_data.cipher_mode(); + + SubLicenseSessionMap::iterator oec_session_id = + sub_license_oec_sessions_.find(key_data.sub_session_key_id()); + if (oec_session_id == sub_license_oec_sessions_.end()) { + LOGE("CryptoSession::LoadKeys: Unrecognized sub session %s", + key_data.sub_session_key_id().c_str()); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_LoadKeys( + oec_session_id->second, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, pst, + provider_session_token.length(), srm_req, OEMCrypto_ContentLicense), + metrics_, oemcrypto_load_keys_, sts); + + if (sts != OEMCrypto_SUCCESS) { + return sts; + } + + M_TIME(sts = OEMCrypto_SelectKey( + oec_session_id->second, + reinterpret_cast(key_data.key_id().data()), + key_data.key_id().size(), + static_cast(key_data.cipher_mode())), + metrics_, oemcrypto_select_key_, sts); + + if (sts != OEMCrypto_SUCCESS) { + return sts; + } + } + keys_ = keys; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SubLicenseKeySession::DoSubLicenseLoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const CryptoKey& key, const std::string& provider_session_token, + CdmCipherMode*, const std::string& srm_requirement) { + SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.end(); + size_t key_index = 0; + for (; key_index < keys_.size(); key_index++) { + if (keys_[key_index].track_label() == key.track_label()) { + it = + sub_license_oec_sessions_.find(keys_[key_index].sub_session_key_id()); + CryptoKey tmp = key; + tmp.set_sub_session_key_id(keys_[key_index].sub_session_key_id()); + tmp.set_sub_session_key(keys_[key_index].sub_session_key()); + keys_[key_index] = tmp; + break; + } + } + if (it == sub_license_oec_sessions_.end()) { + return OEMCrypto_SUCCESS; + } + + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second); + + std::string mac_deriv_message; + std::string enc_deriv_message; + GenerateMacContext(group_id_ + message.c_str(), + &mac_deriv_message); + GenerateEncryptContext(group_id_ + message.c_str(), + &enc_deriv_message); + + const uint8_t* msg = reinterpret_cast(message.data()); + const uint8_t* enc_mac_key = NULL; + const uint8_t* enc_mac_key_iv = NULL; + if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { + enc_mac_key = msg + GetOffset(message, mac_key); + enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); + } else { + LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); + } + + uint8_t* pst = NULL; + if (!provider_session_token.empty()) { + pst = + const_cast(msg) + GetOffset(message, provider_session_token); + } + + uint8_t* srm_req = NULL; + if (!srm_requirement.empty()) { + srm_req = const_cast(msg) + GetOffset(message, srm_requirement); + } + + OEMCryptoResult sts; + const std::string& sub_session_key = keys_[key_index].sub_session_key(); + LOGV("ssksize = %d", sub_session_key.size()); + + M_TIME( + sts = OEMCrypto_DeriveKeysFromSessionKey( + it->second, reinterpret_cast(sub_session_key.data()), + sub_session_key.size(), + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, oemcrypto_derive_keys_from_session_key_, sts); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts); + return sts; + } + + OEMCrypto_KeyObject key_object; + key_object.key_id = msg + GetOffset(message, keys_[key_index].key_id()); + key_object.key_id_length = keys_[key_index].key_id().length(); + key_object.key_data_iv = + msg + GetOffset(message, keys_[key_index].key_data_iv()); + key_object.key_data = msg + GetOffset(message, keys_[key_index].key_data()); + key_object.key_data_length = keys_[key_index].key_data().length(); + if (key.HasKeyControl()) { + key_object.key_control_iv = + msg + GetOffset(message, keys_[key_index].key_control_iv()); + key_object.key_control = + msg + GetOffset(message, keys_[key_index].key_control()); + } + + M_TIME( + sts = OEMCrypto_LoadKeys( + it->second, msg, message.size(), + reinterpret_cast(signature.data()), signature.size(), + enc_mac_key_iv, enc_mac_key, 1, &key_object, pst, + provider_session_token.length(), srm_req, OEMCrypto_ContentLicense), + metrics_, oemcrypto_load_keys_, sts); + + if (sts != OEMCrypto_SUCCESS) { + return sts; + } + + M_TIME(sts = OEMCrypto_SelectKey( + it->second, + reinterpret_cast(keys_[key_index].key_id().data()), + keys_[key_index].key_id().size(), + static_cast(keys_[key_index].cipher_mode())), + metrics_, oemcrypto_select_key_, sts); + + return sts; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp b/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp index 59c9a124..662af053 100644 --- a/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp @@ -5,229 +5,231 @@ #include #include "crypto_session.h" +#include "google/protobuf/text_format.h" +#include "key_session.h" +#include "license_protocol.pb.h" #include "log.h" -#include "metrics_collections.h" #include "metrics.pb.h" +#include "metrics_collections.h" #include "scoped_ptr.h" #include "wv_cdm_types.h" namespace { const uint8_t kOemCert[] = { - 0x30, 0x82, 0x09, 0xf7, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x07, 0x02, 0xa0, 0x82, 0x09, 0xe8, 0x30, 0x82, 0x09, 0xe4, 0x02, - 0x01, 0x01, 0x31, 0x00, 0x30, 0x0f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x02, 0x04, 0x00, 0xa0, 0x82, 0x09, - 0xc8, 0x30, 0x82, 0x04, 0x1a, 0x30, 0x82, 0x03, 0x02, 0xa0, 0x03, 0x02, - 0x01, 0x02, 0x02, 0x11, 0x00, 0xf2, 0xa1, 0x08, 0xdf, 0x12, 0x84, 0xb9, - 0x73, 0x6c, 0x23, 0x73, 0xe1, 0x1f, 0xf3, 0xac, 0x7a, 0x30, 0x0d, 0x06, - 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, - 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, - 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, - 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, - 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, - 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, - 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, - 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x30, 0x30, 0x2e, - 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x27, 0x47, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x3b, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x33, 0x34, 0x36, 0x30, 0x1e, - 0x17, 0x0d, 0x31, 0x37, 0x30, 0x33, 0x31, 0x33, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x30, 0x38, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x6d, 0x31, 0x12, 0x30, 0x10, - 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x37, 0x33, 0x34, 0x36, 0x2d, - 0x6c, 0x65, 0x61, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, - 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, - 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, - 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, - 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, - 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x30, 0x82, 0x01, - 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, - 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30, 0x82, 0x01, - 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xf5, 0x09, 0x64, 0x4a, 0x26, 0xfe, - 0xc0, 0x98, 0x55, 0x6a, 0x1d, 0x5d, 0x1c, 0xc7, 0x38, 0xaf, 0xfd, 0x49, - 0x9e, 0x85, 0x3f, 0xd6, 0x45, 0x0e, 0x99, 0x09, 0x85, 0x69, 0x84, 0x3c, - 0xfe, 0x72, 0xa5, 0x56, 0xfa, 0x11, 0x4f, 0x6b, 0x7d, 0x32, 0x2b, 0x0c, - 0xbf, 0x8f, 0xac, 0x47, 0x96, 0x22, 0x82, 0x3d, 0xf5, 0x64, 0x74, 0x7e, - 0x62, 0x68, 0x74, 0xcd, 0x0a, 0xec, 0x84, 0xc5, 0x15, 0x06, 0x0e, 0x5a, - 0x2f, 0x20, 0xe3, 0xc9, 0x67, 0xcd, 0xdd, 0x01, 0xb8, 0xb3, 0x18, 0x87, - 0x8c, 0xa9, 0x58, 0x86, 0x0f, 0xb6, 0xc3, 0x42, 0x7e, 0x87, 0x48, 0x5e, - 0x10, 0x49, 0xc7, 0xd7, 0xb7, 0xb8, 0xa6, 0x34, 0x08, 0x0c, 0x94, 0xf4, - 0xbb, 0x2a, 0x06, 0xa4, 0x4f, 0xec, 0xbc, 0xc4, 0x37, 0xbe, 0x99, 0x10, - 0x23, 0x37, 0x24, 0xb1, 0xdf, 0xcb, 0xe6, 0x3f, 0xc1, 0xf0, 0x0f, 0x04, - 0x03, 0xc8, 0xb0, 0x1e, 0xd6, 0xb8, 0xae, 0x77, 0xe1, 0x4d, 0x6d, 0x97, - 0x69, 0x6d, 0x8a, 0x73, 0x66, 0x32, 0x57, 0x6f, 0xcf, 0xea, 0x1e, 0x7b, - 0x87, 0x03, 0x75, 0xb1, 0xef, 0x83, 0x64, 0x26, 0xf1, 0x3f, 0xbf, 0xe6, - 0x28, 0x03, 0x72, 0x57, 0xbf, 0x47, 0x29, 0x99, 0x8f, 0x74, 0x1d, 0x01, - 0x16, 0xad, 0xb2, 0xdf, 0x80, 0xa4, 0xd3, 0x8b, 0xeb, 0x61, 0xd1, 0x40, - 0x68, 0xb9, 0xa2, 0xa5, 0xef, 0x2b, 0xe5, 0x78, 0xe8, 0x28, 0x88, 0x87, - 0xb7, 0x53, 0x49, 0xbb, 0xe4, 0xea, 0x0d, 0x5e, 0x96, 0xa5, 0xdd, 0x1f, - 0x0b, 0x25, 0x8b, 0xb5, 0x95, 0x46, 0xe7, 0xba, 0xb8, 0xc4, 0x0a, 0x36, - 0xb1, 0x89, 0xeb, 0x27, 0x5d, 0xd9, 0x97, 0x24, 0x59, 0xa3, 0x9b, 0xb0, - 0x23, 0x0b, 0xd2, 0xec, 0x65, 0x91, 0xf9, 0xf0, 0xa0, 0x74, 0x5f, 0xb4, - 0xce, 0x22, 0x27, 0x18, 0x37, 0xe2, 0x4b, 0xfc, 0x91, 0xf9, 0x09, 0x15, - 0xe6, 0xdb, 0x06, 0x9b, 0x4d, 0x82, 0xdc, 0x36, 0x14, 0x48, 0xc6, 0xd5, - 0x87, 0xca, 0xec, 0x5a, 0xa2, 0x29, 0x33, 0xef, 0x22, 0x0c, 0x4b, 0xbf, - 0xe7, 0x2f, 0x95, 0xe1, 0xd3, 0xa5, 0xd8, 0xaa, 0x44, 0x77, 0x29, 0xa3, - 0x20, 0x33, 0xd2, 0x51, 0xa2, 0xf9, 0x4a, 0x6f, 0xf7, 0x3e, 0xf7, 0x0b, - 0x8a, 0xec, 0xc1, 0x99, 0x1d, 0x47, 0xf3, 0x74, 0x02, 0x04, 0xab, 0x8e, - 0x62, 0x4c, 0x9e, 0x00, 0xc2, 0x84, 0xd7, 0xd0, 0xf8, 0xe4, 0x1c, 0x9d, - 0x98, 0x15, 0xa8, 0x8f, 0x08, 0x98, 0x4e, 0x5a, 0xfa, 0xd6, 0x60, 0x87, - 0x12, 0xdc, 0x8e, 0xfd, 0xcb, 0xb3, 0x13, 0x97, 0x7a, 0xa8, 0x8c, 0x56, - 0x2e, 0x49, 0x26, 0x60, 0xe9, 0x4a, 0xdc, 0xec, 0x3f, 0xf0, 0x94, 0xcd, - 0x90, 0x8e, 0x7c, 0x21, 0x3f, 0x80, 0x14, 0x33, 0xdd, 0xb0, 0x00, 0xe2, - 0x09, 0x37, 0x06, 0xdd, 0x17, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, - 0x16, 0x30, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, - 0xd6, 0x79, 0x04, 0x01, 0x01, 0x04, 0x04, 0x02, 0x02, 0x1c, 0xb2, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, - 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x8e, 0x2d, 0x13, 0x1e, 0x60, - 0xaa, 0xda, 0x52, 0x53, 0x55, 0x64, 0x3a, 0xdc, 0xb6, 0x7a, 0xc0, 0xba, - 0xfa, 0xeb, 0x20, 0xab, 0xb6, 0x63, 0xcf, 0xcd, 0x9b, 0xdb, 0x71, 0xf3, - 0xa0, 0xd6, 0x91, 0xbf, 0x0c, 0xc1, 0xae, 0x8f, 0x02, 0x18, 0x00, 0x54, - 0xfb, 0x49, 0x03, 0x34, 0x8d, 0x92, 0x9d, 0x5d, 0x8d, 0xa8, 0x1c, 0x20, - 0x0f, 0x85, 0x60, 0xf9, 0xf6, 0x8b, 0xbb, 0x2b, 0x82, 0xce, 0xb3, 0xe2, - 0x91, 0xe7, 0xbd, 0x91, 0x61, 0x52, 0x36, 0x40, 0x9f, 0x2f, 0x5e, 0xa6, - 0x5d, 0x2f, 0xb3, 0x81, 0xe7, 0xf1, 0x87, 0xbe, 0xc5, 0x9d, 0x67, 0x5a, - 0xf7, 0x41, 0x1e, 0x73, 0xb0, 0x1e, 0xdc, 0x4f, 0x8d, 0x53, 0x21, 0x38, - 0x1b, 0xfd, 0x92, 0x43, 0x68, 0x83, 0x03, 0xd0, 0x9a, 0xca, 0x92, 0x14, - 0x73, 0x04, 0x94, 0x2a, 0x93, 0x22, 0x60, 0x5e, 0xee, 0xb6, 0xec, 0x0f, - 0xb0, 0xc8, 0x92, 0x97, 0xfb, 0x5d, 0xed, 0x1f, 0xa0, 0x5f, 0xe4, 0x98, - 0x2f, 0xf6, 0x13, 0x78, 0x99, 0xec, 0xb3, 0xf1, 0x0d, 0x27, 0xaa, 0x19, - 0x95, 0x39, 0xdb, 0xb0, 0x7b, 0x96, 0x74, 0x03, 0x5e, 0x51, 0xf5, 0x15, - 0x27, 0xce, 0xca, 0x0b, 0x2a, 0x0d, 0x43, 0xb3, 0x68, 0x17, 0x1e, 0x11, - 0x60, 0xd9, 0x84, 0x9b, 0xc3, 0x53, 0xce, 0xbd, 0xf4, 0x61, 0x51, 0x4b, - 0x41, 0x00, 0x7e, 0xe1, 0x5f, 0x69, 0xb3, 0x4a, 0x89, 0x7e, 0x47, 0x67, - 0xfd, 0x76, 0xf8, 0x94, 0x2f, 0x72, 0xb6, 0x14, 0x08, 0x2c, 0x16, 0x4e, - 0x9d, 0x37, 0x62, 0xbf, 0x11, 0x67, 0xc0, 0x70, 0x71, 0xec, 0x55, 0x51, - 0x4e, 0x46, 0x76, 0xb4, 0xc3, 0xeb, 0x52, 0x06, 0x17, 0x06, 0xce, 0x61, - 0x43, 0xce, 0x26, 0x80, 0x68, 0xb6, 0x2d, 0x57, 0xba, 0x8c, 0x7d, 0xb7, - 0xc5, 0x05, 0x2c, 0xf8, 0xa3, 0x69, 0xf8, 0x96, 0xad, 0xac, 0xd1, 0x30, - 0x82, 0x05, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01, 0x02, - 0x02, 0x10, 0x73, 0xd1, 0xe1, 0x1d, 0xa9, 0x75, 0xfd, 0x0c, 0xda, 0x7f, - 0xfa, 0x43, 0x3c, 0x26, 0xbd, 0x3d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, - 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7e, 0x31, - 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, - 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, - 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, - 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, - 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, - 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, - 0x76, 0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, - 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, - 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, - 0x33, 0x31, 0x34, 0x30, 0x33, 0x30, 0x32, 0x34, 0x31, 0x5a, 0x17, 0x0d, - 0x32, 0x37, 0x30, 0x33, 0x31, 0x34, 0x30, 0x33, 0x30, 0x32, 0x34, 0x31, - 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, - 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, - 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, - 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, - 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, - 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x30, 0x30, - 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x27, 0x47, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x3b, 0x20, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x33, 0x34, 0x36, 0x30, - 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, - 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0x45, 0x13, 0xf2, - 0xb2, 0xcb, 0x4b, 0x0f, 0xb4, 0x44, 0x25, 0x9c, 0x8a, 0x68, 0x54, 0xd5, - 0x45, 0x1e, 0x15, 0x89, 0x5b, 0xb8, 0xce, 0xda, 0x5a, 0x42, 0xe6, 0x9a, - 0x8c, 0xc1, 0xcb, 0xe8, 0xc5, 0xf5, 0x8f, 0x49, 0x0e, 0x02, 0xef, 0x5e, - 0x97, 0x1a, 0x91, 0xa4, 0x94, 0xc3, 0x50, 0x13, 0xe5, 0x13, 0xb7, 0x7f, - 0x26, 0x53, 0x19, 0xb0, 0x37, 0xa5, 0xef, 0xe6, 0x2a, 0x39, 0xdc, 0x93, - 0x37, 0xe2, 0x3d, 0x7f, 0xcb, 0x4b, 0x93, 0xa2, 0xc3, 0x69, 0x78, 0xc9, - 0x01, 0xfa, 0x68, 0x3b, 0xe0, 0xe2, 0x22, 0x6c, 0xeb, 0xe4, 0x8a, 0xa8, - 0x3e, 0xf5, 0x20, 0x82, 0xa8, 0x62, 0x68, 0x59, 0x78, 0x24, 0xde, 0xef, - 0x47, 0x43, 0xb1, 0x6c, 0x38, 0x29, 0xd3, 0x69, 0x3f, 0xae, 0x35, 0x57, - 0x75, 0x80, 0xc9, 0x21, 0xe7, 0x01, 0xb9, 0x54, 0x8b, 0x6e, 0x4e, 0x2e, - 0x5a, 0x5b, 0x77, 0xa4, 0x22, 0xc2, 0x7b, 0x95, 0xb9, 0x39, 0x2c, 0xbd, - 0xc2, 0x1e, 0x02, 0xa6, 0xb2, 0xbc, 0x0f, 0x7a, 0xcb, 0xdc, 0xbc, 0xbc, - 0x90, 0x66, 0xe3, 0xca, 0x46, 0x53, 0x3e, 0x98, 0xff, 0x2e, 0x78, 0x9f, - 0xd3, 0xa1, 0x12, 0x93, 0x66, 0x7d, 0xcc, 0x94, 0x6b, 0xec, 0x19, 0x0e, - 0x20, 0x45, 0x22, 0x57, 0x6d, 0x9e, 0xd0, 0x89, 0xf2, 0xa9, 0x34, 0xdc, - 0xab, 0xa5, 0x73, 0x47, 0x38, 0xe3, 0x7f, 0x98, 0x3a, 0x61, 0xae, 0x6c, - 0x4d, 0xf2, 0x31, 0x90, 0xcb, 0x83, 0xc1, 0xee, 0xb4, 0xf2, 0x9a, 0x28, - 0x5f, 0xbb, 0x7d, 0x89, 0xdf, 0xa2, 0x31, 0xb6, 0x1d, 0x39, 0x2b, 0x70, - 0xbf, 0x1e, 0xad, 0xe1, 0x74, 0x94, 0x1d, 0xf8, 0xc5, 0x1a, 0x8d, 0x13, - 0x45, 0xf0, 0x6a, 0x80, 0x0c, 0x5d, 0xbb, 0x46, 0x8a, 0x43, 0xd0, 0xff, - 0x21, 0x39, 0x57, 0x53, 0x5b, 0x51, 0xf8, 0xa2, 0x8f, 0x7f, 0x27, 0xc7, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x10, 0x30, 0x82, 0x01, - 0x0c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, - 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, - 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, - 0x04, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, - 0xe8, 0xe9, 0xac, 0x16, 0x5c, 0x5e, 0xb2, 0xe8, 0xeb, 0xff, 0x57, 0x27, - 0x20, 0x08, 0x72, 0x63, 0x9b, 0xe5, 0xb5, 0x16, 0x30, 0x81, 0xb2, 0x06, - 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0xaa, 0x30, 0x81, 0xa7, 0x80, 0x14, - 0x04, 0x94, 0x66, 0xaa, 0xf9, 0x61, 0x89, 0xb6, 0xdb, 0xb5, 0xf7, 0x13, - 0x38, 0x3d, 0x62, 0x84, 0xb8, 0x18, 0x0a, 0x8f, 0xa1, 0x81, 0x83, 0xa4, - 0x81, 0x80, 0x30, 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, - 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, - 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, - 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, - 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, - 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, - 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, - 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, - 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x82, 0x09, - 0x00, 0xdf, 0x86, 0x05, 0x31, 0x01, 0xbe, 0x9a, 0x9a, 0x30, 0x12, 0x06, - 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x04, 0x01, 0x01, 0x04, - 0x04, 0x02, 0x02, 0x1c, 0xb2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, - 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, - 0x00, 0x25, 0xce, 0xd2, 0x02, 0x48, 0xbb, 0xbe, 0xfc, 0xb6, 0xa4, 0x87, - 0x87, 0xe0, 0x21, 0x7d, 0xfa, 0x23, 0xc3, 0x0d, 0x73, 0x8f, 0x46, 0xe7, - 0x09, 0x59, 0xda, 0x2e, 0x55, 0x59, 0xff, 0x3c, 0x1b, 0xf6, 0xf8, 0x9a, - 0xc4, 0x1c, 0xf7, 0xac, 0xca, 0xe7, 0x63, 0xf2, 0xc7, 0xd6, 0x0c, 0x2d, - 0xa6, 0xad, 0x55, 0xf4, 0x10, 0x0e, 0xa8, 0x82, 0x0f, 0x88, 0xb5, 0x44, - 0xe8, 0x8e, 0x84, 0x08, 0xf7, 0xdd, 0xe7, 0x10, 0xce, 0x71, 0x56, 0x57, - 0x3f, 0xed, 0x48, 0xee, 0xe2, 0x5d, 0x08, 0x0a, 0x58, 0xe4, 0xfe, 0xbc, - 0x8c, 0x27, 0x1a, 0x46, 0x3f, 0xd5, 0x2d, 0xdb, 0x0b, 0x71, 0x73, 0xd1, - 0x49, 0xf3, 0x5c, 0x86, 0x4d, 0x0a, 0xe1, 0xeb, 0x53, 0x21, 0x38, 0x4f, - 0xec, 0x1e, 0xc2, 0x68, 0x1f, 0x7d, 0xa6, 0x33, 0xe9, 0xa5, 0x37, 0x2a, - 0xef, 0xcd, 0x78, 0x56, 0xb3, 0x39, 0x60, 0xf4, 0xa5, 0xf9, 0x2b, 0x85, - 0xcf, 0xe6, 0x1c, 0x7c, 0x8a, 0x5d, 0xe8, 0x26, 0x02, 0xcf, 0x7a, 0x56, - 0x1f, 0xae, 0x0d, 0x71, 0x20, 0xee, 0xec, 0x3b, 0xae, 0x95, 0x25, 0x15, - 0xc8, 0xf6, 0x92, 0x5d, 0xb8, 0x9b, 0xc2, 0xb4, 0x95, 0x33, 0x13, 0x76, - 0x45, 0xbe, 0x21, 0xe2, 0x3a, 0x69, 0x66, 0xd7, 0xff, 0x22, 0x00, 0x89, - 0xc9, 0x44, 0xb6, 0x54, 0x38, 0x1f, 0x33, 0xe4, 0xda, 0x7b, 0x87, 0xf3, - 0x23, 0xed, 0xf5, 0x16, 0x08, 0xbe, 0x4b, 0xea, 0x91, 0x8f, 0x91, 0x8b, - 0x4e, 0xd1, 0x02, 0x06, 0xa2, 0x77, 0x15, 0x03, 0x46, 0x11, 0x7d, 0x5b, - 0xea, 0x7a, 0xf6, 0x86, 0x7d, 0x96, 0xb7, 0x73, 0x9b, 0x5b, 0x32, 0xc3, - 0xf8, 0x92, 0x36, 0xe3, 0xe3, 0x2f, 0xe8, 0xf1, 0x72, 0xec, 0x0d, 0x50, - 0xd4, 0x86, 0xc5, 0x62, 0x83, 0xf1, 0x2a, 0x4c, 0xd1, 0xbf, 0x76, 0x62, - 0xd4, 0x21, 0x11, 0x68, 0xb2, 0xd6, 0x8d, 0xc4, 0xf8, 0xe4, 0x70, 0x85, - 0x19, 0xa7, 0x82, 0x27, 0x2c, 0x24, 0x21, 0x7a, 0x3b, 0xad, 0x8a, 0xd3, - 0xae, 0xda, 0x78, 0x3c, 0x6c, 0xab, 0xa2, 0xaa, 0x36, 0xf0, 0x1c, 0x58, - 0xd4, 0x72, 0x5e, 0xe8, 0x8b, 0x41, 0x08, 0xf5, 0x85, 0xdd, 0xee, 0x99, - 0x12, 0xf4, 0xd6, 0x41, 0x83, 0x69, 0xe7, 0x79, 0x19, 0xa3, 0x74, 0xc4, - 0x34, 0x2a, 0x8a, 0x7e, 0x4d, 0xbb, 0x2c, 0x49, 0x19, 0xf7, 0x98, 0x98, - 0xfc, 0x81, 0xf7, 0x9b, 0x7f, 0xff, 0xd9, 0x66, 0xf4, 0x51, 0x14, 0x29, - 0x2a, 0x14, 0x1d, 0x4f, 0xbd, 0x91, 0xba, 0x6f, 0x32, 0x34, 0x3c, 0x40, - 0x28, 0x6c, 0x97, 0xf8, 0x6d, 0x38, 0xcd, 0xa3, 0x7b, 0x18, 0xc8, 0x77, - 0x58, 0x4d, 0x53, 0x30, 0x7f, 0x4d, 0x89, 0xca, 0x95, 0x6e, 0xb5, 0xb8, - 0x8e, 0xc8, 0x2d, 0x18, 0x2f, 0x52, 0x2a, 0xde, 0xac, 0x56, 0x8d, 0x8c, - 0x67, 0x14, 0xf6, 0xb9, 0xf1, 0x65, 0xd3, 0x22, 0x43, 0xa3, 0x98, 0x42, - 0x20, 0x43, 0x4c, 0xdf, 0xf2, 0xeb, 0x31, 0x8c, 0x0e, 0x53, 0x5b, 0x99, - 0x82, 0xc3, 0x48, 0x04, 0x53, 0xad, 0x96, 0xb6, 0x9f, 0x52, 0xcc, 0x01, - 0xc8, 0xb3, 0x87, 0x6b, 0x9e, 0xea, 0xa9, 0xeb, 0xda, 0xac, 0xf9, 0x6f, - 0xde, 0xa1, 0x44, 0x32, 0x52, 0x49, 0x47, 0xff, 0x65, 0x79, 0x1e, 0xc5, - 0x73, 0x17, 0xb3, 0x36, 0xfc, 0x45, 0xca, 0x90, 0x37, 0x59, 0x1e, 0x16, - 0xab, 0x09, 0x69, 0xcf, 0xda, 0x56, 0x51, 0xfd, 0xeb, 0xcf, 0xcb, 0x8f, - 0xb1, 0xc3, 0x45, 0x2b, 0x7c, 0x0a, 0xa5, 0x9c, 0x0d, 0x2c, 0xad, 0x1c, - 0xd3, 0x33, 0xdd, 0xfe, 0x93, 0x69, 0xa2, 0x4b, 0x4b, 0xcf, 0x1d, 0x20, - 0x98, 0x4a, 0x4f, 0x5b, 0xe9, 0x24, 0xca, 0xfa, 0x18, 0x11, 0x81, 0x8b, - 0x7a, 0xb4, 0x5a, 0xc8, 0xdf, 0x6f, 0x5f, 0x21, 0x07, 0x31, 0x00 -}; + 0x30, 0x82, 0x09, 0xf7, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x07, 0x02, 0xa0, 0x82, 0x09, 0xe8, 0x30, 0x82, 0x09, 0xe4, 0x02, + 0x01, 0x01, 0x31, 0x00, 0x30, 0x0f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x02, 0x04, 0x00, 0xa0, 0x82, 0x09, + 0xc8, 0x30, 0x82, 0x04, 0x1a, 0x30, 0x82, 0x03, 0x02, 0xa0, 0x03, 0x02, + 0x01, 0x02, 0x02, 0x11, 0x00, 0xf2, 0xa1, 0x08, 0xdf, 0x12, 0x84, 0xb9, + 0x73, 0x6c, 0x23, 0x73, 0xe1, 0x1f, 0xf3, 0xac, 0x7a, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, + 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, + 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, + 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x30, 0x30, 0x2e, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x27, 0x47, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x3b, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x33, 0x34, 0x36, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x37, 0x30, 0x33, 0x31, 0x33, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x30, 0x38, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x6d, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x37, 0x33, 0x34, 0x36, 0x2d, + 0x6c, 0x65, 0x61, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, + 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, + 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x30, 0x82, 0x01, + 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30, 0x82, 0x01, + 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xf5, 0x09, 0x64, 0x4a, 0x26, 0xfe, + 0xc0, 0x98, 0x55, 0x6a, 0x1d, 0x5d, 0x1c, 0xc7, 0x38, 0xaf, 0xfd, 0x49, + 0x9e, 0x85, 0x3f, 0xd6, 0x45, 0x0e, 0x99, 0x09, 0x85, 0x69, 0x84, 0x3c, + 0xfe, 0x72, 0xa5, 0x56, 0xfa, 0x11, 0x4f, 0x6b, 0x7d, 0x32, 0x2b, 0x0c, + 0xbf, 0x8f, 0xac, 0x47, 0x96, 0x22, 0x82, 0x3d, 0xf5, 0x64, 0x74, 0x7e, + 0x62, 0x68, 0x74, 0xcd, 0x0a, 0xec, 0x84, 0xc5, 0x15, 0x06, 0x0e, 0x5a, + 0x2f, 0x20, 0xe3, 0xc9, 0x67, 0xcd, 0xdd, 0x01, 0xb8, 0xb3, 0x18, 0x87, + 0x8c, 0xa9, 0x58, 0x86, 0x0f, 0xb6, 0xc3, 0x42, 0x7e, 0x87, 0x48, 0x5e, + 0x10, 0x49, 0xc7, 0xd7, 0xb7, 0xb8, 0xa6, 0x34, 0x08, 0x0c, 0x94, 0xf4, + 0xbb, 0x2a, 0x06, 0xa4, 0x4f, 0xec, 0xbc, 0xc4, 0x37, 0xbe, 0x99, 0x10, + 0x23, 0x37, 0x24, 0xb1, 0xdf, 0xcb, 0xe6, 0x3f, 0xc1, 0xf0, 0x0f, 0x04, + 0x03, 0xc8, 0xb0, 0x1e, 0xd6, 0xb8, 0xae, 0x77, 0xe1, 0x4d, 0x6d, 0x97, + 0x69, 0x6d, 0x8a, 0x73, 0x66, 0x32, 0x57, 0x6f, 0xcf, 0xea, 0x1e, 0x7b, + 0x87, 0x03, 0x75, 0xb1, 0xef, 0x83, 0x64, 0x26, 0xf1, 0x3f, 0xbf, 0xe6, + 0x28, 0x03, 0x72, 0x57, 0xbf, 0x47, 0x29, 0x99, 0x8f, 0x74, 0x1d, 0x01, + 0x16, 0xad, 0xb2, 0xdf, 0x80, 0xa4, 0xd3, 0x8b, 0xeb, 0x61, 0xd1, 0x40, + 0x68, 0xb9, 0xa2, 0xa5, 0xef, 0x2b, 0xe5, 0x78, 0xe8, 0x28, 0x88, 0x87, + 0xb7, 0x53, 0x49, 0xbb, 0xe4, 0xea, 0x0d, 0x5e, 0x96, 0xa5, 0xdd, 0x1f, + 0x0b, 0x25, 0x8b, 0xb5, 0x95, 0x46, 0xe7, 0xba, 0xb8, 0xc4, 0x0a, 0x36, + 0xb1, 0x89, 0xeb, 0x27, 0x5d, 0xd9, 0x97, 0x24, 0x59, 0xa3, 0x9b, 0xb0, + 0x23, 0x0b, 0xd2, 0xec, 0x65, 0x91, 0xf9, 0xf0, 0xa0, 0x74, 0x5f, 0xb4, + 0xce, 0x22, 0x27, 0x18, 0x37, 0xe2, 0x4b, 0xfc, 0x91, 0xf9, 0x09, 0x15, + 0xe6, 0xdb, 0x06, 0x9b, 0x4d, 0x82, 0xdc, 0x36, 0x14, 0x48, 0xc6, 0xd5, + 0x87, 0xca, 0xec, 0x5a, 0xa2, 0x29, 0x33, 0xef, 0x22, 0x0c, 0x4b, 0xbf, + 0xe7, 0x2f, 0x95, 0xe1, 0xd3, 0xa5, 0xd8, 0xaa, 0x44, 0x77, 0x29, 0xa3, + 0x20, 0x33, 0xd2, 0x51, 0xa2, 0xf9, 0x4a, 0x6f, 0xf7, 0x3e, 0xf7, 0x0b, + 0x8a, 0xec, 0xc1, 0x99, 0x1d, 0x47, 0xf3, 0x74, 0x02, 0x04, 0xab, 0x8e, + 0x62, 0x4c, 0x9e, 0x00, 0xc2, 0x84, 0xd7, 0xd0, 0xf8, 0xe4, 0x1c, 0x9d, + 0x98, 0x15, 0xa8, 0x8f, 0x08, 0x98, 0x4e, 0x5a, 0xfa, 0xd6, 0x60, 0x87, + 0x12, 0xdc, 0x8e, 0xfd, 0xcb, 0xb3, 0x13, 0x97, 0x7a, 0xa8, 0x8c, 0x56, + 0x2e, 0x49, 0x26, 0x60, 0xe9, 0x4a, 0xdc, 0xec, 0x3f, 0xf0, 0x94, 0xcd, + 0x90, 0x8e, 0x7c, 0x21, 0x3f, 0x80, 0x14, 0x33, 0xdd, 0xb0, 0x00, 0xe2, + 0x09, 0x37, 0x06, 0xdd, 0x17, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x16, 0x30, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, + 0xd6, 0x79, 0x04, 0x01, 0x01, 0x04, 0x04, 0x02, 0x02, 0x1c, 0xb2, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x8e, 0x2d, 0x13, 0x1e, 0x60, + 0xaa, 0xda, 0x52, 0x53, 0x55, 0x64, 0x3a, 0xdc, 0xb6, 0x7a, 0xc0, 0xba, + 0xfa, 0xeb, 0x20, 0xab, 0xb6, 0x63, 0xcf, 0xcd, 0x9b, 0xdb, 0x71, 0xf3, + 0xa0, 0xd6, 0x91, 0xbf, 0x0c, 0xc1, 0xae, 0x8f, 0x02, 0x18, 0x00, 0x54, + 0xfb, 0x49, 0x03, 0x34, 0x8d, 0x92, 0x9d, 0x5d, 0x8d, 0xa8, 0x1c, 0x20, + 0x0f, 0x85, 0x60, 0xf9, 0xf6, 0x8b, 0xbb, 0x2b, 0x82, 0xce, 0xb3, 0xe2, + 0x91, 0xe7, 0xbd, 0x91, 0x61, 0x52, 0x36, 0x40, 0x9f, 0x2f, 0x5e, 0xa6, + 0x5d, 0x2f, 0xb3, 0x81, 0xe7, 0xf1, 0x87, 0xbe, 0xc5, 0x9d, 0x67, 0x5a, + 0xf7, 0x41, 0x1e, 0x73, 0xb0, 0x1e, 0xdc, 0x4f, 0x8d, 0x53, 0x21, 0x38, + 0x1b, 0xfd, 0x92, 0x43, 0x68, 0x83, 0x03, 0xd0, 0x9a, 0xca, 0x92, 0x14, + 0x73, 0x04, 0x94, 0x2a, 0x93, 0x22, 0x60, 0x5e, 0xee, 0xb6, 0xec, 0x0f, + 0xb0, 0xc8, 0x92, 0x97, 0xfb, 0x5d, 0xed, 0x1f, 0xa0, 0x5f, 0xe4, 0x98, + 0x2f, 0xf6, 0x13, 0x78, 0x99, 0xec, 0xb3, 0xf1, 0x0d, 0x27, 0xaa, 0x19, + 0x95, 0x39, 0xdb, 0xb0, 0x7b, 0x96, 0x74, 0x03, 0x5e, 0x51, 0xf5, 0x15, + 0x27, 0xce, 0xca, 0x0b, 0x2a, 0x0d, 0x43, 0xb3, 0x68, 0x17, 0x1e, 0x11, + 0x60, 0xd9, 0x84, 0x9b, 0xc3, 0x53, 0xce, 0xbd, 0xf4, 0x61, 0x51, 0x4b, + 0x41, 0x00, 0x7e, 0xe1, 0x5f, 0x69, 0xb3, 0x4a, 0x89, 0x7e, 0x47, 0x67, + 0xfd, 0x76, 0xf8, 0x94, 0x2f, 0x72, 0xb6, 0x14, 0x08, 0x2c, 0x16, 0x4e, + 0x9d, 0x37, 0x62, 0xbf, 0x11, 0x67, 0xc0, 0x70, 0x71, 0xec, 0x55, 0x51, + 0x4e, 0x46, 0x76, 0xb4, 0xc3, 0xeb, 0x52, 0x06, 0x17, 0x06, 0xce, 0x61, + 0x43, 0xce, 0x26, 0x80, 0x68, 0xb6, 0x2d, 0x57, 0xba, 0x8c, 0x7d, 0xb7, + 0xc5, 0x05, 0x2c, 0xf8, 0xa3, 0x69, 0xf8, 0x96, 0xad, 0xac, 0xd1, 0x30, + 0x82, 0x05, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x10, 0x73, 0xd1, 0xe1, 0x1d, 0xa9, 0x75, 0xfd, 0x0c, 0xda, 0x7f, + 0xfa, 0x43, 0x3c, 0x26, 0xbd, 0x3d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7e, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, + 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, + 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, + 0x76, 0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, + 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, + 0x33, 0x31, 0x34, 0x30, 0x33, 0x30, 0x32, 0x34, 0x31, 0x5a, 0x17, 0x0d, + 0x32, 0x37, 0x30, 0x33, 0x31, 0x34, 0x30, 0x33, 0x30, 0x32, 0x34, 0x31, + 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, + 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, + 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x30, 0x30, + 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x27, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x3b, 0x20, 0x73, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x33, 0x34, 0x36, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0x45, 0x13, 0xf2, + 0xb2, 0xcb, 0x4b, 0x0f, 0xb4, 0x44, 0x25, 0x9c, 0x8a, 0x68, 0x54, 0xd5, + 0x45, 0x1e, 0x15, 0x89, 0x5b, 0xb8, 0xce, 0xda, 0x5a, 0x42, 0xe6, 0x9a, + 0x8c, 0xc1, 0xcb, 0xe8, 0xc5, 0xf5, 0x8f, 0x49, 0x0e, 0x02, 0xef, 0x5e, + 0x97, 0x1a, 0x91, 0xa4, 0x94, 0xc3, 0x50, 0x13, 0xe5, 0x13, 0xb7, 0x7f, + 0x26, 0x53, 0x19, 0xb0, 0x37, 0xa5, 0xef, 0xe6, 0x2a, 0x39, 0xdc, 0x93, + 0x37, 0xe2, 0x3d, 0x7f, 0xcb, 0x4b, 0x93, 0xa2, 0xc3, 0x69, 0x78, 0xc9, + 0x01, 0xfa, 0x68, 0x3b, 0xe0, 0xe2, 0x22, 0x6c, 0xeb, 0xe4, 0x8a, 0xa8, + 0x3e, 0xf5, 0x20, 0x82, 0xa8, 0x62, 0x68, 0x59, 0x78, 0x24, 0xde, 0xef, + 0x47, 0x43, 0xb1, 0x6c, 0x38, 0x29, 0xd3, 0x69, 0x3f, 0xae, 0x35, 0x57, + 0x75, 0x80, 0xc9, 0x21, 0xe7, 0x01, 0xb9, 0x54, 0x8b, 0x6e, 0x4e, 0x2e, + 0x5a, 0x5b, 0x77, 0xa4, 0x22, 0xc2, 0x7b, 0x95, 0xb9, 0x39, 0x2c, 0xbd, + 0xc2, 0x1e, 0x02, 0xa6, 0xb2, 0xbc, 0x0f, 0x7a, 0xcb, 0xdc, 0xbc, 0xbc, + 0x90, 0x66, 0xe3, 0xca, 0x46, 0x53, 0x3e, 0x98, 0xff, 0x2e, 0x78, 0x9f, + 0xd3, 0xa1, 0x12, 0x93, 0x66, 0x7d, 0xcc, 0x94, 0x6b, 0xec, 0x19, 0x0e, + 0x20, 0x45, 0x22, 0x57, 0x6d, 0x9e, 0xd0, 0x89, 0xf2, 0xa9, 0x34, 0xdc, + 0xab, 0xa5, 0x73, 0x47, 0x38, 0xe3, 0x7f, 0x98, 0x3a, 0x61, 0xae, 0x6c, + 0x4d, 0xf2, 0x31, 0x90, 0xcb, 0x83, 0xc1, 0xee, 0xb4, 0xf2, 0x9a, 0x28, + 0x5f, 0xbb, 0x7d, 0x89, 0xdf, 0xa2, 0x31, 0xb6, 0x1d, 0x39, 0x2b, 0x70, + 0xbf, 0x1e, 0xad, 0xe1, 0x74, 0x94, 0x1d, 0xf8, 0xc5, 0x1a, 0x8d, 0x13, + 0x45, 0xf0, 0x6a, 0x80, 0x0c, 0x5d, 0xbb, 0x46, 0x8a, 0x43, 0xd0, 0xff, + 0x21, 0x39, 0x57, 0x53, 0x5b, 0x51, 0xf8, 0xa2, 0x8f, 0x7f, 0x27, 0xc7, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x10, 0x30, 0x82, 0x01, + 0x0c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, + 0x04, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xe8, 0xe9, 0xac, 0x16, 0x5c, 0x5e, 0xb2, 0xe8, 0xeb, 0xff, 0x57, 0x27, + 0x20, 0x08, 0x72, 0x63, 0x9b, 0xe5, 0xb5, 0x16, 0x30, 0x81, 0xb2, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0xaa, 0x30, 0x81, 0xa7, 0x80, 0x14, + 0x04, 0x94, 0x66, 0xaa, 0xf9, 0x61, 0x89, 0xb6, 0xdb, 0xb5, 0xf7, 0x13, + 0x38, 0x3d, 0x62, 0x84, 0xb8, 0x18, 0x0a, 0x8f, 0xa1, 0x81, 0x83, 0xa4, + 0x81, 0x80, 0x30, 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, + 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, + 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, + 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, + 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, + 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x82, 0x09, + 0x00, 0xdf, 0x86, 0x05, 0x31, 0x01, 0xbe, 0x9a, 0x9a, 0x30, 0x12, 0x06, + 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x04, 0x01, 0x01, 0x04, + 0x04, 0x02, 0x02, 0x1c, 0xb2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, + 0x00, 0x25, 0xce, 0xd2, 0x02, 0x48, 0xbb, 0xbe, 0xfc, 0xb6, 0xa4, 0x87, + 0x87, 0xe0, 0x21, 0x7d, 0xfa, 0x23, 0xc3, 0x0d, 0x73, 0x8f, 0x46, 0xe7, + 0x09, 0x59, 0xda, 0x2e, 0x55, 0x59, 0xff, 0x3c, 0x1b, 0xf6, 0xf8, 0x9a, + 0xc4, 0x1c, 0xf7, 0xac, 0xca, 0xe7, 0x63, 0xf2, 0xc7, 0xd6, 0x0c, 0x2d, + 0xa6, 0xad, 0x55, 0xf4, 0x10, 0x0e, 0xa8, 0x82, 0x0f, 0x88, 0xb5, 0x44, + 0xe8, 0x8e, 0x84, 0x08, 0xf7, 0xdd, 0xe7, 0x10, 0xce, 0x71, 0x56, 0x57, + 0x3f, 0xed, 0x48, 0xee, 0xe2, 0x5d, 0x08, 0x0a, 0x58, 0xe4, 0xfe, 0xbc, + 0x8c, 0x27, 0x1a, 0x46, 0x3f, 0xd5, 0x2d, 0xdb, 0x0b, 0x71, 0x73, 0xd1, + 0x49, 0xf3, 0x5c, 0x86, 0x4d, 0x0a, 0xe1, 0xeb, 0x53, 0x21, 0x38, 0x4f, + 0xec, 0x1e, 0xc2, 0x68, 0x1f, 0x7d, 0xa6, 0x33, 0xe9, 0xa5, 0x37, 0x2a, + 0xef, 0xcd, 0x78, 0x56, 0xb3, 0x39, 0x60, 0xf4, 0xa5, 0xf9, 0x2b, 0x85, + 0xcf, 0xe6, 0x1c, 0x7c, 0x8a, 0x5d, 0xe8, 0x26, 0x02, 0xcf, 0x7a, 0x56, + 0x1f, 0xae, 0x0d, 0x71, 0x20, 0xee, 0xec, 0x3b, 0xae, 0x95, 0x25, 0x15, + 0xc8, 0xf6, 0x92, 0x5d, 0xb8, 0x9b, 0xc2, 0xb4, 0x95, 0x33, 0x13, 0x76, + 0x45, 0xbe, 0x21, 0xe2, 0x3a, 0x69, 0x66, 0xd7, 0xff, 0x22, 0x00, 0x89, + 0xc9, 0x44, 0xb6, 0x54, 0x38, 0x1f, 0x33, 0xe4, 0xda, 0x7b, 0x87, 0xf3, + 0x23, 0xed, 0xf5, 0x16, 0x08, 0xbe, 0x4b, 0xea, 0x91, 0x8f, 0x91, 0x8b, + 0x4e, 0xd1, 0x02, 0x06, 0xa2, 0x77, 0x15, 0x03, 0x46, 0x11, 0x7d, 0x5b, + 0xea, 0x7a, 0xf6, 0x86, 0x7d, 0x96, 0xb7, 0x73, 0x9b, 0x5b, 0x32, 0xc3, + 0xf8, 0x92, 0x36, 0xe3, 0xe3, 0x2f, 0xe8, 0xf1, 0x72, 0xec, 0x0d, 0x50, + 0xd4, 0x86, 0xc5, 0x62, 0x83, 0xf1, 0x2a, 0x4c, 0xd1, 0xbf, 0x76, 0x62, + 0xd4, 0x21, 0x11, 0x68, 0xb2, 0xd6, 0x8d, 0xc4, 0xf8, 0xe4, 0x70, 0x85, + 0x19, 0xa7, 0x82, 0x27, 0x2c, 0x24, 0x21, 0x7a, 0x3b, 0xad, 0x8a, 0xd3, + 0xae, 0xda, 0x78, 0x3c, 0x6c, 0xab, 0xa2, 0xaa, 0x36, 0xf0, 0x1c, 0x58, + 0xd4, 0x72, 0x5e, 0xe8, 0x8b, 0x41, 0x08, 0xf5, 0x85, 0xdd, 0xee, 0x99, + 0x12, 0xf4, 0xd6, 0x41, 0x83, 0x69, 0xe7, 0x79, 0x19, 0xa3, 0x74, 0xc4, + 0x34, 0x2a, 0x8a, 0x7e, 0x4d, 0xbb, 0x2c, 0x49, 0x19, 0xf7, 0x98, 0x98, + 0xfc, 0x81, 0xf7, 0x9b, 0x7f, 0xff, 0xd9, 0x66, 0xf4, 0x51, 0x14, 0x29, + 0x2a, 0x14, 0x1d, 0x4f, 0xbd, 0x91, 0xba, 0x6f, 0x32, 0x34, 0x3c, 0x40, + 0x28, 0x6c, 0x97, 0xf8, 0x6d, 0x38, 0xcd, 0xa3, 0x7b, 0x18, 0xc8, 0x77, + 0x58, 0x4d, 0x53, 0x30, 0x7f, 0x4d, 0x89, 0xca, 0x95, 0x6e, 0xb5, 0xb8, + 0x8e, 0xc8, 0x2d, 0x18, 0x2f, 0x52, 0x2a, 0xde, 0xac, 0x56, 0x8d, 0x8c, + 0x67, 0x14, 0xf6, 0xb9, 0xf1, 0x65, 0xd3, 0x22, 0x43, 0xa3, 0x98, 0x42, + 0x20, 0x43, 0x4c, 0xdf, 0xf2, 0xeb, 0x31, 0x8c, 0x0e, 0x53, 0x5b, 0x99, + 0x82, 0xc3, 0x48, 0x04, 0x53, 0xad, 0x96, 0xb6, 0x9f, 0x52, 0xcc, 0x01, + 0xc8, 0xb3, 0x87, 0x6b, 0x9e, 0xea, 0xa9, 0xeb, 0xda, 0xac, 0xf9, 0x6f, + 0xde, 0xa1, 0x44, 0x32, 0x52, 0x49, 0x47, 0xff, 0x65, 0x79, 0x1e, 0xc5, + 0x73, 0x17, 0xb3, 0x36, 0xfc, 0x45, 0xca, 0x90, 0x37, 0x59, 0x1e, 0x16, + 0xab, 0x09, 0x69, 0xcf, 0xda, 0x56, 0x51, 0xfd, 0xeb, 0xcf, 0xcb, 0x8f, + 0xb1, 0xc3, 0x45, 0x2b, 0x7c, 0x0a, 0xa5, 0x9c, 0x0d, 0x2c, 0xad, 0x1c, + 0xd3, 0x33, 0xdd, 0xfe, 0x93, 0x69, 0xa2, 0x4b, 0x4b, 0xcf, 0x1d, 0x20, + 0x98, 0x4a, 0x4f, 0x5b, 0xe9, 0x24, 0xca, 0xfa, 0x18, 0x11, 0x81, 0x8b, + 0x7a, 0xb4, 0x5a, 0xc8, 0xdf, 0x6f, 0x5f, 0x21, 0x07, 0x31, 0x00}; const uint32_t kOemCertSystemId = 7346; @@ -235,17 +237,29 @@ const uint32_t kOemCertSystemId = 7346; namespace wvcdm { -class CryptoSessionForTest: public CryptoSession { +class CryptoSessionForTest : public CryptoSession, public testing::Test { public: using CryptoSession::ExtractSystemIdFromOemCert; + CryptoSessionForTest() : CryptoSession(metrics_.GetCryptoMetrics()) {} + + void SetUp() { + // crypto_metrics_.crypto_session_security_level_.Record(kSecurityLevelL3); + } + + KeySession* key_session() { return key_session_.get(); } + + private: + static metrics::SessionMetrics metrics_; }; +metrics::SessionMetrics CryptoSessionForTest::metrics_; + TEST(CryptoSessionTest, CanExtractSystemIdFromOemCertificate) { std::string oem_cert(reinterpret_cast(kOemCert), sizeof(kOemCert)); uint32_t system_id; - ASSERT_TRUE(CryptoSessionForTest::ExtractSystemIdFromOemCert(oem_cert, - &system_id)); + ASSERT_TRUE( + CryptoSessionForTest::ExtractSystemIdFromOemCert(oem_cert, &system_id)); ASSERT_EQ(kOemCertSystemId, system_id); } diff --git a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp index d718568b..26a4d8e9 100644 --- a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp @@ -441,7 +441,7 @@ TEST_F(InitializationDataTest, ExtractSubLicense) { InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, kSubLicensePsshBox); ASSERT_FALSE(init_data.IsEmpty()); std::vector keys = - init_data.ExtractEmbeddedKeys(); + init_data.ExtractSublicenseKeys(); ASSERT_EQ(keys.size(), 2UL); EXPECT_EQ(keys[0].sub_session_key_id(), "sub_session_key_id_0"); EXPECT_EQ(keys[1].sub_session_key_id(), "sub_session_key_id_1"); @@ -453,7 +453,7 @@ TEST_F(InitializationDataTest, ExtractEmptySubLicense) { InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, kWidevinePssh); ASSERT_FALSE(init_data.IsEmpty()); std::vector keys = - init_data.ExtractEmbeddedKeys(); + init_data.ExtractSublicenseKeys(); ASSERT_TRUE(keys.empty()); } @@ -644,8 +644,8 @@ TEST_P(HlsConstructionTest, InitData) { for (size_t i = 0; i < param.key_ids_.size(); ++i) { bool key_id_found = false; if (param.key_ids_[i].size() != 32) continue; - for (int j = 0; j < cenc_header.key_id_size(); ++j) { - if (param.key_ids_[i] == b2a_hex(cenc_header.key_id(j))) { + for (int j = 0; j < cenc_header.key_ids_size(); ++j) { + if (param.key_ids_[i] == b2a_hex(cenc_header.key_ids(j))) { key_id_found = true; break; } @@ -752,7 +752,7 @@ TEST_P(HlsParseTest, Parse) { } else if (param.key_.compare(kJsonContentId) == 0) { EXPECT_EQ(param.value_, cenc_header.content_id()); } else if (param.key_.compare(kJsonKeyIds) == 0) { - EXPECT_EQ(param.value_, b2a_hex(cenc_header.key_id(0))); + EXPECT_EQ(param.value_, b2a_hex(cenc_header.key_ids(0))); } EXPECT_EQ(kHlsIvHexValue, b2a_hex(init_data.hls_iv())); diff --git a/libwvdrmengine/cdm/metrics/include/metrics_collections.h b/libwvdrmengine/cdm/metrics/include/metrics_collections.h index 435edb58..ccde3ed2 100644 --- a/libwvdrmengine/cdm/metrics/include/metrics_collections.h +++ b/libwvdrmengine/cdm/metrics/include/metrics_collections.h @@ -131,6 +131,7 @@ class CryptoMetrics { ValueMetric oemcrypto_is_anti_rollback_hw_present_; ValueMetric oemcrypto_is_keybox_valid_; EventMetric oemcrypto_load_device_rsa_key_; + EventMetric oemcrypto_load_entitled_keys_; EventMetric oemcrypto_load_keys_; ValueMetric oemcrypto_max_hdcp_capability_; ValueMetric oemcrypto_max_number_of_sessions_; diff --git a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp index 303bb6b4..13e76ce8 100644 --- a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp +++ b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp @@ -196,6 +196,9 @@ CryptoMetrics::CryptoMetrics() : oemcrypto_load_device_rsa_key_( "/drm/widevine/oemcrypto/load_device_rsa_key/time", "oemcrypto_error"), + oemcrypto_load_entitled_keys_( + "/drm/widevine/oemcrypto/load_entitled_keys/time", + "oemcrypto_error"), oemcrypto_load_keys_( "/drm/widevine/oemcrypto/load_keys/time", "oemcrypto_error"), @@ -277,6 +280,7 @@ void CryptoMetrics::Serialize(MetricsGroup* metrics) { oemcrypto_is_anti_rollback_hw_present_.Serialize(&serializer); oemcrypto_is_keybox_valid_.Serialize(&serializer); oemcrypto_load_device_rsa_key_.Serialize(&serializer); + oemcrypto_load_entitled_keys_.Serialize(&serializer); oemcrypto_load_keys_.Serialize(&serializer); oemcrypto_max_hdcp_capability_.Serialize(&serializer); oemcrypto_max_number_of_sessions_.Serialize(&serializer); diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index f7b38594..5590a431 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -1112,7 +1112,7 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { if (kHttpOk != http_status_code) { LogResponseError(message, http_status_code); } - EXPECT_EQ(kHttpOk, http_status_code); + EXPECT_EQ(kHttpOk, http_status_code) << message; std::string drm_msg; if (kHttpOk == http_status_code) { diff --git a/libwvdrmengine/cdm/test/unit-test.mk b/libwvdrmengine/cdm/test/unit-test.mk index 33c6007d..a3330769 100644 --- a/libwvdrmengine/cdm/test/unit-test.mk +++ b/libwvdrmengine/cdm/test/unit-test.mk @@ -56,8 +56,6 @@ LOCAL_PROPRIETARY_MODULE := true # When built, explicitly put it in the DATA/bin directory. LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/bin -LOCAL_PROPRIETARY_MODULE := true - ifneq ($(TARGET_ENABLE_MEDIADRM_64), true) LOCAL_MODULE_TARGET_ARCH := arm x86 mips endif diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h index 2860ac02..3c5f3b39 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h @@ -95,8 +95,8 @@ class EntitlementKey : public Key { const std::vector& content_key() { return content_key_; } const std::vector& content_key_id() { return content_key_id_; } const std::vector& entitlement_key() { return Key::value(); } - bool SetContentKey(const std::vector& content_key, - const std::vector& content_key_id) { + bool SetContentKey(const std::vector& content_key_id, + const std::vector& content_key) { content_key_.assign(content_key.begin(), content_key.end()); content_key_id_.assign(content_key_id.begin(), content_key_id.end()); return true; diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp index 1e63af81..4a809853 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp @@ -73,9 +73,8 @@ class ContentKeysContext : public SessionContextKeys { virtual bool SetContentKey(const KeyId& entitlement_id, const KeyId& content_key_id, const std::vector& content_key); - virtual bool GetEntitlementKey( - const KeyId& entitlement_id, - const std::vector** entitlement_key); + virtual bool GetEntitlementKey(const KeyId& entitlement_id, + const std::vector** entitlement_key); private: SessionKeyTable session_keys_; @@ -105,8 +104,8 @@ bool ContentKeysContext::SetContentKey( return false; } -bool ContentKeysContext::GetEntitlementKey( - const KeyId& entitlement_id, const std::vector** key) { +bool ContentKeysContext::GetEntitlementKey(const KeyId& entitlement_id, + const std::vector** key) { // Unsupported action for this type. return false; }; @@ -314,7 +313,7 @@ bool SessionContext::GenerateSignature(const uint8_t* message, return false; } - const uint8_t *mac_key = NULL; + const uint8_t* mac_key = NULL; bool using_usage_mac_key_client = false; if (mac_key_client_.size() == wvcdm::MAC_KEY_SIZE) { // If we have a mac key, use it. @@ -333,7 +332,7 @@ bool SessionContext::GenerateSignature(const uint8_t* message, } if (using_usage_mac_key_client && - LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LogCategoryEnabled(kLoggingDumpDerivedKeys)) { std::vector usage_entry_mac_key_client( usage_entry_->mac_key_client(), usage_entry_->mac_key_client() + wvcdm::MAC_KEY_SIZE * sizeof(uint8_t)); @@ -620,9 +619,9 @@ OEMCryptoResult SessionContext::LoadKeys( key_control_iv.assign(key_array[i].key_control_iv, key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); - OEMCryptoResult result = InstallKey( - key_id, enc_key_data, key_data_iv, key_control, key_control_iv, - second_license); + OEMCryptoResult result = + InstallKey(key_id, enc_key_data, key_data_iv, key_control, + key_control_iv, second_license); if (result != OEMCrypto_SUCCESS) { status = result; break; @@ -683,8 +682,7 @@ OEMCryptoResult SessionContext::LoadKeys( } OEMCryptoResult SessionContext::LoadEntitledContentKeys( - size_t num_keys, - const OEMCrypto_EntitledContentKeyObject* key_array) { + size_t num_keys, const OEMCrypto_EntitledContentKeyObject* key_array) { if (!key_array) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -694,9 +692,9 @@ OEMCryptoResult SessionContext::LoadEntitledContentKeys( for (size_t i = 0; i < num_keys; ++i) { const OEMCrypto_EntitledContentKeyObject* key_data = &key_array[i]; std::vector entitlement_key_id; - entitlement_key_id.assign(key_data->entitlement_key_id, - key_data->entitlement_key_id + - key_data->entitlement_key_id_length); + entitlement_key_id.assign( + key_data->entitlement_key_id, + key_data->entitlement_key_id + key_data->entitlement_key_id_length); const std::vector* entitlement_key = NULL; if (!session_keys_->GetEntitlementKey(entitlement_key_id, @@ -716,12 +714,12 @@ OEMCryptoResult SessionContext::LoadEntitledContentKeys( content_key_id.assign( key_data->content_key_id, key_data->content_key_id + key_data->content_key_id_length); - if (!DecryptEntitlement(*entitlement_key, iv, - encrypted_content_key, &content_key)) { + if (!DecryptMessage(*entitlement_key, iv, encrypted_content_key, + &content_key)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (!session_keys_->SetContentKey( - entitlement_key_id, content_key_id, content_key)) { + if (!session_keys_->SetContentKey(entitlement_key_id, content_key_id, + content_key)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } @@ -732,8 +730,7 @@ OEMCryptoResult SessionContext::InstallKey( const KeyId& key_id, const std::vector& key_data, const std::vector& key_data_iv, const std::vector& key_control, - const std::vector& key_control_iv, - bool second_license) { + const std::vector& key_control_iv, bool second_license) { // Decrypt encrypted key_data using derived encryption key and offered iv std::vector content_key; std::vector key_control_str; @@ -1339,26 +1336,7 @@ bool SessionContext::DecryptMessage(const std::vector& key, uint8_t iv_buffer[16]; memcpy(iv_buffer, &iv[0], 16); AES_KEY aes_key; - AES_set_decrypt_key(&key[0], 128, &aes_key); - AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), &aes_key, - iv_buffer, AES_DECRYPT); - return true; -} - -bool SessionContext::DecryptEntitlement( - const std::vector& key, - const std::vector& iv, - const std::vector& message, - std::vector* decrypted) { - if (key.empty() || iv.empty() || message.empty() || !decrypted) { - LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); - return false; - } - decrypted->resize(message.size()); - uint8_t iv_buffer[16]; - memcpy(iv_buffer, &iv[0], 16); - AES_KEY aes_key; - AES_set_decrypt_key(&key[0], 256, &aes_key); + AES_set_decrypt_key(&key[0], key.size() * 8, &aes_key); AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), &aes_key, iv_buffer, AES_DECRYPT); return true; diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h index edba1dfa..ea653a50 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h @@ -190,10 +190,6 @@ class SessionContext { const std::vector& iv, const std::vector& message, std::vector* decrypted); - bool DecryptEntitlement(const std::vector& key, - const std::vector& iv, - const std::vector& message, - std::vector* decrypted); // Either verify the nonce or usage entry, as required by the key control // block. OEMCryptoResult CheckNonceOrEntry(const KeyControlBlock& key_control_block); diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index c5afd7c4..bcfa5330 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -566,7 +566,7 @@ void Session::FillRefreshMessage(size_t key_count, uint32_t control_bits, encrypted_license().keys[i].key_id_length); if (global_features.api_version == 14) { // For version 14, we require OEMCrypto to handle kc14 for all licenses. - memcpy(license_.keys[i].control.verification, "kc14", 4); + memcpy(encrypted_license().keys[i].control.verification, "kc14", 4); } else if (global_features.api_version == 13) { // For version 13, we require OEMCrypto to handle kc13 for all licenses. memcpy(encrypted_license().keys[i].control.verification, "kc13", 4); @@ -596,7 +596,8 @@ void Session::EncryptAndSign() { for (unsigned int i = 0; i < num_keys_; i++) { memcpy(iv_buffer, &license_.keys[i].control_iv[0], wvcdm::KEY_IV_SIZE); - AES_set_encrypt_key(&license_.keys[i].key_data[0], 128, &aes_key); + AES_set_encrypt_key(&license_.keys[i].key_data[0], + license_.keys[i].key_data_length * 8, &aes_key); AES_cbc_encrypt( reinterpret_cast(&license_.keys[i].control), reinterpret_cast(&encrypted_license().keys[i].control), diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 76155408..5116385f 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -5265,7 +5265,7 @@ TEST_F(UsageTableDefragTest, ReloadUsageEntryBadData) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); vector data = s.encrypted_usage_entry(); - ASSERT_LT(0, data.size()); + ASSERT_LT(0UL, data.size()); data[0] ^= 42; // Error could be signature or verification error. ASSERT_NE(OEMCrypto_SUCCESS,