diff --git a/libwvdrmengine/cdm/core/include/cdm_client_property_set.h b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h index a081a285..853c7658 100644 --- a/libwvdrmengine/cdm/core/include/cdm_client_property_set.h +++ b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h @@ -13,9 +13,9 @@ class CdmClientPropertySet { public: virtual ~CdmClientPropertySet() {} - virtual std::string security_level() const = 0; + virtual const std::string& security_level() const = 0; virtual bool use_privacy_mode() const = 0; - virtual std::vector service_certificate() const = 0; + virtual const std::string& service_certificate() const = 0; virtual bool is_session_sharing_enabled() const = 0; virtual uint32_t session_sharing_id() const = 0; virtual void set_session_sharing_id(uint32_t id) = 0; diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index f0b3205d..4dc6e2f7 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -3,15 +3,16 @@ #ifndef WVCDM_CORE_CDM_ENGINE_H_ #define WVCDM_CORE_CDM_ENGINE_H_ +#include "cdm_session.h" #include "certificate_provisioning.h" #include "initialization_data.h" #include "oemcrypto_adapter.h" +#include "scoped_ptr.h" #include "wv_cdm_types.h" namespace wvcdm { class CdmClientPropertySet; -class CdmSession; class CryptoEngine; class WvCdmEventListener; @@ -24,98 +25,94 @@ class CdmEngine { virtual ~CdmEngine(); // Session related methods - CdmResponseType OpenSession(const CdmKeySystem& key_system, - const CdmClientPropertySet* property_set, - CdmSessionId* session_id); - CdmResponseType CloseSession(const CdmSessionId& session_id); + virtual CdmResponseType OpenSession(const CdmKeySystem& key_system, + const CdmClientPropertySet* property_set, + CdmSessionId* session_id); + virtual CdmResponseType CloseSession(const CdmSessionId& session_id); - CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); - CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); + virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); + virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); // License related methods // Construct a valid license request - CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, - const CdmKeySetId& key_set_id, - const InitializationData& init_data, - const CdmLicenseType license_type, - CdmAppParameterMap& app_parameters, - CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateKeyRequest( + const CdmSessionId& session_id, const CdmKeySetId& key_set_id, + const InitializationData& init_data, const CdmLicenseType license_type, + CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, + std::string* server_url); // Accept license response and extract key info. - CdmResponseType AddKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data, - CdmKeySetId* key_set_id); + virtual CdmResponseType AddKey(const CdmSessionId& session_id, + const CdmKeyResponse& key_data, + CdmKeySetId* key_set_id); - CdmResponseType RestoreKey(const CdmSessionId& session_id, - const CdmKeySetId& key_set_id); + virtual CdmResponseType RestoreKey(const CdmSessionId& session_id, + const CdmKeySetId& key_set_id); - CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); + virtual CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); // Construct valid renewal request for the current session keys. - CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, - CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, + CdmKeyMessage* key_request, + std::string* server_url); // Accept renewal response and update key info. - CdmResponseType RenewKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data); + virtual CdmResponseType RenewKey(const CdmSessionId& session_id, + const CdmKeyResponse& key_data); // Query system information - CdmResponseType QueryStatus(CdmQueryMap* info); + virtual CdmResponseType QueryStatus(CdmQueryMap* info); // Query session information virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id, CdmQueryMap* key_info); // Query license information - CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, - CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, + CdmQueryMap* key_info); // Query seesion control information - CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, - CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, + CdmQueryMap* key_info); // Provisioning related methods - CdmResponseType GetProvisioningRequest( - CdmCertificateType cert_type, - const std::string& cert_authority, - CdmProvisioningRequest* request, - std::string* default_url); + virtual CdmResponseType GetProvisioningRequest( + CdmCertificateType cert_type, const std::string& cert_authority, + CdmProvisioningRequest* request, std::string* default_url); - CdmResponseType HandleProvisioningResponse( - CdmProvisioningResponse& response, - std::string* cert, + virtual CdmResponseType HandleProvisioningResponse( + CdmProvisioningResponse& response, std::string* cert, std::string* wrapped_key); + virtual CdmResponseType Unprovision(CdmSecurityLevel security_level); + // Usage related methods for streaming licenses - CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info); - CdmResponseType ReleaseUsageInfo(const CdmUsageInfoReleaseMessage& message); + virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info); + virtual CdmResponseType ReleaseUsageInfo( + const CdmUsageInfoReleaseMessage& message); // Decryption and key related methods // Accept encrypted buffer and return decrypted data. - CdmResponseType Decrypt(const CdmSessionId& session_id, - const CdmDecryptionParameters& parameters); + virtual CdmResponseType Decrypt(const CdmSessionId& session_id, + const CdmDecryptionParameters& parameters); - size_t SessionSize() const { return sessions_.size(); } + virtual size_t SessionSize() const { return sessions_.size(); } // Is the key known to any session? - bool IsKeyLoaded(const KeyId& key_id); - bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId); + virtual bool IsKeyLoaded(const KeyId& key_id); + virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId); // Event listener related methods - bool AttachEventListener(const CdmSessionId& session_id, - WvCdmEventListener* listener); - bool DetachEventListener(const CdmSessionId& session_id, - WvCdmEventListener* listener); + virtual bool AttachEventListener(const CdmSessionId& session_id, + WvCdmEventListener* listener); + virtual bool DetachEventListener(const CdmSessionId& session_id, + WvCdmEventListener* listener); // Timer expiration method - void OnTimerEvent(); + virtual void OnTimerEvent(); private: // private methods - // Cancel all sessions - bool CancelSessions(); bool ValidateKeySystem(const CdmKeySystem& key_system); void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); @@ -125,8 +122,11 @@ class CdmEngine { CdmReleaseKeySetMap release_key_sets_; CertificateProvisioning cert_provisioning_; SecurityLevel cert_provisioning_requested_security_level_; - CdmSession* usage_session_; + static bool seeded_; + + // usage related variables + scoped_ptr usage_session_; int64_t last_usage_information_update_time; CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine); diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index b10592fc..a675b5cb 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -22,84 +22,83 @@ class WvCdmEventListener; class CdmSession { public: explicit CdmSession(const CdmClientPropertySet* cdm_client_property_set); - ~CdmSession(); + virtual ~CdmSession(); - CdmResponseType Init(); + virtual CdmResponseType Init(); - CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id, - const CdmLicenseType license_type); - CdmResponseType RestoreUsageSession(const CdmKeyMessage& key_request, - const CdmKeyResponse& key_response); + virtual CdmResponseType RestoreOfflineSession( + const CdmKeySetId& key_set_id, const CdmLicenseType license_type); + virtual CdmResponseType RestoreUsageSession( + const CdmKeyMessage& key_request, const CdmKeyResponse& key_response); - void set_key_system(const CdmKeySystem& ksystem) { key_system_ = ksystem; } - const CdmKeySystem& key_system() { return key_system_; } + virtual void set_key_system(const CdmKeySystem& ksystem) { + key_system_ = ksystem; + } + virtual const CdmKeySystem& key_system() { return key_system_; } - const CdmSessionId& session_id() { return session_id_; } + virtual const CdmSessionId& session_id() { return session_id_; } - bool VerifySession(const CdmKeySystem& key_system, - const InitializationData& init_data); - - CdmResponseType GenerateKeyRequest(const InitializationData& init_data, - const CdmLicenseType license_type, - const CdmAppParameterMap& app_parameters, - CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateKeyRequest( + const InitializationData& init_data, const CdmLicenseType license_type, + const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, + std::string* server_url); // AddKey() - Accept license response and extract key info. - CdmResponseType AddKey(const CdmKeyResponse& key_response, - CdmKeySetId* key_set_id); + virtual CdmResponseType AddKey(const CdmKeyResponse& key_response, + CdmKeySetId* key_set_id); // CancelKeyRequest() - Cancel session. - CdmResponseType CancelKeyRequest(); + virtual CdmResponseType CancelKeyRequest(); // Query session status - CdmResponseType QueryStatus(CdmQueryMap* key_info); + virtual CdmResponseType QueryStatus(CdmQueryMap* key_info); // Query license information - CdmResponseType QueryKeyStatus(CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyStatus(CdmQueryMap* key_info); // Query session control info - CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info); // Decrypt() - Accept encrypted buffer and return decrypted data. - CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); + virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); // License renewal // GenerateRenewalRequest() - Construct valid renewal request for the current // session keys. - CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request, + std::string* server_url); // RenewKey() - Accept renewal response and update key info. - CdmResponseType RenewKey(const CdmKeyResponse& key_response); + virtual CdmResponseType RenewKey(const CdmKeyResponse& key_response); // License release // GenerateReleaseRequest() - Construct valid release request for the current // session keys. - CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request, + std::string* server_url); // ReleaseKey() - Accept response and release key. - CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); + virtual CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); - bool IsKeyLoaded(const KeyId& key_id); + virtual bool IsKeyLoaded(const KeyId& key_id); - bool AttachEventListener(WvCdmEventListener* listener); - bool DetachEventListener(WvCdmEventListener* listener); + virtual bool AttachEventListener(WvCdmEventListener* listener); + virtual bool DetachEventListener(WvCdmEventListener* listener); - void OnTimerEvent(); - void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); + virtual void OnTimerEvent(); + virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); - SecurityLevel GetRequestedSecurityLevel(); - CdmSecurityLevel GetSecurityLevel(); + virtual SecurityLevel GetRequestedSecurityLevel(); + virtual CdmSecurityLevel GetSecurityLevel(); - CdmResponseType UpdateUsageInformation(); + virtual CdmResponseType UpdateUsageInformation(); - bool is_usage_update_needed() { return is_usage_update_needed_; } - void reset_is_usage_update_needed() { is_usage_update_needed_ = false; } + virtual bool is_usage_update_needed() { return is_usage_update_needed_; } + virtual void reset_is_usage_update_needed() { + is_usage_update_needed_ = false; + } private: - // Generate unique ID for each new session. CdmSessionId GenerateSessionId(); bool GenerateKeySetId(CdmKeySetId* key_set_id); @@ -115,10 +114,10 @@ class CdmSession { scoped_ptr crypto_session_; PolicyEngine policy_engine_; bool license_received_; - bool reinitialize_session_; bool is_offline_; bool is_release_; bool is_usage_update_needed_; + bool is_initial_decryption_; // information useful for offline and usage scenarios CdmKeyMessage key_request_; @@ -133,10 +132,6 @@ class CdmSession { // license type release and offline related information CdmKeySetId key_set_id_; - // Used for certificate based licensing - std::string wrapped_key_; - bool is_certificate_loaded_; - std::set listeners_; CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession); diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index ecd5020c..d19b20d3 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -19,61 +19,61 @@ typedef std::map CryptoKeyMap; class CryptoSession { public: CryptoSession(); - ~CryptoSession(); + virtual ~CryptoSession(); - bool ValidateKeybox(); - bool GetToken(std::string* token); - CdmSecurityLevel GetSecurityLevel(); - bool GetDeviceUniqueId(std::string* device_id); - bool GetSystemId(uint32_t* system_id); - bool GetProvisioningId(std::string* provisioning_id); + virtual bool ValidateKeybox(); + virtual bool GetToken(std::string* token); + virtual CdmSecurityLevel GetSecurityLevel(); + virtual bool GetDeviceUniqueId(std::string* device_id); + virtual bool GetApiVersion(uint32_t* version); + virtual bool GetSystemId(uint32_t* system_id); + virtual bool GetProvisioningId(std::string* provisioning_id); - CdmResponseType Open() { return Open(kLevelDefault); } - CdmResponseType Open(SecurityLevel requested_security_level); - void Close(); + virtual CdmResponseType Open() { return Open(kLevelDefault); } + virtual CdmResponseType Open(SecurityLevel requested_security_level); + virtual void Close(); - bool IsOpen() { return open_; } - CryptoSessionId oec_session_id() { return oec_session_id_; } + virtual bool IsOpen() { return open_; } + virtual CryptoSessionId oec_session_id() { return oec_session_id_; } // Key request/response - void GenerateRequestId(std::string& req_id_str); - bool PrepareRequest(const std::string& key_deriv_message, - bool is_provisioning, std::string* signature); - bool PrepareRenewalRequest(const std::string& message, - std::string* signature); - 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); - bool LoadCertificatePrivateKey(std::string& wrapped_key); - bool RefreshKeys(const std::string& message, const std::string& signature, - int num_keys, const CryptoKey* key_array); - bool GenerateNonce(uint32_t* nonce); - bool GenerateDerivedKeys(const std::string& message); - bool GenerateDerivedKeys(const std::string& message, - const std::string& session_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); + virtual void GenerateRequestId(std::string& req_id_str); + virtual bool PrepareRequest(const std::string& key_deriv_message, + 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); + virtual bool LoadCertificatePrivateKey(std::string& wrapped_key); + virtual bool RefreshKeys(const std::string& message, + const std::string& signature, int num_keys, + const CryptoKey* key_array); + virtual bool GenerateNonce(uint32_t* nonce); + virtual bool GenerateDerivedKeys(const std::string& message); + virtual bool GenerateDerivedKeys(const std::string& message, + const std::string& session_key); + virtual 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); // Media data path - CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); + virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); - CdmResponseType UpdateUsageInformation(); - CdmResponseType GenerateUsageReport( - const std::string& provider_session_token, - std::string* usage_report); - CdmResponseType ReleaseUsageInformation( - const std::string& message, - const std::string& signature, + virtual CdmResponseType UpdateUsageInformation(); + virtual CdmResponseType GenerateUsageReport( + const std::string& provider_session_token, std::string* usage_report); + virtual CdmResponseType ReleaseUsageInformation( + const std::string& message, const std::string& signature, const std::string& provider_session_token); - bool GetRandom(size_t data_length, uint8_t* random_data); + virtual bool GetRandom(size_t data_length, uint8_t* random_data); private: void Init(); @@ -82,8 +82,8 @@ class CryptoSession { std::string* deriv_context); void GenerateEncryptContext(const std::string& input_context, std::string* deriv_context); - bool GenerateSignature(const std::string& message, bool use_rsa, - std::string* signature); + bool GenerateSignature(const std::string& message, std::string* signature); + bool GenerateRsaSignature(const std::string& message, std::string* signature); size_t GetOffset(std::string message, std::string field); bool SetDestinationBufferType(); diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 23aec697..fe03a32e 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -5,6 +5,10 @@ #include "wv_cdm_types.h" +#if defined(UNIT_TEST) +#include +#endif + namespace wvcdm { class File; @@ -57,23 +61,38 @@ class DeviceFiles { virtual bool RetrieveUsageInfo( std::vector >* usage_info); - // For testing only - static std::string GetCertificateFileName(); - static std::string GetLicenseFileNameExtension(); - static std::string GetUsageInfoFileName(); - void SetTestFile(File* file); - - protected: - bool Hash(const std::string& data, std::string* hash); + private: bool StoreFile(const char* name, const std::string& serialized_file); bool RetrieveFile(const char* name, std::string* serialized_file); - private: // Certificate and offline licenses are now stored in security // level specific directories. In an earlier version they were // stored in a common directory and need to be copied over. virtual void SecurityLevelPathBackwardCompatibility(); + // For testing only: + static std::string GetCertificateFileName(); + static std::string GetLicenseFileNameExtension(); + static std::string GetUsageInfoFileName(); + void SetTestFile(File* file); +#if defined(UNIT_TEST) + FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel); + FRIEND_TEST(DeviceFilesStoreTest, StoreCertificate); + FRIEND_TEST(DeviceFilesStoreTest, StoreLicense); + FRIEND_TEST(DeviceFilesTest, DeleteLicense); + FRIEND_TEST(DeviceFilesTest, ReadCertificate); + FRIEND_TEST(DeviceFilesTest, RetrieveLicenses); + FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility); + FRIEND_TEST(DeviceFilesTest, StoreLicenses); + FRIEND_TEST(DeviceFilesTest, UpdateLicenseState); + FRIEND_TEST(DeviceFilesUsageInfoTest, Delete); + FRIEND_TEST(DeviceFilesUsageInfoTest, Read); + FRIEND_TEST(DeviceFilesUsageInfoTest, Store); + FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest); + FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test); + FRIEND_TEST(WvCdmUsageInfoTest, DISABLED_UsageInfo); +#endif + File* file_; CdmSecurityLevel security_level_; bool initialized_; diff --git a/libwvdrmengine/cdm/core/include/file_store.h b/libwvdrmengine/cdm/core/include/file_store.h index 5794240d..65f4f3d4 100644 --- a/libwvdrmengine/cdm/core/include/file_store.h +++ b/libwvdrmengine/cdm/core/include/file_store.h @@ -7,6 +7,9 @@ #include "wv_cdm_types.h" +#include +#include + namespace wvcdm { // File class. The implementation is platform dependent. diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index 677c73e5..54fc8286 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -23,32 +23,32 @@ class CdmLicense { public: CdmLicense() : session_(NULL), initialized_(false) {} - ~CdmLicense() {} + virtual ~CdmLicense() {} - bool Init(const std::string& token, CryptoSession* session, + virtual bool Init(const std::string& token, CryptoSession* session, PolicyEngine* policy_engine); - bool PrepareKeyRequest(const InitializationData& init_data, + virtual bool PrepareKeyRequest(const InitializationData& init_data, const CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, const CdmSessionId& session_id, CdmKeyMessage* signed_request, std::string* server_url); - bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request, + virtual bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request, std::string* server_url); - CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response); - CdmResponseType HandleKeyUpdateResponse( + virtual CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response); + virtual CdmResponseType HandleKeyUpdateResponse( bool is_renewal, const CdmKeyResponse& license_response); - bool RestoreOfflineLicense(const CdmKeyMessage& license_request, + virtual bool RestoreOfflineLicense(const CdmKeyMessage& license_request, const CdmKeyResponse& license_response, const CdmKeyResponse& license_renewal_response); - bool RestoreUsageLicense(const CdmKeyMessage& license_request, + virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request, const CdmKeyResponse& license_response); - bool HasInitData() { return !stored_init_data_.empty(); } - bool IsKeyLoaded(const KeyId& key_id); + virtual bool HasInitData() { return !stored_init_data_.empty(); } + virtual bool IsKeyLoaded(const KeyId& key_id); - std::string provider_session_token() { return provider_session_token_; } + virtual std::string provider_session_token() { return provider_session_token_; } private: bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request, diff --git a/libwvdrmengine/cdm/core/include/log.h b/libwvdrmengine/cdm/core/include/log.h index 642ad710..c531d267 100644 --- a/libwvdrmengine/cdm/core/include/log.h +++ b/libwvdrmengine/cdm/core/include/log.h @@ -17,6 +17,8 @@ typedef enum { LOG_VERBOSE } LogPriority; +extern LogPriority g_cutoff; + // Enable/disable verbose logging (LOGV). // This function is supplied for cases where the system layer does not // initialize logging. This is also needed to initialize logging in diff --git a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h index 71278c89..aac9ecd5 100644 --- a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h +++ b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h @@ -1,9 +1,5 @@ // Copyright 2013 Google Inc. All Rights Reserved. // -// oemcrypto_adapter.h -// This interface allows CDM to open a Level 3 session instead of -// letting the wrapper function choose between level 1 or level 3. -// #ifndef WVCDM_CORE_OEMCRYPTO_ADAPTER_H_ #define WVCDM_CORE_OEMCRYPTO_ADAPTER_H_ diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index e327391f..8ef323ac 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -10,6 +10,8 @@ namespace wvcdm { +using video_widevine_server::sdk::LicenseIdentification; + class Clock; class PolicyEngineTest; @@ -18,53 +20,46 @@ class PolicyEngineTest; class PolicyEngine { public: PolicyEngine(); - ~PolicyEngine(); + virtual ~PolicyEngine(); // The value returned should be taken as a hint rather than an absolute // status. It is computed during the last call to either SetLicense/ // UpdateLicense/OnTimerEvent/BeginDecryption and may be out of sync // depending on the amount of time elapsed. The current decryption // status is not calculated to avoid overhead in the decryption path. - inline bool can_decrypt() { return can_decrypt_; } + virtual bool can_decrypt() { return can_decrypt_; } // OnTimerEvent is called when a timer fires. It notifies the Policy Engine // that the timer has fired and that it should check whether any events have // occurred since the last timer event. If so, it sets event_occurred to true // and sets event to point to the event that occurred. If not, it sets // event_occurred to false. - void OnTimerEvent(bool* event_occurred, CdmEventType* event); + virtual void OnTimerEvent(bool* event_occurred, CdmEventType* event); // SetLicense is used in handling the initial license response. It stores // an exact copy of the policy information stored in the license. // The license state transitions to kLicenseStateCanPlay if the license // permits playback. - void SetLicense(const video_widevine_server::sdk::License& license); + virtual void SetLicense(const video_widevine_server::sdk::License& license); - // Call this on first decrypt to set the start of playback. This is - // for cases where usage begins not when the license is received, - // but at the start of playback - void BeginDecryption(void); + // Call this on first decrypt to set the start of playback. + virtual void BeginDecryption(void); // UpdateLicense is used in handling a license response for a renewal request. // The response may only contain any policy fields that have changed. In this // case an exact copy is not what we want to happen. We also will receive an // updated license_start_time from the server. The license will transition to // kLicenseStateCanPlay if the license permits playback. - void UpdateLicense(const video_widevine_server::sdk::License& license); + virtual void UpdateLicense( + const video_widevine_server::sdk::License& license); - CdmResponseType Query(CdmQueryMap* key_info); + virtual CdmResponseType Query(CdmQueryMap* key_info); - const video_widevine_server::sdk::LicenseIdentification& license_id() { - return license_id_; - } - - bool IsLicenseDurationExpired(int64_t current_time); - bool IsPlaybackDurationExpired(int64_t current_time); + virtual const LicenseIdentification& license_id() { return license_id_; } private: typedef enum { kLicenseStateInitial, - kLicenseStateInitialPendingUsage, kLicenseStateCanPlay, kLicenseStateNeedRenewal, kLicenseStateWaitingLicenseUpdate, @@ -73,6 +68,11 @@ class PolicyEngine { void Init(Clock* clock); + bool IsLicenseDurationExpired(int64_t current_time); + int64_t GetLicenseDurationRemaining(int64_t current_time); + bool IsPlaybackDurationExpired(int64_t current_time); + int64_t GetPlaybackDurationRemaining(int64_t current_time); + bool IsRenewalDelayExpired(int64_t current_time); bool IsRenewalRecoveryDurationExpired(int64_t current_time); bool IsRenewalRetryIntervalExpired(int64_t current_time); diff --git a/libwvdrmengine/cdm/core/include/privacy_crypto.h b/libwvdrmengine/cdm/core/include/privacy_crypto.h index 69e292a7..309aeecc 100644 --- a/libwvdrmengine/cdm/core/include/privacy_crypto.h +++ b/libwvdrmengine/cdm/core/include/privacy_crypto.h @@ -24,30 +24,27 @@ #include -#include "openssl/evp.h" -#include "openssl/rsa.h" #include "wv_cdm_types.h" namespace wvcdm { class AesCbcKey { public: - AesCbcKey() : initialized_(false) {}; - ~AesCbcKey() {}; + AesCbcKey(); + ~AesCbcKey(); bool Init(const std::string& key); bool Encrypt(const std::string& in, std::string* out, std::string* iv); private: - EVP_CIPHER_CTX ctx_; - bool initialized_; + std::string key_; CORE_DISALLOW_COPY_AND_ASSIGN(AesCbcKey); }; class RsaPublicKey { public: - RsaPublicKey() : key_(NULL) {} + RsaPublicKey(); ~RsaPublicKey(); // Initializes an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey @@ -64,7 +61,7 @@ class RsaPublicKey { const std::string& signature); private: - RSA* key_; + std::string serialized_key_; CORE_DISALLOW_COPY_AND_ASSIGN(RsaPublicKey); }; diff --git a/libwvdrmengine/cdm/core/include/properties.h b/libwvdrmengine/cdm/core/include/properties.h index ea440642..f5406e0f 100644 --- a/libwvdrmengine/cdm/core/include/properties.h +++ b/libwvdrmengine/cdm/core/include/properties.h @@ -26,12 +26,6 @@ class Properties { public: static void Init(); - static inline bool begin_license_usage_when_received() { - return begin_license_usage_when_received_; - } - static inline bool require_explicit_renew_request() { - return require_explicit_renew_request_; - } static inline bool oem_crypto_use_secure_buffers() { return oem_crypto_use_secure_buffers_; } @@ -45,9 +39,6 @@ class Properties { static inline bool use_certificates_as_identification() { return use_certificates_as_identification_; } - static inline bool decrypt_with_empty_session_support() { - return decrypt_with_empty_session_support_; - } static inline bool security_level_path_backward_compatibility_support() { return security_level_path_backward_compatibility_support_; } @@ -62,9 +53,10 @@ class Properties { static bool GetFactoryKeyboxPath(std::string* keybox); static bool GetOEMCryptoPath(std::string* library_name); static bool GetSecurityLevelDirectories(std::vector* dirs); - static const std::string GetSecurityLevel(const CdmSessionId& session_id); - static const std::vector GetServiceCertificate( - const CdmSessionId& session_id); + static bool GetSecurityLevel(const CdmSessionId& session_id, + std::string* security_level); + static bool GetServiceCertificate(const CdmSessionId& session_id, + std::string* service_certificate); static bool UsePrivacyMode(const CdmSessionId& session_id); static uint32_t GetSessionSharingId(const CdmSessionId& session_id); @@ -75,12 +67,6 @@ class Properties { private: static const CdmClientPropertySet* GetCdmClientPropertySet( const CdmSessionId& session_id); - static void set_begin_license_usage_when_received(bool flag) { - begin_license_usage_when_received_ = flag; - } - static void set_require_explicit_renew_request(bool flag) { - require_explicit_renew_request_ = flag; - } static void set_oem_crypto_use_secure_buffers(bool flag) { oem_crypto_use_secure_buffers_ = flag; } @@ -96,23 +82,17 @@ class Properties { static void set_use_certificates_as_identification(bool flag) { use_certificates_as_identification_ = flag; } - static void set_decrypt_with_empty_session_support(bool flag) { - decrypt_with_empty_session_support_ = flag; - } static void set_security_level_path_backward_compatibility_support( bool flag) { security_level_path_backward_compatibility_support_ = flag; } private: - static bool begin_license_usage_when_received_; - static bool require_explicit_renew_request_; static bool oem_crypto_use_secure_buffers_; static bool oem_crypto_use_fifo_; static bool oem_crypto_use_userspace_buffers_; static bool oem_crypto_require_usage_tables_; static bool use_certificates_as_identification_; - static bool decrypt_with_empty_session_support_; static bool security_level_path_backward_compatibility_support_; static scoped_ptr session_property_set_; diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index ae5e6cb0..fea94185 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -28,11 +28,6 @@ typedef std::string CdmUsageInfoReleaseMessage; typedef std::string CdmProvisioningRequest; typedef std::string CdmProvisioningResponse; -// Types for shared host/cdm interface pairs used to shared vendor data. -typedef std::pair kStringPairs; -typedef std::vector kVectorBytes; -typedef std::pair kVectorPairs; - enum CdmResponseType { NO_ERROR, UNKNOWN_ERROR, diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index e357ea92..d136ac57 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -2,6 +2,8 @@ #include "cdm_engine.h" +#include + #include #include @@ -18,27 +20,30 @@ namespace { const uint32_t kUpdateUsageInformationPeriod = 60; // seconds - const size_t kMinNoncesPerSession = 4; + const size_t kUsageReportsPerRequest = 1; } // unnamed namespace namespace wvcdm { +bool CdmEngine::seeded_ = false; + CdmEngine::CdmEngine() : cert_provisioning_requested_security_level_(kLevelDefault), usage_session_(NULL), last_usage_information_update_time(0) { Properties::Init(); + if (!seeded_) { + Clock clock; + srand(clock.GetCurrentTime()); + seeded_ = true; + } } CdmEngine::~CdmEngine() { - CancelSessions(); - - if (NULL != usage_session_) - delete usage_session_; - CdmSessionMap::iterator i(sessions_.begin()); - for (; i != sessions_.end(); ++i) + for (; i != sessions_.end(); ++i) { delete i->second; + } sessions_.clear(); } @@ -286,9 +291,6 @@ CdmResponseType CdmEngine::RestoreKey( CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) { LOGI("CdmEngine::CancelKeyRequest"); - //TODO(gmorgan): Issue: what is semantics of canceling a key request. Should - //this call cancel all keys for the session? - CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { LOGE("CdmEngine::CancelKeyRequest: session_id not found = %s", @@ -494,11 +496,23 @@ CdmResponseType CdmEngine::HandleProvisioningResponse( wrapped_key); } -CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) { - if (NULL == usage_session_) { - usage_session_ = new CdmSession(NULL); +CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) { + DeviceFiles handle; + if (!handle.Init(security_level)) { + LOGE("CdmEngine::Unprovision: unable to initialize device files"); + return UNKNOWN_ERROR; } + if (!handle.DeleteAllFiles()) { + LOGE("CdmEngine::Unprovision: unable to delete files"); + return UNKNOWN_ERROR; + } + return NO_ERROR; +} + +CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) { + usage_session_.reset(new CdmSession(NULL)); + CdmResponseType status = usage_session_->Init(); if (NO_ERROR != status) { LOGE("CdmEngine::GetUsageInfo: session init error"); @@ -523,35 +537,32 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) { } std::string server_url; - // rate limit secure stop messages based on minimum nonce - // table size per session - usage_info->resize(license_info.size() >= kMinNoncesPerSession - 1 - ? kMinNoncesPerSession - 1 - : license_info.size()); - for (size_t i = 0; i < usage_info->size(); ++i) { - status = usage_session_->RestoreUsageSession(license_info[i].first, - license_info[i].second); - if (KEY_ADDED != status) { - LOGE("CdmEngine::GetUsageInfo: restore usage session error: %ld", - status); - usage_info->clear(); - return status; - } - status = usage_session_->GenerateReleaseRequest(&(*usage_info)[i], - &server_url); - if (KEY_MESSAGE != status) { - LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld", - status); - usage_info->clear(); - return status; - } + usage_info->resize(kUsageReportsPerRequest); + + uint32_t index = rand() % license_info.size(); + status = usage_session_->RestoreUsageSession(license_info[index].first, + license_info[index].second); + if (KEY_ADDED != status) { + LOGE("CdmEngine::GetUsageInfo: restore usage session (%d) error %ld", + index, status); + usage_info->clear(); + return status; + } + + status = usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url); + + if (KEY_MESSAGE != status) { + LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld", + status); + usage_info->clear(); + return status; } return KEY_MESSAGE; } CdmResponseType CdmEngine::ReleaseUsageInfo( const CdmUsageInfoReleaseMessage& message) { - if (NULL == usage_session_) { + if (NULL == usage_session_.get()) { LOGE("CdmEngine::ReleaseUsageInfo: cdm session not initialized"); return UNKNOWN_ERROR; } @@ -559,9 +570,8 @@ CdmResponseType CdmEngine::ReleaseUsageInfo( CdmResponseType status = usage_session_->ReleaseKey(message); if (NO_ERROR != status) { LOGE("CdmEngine::ReleaseUsageInfo: release key error: %ld", status); - return UNKNOWN_ERROR; } - return NO_ERROR; + return status; } CdmResponseType CdmEngine::Decrypt( @@ -588,23 +598,23 @@ CdmResponseType CdmEngine::Decrypt( LOGE("CdmEngine::Decrypt: no dest decrypt buffer"); return KEY_ERROR; } // else we must be level 1 direct and we don't need to return a buffer. - // TODO:(eschacker) look at renaming Properties::oem_crypto_use_fifo() - // to something like Properties::oem_crypto_use_direct_rendering(). } CdmSessionMap::iterator iter; if (session_id.empty()) { - if (!Properties::decrypt_with_empty_session_support()) return KEY_ERROR; - // Loop through the sessions to find the session containing the key_id. for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) { - if (iter->second->IsKeyLoaded(*parameters.key_id)) break; + if (iter->second->IsKeyLoaded(*parameters.key_id)) { + break; + } } } else { iter = sessions_.find(session_id); } if (iter == sessions_.end()) { - LOGE("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str()); + LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d", + session_id.c_str(), + session_id.size()); return KEY_ERROR; } @@ -679,11 +689,6 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) { return (key_system.find("widevine") != std::string::npos); } -bool CdmEngine::CancelSessions() { - // TODO(gmorgan) Implement CancelSessions() - return true; -} - void CdmEngine::OnTimerEvent() { Clock clock; uint64_t current_time = clock.GetCurrentTime(); diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 566cb672..a5b24947 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -1,5 +1,4 @@ // Copyright 2012 Google Inc. All Rights Reserved. -// Author: jfore@google.com (Jeff Fore), rkuroiwa@google.com (Rintaro Kuroiwa) #include "cdm_session.h" @@ -30,11 +29,10 @@ CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set) : session_id_(GenerateSessionId()), crypto_session_(NULL), license_received_(false), - reinitialize_session_(false), is_offline_(false), is_release_(false), is_usage_update_needed_(false), - is_certificate_loaded_(false) { + is_initial_decryption_(true) { if (cdm_client_property_set) { Properties::AddSessionPropertySet(session_id_, cdm_client_property_set); } @@ -51,8 +49,10 @@ CdmResponseType CdmSession::Init() { std::string token; if (Properties::use_certificates_as_identification()) { DeviceFiles handle; + std::string wrapped_key; if (!handle.Init(session.get()->GetSecurityLevel()) || - !handle.RetrieveCertificate(&token, &wrapped_key_)) { + !handle.RetrieveCertificate(&token, &wrapped_key) || + !session->LoadCertificatePrivateKey(wrapped_key)) { return NEED_PROVISIONING; } } else { @@ -64,7 +64,7 @@ CdmResponseType CdmSession::Init() { crypto_session_.reset(session.release()); license_received_ = false; - reinitialize_session_ = false; + is_initial_decryption_ = true; return NO_ERROR; } @@ -94,15 +94,6 @@ CdmResponseType CdmSession::RestoreOfflineSession( return UNKNOWN_ERROR; } - if (Properties::use_certificates_as_identification()) { - if (is_certificate_loaded_ || - crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { - is_certificate_loaded_ = true; - } else { - return NEED_PROVISIONING; - } - } - if (!license_parser_.RestoreOfflineLicense(key_request_, key_response_, offline_key_renewal_response_)) { return UNKNOWN_ERROR; @@ -120,15 +111,6 @@ CdmResponseType CdmSession::RestoreUsageSession( key_request_ = key_request; key_response_ = key_response; - if (Properties::use_certificates_as_identification()) { - if (is_certificate_loaded_ || - crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { - is_certificate_loaded_ = true; - } else { - return NEED_PROVISIONING; - } - } - if (!license_parser_.RestoreUsageLicense(key_request_, key_response_)) { return UNKNOWN_ERROR; } @@ -139,25 +121,10 @@ CdmResponseType CdmSession::RestoreUsageSession( return KEY_ADDED; } -bool CdmSession::VerifySession(const CdmKeySystem& key_system, - const InitializationData& init_data) { - // TODO(gmorgan): Compare key_system and init_data with value received - // during session startup - they should be the same. - return true; -} - CdmResponseType CdmSession::GenerateKeyRequest( const InitializationData& init_data, const CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, std::string* server_url) { - if (reinitialize_session_) { - CdmResponseType sts = Init(); - if (sts != NO_ERROR) { - LOGW("CdmSession::GenerateKeyRequest: Reinitialization failed"); - return sts; - } - } - if (crypto_session_.get() == NULL) { LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session"); return UNKNOWN_ERROR; @@ -181,9 +148,7 @@ CdmResponseType CdmSession::GenerateKeyRequest( if (is_release_) { return GenerateReleaseRequest(key_request, server_url); } else if (license_received_) { // renewal - return Properties::require_explicit_renew_request() - ? UNKNOWN_ERROR - : GenerateRenewalRequest(key_request, server_url); + return GenerateRenewalRequest(key_request, server_url); } else { if (!init_data.is_supported()) { LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)", @@ -195,16 +160,6 @@ CdmResponseType CdmSession::GenerateKeyRequest( return KEY_ERROR; } - if (Properties::use_certificates_as_identification()) { - if (is_certificate_loaded_ || - crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { - is_certificate_loaded_ = true; - } else { - reinitialize_session_ = true; - return NEED_PROVISIONING; - } - } - if (!license_parser_.PrepareKeyRequest(init_data, license_type, app_parameters, session_id_, key_request, server_url)) { @@ -235,11 +190,10 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response, } if (is_release_) { - return ReleaseKey(key_response); + CdmResponseType sts = ReleaseKey(key_response); + return (NO_ERROR == sts) ? KEY_ADDED : sts; } else if (license_received_) { // renewal - return Properties::require_explicit_renew_request() - ? UNKNOWN_ERROR - : RenewKey(key_response); + return RenewKey(key_response); } else { CdmResponseType sts = license_parser_.HandleKeyResponse(key_response); @@ -313,7 +267,6 @@ CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) { // CancelKeyRequest() - Cancel session. CdmResponseType CdmSession::CancelKeyRequest() { - // TODO(gmorgan): cancel and clean up session crypto_session_->Close(); return NO_ERROR; } @@ -324,17 +277,12 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { return UNKNOWN_ERROR; CdmResponseType status = crypto_session_->Decrypt(params); - // TODO(rfrias): Remove after support for OEMCrypto_ERROR_KEY_EXPIRED is in - if (UNKNOWN_ERROR == status) { - Clock clock; - int64_t current_time = clock.GetCurrentTime(); - if (policy_engine_.IsLicenseDurationExpired(current_time) || - policy_engine_.IsPlaybackDurationExpired(current_time)) { - return NEED_KEY; - } - } if (NO_ERROR == status) { + if (is_initial_decryption_) { + policy_engine_.BeginDecryption(); + is_initial_decryption_ = false; + } if (!is_usage_update_needed_) { is_usage_update_needed_ = !license_parser_.provider_session_token().empty(); @@ -390,7 +338,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request, CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false, key_response); - if (NO_ERROR != sts) + if (KEY_ADDED != sts) return sts; if (is_offline_ || !license_parser_.provider_session_token().empty()) { @@ -405,8 +353,6 @@ bool CdmSession::IsKeyLoaded(const KeyId& key_id) { CdmSessionId CdmSession::GenerateSessionId() { static int session_num = 1; - // TODO(rkuroiwa): Want this to be unique. Probably doing Hash(time+init_data) - // to get something that is reasonably unique. return SESSION_ID_PREFIX + IntToString(++session_num); } @@ -539,8 +485,9 @@ void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { } SecurityLevel CdmSession::GetRequestedSecurityLevel() { - if (Properties::GetSecurityLevel(session_id_) - .compare(QUERY_VALUE_SECURITY_LEVEL_L3) == 0) { + std::string security_level; + if (Properties::GetSecurityLevel(session_id_, &security_level) && + security_level == QUERY_VALUE_SECURITY_LEVEL_L3) { return kLevel3; } diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index 7d933ca6..6469761f 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -11,7 +11,7 @@ namespace { // URL for Google Provisioning Server. -// This server supplies the certificate that is needed +// The provisioning server supplies the certificate that is needed // to communicate with the License Server. const std::string kProvisioningServerUrl = "https://www.googleapis.com/" @@ -28,10 +28,9 @@ using video_widevine_server::sdk::ProvisioningResponse; using video_widevine_server::sdk::SignedProvisioningMessage; /* - * This function converts SignedProvisioningRequest into base64 string. - * It then wraps it in JSON format expected by the Apiary frontend. - * Apiary requires the base64 encoding to replace '+' with minus '-', - * and '/' with underscore '_'; opposite to stubby's. + * This function converts SignedProvisioningRequest into base64 string. It then + * wraps it in JSON format expected by the frontend. This server requires a + * "web-safe" base 64 encoding, where '+' becomes '-' and '/' becomes '_'. * * Returns the JSON formated string in *request. The JSON string will be * appended as a query parameter, i.e. signedRequest=(message.data()), - message.size(), NULL, &length, kSign_RSASSA_PSS); + size_t length = signature->size(); + OEMCryptoResult sts = OEMCrypto_GenerateSignature( + oec_session_id_, reinterpret_cast(message.data()), + message.size(), + reinterpret_cast(const_cast(signature->data())), + &length); + + if (OEMCrypto_SUCCESS != sts) { if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { - LOGD("GenerateSignature: OEMCrypto_GenerateRSASignature err=%d", sts); + LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); return false; } - } else { - length = kSignatureSize; - // TODO(gmorgan,kqyang): Use OEMCrypto_GenerateSignature to determine - // length after marvell fixes their implementation. - /* - sts = OEMCrypto_GenerateSignature( - oec_session_id_, reinterpret_cast(message.data()), - message.size(), NULL, &length); - */ - } - signature->resize(length); - - if (use_rsa) { - sts = OEMCrypto_GenerateRSASignature( - oec_session_id_, reinterpret_cast(message.data()), - message.size(), - reinterpret_cast(const_cast(signature->data())), - &length, kSign_RSASSA_PSS); - } else { + // Retry with proper-sized signature buffer + signature->resize(length); sts = OEMCrypto_GenerateSignature( oec_session_id_, reinterpret_cast(message.data()), message.size(), reinterpret_cast(const_cast(signature->data())), &length); + + if (OEMCrypto_SUCCESS != sts) { + LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); + return false; + } } + // Trim signature buffer + signature->resize(length); + + return true; +} + +bool CryptoSession::GenerateRsaSignature(const std::string& message, + std::string* signature) { + LOGV("GenerateRsaSignature: id=%ld", (uint32_t)oec_session_id_); + if (!signature) return false; + + size_t length = signature->size(); + OEMCryptoResult sts = OEMCrypto_GenerateRSASignature( + oec_session_id_, reinterpret_cast(message.data()), + message.size(), + reinterpret_cast(const_cast(signature->data())), + &length, kSign_RSASSA_PSS); + if (OEMCrypto_SUCCESS != sts) { - LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); - return false; + if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { + LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts); + return false; + } + + // Retry with proper-sized signature buffer + signature->resize(length); + sts = OEMCrypto_GenerateRSASignature( + oec_session_id_, reinterpret_cast(message.data()), + message.size(), + reinterpret_cast(const_cast(signature->data())), + &length, kSign_RSASSA_PSS); + + if (OEMCrypto_SUCCESS != sts) { + LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts); + return false; + } } - // TODO(fredgc): b/8878371 - // remove in K, when L1 library reports correct length. + // Trim signature buffer signature->resize(length); return true; @@ -588,7 +629,8 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { buffer_descriptor.buffer.clear.address = static_cast(params.decrypt_buffer) + params.decrypt_buffer_offset; - buffer_descriptor.buffer.clear.max_length = params.decrypt_buffer_length; + buffer_descriptor.buffer.clear.max_length = + params.decrypt_buffer_length - params.decrypt_buffer_offset; break; case OEMCrypto_BufferType_Secure: buffer_descriptor.buffer.secure.handle = params.decrypt_buffer; @@ -768,8 +810,6 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, reinterpret_cast(&(*wrapped_rsa_key)[0]), &wrapped_rsa_key_length); - // TODO(fredgc): b/8878371 - // remove in K, when L1 library reports correct length. wrapped_rsa_key->resize(wrapped_rsa_key_length); if (OEMCrypto_SUCCESS != status) { diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index 4a016ded..2b8487e2 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -2,13 +2,20 @@ #include "device_files.h" +#if defined(__APPLE__) +# include +# define SHA256 CC_SHA256 +# define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH +#else +# include +#endif + #include #include #include "device_files.pb.h" #include "file_store.h" #include "log.h" -#include "openssl/sha.h" #include "properties.h" #include "string_conversions.h" @@ -22,6 +29,7 @@ using video_widevine_client::sdk::UsageInfo; using video_widevine_client::sdk::UsageInfo_ProviderSession; namespace { + const char kCertificateFileName[] = "cert.bin"; const char kUsageInfoFileName[] = "usage.bin"; const char kLicenseFileNameExt[] = ".lic"; @@ -31,6 +39,18 @@ const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"}; size_t kSecurityLevelPathCompatibilityExclusionListSize = sizeof(kSecurityLevelPathCompatibilityExclusionList) / sizeof(*kSecurityLevelPathCompatibilityExclusionList); + +bool Hash(const std::string& data, std::string* hash) { + if (!hash) return false; + hash->resize(SHA256_DIGEST_LENGTH); + + const unsigned char* input = + reinterpret_cast(data.data()); + unsigned char* output = reinterpret_cast(&(*hash)[0]); + SHA256(input, data.size(), output); + return true; +} + } // unnamed namespace namespace wvcdm { @@ -343,18 +363,13 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) { return false; } - UsageInfo* updated_info = file.mutable_usage_info(); - UsageInfo info(*(const_cast(updated_info))); - updated_info->clear_sessions(); + UsageInfo* usage_info = file.mutable_usage_info(); + int index = 0; bool found = false; - for (int i = 0; i < info.sessions_size(); ++i) { - if (info.sessions(i).token().compare(provider_session_token) == 0) { + for (; index < usage_info->sessions_size(); ++index) { + if (usage_info->sessions(index).token().compare(provider_session_token) == 0) { found = true; - } else { - updated_info->add_sessions()->set_token(info.sessions(i).token()); - updated_info->add_sessions()->set_license_request( - info.sessions(i).license_request()); - updated_info->add_sessions()->set_license(info.sessions(i).license()); + break; } } @@ -364,6 +379,13 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) { return false; } + google::protobuf::RepeatedPtrField* sessions = + usage_info->mutable_sessions(); + if (index < usage_info->sessions_size() - 1) { + sessions->SwapElements(index, usage_info->sessions_size() - 1); + } + sessions->RemoveLast(); + file.SerializeToString(&serialized_file); return StoreFile(kUsageInfoFileName, serialized_file); } @@ -430,17 +452,6 @@ bool DeviceFiles::RetrieveUsageInfo(std::vector< return true; } -bool DeviceFiles::Hash(const std::string& data, std::string* hash) { - if (!hash) return false; - - hash->resize(SHA256_DIGEST_LENGTH); - SHA256_CTX sha256; - SHA256_Init(&sha256); - SHA256_Update(&sha256, data.data(), data.size()); - SHA256_Final(reinterpret_cast(&(*hash)[0]), &sha256); - return true; -} - bool DeviceFiles::StoreFile(const char* name, const std::string& serialized_file) { if (!file_) { diff --git a/libwvdrmengine/cdm/core/src/device_files.proto b/libwvdrmengine/cdm/core/src/device_files.proto index 7d8583e6..fb57ca9e 100644 --- a/libwvdrmengine/cdm/core/src/device_files.proto +++ b/libwvdrmengine/cdm/core/src/device_files.proto @@ -47,7 +47,7 @@ message File { enum FileType { DEVICE_CERTIFICATE = 1; LICENSE = 2; - USAGE_INFO = 2; + USAGE_INFO = 3; } enum FileVersion { @@ -63,5 +63,6 @@ message File { message HashedFile { optional bytes file = 1; + // A raw (not hex-encoded) SHA256, taken over the bytes of 'file'. optional bytes hash = 2; } diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index fc36b2a4..adde2746 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -22,6 +22,7 @@ std::string kDeviceNameKey = "device_name"; std::string kProductNameKey = "product_name"; std::string kBuildInfoKey = "build_info"; std::string kDeviceIdKey = "device_id"; +std::string kOemCryptoApiVersion = "oemcrypto_api_version"; const unsigned char kServiceCertificateCAPublicKey[] = { 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03, @@ -193,10 +194,10 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data, } bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id); - std::vector cert = Properties::GetServiceCertificate(session_id); - std::string serialized_service_certificate(cert.begin(), cert.end()); - - if (serialized_service_certificate.empty()) + std::string serialized_service_certificate; + if (!Properties::GetServiceCertificate(session_id, + &serialized_service_certificate) || + serialized_service_certificate.empty()) serialized_service_certificate = service_certificate_; if (privacy_mode_enabled && serialized_service_certificate.empty()) { @@ -254,12 +255,17 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data, client_info->set_name(kBuildInfoKey); client_info->set_value(value); } - if (session_->GetDeviceUniqueId(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kDeviceIdKey); client_info->set_value(value); } + uint32_t version = 0; + if (session_->GetApiVersion(&version)) { + client_info = client_id->add_client_info(); + client_info->set_name(kOemCryptoApiVersion); + client_info->set_value(UintToString(version)); + } if (privacy_mode_enabled) { EncryptedClientIdentification* encrypted_client_id = @@ -353,7 +359,7 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data, return false; } - // TODO(jfore): The time field will be updated once the cdm wrapper + // The time field will be updated once the cdm wrapper // has been updated to pass us in the time. license_request.set_request_time(0); @@ -432,8 +438,9 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal, if (!is_renewal) { if (license_id.has_provider_session_token()) { std::string usage_report; - if (!session_->GenerateUsageReport(license_id.provider_session_token(), - &usage_report)) { + if (NO_ERROR != + session_->GenerateUsageReport(license_id.provider_session_token(), + &usage_report)) { return false; } current_license->set_session_usage_table_entry(usage_report); @@ -560,7 +567,6 @@ CdmResponseType CdmLicense::HandleKeyResponse( return KEY_ERROR; } - std::string provider_session_token; if (license.id().has_provider_session_token()) provider_session_token_ = license.id().provider_session_token(); @@ -568,9 +574,6 @@ CdmResponseType CdmLicense::HandleKeyResponse( server_url_ = license.policy().renewal_server_url(); } - // TODO(kqyang, jfore, gmorgan): change SetLicense function signature to - // be able to return true/false to accept/reject the license. (Pending code - // merge from Eureka) policy_engine_->SetLicense(license); CdmResponseType resp = session_->LoadKeys(signed_response.msg(), @@ -578,7 +581,7 @@ CdmResponseType CdmLicense::HandleKeyResponse( mac_key_iv, mac_key, key_array, - provider_session_token); + provider_session_token_); if (KEY_ADDED == resp) { loaded_keys_.clear(); @@ -630,27 +633,23 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse( return KEY_ERROR; } - if (is_renewal) { - if (license.policy().has_renewal_server_url() && - license.policy().renewal_server_url().size() > 0) { - server_url_ = license.policy().renewal_server_url(); - } - } - else { - if (license.id().has_provider_session_token()) { - provider_session_token_ = license.id().provider_session_token(); - session_->ReleaseUsageInformation(signed_response.msg(), - signed_response.signature(), - provider_session_token_); - } - } - - // TODO(kqyang, jfore, gmorgan): change UpdateLicense function signature to - // be able to return true/false to accept/reject the license. (Pending code - // merge from Eureka) policy_engine_->UpdateLicense(license); - if (!is_renewal) return KEY_ADDED; + if (!is_renewal) { + if (!license.id().has_provider_session_token()) return KEY_ADDED; + + provider_session_token_ = license.id().provider_session_token(); + CdmResponseType status = + session_->ReleaseUsageInformation(signed_response.msg(), + signed_response.signature(), + provider_session_token_); + return (NO_ERROR == status) ? KEY_ADDED : status; + } + + if (license.policy().has_renewal_server_url() && + license.policy().renewal_server_url().size() > 0) { + server_url_ = license.policy().renewal_server_url(); + } std::vector key_array = ExtractContentKeys(license); diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index ea9704c2..d62034a7 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -315,7 +315,7 @@ class Adapter { if (level1_.InstallKeybox(keybox, size) != OEMCrypto_SUCCESS) { LOGE("Could NOT install keybox from %s. Falling Back to L3.", filename.c_str()); - false; + return false; } LOGI("Installed keybox from %s", filename.c_str()); return true; diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_static_v8.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_static_v8.cpp index 4dbfcf4f..e631d05b 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_static_v8.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_static_v8.cpp @@ -76,7 +76,7 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys( const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst, size_t pst_length) { - return OEMCrypto_LoadKeys_V8(pair.session, message, message_length, signature, + return OEMCrypto_LoadKeys_V8(session, message, message_length, signature, signature_length, enc_mac_key_iv, enc_mac_key, num_keys, key_array); } diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index 5d76079e..2b4f0244 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -2,16 +2,16 @@ #include "policy_engine.h" -#include -#include +#include + #include #include #include +#include "clock.h" #include "log.h" #include "properties.h" #include "string_conversions.h" -#include "clock.h" #include "wv_cdm_constants.h" namespace wvcdm { @@ -59,7 +59,6 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) { // Test to determine if renewal should be attempted. switch (license_state_) { - case kLicenseStateInitialPendingUsage: case kLicenseStateCanPlay: { if (IsRenewalDelayExpired(current_time)) renewal_needed = true; @@ -115,11 +114,6 @@ void PolicyEngine::UpdateLicense( policy_.MergeFrom(license.policy()); - if (!policy_.can_play()) { - license_state_ = kLicenseStateExpired; - return; - } - // some basic license validation if (license_state_ == kLicenseStateInitial) { // license start time needs to be present in the initial response @@ -158,35 +152,23 @@ void PolicyEngine::UpdateLicense( policy_max_duration_seconds_ = policy_.license_duration_seconds(); } - if (Properties::begin_license_usage_when_received()) - playback_start_time_ = current_time; + if (!policy_.can_play()) { + license_state_ = kLicenseStateExpired; + return; + } + + if (IsLicenseDurationExpired(current_time)) return; + if (IsPlaybackDurationExpired(current_time)) return; // Update state - if (Properties::begin_license_usage_when_received()) { - if (policy_.renew_with_usage()) { - license_state_ = kLicenseStateNeedRenewal; - } - else { - license_state_ = kLicenseStateCanPlay; - can_decrypt_ = true; - } - } - else { - if (license_state_ == kLicenseStateInitial) { - license_state_ = kLicenseStateInitialPendingUsage; - } - else { - license_state_ = kLicenseStateCanPlay; - can_decrypt_ = true; - } - } + license_state_ = kLicenseStateCanPlay; + can_decrypt_ = true; } void PolicyEngine::BeginDecryption() { - if ((playback_start_time_ == 0) && - (!Properties::begin_license_usage_when_received())) { + if (playback_start_time_ == 0) { switch (license_state_) { - case kLicenseStateInitialPendingUsage: + case kLicenseStateCanPlay: case kLicenseStateNeedRenewal: case kLicenseStateWaitingLicenseUpdate: playback_start_time_ = clock_->GetCurrentTime(); @@ -194,12 +176,7 @@ void PolicyEngine::BeginDecryption() { if (policy_.renew_with_usage()) { license_state_ = kLicenseStateNeedRenewal; } - else { - license_state_ = kLicenseStateCanPlay; - can_decrypt_ = true; - } break; - case kLicenseStateCanPlay: case kLicenseStateInitial: case kLicenseStateExpired: default: @@ -224,17 +201,10 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) { QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; (*key_info)[QUERY_KEY_RENEW_ALLOWED] = policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; - int64_t remaining_time = policy_max_duration_seconds_ + - license_received_time_ - current_time; - if (remaining_time < 0) - remaining_time = 0; - ss << remaining_time; + ss << GetLicenseDurationRemaining(current_time); (*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str(); - remaining_time = policy_.playback_duration_seconds() + playback_start_time_ - - current_time; - if (remaining_time < 0) - remaining_time = 0; - ss << remaining_time; + ss.str(""); + ss << GetPlaybackDurationRemaining(current_time); (*key_info)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str(); (*key_info)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url(); @@ -255,6 +225,16 @@ bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) { current_time; } +int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) { + if (0 == policy_max_duration_seconds_) return LLONG_MAX; + + int64_t remaining_time = policy_max_duration_seconds_ + + license_received_time_ - current_time; + + if (remaining_time < 0) remaining_time = 0; + return remaining_time; +} + bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) { return (policy_.playback_duration_seconds() > 0) && playback_start_time_ && @@ -262,6 +242,17 @@ bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) { current_time; } +int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) { + if (0 == policy_.playback_duration_seconds()) return LLONG_MAX; + if (0 == playback_start_time_) return policy_.playback_duration_seconds(); + + int64_t remaining_time = policy_.playback_duration_seconds() + + playback_start_time_ - current_time; + + if (remaining_time < 0) remaining_time = 0; + return remaining_time; +} + bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) { return policy_.can_renew() && (policy_.renewal_delay_seconds() > 0) && diff --git a/libwvdrmengine/cdm/core/src/privacy_crypto.cpp b/libwvdrmengine/cdm/core/src/privacy_crypto.cpp index 422e63dd..70bfd5d3 100644 --- a/libwvdrmengine/cdm/core/src/privacy_crypto.cpp +++ b/libwvdrmengine/cdm/core/src/privacy_crypto.cpp @@ -7,38 +7,60 @@ #include "privacy_crypto.h" +#include +#include +#include +#include +#include +#include +#include + #include "log.h" -#include "openssl/aes.h" -#include "openssl/bio.h" -#include "openssl/err.h" -#include "openssl/pem.h" -#include "openssl/sha.h" namespace { const int kPssSaltLength = 20; const int kRsaPkcs1OaepPaddingLength = 41; + +RSA* GetKey(const std::string& serialized_key) { + BIO* bio = BIO_new_mem_buf(const_cast(serialized_key.data()), + serialized_key.size()); + if (bio == NULL) { + LOGE("GetKey: BIO_new_mem_buf returned NULL"); + return NULL; + } + RSA* key = d2i_RSAPublicKey_bio(bio, NULL); + BIO_free(bio); + + if (key == NULL) { + LOGE("GetKey: RSA key deserialization failure: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + + return key; +} + +void FreeKey(RSA* key) { + if (key != NULL) { + RSA_free(key); + } +} + } // namespace namespace wvcdm { +AesCbcKey::AesCbcKey() {} + +AesCbcKey::~AesCbcKey() {} + bool AesCbcKey::Init(const std::string& key) { - if (key.empty()) { - LOGE("AesCbcKey::Init: no key provided"); - return false; - } if (key.size() != AES_BLOCK_SIZE) { LOGE("AesCbcKey::Init: unexpected key size: %d", key.size()); return false; } - EVP_CIPHER_CTX_init(&ctx_); - if (EVP_EncryptInit(&ctx_, EVP_aes_128_cbc(), - reinterpret_cast(&key[0]), NULL) == 0) { - LOGE("AesCbcKey::Init: AES CBC key setup failure: %s", - ERR_error_string(ERR_get_error(), NULL)); - return false; - } - initialized_ = true; + key_ = key; return true; } @@ -60,14 +82,16 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, LOGE("AesCbcKey::Encrypt: crypttext destination not provided"); return false; } - if (!initialized_) { + if (key_.empty()) { LOGE("AesCbcKey::Encrypt: AES key not initialized"); return false; } - if (EVP_EncryptInit(&ctx_, NULL, NULL, - reinterpret_cast(iv->data())) == 0) { - LOGE("AesCbcKey::Encrypt: AES CBC iv setup failure: %s", + EVP_CIPHER_CTX ctx; + if (EVP_EncryptInit(&ctx, EVP_aes_128_cbc(), + reinterpret_cast(&key_[0]), + reinterpret_cast(&(*iv)[0])) == 0) { + LOGE("AesCbcKey::Encrypt: AES CBC setup failure: %s", ERR_error_string(ERR_get_error(), NULL)); return false; } @@ -75,7 +99,7 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, out->resize(in.size() + AES_BLOCK_SIZE); int out_length = out->size(); if (EVP_EncryptUpdate( - &ctx_, reinterpret_cast(&(*out)[0]), &out_length, + &ctx, reinterpret_cast(&(*out)[0]), &out_length, reinterpret_cast(const_cast(in.data())), in.size()) == 0) { LOGE("AesCbcKey::Encrypt: encryption failure: %s", @@ -84,7 +108,7 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, } int padding = 0; - if (EVP_EncryptFinal(&ctx_, reinterpret_cast(&(*out)[out_length]), + if (EVP_EncryptFinal(&ctx, reinterpret_cast(&(*out)[out_length]), &padding) == 0) { LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s", ERR_error_string(ERR_get_error(), NULL)); @@ -95,34 +119,17 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, return true; } -RsaPublicKey::~RsaPublicKey() { - if (key_ != NULL) { - RSA_free(key_); - } -} +RsaPublicKey::RsaPublicKey() {} + +RsaPublicKey::~RsaPublicKey() {} bool RsaPublicKey::Init(const std::string& serialized_key) { - if (serialized_key.empty()) { LOGE("RsaPublicKey::Init: no serialized key provided"); return false; } - BIO* bio = BIO_new_mem_buf(const_cast(serialized_key.data()), - serialized_key.size()); - if (bio == NULL) { - LOGE("RsaPublicKey::Init: BIO_new_mem_buf returned NULL"); - return false; - } - key_ = d2i_RSAPublicKey_bio(bio, NULL); - BIO_free(bio); - - if (key_ == NULL) { - LOGE("RsaPublicKey::Init: RSA key deserialization failure: %s", - ERR_error_string(ERR_get_error(), NULL)); - return false; - } - + serialized_key_ = serialized_key; return true; } @@ -136,36 +143,46 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message, LOGE("RsaPublicKey::Encrypt: no encrypt message buffer provided"); return false; } - if (key_ == NULL) { + if (serialized_key_.empty()) { LOGE("RsaPublicKey::Encrypt: RSA key not initialized"); return false; } - int rsa_size = RSA_size(key_); + RSA* key = GetKey(serialized_key_); + if (key == NULL) { + // Error already logged by GetKey. + return false; + } + + int rsa_size = RSA_size(key); if (static_cast(clear_message.size()) > rsa_size - kRsaPkcs1OaepPaddingLength) { LOGE("RsaPublicKey::Encrypt: message too large to be encrypted (actual %d", " max allowed %d)", clear_message.size(), rsa_size - kRsaPkcs1OaepPaddingLength); + FreeKey(key); return false; } + encrypted_message->assign(rsa_size, 0); if (RSA_public_encrypt( clear_message.size(), const_cast( reinterpret_cast(clear_message.data())), - reinterpret_cast(&(*encrypted_message)[0]), key_, + reinterpret_cast(&(*encrypted_message)[0]), key, RSA_PKCS1_OAEP_PADDING) != rsa_size) { LOGE("RsaPublicKey::Encrypt: encrypt failure: %s", ERR_error_string(ERR_get_error(), NULL)); + FreeKey(key); return false; } + return true; } bool RsaPublicKey::VerifySignature(const std::string& message, const std::string& signature) { - if (key_ == NULL) { + if (serialized_key_.empty()) { LOGE("RsaPublicKey::VerifySignature: RSA key not initialized"); return false; } @@ -173,25 +190,33 @@ bool RsaPublicKey::VerifySignature(const std::string& message, LOGE("RsaPublicKey::VerifySignature: signed message is empty"); return false; } + RSA* key = GetKey(serialized_key_); + if (key == NULL) { + // Error already logged by GetKey. + return false; + } - int rsa_size = RSA_size(key_); + int rsa_size = RSA_size(key); if (static_cast(signature.size()) != rsa_size) { LOGE( "RsaPublicKey::VerifySignature: message signature is of the wrong " "size (expected %d, actual %d)", rsa_size, signature.size()); + FreeKey(key); return false; } + // Decrypt the signature. std::string padded_digest(signature.size(), 0); if (RSA_public_decrypt( signature.size(), const_cast( reinterpret_cast(signature.data())), - reinterpret_cast(&padded_digest[0]), key_, + reinterpret_cast(&padded_digest[0]), key, RSA_NO_PADDING) != rsa_size) { LOGE("RsaPublicKey::VerifySignature: RSA public decrypt failure: %s", ERR_error_string(ERR_get_error(), NULL)); + FreeKey(key); return false; } @@ -202,12 +227,13 @@ bool RsaPublicKey::VerifySignature(const std::string& message, // Verify PSS padding. if (RSA_verify_PKCS1_PSS( - key_, reinterpret_cast(message_digest.data()), + key, reinterpret_cast(message_digest.data()), EVP_sha1(), reinterpret_cast(padded_digest.data()), kPssSaltLength) == 0) { LOGE("RsaPublicKey::VerifySignature: RSA verify failure: %s", ERR_error_string(ERR_get_error(), NULL)); + FreeKey(key); return false; } diff --git a/libwvdrmengine/cdm/core/src/privacy_crypto_dummy.cpp b/libwvdrmengine/cdm/core/src/privacy_crypto_dummy.cpp new file mode 100644 index 00000000..d59d8ddf --- /dev/null +++ b/libwvdrmengine/cdm/core/src/privacy_crypto_dummy.cpp @@ -0,0 +1,43 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Description: +// Dummy version of privacy crypto classes for systems which +// can't tolerate OpenSSL as a dependency. +// + +#include "privacy_crypto.h" + +namespace wvcdm { + +AesCbcKey::AesCbcKey() {} + +AesCbcKey::~AesCbcKey() {} + +bool AesCbcKey::Init(const std::string& key) { + return false; +} + +bool AesCbcKey::Encrypt(const std::string& in, std::string* out, + std::string* iv) { + return false; +} + +RsaPublicKey::RsaPublicKey() {} + +RsaPublicKey::~RsaPublicKey() {} + +bool RsaPublicKey::Init(const std::string& serialized_key) { + return false; +} + +bool RsaPublicKey::Encrypt(const std::string& clear_message, + std::string* encrypted_message) { + return false; +} + +bool RsaPublicKey::VerifySignature(const std::string& message, + const std::string& signature) { + return false; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/properties.cpp b/libwvdrmengine/cdm/core/src/properties.cpp index 379ad89e..962c627b 100644 --- a/libwvdrmengine/cdm/core/src/properties.cpp +++ b/libwvdrmengine/cdm/core/src/properties.cpp @@ -9,27 +9,21 @@ const char* kSecurityLevelDirs[] = {"L1/", "L3/"}; } // namespace namespace wvcdm { -bool Properties::begin_license_usage_when_received_; -bool Properties::require_explicit_renew_request_; bool Properties::oem_crypto_use_secure_buffers_; bool Properties::oem_crypto_use_fifo_; bool Properties::oem_crypto_use_userspace_buffers_; bool Properties::oem_crypto_require_usage_tables_; bool Properties::use_certificates_as_identification_; -bool Properties::decrypt_with_empty_session_support_; bool Properties::security_level_path_backward_compatibility_support_; scoped_ptr Properties::session_property_set_; void Properties::Init() { - begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived; - require_explicit_renew_request_ = kPropertyRequireExplicitRenewRequest; oem_crypto_use_secure_buffers_ = kPropertyOemCryptoUseSecureBuffers; oem_crypto_use_fifo_ = kPropertyOemCryptoUseFifo; oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers; oem_crypto_require_usage_tables_ = kPropertyOemCryptoRequireUsageTable; use_certificates_as_identification_ = kPropertyUseCertificatesAsIdentification; - decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport; security_level_path_backward_compatibility_support_ = kSecurityLevelPathBackwardCompatibilitySupport; session_property_set_.reset(new CdmClientPropertySetMap()); @@ -66,27 +60,30 @@ const CdmClientPropertySet* Properties::GetCdmClientPropertySet( return NULL; } -const std::string Properties::GetSecurityLevel(const CdmSessionId& session_id) { +bool Properties::GetSecurityLevel(const CdmSessionId& session_id, + std::string* security_level) { const CdmClientPropertySet* property_set = GetCdmClientPropertySet(session_id); if (NULL == property_set) { LOGE("Properties::GetSecurityLevel: cannot find property set for %s", session_id.c_str()); - return ""; + return false; } - return property_set->security_level(); + *security_level = property_set->security_level(); + return true; } -const std::vector Properties::GetServiceCertificate( - const CdmSessionId& session_id) { +bool Properties::GetServiceCertificate(const CdmSessionId& session_id, + std::string* service_certificate) { const CdmClientPropertySet* property_set = GetCdmClientPropertySet(session_id); if (NULL == property_set) { LOGE("Properties::GetServiceCertificate: cannot find property set for %s", session_id.c_str()); - return std::vector(); + return false; } - return property_set->service_certificate(); + *service_certificate = property_set->service_certificate(); + return true; } bool Properties::UsePrivacyMode(const CdmSessionId& session_id) { diff --git a/libwvdrmengine/cdm/core/src/string_conversions.cpp b/libwvdrmengine/cdm/core/src/string_conversions.cpp index 77034eb0..8fc04124 100644 --- a/libwvdrmengine/cdm/core/src/string_conversions.cpp +++ b/libwvdrmengine/cdm/core/src/string_conversions.cpp @@ -77,11 +77,11 @@ std::string b2a_hex(const std::string& byte) { // Filename-friendly base64 encoding (RFC4648), commonly referred to // as Base64WebSafeEncode. -// This is the encoding required to interface with the provisioning -// server's Apiary interface as well as for certain license server -// transactions. It is also used for logging certain strings. -// The difference between web safe encoding vs regular encoding is that -// the web safe version replaces '+' with '-' and '/' with '_'. +// +// This is the encoding required to interface with the provisioning server, as +// well as for certain license server transactions. It is also used for logging +// certain strings. The difference between web safe encoding vs regular encoding +// is that the web safe version replaces '+' with '-' and '/' with '_'. std::string Base64SafeEncode(const std::vector& bin_input) { if (bin_input.empty()) { return std::string(); diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index 37af5ead..3ee128b6 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -33,13 +33,13 @@ wvcdm::KeyId g_key_id_pssh; wvcdm::KeyId g_key_id_unwrapped; wvcdm::CdmKeySystem g_key_system; std::string g_license_server; -std::string g_port; wvcdm::KeyId g_wrong_key_id; -int g_use_full_path = 0; // cannot use boolean in getopt_long -// This is the RSA certificate from the provisioning server. The client -// sends this certificate to a license server as verification in the -// provisioning test case. +// This is an RSA certificate message from the provisioning server. +// The client sends this certificate to a license server for device +// authentication by the license server. +// This certificate is used to test the CDM engine's provisioning +// response handling. static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse = "{\"signedResponse\": {" "\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo" @@ -136,16 +136,16 @@ class WvCdmEngineTest : public testing::Test { std::string GetKeyRequestResponse(const std::string& server_url, const std::string& client_auth) { // Use secure connection and chunk transfer coding. - UrlRequest url_request(server_url + client_auth, g_port, true, true); + UrlRequest url_request(server_url + client_auth); if (!url_request.is_connected()) { return ""; } url_request.PostRequest(key_msg_); std::string response; - int resp_bytes = url_request.GetResponse(&response); - LOGD("response:\r\n%s", response.c_str()); - LOGD("end %d bytes response dump", resp_bytes); + bool ok = url_request.GetResponse(&response); + LOGD("response: %s\n", response.c_str()); + EXPECT_TRUE(ok); int status_code = url_request.GetStatusCode(response); EXPECT_EQ(kHttpOk, status_code); @@ -252,41 +252,38 @@ int main(int argc, char **argv) { // The following variables are configurable through command line options. g_license_server.assign(config.license_server()); g_key_id_pssh.assign(config.key_id()); - g_port.assign(config.port()); std::string license_server(g_license_server); int show_usage = 0; static const struct option long_options[] = { - { "use_full_path", no_argument, &g_use_full_path, 0 }, { "keyid", required_argument, NULL, 'k' }, - { "port", required_argument, NULL, 'p' }, { "server", required_argument, NULL, 's' }, - { "vmodule", required_argument, NULL, 0 }, - { "v", required_argument, NULL, 0 }, { NULL, 0, NULL, '\0' } }; int option_index = 0; int opt = 0; - while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) { + while ((opt = getopt_long(argc, argv, "k:s:v", long_options, &option_index)) != -1) { switch (opt) { case 'k': { g_key_id_pssh.clear(); g_key_id_pssh.assign(optarg); break; } - case 'p': { - g_port.clear(); - g_port.assign(optarg); - break; - } case 's': { g_license_server.clear(); g_license_server.assign(optarg); break; } - case 'u': { - g_use_full_path = 1; + case 'v': { + // This option _may_ have already been consumed by wvcdm::InitLogging() + // above, depending on the platform-specific logging implementation. + // We only tell getopt about it so that it is not an error. We ignore + // the option here when seen. + // TODO: Stop passing argv to InitLogging, and instead set the log + // level here through the logging API. We should keep all command-line + // parsing at the application level, rather than split between various + // apps and various platform-specific logging implementations. break; } case '?': { @@ -302,11 +299,6 @@ int main(int argc, char **argv) { std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl; std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl; - std::cout << std::setw(30) << std::left << " --port="; - std::cout << "specifies the port number, in decimal format" << std::endl; - std::cout << std::setw(30) << std::left << " "; - std::cout << "default: " << g_port << std::endl; - std::cout << std::setw(30) << std::left << " --server="; std::cout << "configure the license server url, please include http[s] in the url" << std::endl; std::cout << std::setw(30) << std::left << " "; @@ -316,21 +308,15 @@ int main(int argc, char **argv) { std::cout << "configure the key id or pssh, in hex format" << std::endl; std::cout << std::setw(30) << std::left << " default keyid:"; std::cout << g_key_id_pssh << std::endl; - - std::cout << std::setw(30) << std::left << " --use_full_path"; - std::cout << "specify server url is not a proxy server" << std::endl; - std::cout << std::endl; return 0; } std::cout << std::endl; std::cout << "Server: " << g_license_server << std::endl; - std::cout << "Port: " << g_port << std::endl; std::cout << "KeyID: " << g_key_id_pssh << std::endl << std::endl; g_key_id_pssh = wvcdm::a2bs_hex(g_key_id_pssh); config.set_license_server(g_license_server); - config.set_port(g_port); config.set_key_id(g_key_id_pssh); // Extract the key ID from the PSSH box. diff --git a/libwvdrmengine/cdm/core/test/config_test_env.cpp b/libwvdrmengine/cdm/core/test/config_test_env.cpp index ad4fb9ce..d289fa2f 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.cpp +++ b/libwvdrmengine/cdm/core/test/config_test_env.cpp @@ -5,42 +5,50 @@ namespace { const std::string kWidevineKeySystem = "com.widevine.alpha"; -// Youtube Content Protection license server data -const std::string kYtCpLicenseServer = +// Content Protection license server data +const std::string kCpLicenseServer = "http://wv-ref-eme-player.appspot.com/proxy"; -const std::string kYtCpClientAuth = ""; -const std::string kYtCpKeyId = - "000000427073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id - "08011a0d7769646576696e655f7465737422" // pssh data (streaming) - "0f73747265616d696e675f636c697031"; - -const std::string kYtCpOfflineKeyId = - "000000407073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id - "08011a0d7769646576696e655f7465737422" //pssh data (offline) - "0d6f66666c696e655f636c697031"; - -// Youtube license server data -const std::string kYtLicenseServer = - "https://www.youtube.com/api/drm/" - "widevine?video_id=03681262dc412c06&source=YOUTUBE"; -const std::string kYtClientAuth = ""; -const std::string kYtKeyId = - "000000347073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id - "0801121093789920E8D6520098577DF8F2DD5546"; // pssh data +const std::string kCpClientAuth = ""; +const std::string kCpKeyId = + "00000042" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000022" // pssh data size + // pssh data: + "08011a0d7769646576696e655f746573" + "74220f73747265616d696e675f636c69" + "7031"; +const std::string kCpOfflineKeyId = + "00000040" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000020" // pssh data size + // pssh data: + "08011a0d7769646576696e655f746573" + "74220d6f66666c696e655f636c697031"; // Google Play license server data const std::string kGpLicenseServer = "https://jmt17.google.com/video/license/GetCencLicense"; - +// Test client authorization string. // NOTE: Append a userdata attribute to place a unique marker that the // server team can use to track down specific requests during debugging // e.g., "&userdata=." // "&userdata=jbmr2.dev" const std::string kGpClientAuth = "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine"; +const std::string kGpKeyId = + "00000034" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000014" // pssh data size + // pssh data: + "08011210e02562e04cd55351b14b3d74" + "8d36ed8e"; +const std::string kGpOfflineKeyId = kGpKeyId; const std::string kGpClientOfflineQueryParameters = "&offline=true"; @@ -49,33 +57,30 @@ const std::string kGpClientOfflineRenewalQueryParameters = const std::string kGpClientOfflineReleaseQueryParameters = "&offline=true&release=true"; -const std::string kGpKeyId = - "000000347073736800000000" // blob size and pssh - "edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id - "08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data - // An invalid key id, expected to fail const std::string kWrongKeyId = - "000000347073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id - "0901121094889920E8D6520098577DF8F2DD5546"; // pssh data + "00000034" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000014" // pssh data size + // pssh data: + "0901121094889920e8d6520098577df8" + "f2dd5546"; -// Url returned by GetProvisioningRequest() +// URL of provisioning server (returned by GetProvisioningRequest()) const std::string kProductionProvisioningServerUrl = "https://www.googleapis.com/" "certificateprovisioning/v1/devicecertificates/create" "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; -const std::string kServerSdkLicenseServer = - "http://kir03fcpg174.widevine.net/widevine/cgi-bin/drm.cgi"; - const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = { - { wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId, - kGpKeyId, kDefaultHttpsPort, true, true }, - { wvcdm::kYouTubeContentProtectionServer, kYtCpLicenseServer, - kYtCpClientAuth, kYtCpKeyId, kYtCpOfflineKeyId, kDefaultHttpPort, - false, false } + { wvcdm::kGooglePlayServer, kGpLicenseServer, + kGpClientAuth, kGpKeyId, kGpOfflineKeyId }, + { wvcdm::kContentProtectionServer, kCpLicenseServer, + kCpClientAuth, kCpKeyId, kCpOfflineKeyId }, }; + } // namespace namespace wvcdm { @@ -113,11 +118,7 @@ void ConfigTestEnv::Init(LicenseServerId server_id) { key_id_ = license_servers[server_id].key_id; key_system_ = kWidevineKeySystem; license_server_ = license_servers[server_id].url; - port_ = license_servers[server_id].port; provisioning_server_url_ = kProductionProvisioningServerUrl; - server_sdk_license_server_ = kServerSdkLicenseServer; - use_chunked_transfer_ = license_servers[server_id].use_chunked_transfer; - use_secure_transfer_ = license_servers[server_id].use_secure_transfer; wrong_key_id_= kWrongKeyId; } diff --git a/libwvdrmengine/cdm/core/test/config_test_env.h b/libwvdrmengine/cdm/core/test/config_test_env.h index 1109885d..fd7c7000 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.h +++ b/libwvdrmengine/cdm/core/test/config_test_env.h @@ -6,15 +6,10 @@ #include #include "wv_cdm_types.h" -namespace { -const std::string kDefaultHttpsPort = "443"; -const std::string kDefaultHttpPort = "80"; -} - namespace wvcdm { typedef enum { kGooglePlayServer, - kYouTubeContentProtectionServer + kContentProtectionServer, } LicenseServerId; // Configures default test environment. @@ -26,9 +21,6 @@ class ConfigTestEnv { std::string client_tag; std::string key_id; std::string offline_key_id; - std::string port; - bool use_chunked_transfer; - bool use_secure_transfer; } LicenseServerConfiguration; explicit ConfigTestEnv(LicenseServerId server_id); @@ -41,15 +33,9 @@ class ConfigTestEnv { const KeyId& key_id() const { return key_id_; } const CdmKeySystem& key_system() const { return key_system_; } const std::string& license_server() const { return license_server_; } - const std::string& port() const { return port_; } const std::string& provisioning_server_url() const { return provisioning_server_url_; } - const std::string& server_sdk_license_server() const { - return server_sdk_license_server_; - } - bool use_chunked_transfer() { return use_chunked_transfer_; } - bool use_secure_transfer() { return use_secure_transfer_; } const KeyId& wrong_key_id() const { return wrong_key_id_; } void set_key_id(KeyId& key_id) { key_id_.assign(key_id); } @@ -59,7 +45,6 @@ class ConfigTestEnv { void set_license_server(std::string& license_server) { license_server_.assign(license_server); } - void set_port(std::string& port) { port_.assign(port); } private: void Init(LicenseServerId server_id); @@ -68,11 +53,7 @@ class ConfigTestEnv { KeyId key_id_; CdmKeySystem key_system_; std::string license_server_; - std::string port_; std::string provisioning_server_url_; - std::string server_sdk_license_server_; - bool use_chunked_transfer_; - bool use_secure_transfer_; KeyId wrong_key_id_; CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv); diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 78ea5050..0d89a2db 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -26,10 +26,15 @@ using ::testing::StrEq; namespace { const uint32_t kCertificateLen = 700; const uint32_t kWrappedKeyLen = 500; + const uint32_t kProtobufEstimatedOverhead = 75; const uint32_t kLicenseRequestLen = 300; const uint32_t kLicenseLen = 500; const uint32_t kProviderSessionTokenLen = 128; + +// Structurally valid test certificate. +// The data elements in this module are used to test the storage and +// retrieval of certificates and licenses const std::string kTestCertificate = "124B035F3D256A656F0E505A085E7A6C482B61035E0C4A540F7803137F4C3B45206B7F33" "347F4D7A005E56400F0955011F4E07072D0D46781817460974326A516E3944385760280E" @@ -51,6 +56,10 @@ const std::string kTestCertificate = "523B102906195E003C2D111A7D4740122C6941003726602B59263B5C09473D4E025E3541" "701B122D340A3D145436137002687E4C470D2F6F4C357A3245384D737B734E2274301179" "402473486311156E5A0C78644C593273"; + +// A Wrapped Private Key +// The data elements in this module are used to test the storage and +// retrieval of certificates and licenses const std::string kTestWrappedPrivateKey = "4F724B065326371A2F5F6F51467C2E26555C453B5C7C1B4F2738454B782E3E7B5340435A" "66374D0612052C521A233D7A67194871751C78575E5177070130264C4F037633320E667B" @@ -66,6 +75,10 @@ const std::string kTestWrappedPrivateKey = "5F7F3D6B64525E7227165948101540243C19495C4C702F37490F26613353797825624143" "263043020E1E6760123D51056F2F1E482F2E3D021B27677D3E7E3C0C11757C3448275E08" "382E111263644C6D224714706D760A054A586E17505C3429575A41043F184209"; + +// The test certificate in file storage format. +// The data elements in this module are used to test the storage and +// retrieval of certificates and licenses const std::string kTestCertificateFileData = "0ABD09080110011AB6090ABC05124B035F3D256A656F0E505A085E7A6C482B61035E0C4A" "540F7803137F4C3B45206B7F33347F4D7A005E56400F0955011F4E07072D0D4678181746" @@ -115,10 +128,13 @@ struct LicenseInfo { std::string file_data; }; -// Sample data to test storage and retrieval of license-related data -// The license data and URLs in this test are not real. size_t kNumberOfLicenses = 3; + +// Sample license data and related data for storage and use for offline +// playback. The license data and URLs in this test are not real. Test +// storage and retrieval of license-related data. LicenseInfo license_test_data[] = { + // license 0 {"ksid54C57C966E23CEF5", DeviceFiles::kLicenseStateActive, wvcdm::a2bs_hex("0801121030313233343536373839414243444546"), @@ -310,6 +326,7 @@ LicenseInfo license_test_data[] = { "652E636F6D2F766964656F2D6465762F6C6963656E73652F47657443656E" "634C6963656E73651220F6974C1CFFD00E3144488FC092D3DF4F6007A3CA" "C4756EB046DC74B1C2E512CC")}, + // license 1 {"ksidC8EAA2579A282EB0", DeviceFiles::kLicenseStateReleasing, wvcdm::a2bs_hex("0801121030313233343536373839414243444546"), @@ -502,6 +519,7 @@ LicenseInfo license_test_data[] = { "3D3033363831323632646334313263303626736F757263653D594F555455" "42451220EC449C6B026C43004743061B3A3DCB7208B2AD11600254841B96" "1CFA1AD57172")}, + // license 2 {"ksidE8C37662C88DC673", DeviceFiles::kLicenseStateReleasing, wvcdm::a2bs_hex("0801121030313233343536373839414243444546"), @@ -693,6 +711,9 @@ LicenseInfo license_test_data[] = { "72702E676F6F676C652E636F6D3A383838382F64726D12205CD2C43C618C" "CA27BBCB2EEBDE32B57CBD51B424FD85DAB715B7F5A87546FD40")}}; +// Sample license data and related data for storage and use for offline +// playback. The license data and URLs in this test are not real. +// The data is used to test license-related functions. LicenseInfo license_update_test_data[] = { // active license {"key_set_id_: ksid2A048BC7FAEC885A", DeviceFiles::kLicenseStateActive, diff --git a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp index e26f9b72..4f5cf13d 100644 --- a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp @@ -45,10 +45,10 @@ class FileTest : public testing::Test { TEST_F(FileTest, FileExists) { File file; - EXPECT_TRUE(file.Exists(test_vectors::kFileExists)); - EXPECT_TRUE(file.Exists(test_vectors::kDirExists)); - EXPECT_FALSE(file.Exists(test_vectors::kFileDoesNotExist)); - EXPECT_FALSE(file.Exists(test_vectors::kDirDoesNotExist)); + EXPECT_TRUE(file.Exists(test_vectors::kExistentFile)); + EXPECT_TRUE(file.Exists(test_vectors::kExistentDir)); + EXPECT_FALSE(file.Exists(test_vectors::kNonExistentFile)); + EXPECT_FALSE(file.Exists(test_vectors::kNonExistentDir)); } TEST_F(FileTest, CreateDirectory) { diff --git a/libwvdrmengine/cdm/core/test/http_socket.cpp b/libwvdrmengine/cdm/core/test/http_socket.cpp index e704eac3..392fc863 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.cpp +++ b/libwvdrmengine/cdm/core/test/http_socket.cpp @@ -5,17 +5,35 @@ #include #include #include -#include +#include +#include #include +#include + +#include +#include +#include #include "log.h" -#include "openssl/bio.h" -#include "openssl/err.h" -#include "openssl/x509.h" -namespace wvcdm { +namespace { -SSL_CTX* HttpSocket::InitSslContext(void) { +// Helper function to tokenize a string. This makes it easier to avoid silly +// parsing bugs that creep in easily when each part of the string is parsed +// with its own piece of code. +bool Tokenize(const std::string& source, const std::string& delim, + const size_t offset, std::string* substring_output, + size_t* next_offset) { + size_t start_of_delim = source.find(delim, offset); + if (start_of_delim == std::string::npos) { + return false; + } + substring_output->assign(source, offset, start_of_delim - offset); + *next_offset = start_of_delim + delim.size(); + return true; +} + +SSL_CTX* InitSslContext() { const SSL_METHOD* method; SSL_CTX* ctx; @@ -23,21 +41,18 @@ SSL_CTX* HttpSocket::InitSslContext(void) { SSL_load_error_strings(); method = SSLv3_client_method(); ctx = SSL_CTX_new(method); - if (NULL == ctx) { + if (!ctx) LOGE("failed to create SSL context"); - } return ctx; } -void HttpSocket::ShowServerCertificate(const SSL* ssl) { - X509* cert; - char* line; - +// unused, may be useful for debugging SSL-related issues. +void ShowServerCertificate(const SSL* ssl) { // gets the server certificate - cert = SSL_get_peer_certificate(ssl); - if (cert != NULL) { + X509* cert = SSL_get_peer_certificate(ssl); + if (cert) { + char* line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); LOGV("server certificate:"); - line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); LOGV("subject: %s", line); free(line); line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); @@ -49,183 +64,252 @@ void HttpSocket::ShowServerCertificate(const SSL* ssl) { } } -HttpSocket::HttpSocket() - : secure_connect_(true), - socket_fd_(-1), +// Wait for a socket to be ready for reading or writing. +// Establishing a connection counts as "ready for write". +// Returns false on select error or timeout. +// Returns true when the socket is ready. +bool SocketWait(int fd, bool for_read, int timeout_in_ms) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + + struct timeval tv; + tv.tv_sec = timeout_in_ms / 1000; + tv.tv_usec = (timeout_in_ms % 1000) * 1000; + + fd_set *read_fds = NULL; + fd_set *write_fds = NULL; + if (for_read) { + read_fds = &fds; + } else { + write_fds = &fds; + } + + int ret = select(fd + 1, read_fds, write_fds, NULL, &tv); + if (ret == 0) { + LOGE("socket timed out"); + return false; + } else if (ret == -1) { + LOGE("select failed, errno = %d", errno); + return false; + } + + // socket ready. + return true; +} + +} // namespace + +namespace wvcdm { + +// Parses the URL and extracts all relevant information. +// static +bool HttpSocket::ParseUrl(const std::string& url, + std::string* scheme, + bool* secure_connect, + std::string* domain_name, + int* port, + std::string* path) { + size_t offset = 0; + + if (!Tokenize(url, "://", offset, scheme, &offset)) { + LOGE("Invalid URL, scheme not found: %s", url.c_str()); + return false; + } + + // If the scheme is http or https, set secure_connect and port accordingly. + // Otherwise, consider the scheme unsupported and fail. + if (*scheme == "http") { + *secure_connect = false; + *port = 80; + } else if (*scheme == "https") { + *secure_connect = true; + *port = 443; + } else { + LOGE("Invalid URL, scheme not supported: %s", url.c_str()); + return false; + } + + if (!Tokenize(url, "/", offset, domain_name, &offset)) { + // The rest of the URL belongs to the domain name. + domain_name->assign(url, offset, std::string::npos); + // No explicit path after the domain name. + path->assign("/"); + } else { + // The rest of the URL, including the preceding slash, belongs to the path. + path->assign(url, offset - 1, std::string::npos); + } + + // The domain name may optionally contain a port which overrides the default. + std::string domain_name_without_port; + size_t port_offset; + if (Tokenize(*domain_name, ":", 0, &domain_name_without_port, + &port_offset)) { + *port = atoi(domain_name->c_str() + port_offset); + if (*port <= 0 || *port >= 65536) { + LOGE("Invalid URL, port not valid: %s", url.c_str()); + return false; + } + domain_name->assign(domain_name_without_port); + } + + return true; +} + +HttpSocket::HttpSocket(const std::string& url) + : socket_fd_(-1), ssl_(NULL), - ssl_ctx_(NULL), - timeout_enabled_(false) { + ssl_ctx_(NULL) { + valid_url_ = ParseUrl(url, &scheme_, &secure_connect_, &domain_name_, &port_, + &resource_path_); SSL_library_init(); } -HttpSocket::~HttpSocket() { CloseSocket(); } +HttpSocket::~HttpSocket() { + CloseSocket(); +} void HttpSocket::CloseSocket() { if (socket_fd_ != -1) { close(socket_fd_); socket_fd_ = -1; } - if (secure_connect_) { - if (ssl_) { - SSL_free(ssl_); - ssl_ = NULL; - } - if (ssl_ctx_) { - CloseSslContext(ssl_ctx_); - ssl_ctx_ = NULL; - } + if (ssl_) { + SSL_free(ssl_); + ssl_ = NULL; + } + if (ssl_ctx_) { + SSL_CTX_free(ssl_ctx_); + ssl_ctx_ = NULL; } } -// Extracts the domain name and resource path from the input url parameter. -// The results are put in domain_name and resource_path respectively. -// The format of the url can begin with :://domain server/... -// or dowmain server/resource_path -void HttpSocket::GetDomainNameAndPathFromUrl(const std::string& url, - std::string& domain_name, - std::string& resource_path) { - domain_name.clear(); - resource_path.clear(); - - size_t start = url.find("//"); - size_t end = url.npos; - if (start != url.npos) { - end = url.find("/", start + 2); - if (end != url.npos) { - domain_name.assign(url, start + 2, end - start - 2); - resource_path.assign(url, end + 1, url.npos); - } else { - domain_name.assign(url, start + 2, url.npos); - } - } else { - // no scheme/protocol in url - end = url.find("/"); - if (end != url.npos) { - domain_name.assign(url, 0, end); - resource_path.assign(url, end + 1, url.npos); - } else { - domain_name.assign(url); - } +bool HttpSocket::Connect(int timeout_in_ms) { + if (!valid_url_) { + return false; } - // strips port number if present, e.g. https://www.domain.com:8888/... - end = domain_name.find(":"); - if (end != domain_name.npos) { - domain_name.erase(end); - } -} - -bool HttpSocket::Connect(const char* url, const std::string& port, - bool enable_timeout, bool secure_connection) { - secure_connect_ = secure_connection; - if (secure_connect_) ssl_ctx_ = InitSslContext(); - - GetDomainNameAndPathFromUrl(url, domain_name_, resource_path_); + // get a socket socket_fd_ = socket(AF_INET, SOCK_STREAM, 0); if (socket_fd_ < 0) { - LOGE("cannot open socket %d", errno); + LOGE("cannot open socket, errno = %d", errno); return false; } - int reuse = 1; - if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == - -1) { + // set the socket in non-blocking mode + int original_flags = fcntl(socket_fd_, F_GETFL, 0); + if (original_flags == -1) { + LOGE("fcntl error, errno = %d", errno); + CloseSocket(); + return false; + } + if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) { + LOGE("fcntl error, errno = %d", errno); CloseSocket(); - LOGE("setsockopt error %d", errno); return false; } + // lookup the server IP struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; + struct addrinfo* addr_info = NULL; - bool status = true; - int ret = getaddrinfo(domain_name_.c_str(), port.c_str(), &hints, &addr_info); + int ret = getaddrinfo(domain_name_.c_str(), NULL, &hints, &addr_info); if (ret != 0) { - CloseSocket(); - LOGE("getaddrinfo failed with %d", ret); - status = false; + LOGE("getaddrinfo failed, errno = %d", ret); + return false; + } + + // set the port + struct sockaddr_in* addr_ipv4 = reinterpret_cast( + addr_info->ai_addr); + addr_ipv4->sin_port = htons(port_); + + // connect to the server + ret = connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen); + freeaddrinfo(addr_info); + + if (ret == 0) { + // connected right away. } else { - if (connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen) == -1) { + if (errno != EINPROGRESS) { + // failed right away. + LOGE("cannot connect to %s, errno = %d", domain_name_.c_str(), errno); CloseSocket(); - LOGE("cannot connect socket to %s, error=%d", domain_name_.c_str(), - errno); - status = false; + return false; + } else { + // in progress. block until timeout expired or connection established. + if (!SocketWait(socket_fd_, /* for_read */ false, timeout_in_ms)) { + LOGE("cannot connect to %s", domain_name_.c_str()); + CloseSocket(); + return false; + } } } - timeout_enabled_ = enable_timeout; - if (addr_info != NULL) { - freeaddrinfo(addr_info); - } - if (!status) return false; + // set up SSL if needed + if (secure_connect_) { + ssl_ctx_ = InitSslContext(); + if (!ssl_ctx_) { + CloseSocket(); + return false; + } - // secures connection - if (secure_connect_ && ssl_ctx_) { ssl_ = SSL_new(ssl_ctx_); if (!ssl_) { LOGE("failed SSL_new"); + CloseSocket(); return false; } BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE); if (!a_bio) { LOGE("BIO_new_socket error"); + CloseSocket(); return false; } SSL_set_bio(ssl_, a_bio, a_bio); - int ret = SSL_connect(ssl_); - if (1 != ret) { - char buf[256]; - LOGE("SSL_connect error:%s", ERR_error_string(ERR_get_error(), buf)); - return false; - } + do { + ret = SSL_connect(ssl_); + if (ret != 1) { + int ssl_err = SSL_get_error(ssl_, ret); + if (ssl_err != SSL_ERROR_WANT_READ && + ssl_err != SSL_ERROR_WANT_WRITE) { + char buf[256]; + LOGE("SSL_connect error: %s", ERR_error_string(ERR_get_error(), buf)); + CloseSocket(); + return false; + } + bool for_read = ssl_err == SSL_ERROR_WANT_READ; + if (!SocketWait(socket_fd_, for_read, timeout_in_ms)) { + LOGE("cannot connect to %s", domain_name_.c_str()); + CloseSocket(); + return false; + } + } + } while (ret != 1); } + return true; } -int HttpSocket::Read(char* data, int len) { return (Read(data, len, 0)); } - -// makes non-blocking mode only during read, it supports timeout for read -// returns -1 for error, number of bytes read for success +// Returns -1 for error, number of bytes read for success. +// The timeout here only applies to the span between packets of data, for the +// sake of simplicity. int HttpSocket::Read(char* data, int len, int timeout_in_ms) { - bool use_timeout = (timeout_enabled_ && (timeout_in_ms > 0)); - int original_flags = 0; - if (use_timeout) { - original_flags = fcntl(socket_fd_, F_GETFL, 0); - if (original_flags == -1) { - LOGE("fcntl error %d", errno); - return -1; - } - if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) { - LOGE("fcntl error %d", errno); - return -1; - } - } - int total_read = 0; - int read = 0; int to_read = len; + while (to_read > 0) { - if (use_timeout) { - fd_set read_fds; - struct timeval tv; - tv.tv_sec = timeout_in_ms / 1000; - tv.tv_usec = (timeout_in_ms % 1000) * 1000; - FD_ZERO(&read_fds); - FD_SET(socket_fd_, &read_fds); - if (select(socket_fd_ + 1, &read_fds, NULL, NULL, &tv) == -1) { - LOGE("select failed"); - break; - } - if (!FD_ISSET(socket_fd_, &read_fds)) { - LOGD("socket read timeout"); - break; - } + if (!SocketWait(socket_fd_, /* for_read */ true, timeout_in_ms)) { + LOGE("unable to read from %s", domain_name_.c_str()); + return -1; } + int read; if (secure_connect_) read = SSL_read(ssl_, data, to_read); else @@ -236,27 +320,26 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms) { data += read; total_read += read; } else if (read == 0) { - // in blocking mode, zero read mean's peer closed. - // in non-blocking mode, select said that there is data. so it should not - // happen + // The connection has been closed. No more data. break; } else { - LOGE("recv returned %d, error = %d", read, errno); - break; + LOGE("recv returned %d, errno = %d", read, errno); + return -1; } } - if (use_timeout) { - fcntl(socket_fd_, F_SETFL, original_flags); // now blocking again - } return total_read; } -int HttpSocket::Write(const char* data, int len) { +// Returns -1 for error, number of bytes written for success. +// The timeout here only applies to the span between packets of data, for the +// sake of simplicity. +int HttpSocket::Write(const char* data, int len, int timeout_in_ms) { int total_sent = 0; - int sent = 0; int to_send = len; + while (to_send > 0) { + int sent; if (secure_connect_) sent = SSL_write(ssl_, data, to_send); else @@ -267,11 +350,17 @@ int HttpSocket::Write(const char* data, int len) { data += sent; total_sent += sent; } else if (sent == 0) { - usleep(10); // retry later + // We filled up the pipe. Wait for room to write. + if (!SocketWait(socket_fd_, /* for_read */ false, timeout_in_ms)) { + LOGE("unable to write to %s", domain_name_.c_str()); + return -1; + } } else { - LOGE("send returned error %d", errno); + LOGE("send returned %d, errno = %d", sent, errno); + return -1; } } + return total_sent; } diff --git a/libwvdrmengine/cdm/core/test/http_socket.h b/libwvdrmengine/cdm/core/test/http_socket.h index 79363b10..0b628e26 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.h +++ b/libwvdrmengine/cdm/core/test/http_socket.h @@ -4,43 +4,52 @@ #define CDM_TEST_HTTP_SOCKET_H_ #include -#include "openssl/ssl.h" -#include "wv_cdm_types.h" + +#include +#include + +#include "wv_cdm_types.h" // CORE_DISALLOW_COPY_AND_ASSIGN namespace wvcdm { // Provides basic Linux based TCP socket interface. class HttpSocket { public: - HttpSocket(); + // A scheme (http:// or https://) is required for the URL. + explicit HttpSocket(const std::string& url); ~HttpSocket(); + bool Connect(int timeout_in_ms); void CloseSocket(); - bool Connect(const char* url, const std::string& port, bool enable_timeout, - bool secure_connection); - void GetDomainNameAndPathFromUrl(const std::string& url, - std::string& domain_name, - std::string& resource_path); - const std::string& domain_name() const { return domain_name_; }; - const std::string& resource_path() const { return resource_path_; }; - int Read(char* data, int len); + + const std::string& scheme() const { return scheme_; } + bool secure_connect() const { return secure_connect_; } + const std::string& domain_name() const { return domain_name_; } + int port() const { return port_; } + const std::string& resource_path() const { return resource_path_; } + int Read(char* data, int len, int timeout_in_ms); - int Write(const char* data, int len); + int Write(const char* data, int len, int timeout_in_ms); private: - void CloseSslContext(SSL_CTX* ctx) const { - if (ctx) SSL_CTX_free(ctx); - } - SSL_CTX* InitSslContext(void); - void ShowServerCertificate(const SSL* ssl); + static bool ParseUrl(const std::string& url, + std::string* scheme, + bool* secure_connect, + std::string* domain_name, + int* port, + std::string* path); + FRIEND_TEST(HttpSocketTest, ParseUrlTest); - std::string domain_name_; + std::string scheme_; bool secure_connect_; + std::string domain_name_; + int port_; std::string resource_path_; + bool valid_url_; + int socket_fd_; SSL* ssl_; SSL_CTX* ssl_ctx_; - bool timeout_enabled_; CORE_DISALLOW_COPY_AND_ASSIGN(HttpSocket); }; diff --git a/libwvdrmengine/cdm/core/test/http_socket_test.cpp b/libwvdrmengine/cdm/core/test/http_socket_test.cpp index fe8c5366..35624025 100644 --- a/libwvdrmengine/cdm/core/test/http_socket_test.cpp +++ b/libwvdrmengine/cdm/core/test/http_socket_test.cpp @@ -3,6 +3,7 @@ #include #include "gtest/gtest.h" #include "http_socket.h" +#include "scoped_ptr.h" #include "log.h" #include "string_conversions.h" #include "url_request.h" @@ -10,10 +11,14 @@ namespace { // Arbitrary URL for tests. const std::string kHttpsTestServer("https://www.google.com"); +const std::string kHttpTestServer("http://www.google.com"); +// This URL and data are used by RoundTripTest, and can be overridden on the +// command line. std::string gTestServer(kHttpsTestServer); std::string gTestData("Hello"); +// Arbitrary buffer size and timeout settings. const int kHttpBufferSize = 4096; -char gBuffer[kHttpBufferSize]; +const int kTimeout = 3000; } namespace wvcdm { @@ -21,153 +26,179 @@ namespace wvcdm { class HttpSocketTest : public testing::Test { public: HttpSocketTest() {} - ~HttpSocketTest() { socket_.CloseSocket(); } + ~HttpSocketTest() {} protected: - bool Connect(const std::string& server_url, bool secure_connection) { + bool Connect(const std::string& server_url) { + socket_.reset(new HttpSocket(server_url)); - std::string port = secure_connection ? "443" : "80"; - if (socket_.Connect(server_url.c_str(), port, true, secure_connection)) { - LOGD("connected to %s", socket_.domain_name().c_str()); + if (socket_->Connect(kTimeout)) { + LOGD("connected to %s", socket_->domain_name().c_str()); + return true; } else { - LOGE("failed to connect to %s", socket_.domain_name().c_str()); + LOGE("failed to connect to %s", server_url.c_str()); return false; } - return true; } bool PostRequest(const std::string& data) { std::string request("POST "); - if (socket_.resource_path().empty()) - request.append(socket_.domain_name()); - else - request.append(socket_.resource_path()); + request.append(socket_->resource_path()); request.append(" HTTP/1.1\r\n"); + request.append("Host: "); - request.append(socket_.domain_name()); - request.append("\r\nUser-Agent: httpSocketTest/1.0\r\n"); + request.append(socket_->domain_name()); + request.append("\r\n"); + + // Important! Otherwise, the HTTP 1.1 default behavior for a server is to + // keep the connection open for a subsequent request. + request.append("Connection: close\r\n"); + + request.append("User-Agent: httpSocketTest/1.0\r\n"); + + char buffer[32] = {0}; + snprintf(buffer, sizeof(buffer), "%d", static_cast(data.size())); request.append("Content-Length: "); - memset(gBuffer, 0, kHttpBufferSize); - snprintf(gBuffer, kHttpBufferSize, "%d\r\n", static_cast(data.size())); - request.append(gBuffer); + request.append(buffer); + request.append("\r\n"); + request.append("Content-Type: multipart/form-data\r\n"); - // newline terminates header + // an extra newline terminates HTTP headers. request.append("\r\n"); // append data request.append(data); - socket_.Write(request.c_str(), request.size()); + socket_->Write(request.c_str(), request.size(), kTimeout); + LOGD("request: %s", request.c_str()); return true; } - bool GetResponse() { - int bytes = socket_.Read(gBuffer, kHttpBufferSize, 1000); + bool GetResponse(std::string* response) { + char buffer[kHttpBufferSize]; + int bytes = socket_->Read(buffer, sizeof(buffer), kTimeout); if (bytes < 0) { - LOGE("read error = ", errno); + LOGE("read error, errno = %d", errno); return false; - } else { - LOGD("read %d bytes", bytes); - std::string response(gBuffer, bytes); - LOGD("response: %s", response.c_str()); - LOGD("end response dump"); - return true; } + + LOGD("read %d bytes", bytes); + response->assign(buffer, bytes); + return true; } - HttpSocket socket_; + scoped_ptr socket_; std::string domain_name_; std::string resource_path_; }; -TEST_F(HttpSocketTest, GetDomainNameAndPathFromUrlTest) { - socket_.GetDomainNameAndPathFromUrl( - "https://code.google.com/p/googletest/wiki/Primer", domain_name_, - resource_path_); - EXPECT_STREQ("code.google.com", domain_name_.c_str()); - EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str()); +struct ParseUrlTests { + const char* url; + const char* scheme; + bool secure_connect; + const char* domain_name; + int port; + const char* path; +}; - socket_.GetDomainNameAndPathFromUrl( - "http://code.google.com/p/googletest/wiki/Primer/", domain_name_, - resource_path_); - EXPECT_STREQ("code.google.com", domain_name_.c_str()); - EXPECT_STREQ("p/googletest/wiki/Primer/", resource_path_.c_str()); +ParseUrlTests parse_url_tests[] = { + { + "https://code.google.com/p/googletest/wiki/Primer", // url + "https", // scheme + true, // secure_connect + "code.google.com", // domain_name + 443, // port + "/p/googletest/wiki/Primer", // path + }, + { + "http://code.google.com/p/googletest/wiki/Primer/", // url + "http", // scheme + false, // secure_connect + "code.google.com", // domain_name + 80, // port + "/p/googletest/wiki/Primer/", // path + }, + { + "http://code.google.com/", // url + "http", // scheme + false, // secure_connect + "code.google.com", // domain_name + 80, // port + "/", // path + }, + { + "http://code.google.com", // url + "http", // scheme + false, // secure_connect + "code.google.com", // domain_name + 80, // port + "/", // path + }, + { + "http://10.11.12.13:8888/drm", // url + "http", // scheme + false, // secure_connect + "10.11.12.13", // domain_name + 8888, // port + "/drm", // path + }, + { + "http://10.11.12.13:8888", // url + "http", // scheme + false, // secure_connect + "10.11.12.13", // domain_name + 8888, // port + "/", // path + }, + { + "https://10.11.12.13:8888", // url + "https", // scheme + true, // secure_connect + "10.11.12.13", // domain_name + 8888, // port + "/", // path + }, + { NULL } // list terminator +}; - socket_.GetDomainNameAndPathFromUrl("http://code.google.com/", domain_name_, - resource_path_); - EXPECT_STREQ("code.google.com", domain_name_.c_str()); - EXPECT_STREQ("", resource_path_.c_str()); +TEST_F(HttpSocketTest, ParseUrlTest) { + std::string scheme; + bool secure_connect; + std::string domain_name; + int port; + std::string path; + ParseUrlTests* test = NULL; - socket_.GetDomainNameAndPathFromUrl("http://code.google.com", domain_name_, - resource_path_); - EXPECT_STREQ("code.google.com", domain_name_.c_str()); - EXPECT_STREQ("", resource_path_.c_str()); - - socket_.GetDomainNameAndPathFromUrl( - "code.google.com/p/googletest/wiki/Primer", domain_name_, resource_path_); - EXPECT_STREQ("code.google.com", domain_name_.c_str()); - EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str()); - - socket_.GetDomainNameAndPathFromUrl("code.google.com", domain_name_, - resource_path_); - EXPECT_STREQ("code.google.com", domain_name_.c_str()); - EXPECT_STREQ("", resource_path_.c_str()); - - socket_.GetDomainNameAndPathFromUrl("code.google.com/", domain_name_, - resource_path_); - EXPECT_STREQ("code.google.com", domain_name_.c_str()); - EXPECT_STREQ("", resource_path_.c_str()); - - socket_.GetDomainNameAndPathFromUrl("", domain_name_, resource_path_); - EXPECT_TRUE(domain_name_.empty()); - EXPECT_TRUE(resource_path_.empty()); - - // Test with arbitrary numeric URL - socket_.GetDomainNameAndPathFromUrl("http://10.11.12.13:8888/drm", - domain_name_, resource_path_); - EXPECT_STREQ("10.11.12.13", domain_name_.c_str()); - EXPECT_STREQ("drm", resource_path_.c_str()); - - socket_.GetDomainNameAndPathFromUrl("http://10.11.12.13:8888", domain_name_, - resource_path_); - EXPECT_STREQ("10.11.12.13", domain_name_.c_str()); - EXPECT_TRUE(resource_path_.empty()); + for (test = &parse_url_tests[0]; test->url != NULL; ++test) { + bool ok = HttpSocket::ParseUrl(test->url, &scheme, &secure_connect, + &domain_name, &port, &path); + EXPECT_TRUE(ok); + if (ok) { + EXPECT_EQ(test->scheme, scheme); + EXPECT_EQ(test->secure_connect, secure_connect); + EXPECT_EQ(test->domain_name, domain_name); + EXPECT_EQ(test->port, port); + EXPECT_EQ(test->path, path); + } + } } TEST_F(HttpSocketTest, ConnectTest) { - const bool kUseSecureConnection = true; - - if (gTestServer.find("https") != std::string::npos) { - EXPECT_TRUE(Connect(gTestServer, kUseSecureConnection)); - socket_.CloseSocket(); - - // https connection allows insecure connection through port 80 as well - EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection)); - socket_.CloseSocket(); - } else { - EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection)); - socket_.CloseSocket(); - - // Test for the case that non-https connection must not use port 443 - EXPECT_FALSE(Connect(gTestServer, kUseSecureConnection)); - socket_.CloseSocket(); - } - - EXPECT_FALSE(Connect("ww.g.c", kUseSecureConnection)); - socket_.CloseSocket(); - - EXPECT_FALSE(Connect("ww.g.c", !kUseSecureConnection)); - socket_.CloseSocket(); + EXPECT_TRUE(Connect(kHttpsTestServer)); + EXPECT_TRUE(Connect(kHttpTestServer)); + EXPECT_FALSE(Connect("ww.g.c")); + EXPECT_FALSE(Connect("http://ww.g.c")); + EXPECT_FALSE(Connect("https://ww.g.c")); } TEST_F(HttpSocketTest, RoundTripTest) { - int secure_connection = - (gTestServer.find("https") != std::string::npos) ? true : false; - ASSERT_TRUE(Connect(gTestServer, secure_connection)); + ASSERT_TRUE(Connect(gTestServer)); EXPECT_TRUE(PostRequest(gTestData)); - GetResponse(); - socket_.CloseSocket(); + + std::string response; + EXPECT_TRUE(GetResponse(&response)); + LOGD("response: %s", response.c_str()); } } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/license_request.cpp b/libwvdrmengine/cdm/core/test/license_request.cpp index fe08d80c..f6bbd3de 100644 --- a/libwvdrmengine/cdm/core/test/license_request.cpp +++ b/libwvdrmengine/cdm/core/test/license_request.cpp @@ -44,9 +44,6 @@ void LicenseRequest::GetDrmMessage(const std::string& response, if (drm_msg_pos != std::string::npos) { drm_msg = response.substr(drm_msg_pos); } else { - // TODO(edwinwong, rfrias): hack to get HTTP message body out for - // non-Google Play webservers. Need to clean this up. Possibly test - // for GLS and decide which part is the drm message drm_msg = response.substr(header_end_pos); } } else { @@ -59,7 +56,7 @@ void LicenseRequest::GetDrmMessage(const std::string& response, void LicenseRequest::GetHeartbeatUrl(const std::string& response, std::string& heartbeat_url) { if (response.empty()) { - heartbeat_url.clear(); // TODO: assign default heartbeat url + heartbeat_url.clear(); return; } diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index a6843325..24e67314 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -1,5 +1,8 @@ // Copyright 2012 Google Inc. All Rights Reserved. +#include + +#include #include #include "clock.h" @@ -9,9 +12,37 @@ #include "policy_engine.h" #include "wv_cdm_constants.h" +namespace { +const int64_t kDurationUnlimited = 0; +const int64_t kLicenseStartTime = 1413517500; // ~ 01/01/2013 +const int64_t kRentalDuration = 604800; // 7 days +const int64_t kPlaybackDuration = 172800; // 48 hours +const int64_t kStreamingLicenseDuration = 300; // 5 minutes +const int64_t kOfflineLicenseDuration = kRentalDuration; +const int64_t kLicenseRenewalPeriod = 120; // 2 minutes +const int64_t kLicenseRenewalRetryInterval = 30; // 30 seconds +const int64_t kLicenseRenewalRecoveryDuration = 30; // 30 seconds +const int64_t kLowDuration = + std::min(std::min(std::min(kRentalDuration, kPlaybackDuration), + kStreamingLicenseDuration), + kOfflineLicenseDuration); +const int64_t kHighDuration = + std::max(std::max(std::max(kRentalDuration, kPlaybackDuration), + kStreamingLicenseDuration), + kOfflineLicenseDuration); +const char* kRenewalServerUrl = + "https://test.google.com/license/GetCencLicense"; + +int64_t GetLicenseRenewalDelay(int64_t license_duration) { + return license_duration > kLicenseRenewalPeriod + ? license_duration - kLicenseRenewalPeriod + : 0; +} +} // unnamed namespace + namespace wvcdm { -//protobuf generated classes. +// protobuf generated classes. using video_widevine_server::sdk::License; using video_widevine_server::sdk::License_Policy; using video_widevine_server::sdk::LicenseIdentification; @@ -22,7 +53,6 @@ using video_widevine_server::sdk::OFFLINE; using ::testing::Return; using ::testing::AtLeast; - class MockClock : public Clock { public: MOCK_METHOD0(GetCurrentTime, int64_t()); @@ -34,13 +64,7 @@ class PolicyEngineTest : public ::testing::Test { mock_clock_ = new MockClock(); policy_engine_ = new PolicyEngine(mock_clock_); - license_start_time_ = 1413517500; // ~ 01/01/2013 - license_renewal_delay_ = 604200; // 7 days - 10 minutes - license_renewal_retry_interval_ = 30; - license_duration_ = 604800; // 7 days - playback_duration_ = 86400; // 24 hours - - license_.set_license_start_time(license_start_time_); + license_.set_license_start_time(kLicenseStartTime); LicenseIdentification* id = license_.mutable_id(); id->set_version(1); @@ -49,20 +73,18 @@ class PolicyEngineTest : public ::testing::Test { License_Policy* policy = license_.mutable_policy(); policy = license_.mutable_policy(); policy->set_can_play(true); - policy->set_can_persist(true); + policy->set_can_persist(false); policy->set_can_renew(true); - policy->set_rental_duration_seconds(license_duration_); - policy->set_playback_duration_seconds(playback_duration_); - policy->set_license_duration_seconds(license_duration_); - policy->set_renewal_recovery_duration_seconds(license_duration_ - - license_renewal_delay_); // 10 minutes + policy->set_rental_duration_seconds(kRentalDuration); + policy->set_playback_duration_seconds(kPlaybackDuration); + policy->set_license_duration_seconds(kStreamingLicenseDuration); + policy->set_renewal_recovery_duration_seconds( + kLicenseRenewalRecoveryDuration); - // Note: not a real URL - used for testing Policy/PolicyEngine interfaces - policy->set_renewal_server_url( - "https://test.google.com/license/GetCencLicense"); - policy->set_renewal_delay_seconds(license_renewal_delay_); - policy->set_renewal_retry_interval_seconds( - license_renewal_retry_interval_); + policy->set_renewal_server_url(kRenewalServerUrl); + policy->set_renewal_delay_seconds( + GetLicenseRenewalDelay(kStreamingLicenseDuration)); + policy->set_renewal_retry_interval_seconds(kLicenseRenewalRetryInterval); policy->set_renew_with_usage(false); } @@ -73,16 +95,21 @@ class PolicyEngineTest : public ::testing::Test { mock_clock_ = NULL; } + int64_t GetMinOfRentalPlaybackLicenseDurations() { + const License_Policy& policy = license_.policy(); + int64_t rental_duration = policy.rental_duration_seconds(); + int64_t playback_duration = policy.playback_duration_seconds(); + int64_t license_duration = policy.license_duration_seconds(); + if (rental_duration == kDurationUnlimited) rental_duration = LLONG_MAX; + if (playback_duration == kDurationUnlimited) playback_duration = LLONG_MAX; + if (license_duration == kDurationUnlimited) license_duration = LLONG_MAX; + return std::min(std::min(rental_duration, playback_duration), + license_duration); + } + MockClock* mock_clock_; PolicyEngine* policy_engine_; License license_; - License_Policy* policy_; - - int64_t license_start_time_; - int64_t license_renewal_delay_; - int64_t license_renewal_retry_interval_; - int64_t license_duration_; - int64_t playback_duration_; }; TEST_F(PolicyEngineTest, NoLicense) { @@ -91,9 +118,9 @@ TEST_F(PolicyEngineTest, NoLicense) { TEST_F(PolicyEngineTest, PlaybackSuccess) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 10)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + 10)); policy_engine_->SetLicense(license_); @@ -107,13 +134,15 @@ TEST_F(PolicyEngineTest, PlaybackSuccess) { } TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 5)); - License_Policy* policy = license_.mutable_policy(); policy->set_can_play(false); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)); + policy_engine_->SetLicense(license_); + EXPECT_FALSE(policy_engine_->can_decrypt()); bool event_occurred; CdmEventType event; @@ -124,17 +153,18 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) { EXPECT_FALSE(policy_engine_->can_decrypt()); } -// TODO(edwinwong, rfrias): persist license verification test needed - TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 3600)) - .WillOnce(Return(license_start_time_ + 3601)); - License_Policy* policy = license_.mutable_policy(); - policy->set_rental_duration_seconds(3600); + policy->set_rental_duration_seconds(kLowDuration); + policy->set_license_duration_seconds(kHighDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + min_duration + 1)); policy_engine_->SetLicense(license_); @@ -154,14 +184,16 @@ TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) { } TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 10000)) - .WillOnce(Return(license_start_time_ + 13598)) - .WillOnce(Return(license_start_time_ + 13602)); - License_Policy* policy = license_.mutable_policy(); - policy->set_playback_duration_seconds(3600); + policy->set_license_duration_seconds(kHighDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + int64_t playback_start_time = kLicenseStartTime + 10000; + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(playback_start_time)) + .WillOnce(Return(playback_start_time + kPlaybackDuration - 2)) + .WillOnce(Return(playback_start_time + kPlaybackDuration + 2)); policy_engine_->SetLicense(license_); @@ -181,14 +213,43 @@ TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) { } TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 3600)) - .WillOnce(Return(license_start_time_ + 3601)); - License_Policy* policy = license_.mutable_policy(); - policy->set_license_duration_seconds(3600); + policy->set_can_renew(false); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, PlaybackFails_ExpiryBeforeRenewalDelay) { + License_Policy* policy = license_.mutable_policy(); + policy->set_renewal_delay_seconds(kStreamingLicenseDuration + 10); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)); policy_engine_->SetLicense(license_); @@ -208,15 +269,18 @@ TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) { } TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 3600)) - .WillOnce(Return(license_start_time_ + 3601)); - License_Policy* policy = license_.mutable_policy(); - policy->set_rental_duration_seconds(0); - policy->set_license_duration_seconds(3600); + policy->set_rental_duration_seconds(kDurationUnlimited); + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay)) + .WillOnce(Return(kLicenseStartTime + 1 + license_renewal_delay)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration)) + .WillOnce(Return(kLicenseStartTime + 1 + kStreamingLicenseDuration)); policy_engine_->SetLicense(license_); @@ -228,6 +292,14 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) { policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -236,15 +308,19 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) { } TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 10000)) - .WillOnce(Return(license_start_time_ + 10005)) - .WillOnce(Return(license_start_time_ + 13598)) - .WillOnce(Return(license_start_time_ + 13602)); - License_Policy* policy = license_.mutable_policy(); - policy->set_playback_duration_seconds(0); - policy->set_license_duration_seconds(3600); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kHighDuration); + int64_t license_renewal_delay = GetLicenseRenewalDelay(kHighDuration); + policy->set_renewal_delay_seconds(license_renewal_delay); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 2)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 2)) + .WillOnce(Return(kLicenseStartTime + kHighDuration - 2)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 2)); policy_engine_->SetLicense(license_); @@ -256,6 +332,14 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) { policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -264,15 +348,17 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) { } TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 3600)) - .WillOnce(Return(license_start_time_ + 3601)); - License_Policy* policy = license_.mutable_policy(); - policy->set_license_duration_seconds(0); - policy->set_rental_duration_seconds(3600); + policy->set_license_duration_seconds(kDurationUnlimited); + policy->set_rental_duration_seconds(kLowDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)); policy_engine_->SetLicense(license_); @@ -292,17 +378,17 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) { } TEST_F(PolicyEngineTest, PlaybackOk_Durations0) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 604800)) - .WillOnce(Return(license_start_time_ + 604810)); - License_Policy* policy = license_.mutable_policy(); - policy->set_rental_duration_seconds(0); - policy->set_playback_duration_seconds(0); - policy->set_license_duration_seconds(0); - policy->set_renewal_delay_seconds(604900); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kDurationUnlimited); + policy->set_renewal_delay_seconds(kHighDuration + 10); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + kHighDuration)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 10)); policy_engine_->SetLicense(license_); @@ -321,16 +407,17 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) { } TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_duration_ + 10)); - License_Policy* policy = license_.mutable_policy(); policy->set_can_renew(false); + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 10)); policy_engine_->SetLicense(license_); @@ -353,15 +440,17 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) { } TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 15)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + - license_renewal_retry_interval_ + 10)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 15)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + + kLicenseRenewalRetryInterval + 10)); policy_engine_->SetLicense(license_); @@ -379,8 +468,8 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { EXPECT_TRUE(policy_engine_->can_decrypt()); - license_.set_license_start_time(license_start_time_ + - license_renewal_delay_ + 15); + license_.set_license_start_time(kLicenseStartTime + license_renewal_delay + + 15); LicenseIdentification* id = license_.mutable_id(); id->set_version(2); policy_engine_->UpdateLicense(license_); @@ -392,14 +481,16 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { } TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) - .WillOnce(Return(license_start_time_ + license_duration_ + 10)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 40)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 10)); policy_engine_->SetLicense(license_); @@ -417,8 +508,8 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { EXPECT_TRUE(policy_engine_->can_decrypt()); - license_.set_license_start_time(license_start_time_ + - license_renewal_delay_ + 15); + license_.set_license_start_time(kLicenseStartTime + license_renewal_delay + + 15); policy_engine_->UpdateLicense(license_); policy_engine_->OnTimerEvent(&event_occurred, &event); @@ -435,18 +526,20 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { } TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80)) - .WillOnce(Return(license_start_time_ + license_duration_ + 15)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 40)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 50)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 70)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 80)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 15)); policy_engine_->SetLicense(license_); @@ -493,20 +586,22 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) { } TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80)) - .WillOnce(Return(license_start_time_ + license_duration_ + 10)) - .WillOnce(Return(license_start_time_ + license_duration_ + 30)) - .WillOnce(Return(license_start_time_ + license_duration_ + 40)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 40)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 50)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 70)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 80)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 30)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 40)); policy_engine_->SetLicense(license_); @@ -551,14 +646,14 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) { EXPECT_FALSE(policy_engine_->can_decrypt()); - license_.set_license_start_time(license_start_time_ + - license_duration_ + 20); + license_.set_license_start_time(kLicenseStartTime + + kStreamingLicenseDuration + 20); LicenseIdentification* id = license_.mutable_id(); id->set_version(2); License_Policy* policy = license_.mutable_policy(); policy = license_.mutable_policy(); - policy->set_playback_duration_seconds(playback_duration_ + 100); - policy->set_license_duration_seconds(license_duration_ + 100); + policy->set_playback_duration_seconds(kPlaybackDuration + 100); + policy->set_license_duration_seconds(kStreamingLicenseDuration + 100); policy_engine_->UpdateLicense(license_); @@ -569,18 +664,20 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) { } TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 55)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 67)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 200)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 40)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 50)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 55)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 67)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 200)); policy_engine_->SetLicense(license_); @@ -610,8 +707,8 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) { policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - license_.set_license_start_time(license_start_time_ + - license_renewal_delay_ + 55); + license_.set_license_start_time(kLicenseStartTime + license_renewal_delay + + 55); LicenseIdentification* id = license_.mutable_id(); id->set_version(2); policy_engine_->UpdateLicense(license_); @@ -628,32 +725,33 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) { } TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 10)) - .WillOnce(Return(license_start_time_ + 20)) - .WillOnce(Return(license_start_time_ + 40)) - .WillOnce(Return(license_start_time_ + 50)); - License_Policy* policy = license_.mutable_policy(); policy->set_renew_with_usage(true); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + 10)) + .WillOnce(Return(kLicenseStartTime + 20)) + .WillOnce(Return(kLicenseStartTime + 40)) + .WillOnce(Return(kLicenseStartTime + 50)); + policy_engine_->SetLicense(license_); bool event_occurred; CdmEventType event; policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); + EXPECT_TRUE(policy_engine_->can_decrypt()); policy_engine_->BeginDecryption(); - EXPECT_FALSE(policy_engine_->can_decrypt()); + EXPECT_TRUE(policy_engine_->can_decrypt()); policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); - license_.set_license_start_time(license_start_time_ + 30); + license_.set_license_start_time(kLicenseStartTime + 30); policy->set_renew_with_usage(false); LicenseIdentification* id = license_.mutable_id(); id->set_version(2); @@ -667,7 +765,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) { TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_)); + .WillOnce(Return(kLicenseStartTime)); CdmQueryMap query_info; EXPECT_EQ(UNKNOWN_ERROR, policy_engine_->Query(&query_info)); @@ -675,10 +773,8 @@ TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) { TEST_F(PolicyEngineTest, QuerySuccess) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 100)); - - License_Policy* policy = license_.mutable_policy(); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 100)); policy_engine_->SetLicense(license_); @@ -686,76 +782,133 @@ TEST_F(PolicyEngineTest, QuerySuccess) { EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); - EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); int64_t remaining_time; std::istringstream ss; ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); ss >> remaining_time; - EXPECT_LT(0, remaining_time); + ss.clear(); + EXPECT_EQ(kStreamingLicenseDuration - 99, remaining_time); ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); ss >> remaining_time; - EXPECT_LT(0, remaining_time); - - EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL], - policy->renewal_server_url()); + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); } -TEST_F(PolicyEngineTest, QuerySuccess_Offline) { +TEST_F(PolicyEngineTest, QuerySuccess_PlaybackNotBegun) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 100)); - - LicenseIdentification* id = license_.mutable_id(); - id->set_type(OFFLINE); - - License_Policy* policy = license_.mutable_policy(); - policy->set_can_play(false); - policy->set_can_persist(false); - policy->set_can_renew(false); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 100)) + .WillOnce(Return(kLicenseStartTime + 200)); policy_engine_->SetLicense(license_); + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kStreamingLicenseDuration - 99, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + ss.clear(); + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kStreamingLicenseDuration - 199, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_PlaybackBegun) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 50)) + .WillOnce(Return(kLicenseStartTime + 100)) + .WillOnce(Return(kLicenseStartTime + 150)) + .WillOnce(Return(kLicenseStartTime + 200)); + + policy_engine_->SetLicense(license_); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kStreamingLicenseDuration - 49, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + bool event_occurred; CdmEventType event; policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->BeginDecryption(); - EXPECT_FALSE(policy_engine_->can_decrypt()); - - CdmQueryMap query_info; EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); - EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]); - EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); - EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_RENEW_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); - int64_t remaining_time; - std::istringstream ss; + ss.clear(); ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); ss >> remaining_time; - EXPECT_EQ(0, remaining_time); + EXPECT_EQ(kStreamingLicenseDuration - 199, remaining_time); + ss.clear(); ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); ss >> remaining_time; - EXPECT_EQ(0, remaining_time); - - EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL], - policy->renewal_server_url()); + EXPECT_EQ(kPlaybackDuration - 100, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); } -TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 10)) - .WillOnce(Return(license_start_time_ + license_duration_ + 20)); - +TEST_F(PolicyEngineTest, QuerySuccess_Offline) { LicenseIdentification* id = license_.mutable_id(); id->set_type(OFFLINE); License_Policy* policy = license_.mutable_policy(); + policy->set_can_persist(true); + policy->set_can_renew(false); + policy->set_license_duration_seconds(kOfflineLicenseDuration); + policy->set_renewal_delay_seconds( + GetLicenseRenewalDelay(kOfflineLicenseDuration)); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 100)) + .WillOnce(Return(kLicenseStartTime + 200)) + .WillOnce(Return(kLicenseStartTime + 300)); policy_engine_->SetLicense(license_); @@ -772,6 +925,101 @@ TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) { EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kOfflineLicenseDuration - 299, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - 100, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_CanPlayFalse) { + LicenseIdentification* id = license_.mutable_id(); + id->set_type(OFFLINE); + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_play(false); + policy->set_can_persist(true); + policy->set_license_duration_seconds(kOfflineLicenseDuration); + policy->set_renewal_delay_seconds( + GetLicenseRenewalDelay(kOfflineLicenseDuration)); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 100)) + .WillOnce(Return(kLicenseStartTime + 200)); + + policy_engine_->SetLicense(license_); + EXPECT_FALSE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->BeginDecryption(); + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kOfflineLicenseDuration - 199, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_RentalDurationExpired) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kLowDuration); + policy->set_license_duration_seconds(kHighDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + min_duration + 1)) + .WillOnce(Return(kLicenseStartTime + min_duration + 5)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); int64_t remaining_time; @@ -779,12 +1027,338 @@ TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) { ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); ss >> remaining_time; EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - min_duration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_PlaybackDurationExpired) { + License_Policy* policy = license_.mutable_policy(); + policy->set_playback_duration_seconds(kLowDuration); + policy->set_license_duration_seconds(kHighDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + int64_t playback_start_time = kLicenseStartTime + 10000; + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(playback_start_time)) + .WillOnce(Return(playback_start_time - 2 + min_duration)) + .WillOnce(Return(playback_start_time + 2 + min_duration)) + .WillOnce(Return(playback_start_time + 5 + min_duration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kHighDuration - 10004 - min_duration, remaining_time); + ss.clear(); ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); ss >> remaining_time; EXPECT_EQ(0, remaining_time); - - EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL], - policy->renewal_server_url()); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); } +TEST_F(PolicyEngineTest, QuerySuccess_LicenseDurationExpired) { + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)) + .WillOnce(Return(kLicenseStartTime + 5 + min_duration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - min_duration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_RentalDuration0) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kDurationUnlimited); + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay)) + .WillOnce(Return(kLicenseStartTime + 1 + license_renewal_delay)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration)) + .WillOnce(Return(kLicenseStartTime + 1 + kStreamingLicenseDuration)) + .WillOnce(Return(kLicenseStartTime + 5 + kStreamingLicenseDuration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - kStreamingLicenseDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_PlaybackDuration0) { + License_Policy* policy = license_.mutable_policy(); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kHighDuration); + int64_t license_renewal_delay = GetLicenseRenewalDelay(kHighDuration); + policy->set_renewal_delay_seconds(license_renewal_delay); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 2)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 2)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 5)) + .WillOnce(Return(kLicenseStartTime + kHighDuration - 2)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 2)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 5)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kHighDuration - license_renewal_delay - 5, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(LLONG_MAX, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + ss.clear(); + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(LLONG_MAX, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_LicenseDuration0) { + License_Policy* policy = license_.mutable_policy(); + policy->set_license_duration_seconds(kDurationUnlimited); + policy->set_rental_duration_seconds(kStreamingLicenseDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)) + .WillOnce(Return(kLicenseStartTime + 5 + min_duration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - min_duration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_Durations0) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kDurationUnlimited); + policy->set_renewal_delay_seconds(kHighDuration + 10); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + kHighDuration)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 15)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(LLONG_MAX, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(LLONG_MAX, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} } // wvcdm diff --git a/libwvdrmengine/cdm/core/test/url_request.cpp b/libwvdrmengine/cdm/core/test/url_request.cpp index 1da99c1b..e15fb357 100644 --- a/libwvdrmengine/cdm/core/test/url_request.cpp +++ b/libwvdrmengine/cdm/core/test/url_request.cpp @@ -10,52 +10,16 @@ #include "string_conversions.h" namespace { -const int kMaxReadAttempts = 4; -const int kSingleReadAttempt = 1; -} // namespace -namespace wvcdm { - -UrlRequest::UrlRequest(const std::string& url, const std::string& port, - bool secure_connection, bool chunk_transfer_mode) - : chunk_transfer_mode_(chunk_transfer_mode), - is_connected_(false), - port_("80"), - request_(""), - server_url_(url) { - if (!port.empty()) { - port_.assign(port); - } - if (socket_.Connect((server_url_).c_str(), port_, true, secure_connection)) { - is_connected_ = true; - } else { - LOGE("failed to connect to %s, port=%s", socket_.domain_name().c_str(), - port.c_str()); - } -} - -UrlRequest::~UrlRequest() { socket_.CloseSocket(); } - -void UrlRequest::AppendChunkToUpload(const std::string& data) { - // format of chunk: - // size of chunk in hex\r\n - // data\r\n - // . . . - // 0\r\n - - // buffer to store length of chunk - memset(buffer_, 0, kHttpBufferSize); - snprintf(buffer_, kHttpBufferSize, "%zx\r\n", data.size()); - request_.append(buffer_); // appends size of chunk - LOGD("...\r\n%s", request_.c_str()); - request_.append(data); - request_.append("\r\n"); // marks end of data -} +const int kReadBufferSize = 1024; +const int kConnectTimeoutMs = 5000; +const int kWriteTimeoutMs = 3000; +const int kReadTimeoutMs = 3000; // Concatenate all chunks into one blob and returns the response with // header information. -void UrlRequest::ConcatenateChunkedResponse(const std::string http_response, - std::string* modified_response) { +void ConcatenateChunkedResponse(const std::string http_response, + std::string* modified_response) { if (http_response.empty()) return; modified_response->clear(); @@ -103,33 +67,52 @@ void UrlRequest::ConcatenateChunkedResponse(const std::string http_response, } } -int UrlRequest::GetResponse(std::string* message) { - message->clear(); +} // namespace + +namespace wvcdm { + +UrlRequest::UrlRequest(const std::string& url) + : is_connected_(false), + socket_(url) { + if (socket_.Connect(kConnectTimeoutMs)) { + is_connected_ = true; + } else { + LOGE("failed to connect to %s, port=%d", socket_.domain_name().c_str(), + socket_.port()); + } +} + +UrlRequest::~UrlRequest() {} + +bool UrlRequest::GetResponse(std::string* message) { std::string response; - const int kTimeoutInMs = 3000; - int bytes = 0; - for (int attempts = kMaxReadAttempts; attempts > 0; --attempts) { - memset(buffer_, 0, kHttpBufferSize); - bytes = socket_.Read(buffer_, kHttpBufferSize, kTimeoutInMs); + + // Keep reading until end of stream (0 bytes read) or timeout. Partial + // buffers worth of data can and do happen, especially with OpenSSL in + // non-blocking mode. + while (true) { + char read_buffer[kReadBufferSize]; + int bytes = socket_.Read(read_buffer, sizeof(read_buffer), kReadTimeoutMs); if (bytes > 0) { - response.append(buffer_, bytes); - if (bytes < static_cast(kHttpBufferSize)) { - attempts = kSingleReadAttempt; - } + response.append(read_buffer, bytes); + } else if (bytes < 0) { + LOGE("read error, errno = %d", errno); + return false; } else { - if (bytes < 0) LOGE("read error = ", errno); - // bytes == 0 indicates nothing to read + // end of stream. + break; } } ConcatenateChunkedResponse(response, message); LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str()); - return message->size(); + return true; } +// static int UrlRequest::GetStatusCode(const std::string& response) { - const std::string kHttpVersion("HTTP/1.1"); + const std::string kHttpVersion("HTTP/1.1 "); int status_code = -1; size_t pos = response.find(kHttpVersion); @@ -140,79 +123,48 @@ int UrlRequest::GetStatusCode(const std::string& response) { return status_code; } -bool UrlRequest::PostRequestChunk(const std::string& data) { - request_.assign("POST /"); - request_.append(socket_.resource_path()); - request_.append(" HTTP/1.1\r\n"); - request_.append("Host: "); - request_.append(socket_.domain_name()); - request_.append("\r\nConnection: Keep-Alive\r\n"); - request_.append("Transfer-Encoding: chunked\r\n"); - request_.append("User-Agent: Widevine CDM v1.0\r\n"); - request_.append("Accept-Encoding: gzip,deflate\r\n"); - request_.append("Accept-Language: en-us,fr\r\n"); - request_.append("Accept-Charset: iso-8859-1,*,utf-8\r\n"); - request_.append("\r\n"); // empty line to terminate header +bool UrlRequest::PostRequestWithPath(const std::string& path, + const std::string& data) { + std::string request; - // calls AppendChunkToUpload repeatedly for multiple chunks - AppendChunkToUpload(data); + request.append("POST "); + request.append(path); + request.append(" HTTP/1.1\r\n"); - // terminates last chunk with 0\r\n, then ends header with an empty line - request_.append("0\r\n\r\n"); + request.append("Host: "); + request.append(socket_.domain_name()); + request.append("\r\n"); - socket_.Write(request_.c_str(), request_.size()); - return true; + request.append("Connection: close\r\n"); + request.append("User-Agent: Widevine CDM v1.0\r\n"); + + // buffer to store length of data as a string + char data_size_buffer[32] = {0}; + snprintf(data_size_buffer, sizeof(data_size_buffer), "%zd", data.size()); + + request.append("Content-Length: "); + request.append(data_size_buffer); // appends size of data + request.append("\r\n"); + + request.append("\r\n"); // empty line to terminate headers + + request.append(data); + + int ret = socket_.Write(request.c_str(), request.size(), kWriteTimeoutMs); + LOGD("HTTP request: (%d): %s", request.size(), b2a_hex(request).c_str()); + return ret != -1; } bool UrlRequest::PostRequest(const std::string& data) { - if (chunk_transfer_mode_) { - return PostRequestChunk(data); - } - request_.assign("POST /"); - request_.append(socket_.resource_path()); - request_.append(" HTTP/1.1\r\n"); - request_.append("Host: "); - request_.append(socket_.domain_name()); - request_.append("\r\nConnection: Keep-Alive\r\n"); - request_.append("User-Agent: Widevine CDM v1.0\r\n"); - request_.append("Accept-Encoding: gzip,deflate\r\n"); - request_.append("Accept-Language: en-us,fr\r\n"); - request_.append("Accept-Charset: iso-8859-1,*,utf-8\r\n"); - std::ostringstream ss; - ss << data.size(); - request_.append("Content-Length: "); - request_.append(ss.str()); - request_.append("\r\n\r\n"); - request_.append(data); - - // terminates with \r\n, then ends with an empty line - request_.append("\r\n\r\n"); - - socket_.Write(request_.c_str(), request_.size()); - LOGD("HTTP request: (%d): %s", request_.size(), request_.c_str()); - LOGD("HTTP request: (%d): %s", request_.size(), b2a_hex(request_).c_str()); - return true; + return PostRequestWithPath(socket_.resource_path(), data); } bool UrlRequest::PostCertRequestInQueryString(const std::string& data) { - request_.assign("POST /"); - request_.append(socket_.resource_path()); - request_.append("&signedRequest="); - request_.append(data); - request_.append(" HTTP/1.1\r\n"); - request_.append("User-Agent: Widevine CDM v1.0\r\n"); - request_.append("Host: "); - request_.append(socket_.domain_name()); - request_.append("\r\nAccept: */*"); - request_.append("\r\nContent-Type: application/json"); - request_.append("\r\nContent-Length: 0"); - request_.append("\r\n"); // empty line to terminate header - request_.append("\r\n"); // terminates the request + std::string path = socket_.resource_path(); + path.append("&signedRequest="); + path.append(data); - socket_.Write(request_.c_str(), request_.size()); - LOGD("HTTP request: (%d): %s", request_.size(), request_.c_str()); - LOGD("HTTP request: (%d): %s", request_.size(), b2a_hex(request_).c_str()); - return true; + return PostRequestWithPath(path, ""); } } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/url_request.h b/libwvdrmengine/cdm/core/test/url_request.h index 43358f16..498bb624 100644 --- a/libwvdrmengine/cdm/core/test/url_request.h +++ b/libwvdrmengine/cdm/core/test/url_request.h @@ -13,29 +13,22 @@ namespace wvcdm { // Only POST request method is implemented. class UrlRequest { public: - UrlRequest(const std::string& url, const std::string& port, - bool secure_connect, bool chunk_transfer_mode); + explicit UrlRequest(const std::string& url); ~UrlRequest(); - void AppendChunkToUpload(const std::string& data); - void ConcatenateChunkedResponse(const std::string http_response, - std::string* modified_response); - int GetResponse(std::string* message); - int GetStatusCode(const std::string& response); bool is_connected() const { return is_connected_; } + bool PostRequest(const std::string& data); - bool PostRequestChunk(const std::string& data); bool PostCertRequestInQueryString(const std::string& data); + bool GetResponse(std::string* message); + static int GetStatusCode(const std::string& response); + private: - static const unsigned int kHttpBufferSize = 4096; - char buffer_[kHttpBufferSize]; - bool chunk_transfer_mode_; + bool PostRequestWithPath(const std::string& path, const std::string& data); + bool is_connected_; - std::string port_; - std::string request_; HttpSocket socket_; - std::string server_url_; CORE_DISALLOW_COPY_AND_ASSIGN(UrlRequest); }; diff --git a/libwvdrmengine/cdm/include/properties_configuration.h b/libwvdrmengine/cdm/include/properties_configuration.h index 04e4f09f..e8e79641 100644 --- a/libwvdrmengine/cdm/include/properties_configuration.h +++ b/libwvdrmengine/cdm/include/properties_configuration.h @@ -8,13 +8,6 @@ namespace wvcdm { -// If false begin license usage on first playback -const bool kPropertyBeginLicenseUsageWhenReceived = false; - -// If false, calls to Generate Key request, after the first one, -// will result in a renewal request being generated -const bool kPropertyRequireExplicitRenewRequest = false; - // Set only one of the three below to true. If secure buffer // is selected, fallback to userspace buffers may occur // if L1/L2 OEMCrypto APIs fail @@ -29,10 +22,6 @@ const bool kPropertyOemCryptoRequireUsageTable = true; // and passed as the token in the license request const bool kPropertyUseCertificatesAsIdentification = true; -// If true, session_id parameter to CdmEngine::Decrypt can be empty; the -// function will try to find out the session_id from the key_id. -const bool kDecryptWithEmptySessionSupport = false; - // If true, device files will be moved to the directory specified by // Properties::GetDeviceFilesBasePath const bool kSecurityLevelPathBackwardCompatibilitySupport = true; diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index a1cd68ff..2f88876a 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -81,6 +81,8 @@ class WvContentDecryptionModule : public TimerHandler { std::string* cert, std::string* wrapped_key); + virtual CdmResponseType Unprovision(CdmSecurityLevel level); + // Secure stop related methods virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info); virtual CdmResponseType ReleaseUsageInfo( diff --git a/libwvdrmengine/cdm/src/log.cpp b/libwvdrmengine/cdm/src/log.cpp index 6b403504..287be9af 100644 --- a/libwvdrmengine/cdm/src/log.cpp +++ b/libwvdrmengine/cdm/src/log.cpp @@ -24,6 +24,8 @@ namespace wvcdm { +LogPriority g_cutoff = LOG_VERBOSE; + void InitLogging(int argc, const char* const* argv) {} void Log(const char* file, int line, LogPriority level, const char* fmt, ...) { diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index a8347253..ca589647 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -149,6 +149,11 @@ CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse( return cdm_engine_->HandleProvisioningResponse(response, cert, wrapped_key); } +CdmResponseType WvContentDecryptionModule::Unprovision( + CdmSecurityLevel level) { + return cdm_engine_->Unprovision(level); +} + CdmResponseType WvContentDecryptionModule::GetUsageInfo( CdmUsageInfo* usage_info) { return cdm_engine_->GetUsageInfo(usage_info); diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index e0d87674..d535844c 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -34,13 +34,9 @@ wvcdm::ConfigTestEnv* g_config = NULL; wvcdm::KeyId g_key_id; wvcdm::CdmKeySystem g_key_system; std::string g_license_server; -std::string g_port; wvcdm::KeyId g_wrong_key_id; -bool g_use_chunked_transfer = false; -bool g_use_full_path = false; -bool g_use_secure_transfer = false; wvcdm::LicenseServerId g_license_server_id = - wvcdm::kYouTubeContentProtectionServer; + wvcdm::kContentProtectionServer; std::string kServiceCertificate = "0803121028703454C008F63618ADE7443DB6C4C8188BE7F99005228E023082010" @@ -393,8 +389,8 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { session_sharing_id_(0) {} virtual ~TestWvCdmClientPropertySet() {} - virtual std::string security_level() const { return security_level_; } - virtual std::vector service_certificate() const { + virtual const std::string& security_level() const { return security_level_; } + virtual const std::string& service_certificate() const { return service_certificate_; } virtual bool use_privacy_mode() const { return use_privacy_mode_; } @@ -409,8 +405,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { security_level_ = security_level; } } - void set_service_certificate( - const std::vector& service_certificate) { + void set_service_certificate(const std::string& service_certificate) { service_certificate_ = service_certificate; } void set_use_privacy_mode(bool use_privacy_mode) { @@ -423,7 +418,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { private: std::string security_level_; - std::vector service_certificate_; + std::string service_certificate_; bool use_privacy_mode_; bool is_session_sharing_enabled_; uint32_t session_sharing_id_; @@ -450,6 +445,19 @@ class WvCdmRequestLicenseTest : public testing::Test { ~WvCdmRequestLicenseTest() {} protected: + void GetOfflineConfiguration(std::string* key_id, std::string* client_auth) { + ConfigTestEnv config(g_license_server_id, false); + if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0) + key_id->assign(wvcdm::a2bs_hex(config.key_id())); + else + key_id->assign(g_key_id); + + if (g_client_auth.compare(g_config->client_auth()) == 0) + client_auth->assign(config.client_auth()); + else + client_auth->assign(g_client_auth); + } + void GenerateKeyRequest(const std::string& init_data, CdmLicenseType license_type) { wvcdm::CdmAppParameterMap app_parameters; @@ -492,8 +500,7 @@ class WvCdmRequestLicenseTest : public testing::Test { std::string GetKeyRequestResponse(const std::string& server_url, const std::string& client_auth) { // Use secure connection and chunk transfer coding. - UrlRequest url_request(server_url + client_auth, g_port, - g_use_secure_transfer, g_use_chunked_transfer); + UrlRequest url_request(server_url + client_auth); if (!url_request.is_connected()) { return ""; } @@ -517,7 +524,7 @@ class WvCdmRequestLicenseTest : public testing::Test { // the HTTP response. std::string GetCertRequestResponse(const std::string& server_url) { // Use secure connection and chunk transfer coding. - UrlRequest url_request(server_url, kDefaultHttpsPort, true, true); + UrlRequest url_request(server_url); if (!url_request.is_connected()) { return ""; } @@ -538,8 +545,7 @@ class WvCdmRequestLicenseTest : public testing::Test { const std::string& client_auth, const std::string& usage_info_request) { // Use secure connection and chunk transfer coding. - UrlRequest url_request(server_url + client_auth, g_port, - g_use_secure_transfer, g_use_chunked_transfer); + UrlRequest url_request(server_url + client_auth); if (!url_request.is_connected()) { return ""; } @@ -587,6 +593,20 @@ class WvCdmRequestLicenseTest : public testing::Test { return itr->second; } + + CdmSecurityLevel GetDefaultSecurityLevel() { + std::string level = GetSecurityLevel(NULL).c_str(); + CdmSecurityLevel security_level = kSecurityLevelUninitialized; + if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0) { + security_level = kSecurityLevelL1; + } else if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0) { + security_level = kSecurityLevelL3; + } else { + EXPECT_TRUE(false); + } + return security_level; + } + wvcdm::WvContentDecryptionModule decryptor_; CdmKeyMessage key_msg_; CdmSessionId session_id_; @@ -614,6 +634,20 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { decryptor_.CloseSession(session_id_); } +TEST_F(WvCdmRequestLicenseTest, UnprovisionTest) { + CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + DeviceFiles handle; + EXPECT_TRUE(handle.Init(security_level)); + File file; + handle.SetTestFile(&file); + std::string certificate; + std::string wrapped_private_key; + EXPECT_TRUE(handle.RetrieveCertificate(&certificate, &wrapped_private_key)); + + EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(security_level)); + EXPECT_FALSE(handle.RetrieveCertificate(&certificate, &wrapped_private_key)); +} + TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) { decryptor_.OpenSession(g_key_system, NULL, &session_id_); std::string provisioning_server_url; @@ -710,16 +744,20 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) { property_set_Ln.set_security_level(""); decryptor_.OpenSession(g_key_system, &property_set_Ln, &session_id_Ln); - std::string security_level = Properties::GetSecurityLevel(session_id_L1); + std::string security_level; + EXPECT_TRUE(Properties::GetSecurityLevel(session_id_L1, &security_level)); EXPECT_TRUE(!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) || !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)); EXPECT_TRUE(Properties::UsePrivacyMode(session_id_L1)); - EXPECT_EQ(Properties::GetSecurityLevel(session_id_L3), - QUERY_VALUE_SECURITY_LEVEL_L3); + + EXPECT_TRUE(Properties::GetSecurityLevel(session_id_L3, &security_level)); + EXPECT_EQ(security_level, QUERY_VALUE_SECURITY_LEVEL_L3); EXPECT_FALSE(Properties::UsePrivacyMode(session_id_L3)); - security_level = Properties::GetSecurityLevel(session_id_Ln); + + EXPECT_TRUE(Properties::GetSecurityLevel(session_id_Ln, &security_level)); EXPECT_TRUE(security_level.empty() || !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)); + decryptor_.CloseSession(session_id_L1); decryptor_.CloseSession(session_id_L3); decryptor_.CloseSession(session_id_Ln); @@ -776,7 +814,7 @@ TEST_F(WvCdmRequestLicenseTest, PrivacyModeWithServiceCertificateTest) { TestWvCdmClientPropertySet property_set; property_set.set_use_privacy_mode(true); - property_set.set_service_certificate(a2b_hex(kServiceCertificate)); + property_set.set_service_certificate(a2bs_hex(kServiceCertificate)); decryptor_.OpenSession(g_key_system, &property_set, &session_id_); GenerateKeyRequest(g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, false); @@ -797,8 +835,7 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) { GenerateKeyRequest(wrong_message, kLicenseTypeStreaming); // We should receive a response with no license, i.e. the extracted license // response message should be empty or an HTTP error - UrlRequest url_request(g_license_server + g_client_auth, g_port, - g_use_secure_transfer, g_use_chunked_transfer); + UrlRequest url_request(g_license_server + g_client_auth); if (!url_request.is_connected()) { return; } @@ -828,14 +865,9 @@ TEST_F(WvCdmRequestLicenseTest, AddStreamingKeyTest) { TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) { // override default settings unless configured through the command line - std::string key_id = g_key_id; - std::string client_auth = g_client_auth; - - ConfigTestEnv config(g_license_server_id, false); - if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0) - key_id.assign(wvcdm::a2bs_hex(config.key_id())); - if (g_client_auth.compare(g_config->client_auth()) == 0) - client_auth.assign(config.client_auth()); + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(key_id, kLicenseTypeOffline); @@ -845,14 +877,9 @@ TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) { TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { // override default settings unless configured through the command line - std::string key_id = g_key_id; - std::string client_auth = g_client_auth; - - ConfigTestEnv config(g_license_server_id, false); - if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0) - key_id.assign(wvcdm::a2bs_hex(config.key_id())); - if (g_client_auth.compare(g_config->client_auth()) == 0) - client_auth.assign(config.client_auth()); + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(key_id, kLicenseTypeOffline); @@ -870,14 +897,9 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { // override default settings unless configured through the command line - std::string key_id = g_key_id; - std::string client_auth = g_client_auth; - - ConfigTestEnv config(g_license_server_id, false); - if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0) - key_id.assign(wvcdm::a2bs_hex(config.key_id())); - if (g_client_auth.compare(g_config->client_auth()) == 0) - client_auth.assign(config.client_auth()); + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(key_id, kLicenseTypeOffline); @@ -902,14 +924,9 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { // override default settings unless configured through the command line - std::string key_id = g_key_id; - std::string client_auth = g_client_auth; - - ConfigTestEnv config(g_license_server_id, false); - if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0) - key_id.assign(wvcdm::a2bs_hex(config.key_id())); - if (g_client_auth.compare(g_config->client_auth()) == 0) - client_auth.assign(config.client_auth()); + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(key_id, kLicenseTypeOffline); @@ -954,14 +971,9 @@ TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) { TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) { // override default settings unless configured through the command line - std::string key_id = g_key_id; - std::string client_auth = g_client_auth; - - ConfigTestEnv config(g_license_server_id, false); - if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0) - key_id.assign(wvcdm::a2bs_hex(config.key_id())); - if (g_client_auth.compare(g_config->client_auth()) == 0) - client_auth.assign(config.client_auth()); + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(key_id, kLicenseTypeOffline); @@ -979,19 +991,10 @@ class WvCdmUsageInfoTest public ::testing::WithParamInterface {}; TEST_P(WvCdmUsageInfoTest, DISABLED_UsageInfo) { - File file; + CdmSecurityLevel security_level = GetDefaultSecurityLevel(); DeviceFiles handle; - - std::string level = GetSecurityLevel(NULL).c_str(); - CdmSecurityLevel security_level = kSecurityLevelUninitialized; - if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0) { - security_level = kSecurityLevelL1; - } else if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0) { - security_level = kSecurityLevelL3; - } else { - EXPECT_TRUE(false); - } EXPECT_TRUE(handle.Init(security_level)); + File file; handle.SetTestFile(&file); EXPECT_TRUE(handle.DeleteUsageInfo()); @@ -1037,7 +1040,11 @@ TEST_P(WvCdmUsageInfoTest, DISABLED_UsageInfo) { EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg)); } status = decryptor_.GetUsageInfo(&usage_info); - EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status); + switch (status) { + case KEY_MESSAGE: EXPECT_FALSE(usage_info.empty()); break; + case NO_ERROR: EXPECT_TRUE(usage_info.empty()); break; + default: FAIL() << "GetUsageInfo failed with error " << status ; break; + } } } @@ -1499,14 +1506,14 @@ TEST_P(WvCdmSessionSharingTest, SessionSharingTest) { } TEST_F(WvCdmRequestLicenseTest, DecryptionKeyExpiredTest) { - const std::string kYtCpKeyId = a2bs_hex( + const std::string kCpKeyId = a2bs_hex( "000000347073736800000000" // blob size and pssh "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id "0801121030313233343536373839616263646566"); // pssh data SubSampleInfo* data = &single_encrypted_sub_sample_short_expiry; decryptor_.OpenSession(g_key_system, NULL, &session_id_); if (data->retrieve_key) { - GenerateKeyRequest(kYtCpKeyId, kLicenseTypeStreaming); + GenerateKeyRequest(kCpKeyId, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, false); } @@ -1579,10 +1586,6 @@ void show_menu(char* prog_name) { std::cout << " or adb shell '" << prog_name << " -u\"url\"'" << std::endl << std::endl; - std::cout << std::setw(35) << std::left << " -c/--chunked_transfer"; - std::cout << "specifies chunked transfer encoding in request" << std::endl - << std::endl; - std::cout << std::setw(35) << std::left << " -f/--use_full_path"; std::cout << "specify server url is not a proxy server" << std::endl; std::cout << std::endl; @@ -1592,7 +1595,7 @@ void show_menu(char* prog_name) { std::cout << std::setw(35) << std::left << " "; std::cout << "gp (case sensitive) for GooglePlay server" << std::endl; std::cout << std::setw(35) << std::left << " "; - std::cout << "cp (case sensitive) for Youtube Content Protection server" + std::cout << "cp (case sensitive) for Content Protection server" << std::endl << std::endl; std::cout << std::setw(35) << std::left << " -k/--keyid="; @@ -1616,34 +1619,22 @@ int main(int argc, char** argv) { bool show_usage = false; static const struct option long_options[] = { - {"chunked_transfer", no_argument, NULL, 'c'}, {"keyid", required_argument, NULL, 'k'}, {"license_server_id", required_argument, NULL, 'i'}, {"license_server_url", required_argument, NULL, 'u'}, - {"port", required_argument, NULL, 'p'}, - {"secure_transfer", no_argument, NULL, 's'}, - {"use_full_path", no_argument, NULL, 'f'}, {NULL, 0, NULL, '\0'}}; int option_index = 0; int opt = 0; - while ((opt = getopt_long(argc, argv, "cfi:k:p:su:", long_options, + while ((opt = getopt_long(argc, argv, "i:k:u:", long_options, &option_index)) != -1) { switch (opt) { - case 'c': { - g_use_chunked_transfer = true; - break; - } - case 'f': { - g_use_full_path = true; - break; - } case 'i': { std::string license_id(optarg); if (!license_id.compare("gp")) { g_license_server_id = wvcdm::kGooglePlayServer; } else if (!license_id.compare("cp")) { - g_license_server_id = wvcdm::kYouTubeContentProtectionServer; + g_license_server_id = wvcdm::kContentProtectionServer; } else { std::cout << "Invalid license server id" << optarg << std::endl; show_usage = true; @@ -1655,15 +1646,6 @@ int main(int argc, char** argv) { g_key_id.assign(optarg); break; } - case 'p': { - g_port.clear(); - g_port.assign(optarg); - break; - } - case 's': { - g_use_secure_transfer = true; - break; - } case 'u': { g_license_server.clear(); g_license_server.assign(optarg); @@ -1688,32 +1670,21 @@ int main(int argc, char** argv) { // The following variables are configurable through command line // options. If the command line arguments are absent, use the settings - // in license_servers[] pointed to by g_config. + // in kLicenseServers[] pointed to by g_config. if (g_key_id.empty()) { g_key_id.assign(g_config->key_id()); } if (g_license_server.empty()) { g_license_server.assign(g_config->license_server()); } - if (g_port.empty()) { - g_port.assign(g_config->port()); - } - if (!g_use_chunked_transfer) { - g_use_chunked_transfer = g_config->use_chunked_transfer(); - } - if (!g_use_secure_transfer) { - g_use_secure_transfer = g_config->use_secure_transfer(); - } // Displays server url, port and key Id being used std::cout << std::endl; std::cout << "Server: " << g_license_server << std::endl; - std::cout << "Port: " << g_port << std::endl; std::cout << "KeyID: " << g_key_id << std::endl << std::endl; g_key_id = wvcdm::a2bs_hex(g_key_id); g_config->set_license_server(g_license_server); - g_config->set_port(g_port); int status = RUN_ALL_TESTS(); delete g_config; diff --git a/libwvdrmengine/cdm/test/test_vectors.h b/libwvdrmengine/cdm/test/test_vectors.h index 7ba7c987..50b7dd9c 100644 --- a/libwvdrmengine/cdm/test/test_vectors.h +++ b/libwvdrmengine/cdm/test/test_vectors.h @@ -8,10 +8,10 @@ namespace wvcdm { namespace test_vectors { // for FileStore unit tests -static const std::string kFileExists = "/system/bin/sh"; -static const std::string kDirExists = "/system/bin"; -static const std::string kFileDoesNotExist = "/system/bin/enoext"; -static const std::string kDirDoesNotExist = "/system/bin_enoext"; +static const std::string kExistentFile = "/system/bin/sh"; +static const std::string kExistentDir = "/system/bin"; +static const std::string kNonExistentFile = "/system/bin/enoext"; +static const std::string kNonExistentDir = "/system/bin_enoext"; static const std::string kTestDir = "/data/mediadrm/IDM0/"; } // namespace test_vectors diff --git a/libwvdrmengine/cdm/test/unit-test.mk b/libwvdrmengine/cdm/test/unit-test.mk index a35963f4..f85c795a 100644 --- a/libwvdrmengine/cdm/test/unit-test.mk +++ b/libwvdrmengine/cdm/test/unit-test.mk @@ -51,6 +51,8 @@ LOCAL_SHARED_LIBRARIES := \ libstlport \ libutils +LOCAL_CFLAGS += -DUNIT_TEST + # Needed to use gMock 1.7.0 on Android LOCAL_CFLAGS += \ -DGTEST_HAS_TR1_TUPLE \ diff --git a/libwvdrmengine/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement.pdf b/libwvdrmengine/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement.pdf index 01e96f2e..a85247da 100644 Binary files a/libwvdrmengine/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement.pdf and b/libwvdrmengine/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement.pdf differ diff --git a/libwvdrmengine/level3/arm/libwvlevel3.a b/libwvdrmengine/level3/arm/libwvlevel3.a index 2d0505d4..26bfcc4a 100644 Binary files a/libwvdrmengine/level3/arm/libwvlevel3.a and b/libwvdrmengine/level3/arm/libwvlevel3.a differ diff --git a/libwvdrmengine/level3/x86/libwvlevel3.a b/libwvdrmengine/level3/x86/libwvlevel3.a index 819be199..971b5274 100644 Binary files a/libwvdrmengine/level3/x86/libwvlevel3.a and b/libwvdrmengine/level3/x86/libwvlevel3.a differ diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index fea5eeda..7e626aac 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -175,7 +175,7 @@ class WVDrmPlugin : public android::DrmPlugin, virtual ~WVClientPropertySet() {} - virtual std::string security_level() const { + virtual const std::string& security_level() const { return mSecurityLevel; } @@ -191,11 +191,11 @@ class WVDrmPlugin : public android::DrmPlugin, mUsePrivacyMode = usePrivacyMode; } - virtual std::vector service_certificate() const { + virtual const std::string& service_certificate() const { return mServiceCertificate; } - void set_service_certificate(const std::vector& serviceCertificate) { + void set_service_certificate(const std::string& serviceCertificate) { mServiceCertificate = serviceCertificate; } @@ -220,7 +220,7 @@ class WVDrmPlugin : public android::DrmPlugin, std::string mSecurityLevel; bool mUsePrivacyMode; - std::vector mServiceCertificate; + std::string mServiceCertificate; bool mShareKeys; uint32_t mSessionSharingId; } mPropertySet; diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index ce230934..4d2bb2d5 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -498,9 +498,9 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name, value.appendArray(reinterpret_cast(uniqueId.data()), uniqueId.size()); } else if (name == "serviceCertificate") { - vector cert = mPropertySet.service_certificate(); + std::string cert = mPropertySet.service_certificate(); value.clear(); - value.appendArray(&cert[0], cert.size()); + value.appendArray(reinterpret_cast(&cert[0]), cert.size()); } else { ALOGE("App requested unknown byte array property %s", name.string()); return android::ERROR_DRM_CANNOT_HANDLE; @@ -577,7 +577,7 @@ status_t WVDrmPlugin::setPropertyString(const String8& name, status_t WVDrmPlugin::setPropertyByteArray(const String8& name, const Vector& value) { if (name == "serviceCertificate") { - vector cert(value.begin(), value.end()); + std::string cert(value.begin(), value.end()); mPropertySet.set_service_certificate(cert); } else { ALOGE("App set unknown byte array property %s", name.string()); diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index 3d4ed3e5..ba095d4d 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -9,9 +9,9 @@ #ifndef OEMCRYPTO_CENC_H_ #define OEMCRYPTO_CENC_H_ -#include -#include -#include +#include +#include +#include #ifdef __cplusplus extern "C" { diff --git a/libwvdrmengine/oemcrypto/include/oemcrypto_logging.h b/libwvdrmengine/oemcrypto/include/oemcrypto_logging.h new file mode 100644 index 00000000..93af8fb9 --- /dev/null +++ b/libwvdrmengine/oemcrypto/include/oemcrypto_logging.h @@ -0,0 +1,65 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +#ifndef WVOEC_OEMCRYPTO_LOGGING_H_ +#define WVOEC_OEMCRYPTO_LOGGING_H_ + +#include "OEMCryptoCENC.h" + +#include "log.h" +#include +#include + +namespace wvoec_mock { + +// The constants below represent integers with a single "on" bit that +// represents categories of logging This allows users to specify with +// more precision what they want to log. LogCategoryEnabled(category) +// is used to see if the category passed in the parameters is to +// be logged based on the current settings. Categories can be combines +// using the | (or) bitwise operator. For example +// LogCategoryEnabled(category1 | category2) will return true if +// category1 and/or category2 are set to logging. + +const int kLoggingTraceOEMCryptoCalls = 0x01; +const int kLoggingDumpContentKeys = 0x02; +const int kLoggingDumpKeyControlBlocks = 0x04; +const int kLoggingDumpDerivedKeys = 0x08; +const int kLoggingTraceNonce = 0x10; +const int kLoggingTraceDecryption = 0x20; +const int kLoggingTraceUsageTable = 0x40; +const int kLoggingDumpTraceAll = 0xFF; + +void SetLoggingSettings(int level, int categories); + +// set level of logging +void SetLoggingLevel(int level); + +void TurnOffLoggingForAllCategories(); + +// Returns true if the category passed is set to logging. +// Returns false otherwise. The category constant declared +// above are passed. +bool LogCategoryEnabled(int category); + +// Turn on logging for the categories passed. +void AddLoggingForCategories(int categories); + +// Turn off logging for the categories passed. +void RemoveLoggingForCategories(int categories); + +void dump_hex_helper(std::string& buffer, std::string name, + const uint8_t* vector, size_t length); + +void dump_hex(std::string name, const uint8_t* vector, size_t length); + +void dump_array_part_helper(std::string& buffer, std::string array, + size_t index, std::string name, + const uint8_t* vector, size_t length); + +void dump_array_part(std::string array, size_t index, + std::string name, const uint8_t* vector, size_t length); + +} // namespace wvoec_mock + +#endif + diff --git a/libwvdrmengine/oemcrypto/mock/Makefile b/libwvdrmengine/oemcrypto/mock/Makefile deleted file mode 100644 index 8bb0f55f..00000000 --- a/libwvdrmengine/oemcrypto/mock/Makefile +++ /dev/null @@ -1,110 +0,0 @@ -# -# Builds liboemcrypto_mock.so -# - -#PROJECTS_ROOT = ~projects -# -ifndef PROJECTS_ROOT -PROJECTS_ROOT = ../../../.. -endif - -CDM_ROOT = $(PROJECTS_ROOT)/cdm -CDM_SRC_PATH = $(CDM_ROOT)/cdm -CDM_INCLUDE_PATH = $(CDM_SRC_PATH)/include - -EUREKA_ROOT = $(PROJECTS_ROOT)/eureka/eureka/src -CHROME_ROOT = $(EUREKA_ROOT)/chromium/src -# -# build outputs should go into Chrome repository, such as ../chromium/src/out -# or some local equivalent. -# WARNING: splitting outputs from CHROME_ROOT can lead to build errors -ifndef CHROME_ROOT -CHROME_ROOT = $(CDM_ROOT)/out -endif - -# TARGET_PLATFORM from {x86,eureka} -ifndef TARGET_PLATFORM -TARGET_PLATFORM = x86 -endif - -# TARGET_BUILD from {debug,release} -ifndef TARGET_BUILD -TARGET_BUILD = debug -endif - -ifeq ($(TARGET_PLATFORM),x86) - BUILDPLATFORM = out_x86_linux -else ifeq ($(TARGET_PLATFORM),eureka) - BUILDPLATFORM = out_arm_eureka -else - BUILDPLATFORM = UNKNOWN -endif - -ifeq ($(TARGET_BUILD),debug) - BUILDTYPE = Debug -else ifeq ($(TARGET_BUILD),release) - BUILDTYPE = Release -else - BUILDTYPE = UNKNOWN -endif - -BUILDPATH = $(CHROME_ROOT)/$(BUILDPLATFORM)/$(BUILDTYPE) -OBJPATH = $(BUILDPATH)/obj - -#primary build target -TARGET_LIB = liboemcrypto_mock.so - -LIB_OBJECTS = \ - oemcrypto_mock.o \ - oemcrypto_engine_mock.o \ - oemcrypto_key_mock.o \ - oemcrypto_keybox_mock.o \ - oemcrypto_usage_table_mock.o - -INCLUDES = \ - -I$(CDM_INCLUDE_PATH) - -OBJECTDIR = $(OBJPATH)/mock - -INSTALLDIR = $(BUILDPATH) - -OBJECTS := $(patsubst %.o,$(OBJECTDIR)/%.o,$(LIB_OBJECTS)) - -CXXFLAGS = -m64 -fPIC -W -Wall -g -DCDM_TEST -LINK = $(CXX) -MKDIR = mkdir -p - -$(INSTALLDIR)/$(TARGET_LIB): $(OBJECTDIR) $(INSTALLDIR) $(OBJECTS) - $(LINK) -v -fPIC -m64 -shared -static-libgcc $(SHLIBFLAGS) \ - $(OBJECTS) \ - --retain-symbols-file=OECsymbols.txt \ - -Wl,-Bstatic \ - -Wl,-Bdynamic -lcrypto \ - -o $@ - -$(OBJECTDIR)/%.o: src/%.cpp - $(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@ - -$(OBJECTDIR)/%.o: src/%.cc - $(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@ - -test: - make -C test - -clean: - $(RM) -rf $(OBJECTDIR) - $(RM) -rf $(INSTALLDIR)/$(TARGET_LIB) - -$(OBJECTDIR): - @$(MKDIR) $@ - -$(INSTALLDIR): - @$(MKDIR) $@ - -.PHONY: $(OBJECTDIR) - -.PHONY: $(INSTALLDIR) - -.PHONY: clean - -.PHONY: test diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index 7a226880..07b2e053 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -11,6 +11,7 @@ #include "log.h" #include "oemcrypto_key_mock.h" +#include "oemcrypto_logging.h" #include "oemcrypto_usage_table_mock.h" #include "openssl/aes.h" #include "openssl/bio.h" @@ -34,7 +35,7 @@ void ctr128_inc64(uint8_t* counter) { uint32_t n = 16; do { if (++counter[--n] != 0) return; - } while (n>8); + } while (n > 8); } void dump_openssl_error() { while (unsigned long err = ERR_get_error()) { @@ -43,7 +44,7 @@ void dump_openssl_error() { err, ERR_error_string(err, buffer)); } } -} +} // namespace namespace wvoec_mock { @@ -76,7 +77,7 @@ void SessionKeyTable::Remove(const KeyId key_id) { } void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) { - for(KeyMap::iterator it = keys_.begin(); it != keys_.end(); ++it) { + for (KeyMap::iterator it = keys_.begin(); it != keys_.end(); ++it) { it->second->UpdateDuration(control); } } @@ -157,17 +158,13 @@ bool SessionContext::DeriveKeys(const std::vector& master_key, return false; } -#if 0 // Print Derived Keys to stdout. - std::cout << " mac_key_context = " << wvcdm::b2a_hex(mac_key_context) - << std::endl; - std::cout << " enc_key_context = " << wvcdm::b2a_hex(enc_key_context) - << std::endl; - std::cout << " mac_key_server = " << wvcdm::b2a_hex(mac_key_server) - << std::endl; - std::cout << " mac_key_client = " << wvcdm::b2a_hex(mac_key_client) - << std::endl; - std::cout << " enc_key = " << wvcdm::b2a_hex(enc_key) << std::endl; -#endif + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGI((" mac_key_context = " + wvcdm::b2a_hex(mac_key_context)).c_str()); + LOGI((" enc_key_context = " + wvcdm::b2a_hex(enc_key_context)).c_str()); + LOGI((" mac_key_server = " + wvcdm::b2a_hex(mac_key_server)).c_str()); + LOGI((" mac_key_client = " + wvcdm::b2a_hex(mac_key_client)).c_str()); + LOGI((" enc_key = " + wvcdm::b2a_hex(enc_key)).c_str()); + } set_mac_key_server(mac_key_server); set_mac_key_client(mac_key_client); @@ -183,7 +180,7 @@ bool SessionContext::RSADeriveKeys(const std::vector& enc_session_key, return false; } if (enc_session_key.size() != static_cast(RSA_size(rsa_key_))) { - LOGE("[RSADeriveKeys(): encrypted session key is wrong size:%zu, should be %d]", + LOGE("[RSADeriveKeys(): encrypted session key wrong size:%zu, expected %d]", enc_session_key.size(), RSA_size(rsa_key_)); dump_openssl_error(); return false; @@ -214,7 +211,6 @@ bool SessionContext::GenerateSignature(const uint8_t* message, size_t message_length, uint8_t* signature, size_t* signature_length) { - if (message == NULL || message_length == 0 || signature == NULL || signature_length == 0) { LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); @@ -305,8 +301,8 @@ bool SessionContext::GenerateRSASignature(const uint8_t* message, return false; } // Pad the message with PKCS1 padding, and then encrypt. - int status = RSA_private_encrypt(message_length, message, signature, - rsa_key_, RSA_PKCS1_PADDING); + size_t status = RSA_private_encrypt(message_length, message, signature, + rsa_key_, RSA_PKCS1_PADDING); if (status != *signature_length) { LOGE("[GeneratRSASignature(): error in RSA private encrypt. status=%d]", status); dump_openssl_error(); @@ -323,7 +319,6 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message, size_t message_length, const uint8_t* given_signature, size_t signature_length) { - if (signature_length != SHA256_DIGEST_LENGTH) { return false; } @@ -471,14 +466,14 @@ bool SessionContext::InstallKey(const KeyId& key_id, return false; } -#if 0 // Print content key to stdout. - std::cout << " InstallKey: key_id = " - << wvcdm::b2a_hex(key_id) << std::endl; - std::cout << " InstallKey: content_key = " - << wvcdm::b2a_hex(content_key) << std::endl; - std::cout << " InstallKey: key_control = " - << wvcdm::b2a_hex(key_control_str) << std::endl; -#endif + if (LogCategoryEnabled(kLoggingDumpContentKeys)) { + LOGI((" InstallKey: key_id = " + + wvcdm::b2a_hex(key_id)).c_str()); + LOGI((" InstallKey: content_key = " + + wvcdm::b2a_hex(content_key)).c_str()); + LOGI((" InstallKey: key_control = " + + wvcdm::b2a_hex(key_control_str)).c_str()); + } // Key control must be supplied by license server if (key_control.empty()) { @@ -517,7 +512,7 @@ bool SessionContext::RefreshKey(const KeyId& key_id, // Key control is not encrypted if key id is NULL KeyControlBlock key_control_block(key_control); if (!key_control_block.valid()) { - LOGD("Parse key control error."); + LOGE("Parse key control error."); return false; } if ((key_control_block.control_bits() & kControlNonceEnabled) && @@ -533,12 +528,16 @@ bool SessionContext::RefreshKey(const KeyId& key_id, Key* content_key = session_keys_.Find(key_id); if (NULL == content_key) { - LOGD("Error: no matching content key."); + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Error: no matching content key."); + } return false; } if (key_control.empty()) { - LOGD("Error: no key_control."); + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Error: no key_control."); + } return false; } @@ -547,20 +546,28 @@ bool SessionContext::RefreshKey(const KeyId& key_id, // Decrypt encrypted key control block std::vector control; if (key_control_iv.empty()) { - LOGD("Key control block is NOT encrypted."); + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Key control block is NOT encrypted."); + } control = key_control; } else { - LOGD("Key control block is encrypted."); + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Key control block is encrypted."); + } if (!DecryptMessage(content_key_value, key_control_iv, key_control, &control)) { - LOGD("Error decrypting key control block."); + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Error decrypting key control block."); + } return false; } } KeyControlBlock key_control_block(control); if (!key_control_block.valid()) { - LOGD("Parse key control error."); + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Parse key control error."); + } return false; } if ((key_control_block.control_bits() & kControlNonceEnabled) && @@ -606,7 +613,6 @@ bool SessionContext::LoadRSAKey(uint8_t* pkcs8_rsa_key, size_t message_length, const uint8_t* signature, size_t signature_length) { - // Validate message signature if (!ValidateMessage(message, message_length, signature, signature_length)) { LOGE("[LoadRSAKey(): Could not verify signature]"); @@ -627,7 +633,7 @@ bool SessionContext::LoadRSAKey(uint8_t* pkcs8_rsa_key, rsa_key_length -= 8; } BIO *bio = BIO_new_mem_buf(pkcs8_rsa_key, rsa_key_length); - if( bio == NULL ) { + if ( bio == NULL ) { LOGE("[LoadRSAKey(): Could not allocate bio buffer]"); return false; } @@ -663,7 +669,7 @@ bool SessionContext::LoadRSAKey(uint8_t* pkcs8_rsa_key, return false; } switch (RSA_check_key(rsa_key_)) { - case 1: // valid. + case 1: // valid. return true; case 0: // not valid. LOGE("[LoadRSAKey(): rsa key not valid]"); @@ -690,7 +696,7 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, const KeyControlBlock& control = current_content_key()->control(); // Set the AES key. if (static_cast(key.size()) != AES_BLOCK_SIZE) { - LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d",key.size()); + LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (!(control.control_bits() & kControlAllowEncrypt)) { @@ -709,11 +715,11 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } - if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) { + if ( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) { LOGE("[Generic_Encrypt(): algorithm bad."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if( buffer_length % AES_BLOCK_SIZE != 0 ) { + if ( buffer_length % AES_BLOCK_SIZE != 0 ) { LOGE("[Generic_Encrypt(): buffers size bad."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -767,11 +773,11 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } - if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) { + if ( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) { LOGE("[Generic_Decrypt(): bad algorithm."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if( buffer_length % AES_BLOCK_SIZE != 0 ) { + if ( buffer_length % AES_BLOCK_SIZE != 0 ) { LOGE("[Generic_Decrypt(): bad buffer size."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -875,7 +881,7 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } - if( algorithm != OEMCrypto_HMAC_SHA256 ) { + if ( algorithm != OEMCrypto_HMAC_SHA256 ) { LOGE("[Generic_Verify(): bad algorithm."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -896,7 +902,6 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, bool SessionContext::UpdateMacKeys(const std::vector& enc_mac_keys, const std::vector& iv) { - // Decrypt mac key from enc_mac_key using device_keya std::vector mac_keys; if (!DecryptMessage(encryption_key_, iv, enc_mac_keys, &mac_keys)) { @@ -911,12 +916,14 @@ bool SessionContext::UpdateMacKeys(const std::vector& enc_mac_keys, bool SessionContext::SelectContentKey(const KeyId& key_id) { const Key* content_key = session_keys_.Find(key_id); -#if 0 - std::cout << " Select Key: key_id = " - << wvcdm::b2a_hex(key_id) << std::endl; - std::cout << " Select Key: key = " - << wvcdm::b2a_hex(content_key->value()) << std::endl; -#endif + + if (LogCategoryEnabled(kLoggingTraceDecryption)){ + LOGI(( " Select Key: key_id = " + + wvcdm::b2a_hex(key_id) ).c_str()); + LOGI(( " Select Key: key = " + + wvcdm::b2a_hex(content_key->value()) ).c_str()); + } + if (NULL == content_key) { LOGE("[SelectContentKey(): No key matches key id]"); return false; @@ -1176,4 +1183,4 @@ void NonceTable::Flush() { } } -}; // namespace wvoec_mock +}; // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp index 30b99c08..8ac2aed9 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp @@ -5,10 +5,13 @@ #include "oemcrypto_key_mock.h" #include +#include #include "log.h" +#include "oemcrypto_logging.h" #include "wv_cdm_constants.h" + namespace wvoec_mock { #define FOURCC(c1, c2, c3, c4) \ @@ -40,7 +43,7 @@ uint32_t KeyControlBlock::ExtractField(const std::vector& str, KeyControlBlock::KeyControlBlock( const std::vector& key_control_string) { if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) { - LOGE("KCB: BAD Size: %d (not %d)",key_control_string.size(), + LOGE("KCB: BAD Size: %d (not %d)", key_control_string.size(), wvcdm::KEY_CONTROL_SIZE); return; } @@ -49,49 +52,51 @@ KeyControlBlock::KeyControlBlock( duration_ = ExtractField(key_control_string, 1); nonce_ = ExtractField(key_control_string, 2); control_bits_ = ExtractField(key_control_string, 3); - - LOGD("KCB:"); - LOGD(" valid: %d", valid()); - LOGD(" duration: %d", duration()); - LOGD(" nonce: %08X", nonce()); - LOGD(" magic: %08X", verification()); - LOGD(" bits: %08X", control_bits()); - switch (control_bits() & kControlReplayMask) { - case kControlNonceRequired: - LOGD(" bits kControlReplay kControlNonceRequired."); - break; - case kControlNonceOrEntry: - LOGD(" bits kControlReplay kControlNonceOrEntry."); - break; - default: - LOGD(" bits kControlReplay unset."); - break; + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("KCB:"); + LOGD(" valid: %d", valid()); + LOGD(" duration: %d", duration()); + LOGD(" nonce: %08X", nonce()); + LOGD(" magic: %08X", verification()); + LOGD(" bits: %08X", control_bits()); + switch (control_bits() & kControlReplayMask) { + case kControlNonceRequired: + LOGD(" bits kControlReplay kControlNonceRequired."); + break; + case kControlNonceOrEntry: + LOGD(" bits kControlReplay kControlNonceOrEntry."); + break; + default: + LOGD(" bits kControlReplay unset."); + break; + } + LOGD(" bits kControlKDCPVersion 0x%02x.", + (control_bits() & kControlHDCPVersionMask) + >> kControlHDCPVersionShift); + LOGD(" bit kControlAllowEncrypt %s.", + (control_bits() & kControlAllowEncrypt) ? "set" : "unset"); + LOGD(" bit kControlAllowDecrypt %s.", + (control_bits() & kControlAllowDecrypt) ? "set" : "unset"); + LOGD(" bit kControlAllowSign %s.", + (control_bits() & kControlAllowSign) ? "set" : "unset"); + LOGD(" bit kControlAllowVerify %s.", + (control_bits() & kControlAllowVerify) ? "set" : "unset"); + LOGD(" bit kControlObserveDataPath %s.", + (control_bits() & kControlObserveDataPath) ? "set" : "unset"); + LOGD(" bit kControlObserveHDCP %s.", + (control_bits() & kControlObserveHDCP) ? "set" : "unset"); + LOGD(" bit kControlObserveCGMS %s.", + (control_bits() & kControlObserveCGMS) ? "set" : "unset"); + LOGD(" bit kControlDataPathSecure %s.", + (control_bits() & kControlDataPathSecure) ? "set" : "unset"); + LOGD(" bit kControlNonceEnabled %s.", + (control_bits() & kControlNonceEnabled) ? "set" : "unset"); + LOGD(" bit kControlHDCPRequired %s.", + (control_bits() & kControlHDCPRequired) ? "set" : "unset"); + uint32_t cgms_bits = control_bits() & 0x3; + const char* cgms_values[4] = {"free", "BAD", "once", "never"}; + LOGD(" CGMS = %s", cgms_values[cgms_bits]); } - LOGD(" bits kControlKDCPVersion 0x%02x.", - (control_bits() & kControlHDCPVersionMask) >> kControlHDCPVersionShift); - LOGD(" bit kControlAllowEncrypt %s.", - (control_bits() & kControlAllowEncrypt) ? "set" : "unset"); - LOGD(" bit kControlAllowDecrypt %s.", - (control_bits() & kControlAllowDecrypt) ? "set" : "unset"); - LOGD(" bit kControlAllowSign %s.", - (control_bits() & kControlAllowSign) ? "set" : "unset"); - LOGD(" bit kControlAllowVerify %s.", - (control_bits() & kControlAllowVerify) ? "set" : "unset"); - LOGD(" bit kControlObserveDataPath %s.", - (control_bits() & kControlObserveDataPath) ? "set" : "unset"); - LOGD(" bit kControlObserveHDCP %s.", - (control_bits() & kControlObserveHDCP) ? "set" : "unset"); - LOGD(" bit kControlObserveCGMS %s.", - (control_bits() & kControlObserveCGMS) ? "set" : "unset"); - LOGD(" bit kControlDataPathSecure %s.", - (control_bits() & kControlDataPathSecure) ? "set" : "unset"); - LOGD(" bit kControlNonceEnabled %s.", - (control_bits() & kControlNonceEnabled) ? "set" : "unset"); - LOGD(" bit kControlHDCPRequired %s.", - (control_bits() & kControlHDCPRequired) ? "set" : "unset"); - uint32_t cgms_bits = control_bits() & 0x3; - const char* cgms_values[4] = {"free", "BAD", "once", "never"}; - LOGD(" CGMS = %s", cgms_values[cgms_bits]); Validate(); } @@ -102,4 +107,4 @@ void Key::UpdateDuration(const KeyControlBlock& control) { control_.set_duration(control.duration()); } -}; // namespace wvoec_eng +}; // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp index 3439d3ac..ba817a70 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp @@ -18,10 +18,10 @@ const WidevineKeybox kDefaultKeybox = { // Sample keybox used for test vectors { // deviceID - 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 - 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ }, { // key 0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e, @@ -76,7 +76,7 @@ KeyboxError WvKeybox::Validate() { memcpy(keybox.magic_, magic_, sizeof(keybox.magic_)); crc_computed = ntohl(wvcrc32(reinterpret_cast(&keybox), - sizeof(keybox) - 4)); // Drop last 4 bytes. + sizeof(keybox) - 4)); // Drop last 4 bytes. if (crc_computed != *crc_stored) { LOGE("[KEYBOX CRC problem: computed = %08x, stored = %08x]\n", crc_computed, *crc_stored); @@ -104,4 +104,4 @@ bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) { return true; } -}; // namespace wvoec_eng +}; // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_logging.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_logging.cpp new file mode 100644 index 00000000..511e7216 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_logging.cpp @@ -0,0 +1,109 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +#include "oemcrypto_logging.h" + +#include + +namespace wvoec_mock { + +int logging_category_setting = 0x00; + +void SetLoggingSettings(int level, int categories) { + SetLoggingLevel(level); + TurnOffLoggingForAllCategories(); + AddLoggingForCategories(categories); +} + +void TurnOffLoggingForAllCategories() { + logging_category_setting = 0; +} + +void SetLoggingLevel(int level){; + wvcdm::g_cutoff = static_cast(level); +} + +void SetLoggingLevel(wvcdm::LogPriority level) { + wvcdm::g_cutoff = level; +} + +void AddLoggingForCategories(int categories) { + logging_category_setting |= categories; +} + +void RemoveLoggingForCategories(int categories) { + logging_category_setting &= ~categories; +} + +bool LogCategoryEnabled(int categories) { + return ( (logging_category_setting & categories) !=0 ); +} + +void dump_hex_helper(std::string& buffer, std::string name, + const uint8_t* vector, size_t length) { + buffer += name + " = "; + if (vector == NULL) { + buffer +="NULL;\n"; + LOGE(buffer.c_str()); + return; + } + int a, b; + char int_to_hexcar[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + for (size_t i = 0; i < length; i++) { + if (i == 0) { + buffer += "\n wvcdm::a2b_hex(\""; + } else if (i % 32 == 0) { + buffer += "\"\n \""; + } + a = vector[i] % 16; + b = (vector[i] - a) / 16; + buffer += int_to_hexcar[b]; + buffer += int_to_hexcar[a]; + } + buffer += "\");\n"; +} + +void dump_hex(std::string name, const uint8_t* vector, size_t length) { + std::string buffer=""; + dump_hex_helper(buffer, name, vector, length); + LOGV(buffer.c_str()); +} + +void dump_array_part_helper(std::string& buffer, std::string array, + size_t index, std::string name, + const uint8_t* vector, size_t length) { + char index_str[256]; + + snprintf(index_str, sizeof index_str, "%zu", index); + + if (vector == NULL) { + buffer += array.c_str(); + buffer += "["; + buffer += index_str; + buffer += "]."; + buffer += name.c_str(); + buffer += " = NULL;\n"; + LOGW(buffer.c_str()); + return; + } + buffer += "std::string s"; + buffer += index_str; + buffer+= "_"; + dump_hex_helper(buffer, name, vector, length); + buffer += array.c_str(); + buffer += "["; + buffer += index_str; + buffer += "]." + name + " = message_ptr + message.find(s"; + buffer += index_str; + buffer += "_" + name + ".data());\n"; +} + +void dump_array_part(std::string array, size_t index, + std::string name, const uint8_t* vector, size_t length) { + std::string buffer =""; + dump_array_part_helper(buffer, array, index, name, vector, length); + LOGV(buffer.c_str()); +} + +} // namespace wvoec_mock + diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index 4655065e..f19e653a 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -9,23 +9,23 @@ #include #include #include +#include #include "log.h" #include "oemcrypto_engine_mock.h" +#include "oemcrypto_logging.h" #include "oemcrypto_usage_table_mock.h" #include "openssl/cmac.h" #include "openssl/evp.h" #include "openssl/hmac.h" #include "openssl/rand.h" #include "openssl/sha.h" +#include "string_conversions.h" #include "wv_cdm_constants.h" namespace wvoec_mock { static CryptoEngine* crypto_engine = NULL; -// Set this to true when you are generating test vectors. -const bool trace_all_calls = false; - typedef struct { uint8_t signature[wvcdm::MAC_KEY_SIZE]; uint8_t context[wvcdm::MAC_KEY_SIZE]; @@ -33,39 +33,10 @@ typedef struct { uint8_t enc_rsa_key[]; } WrappedRSAKey; -static void dump_hex(std::string name, const uint8_t* vector, size_t length) { - printf("%s = ", name.c_str()); - if (vector == NULL) { - printf("NULL;\n"); - return; - } - for (size_t i = 0; i < length; i++) { - if (i == 0) { - printf("\n wvcdm::a2b_hex(\""); - } else if (i % 32 == 0) { - printf("\"\n \""); - } - printf("%02X", vector[i]); - } - printf("\");\n"); -} - -void dump_array_part(std::string array, size_t index, - std::string name, const uint8_t* vector, size_t length) { - if (vector == NULL) { - printf("%s[%zu].%s = NULL;\n", array.c_str(), index, name.c_str()); - return; - } - printf("std::string s%zu_", index); - dump_hex(name, vector, length); - printf("%s[%zu].%s = message_ptr + message.find(s%zu_%s.data());\n", - array.c_str(), index, name.c_str(), index, name.c_str()); -} - extern "C" OEMCryptoResult OEMCrypto_Initialize(void) { - if (trace_all_calls) { - printf("------------------------- OEMCrypto_Initialize(void)\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("------------------------- OEMCrypto_Initialize(void)\n"); } crypto_engine = new CryptoEngine; @@ -74,14 +45,16 @@ OEMCryptoResult OEMCrypto_Initialize(void) { LOGE("[OEMCrypto_Initialize(): failed]"); return OEMCrypto_ERROR_INIT_FAILED; } - LOGD("[OEMCrypto_Initialize(): success]"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_Initialize(): success]"); + } return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult OEMCrypto_Terminate(void) { - if (trace_all_calls) { - printf("----------------- OEMCryptoResult OEMCrypto_Terminate(void)\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("----------------- OEMCryptoResult OEMCrypto_Terminate(void)\n"); } if (!crypto_engine) { @@ -95,31 +68,41 @@ OEMCryptoResult OEMCrypto_Terminate(void) { delete crypto_engine; crypto_engine = NULL; - LOGD("[OEMCrypto_Terminate(): success]"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_Terminate(): success]"); + } return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session)\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_OpenSession" + "(OEMCrypto_SESSION *session)\n"); } SessionId sid = crypto_engine->CreateSession(); *session = (OEMCrypto_SESSION)sid; - LOGD("[OEMCrypto_OpenSession(): SID=%08x]", sid); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_OpenSession(): SID=%08x]", sid); + } return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session)\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CloseSession" + "(OEMCrypto_SESSION session)\n"); } if (!crypto_engine->DestroySession((SessionId)session)) { - LOGD("[OEMCrypto_CloseSession(SID=%08X): failed]", session); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_CloseSession(SID=%08X): failed]", session); + } return OEMCrypto_ERROR_CLOSE_SESSION_FAILED; } else { - LOGD("[OEMCrypto_CloseSession(SID=%08X): success]", session); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_CloseSession(SID=%08X): success]", session); + } return OEMCrypto_SUCCESS; } } @@ -127,8 +110,9 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { extern "C" OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, uint32_t* nonce) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GenerateNonce" + "(OEMCrypto_SESSION session,\n"); } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { @@ -161,8 +145,8 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, } session_ctx->AddNonce(nonce_value); *nonce = nonce_value; - if (trace_all_calls) { - printf("nonce = %08x\n", nonce_value); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("nonce = %08x\n", nonce_value); } return OEMCrypto_SUCCESS; } @@ -173,12 +157,14 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, uint32_t mac_key_context_length, const uint8_t* enc_key_context, uint32_t enc_key_context_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_GenerateDerivedKeys(\n"); - dump_hex("mac_key_context", mac_key_context, - (size_t)mac_key_context_length); - dump_hex("enc_key_context", enc_key_context, - (size_t)enc_key_context_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GenerateDerivedKeys(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("mac_key_context", mac_key_context, + (size_t)mac_key_context_length); + dump_hex("enc_key_context", enc_key_context, + (size_t)enc_key_context_length); + } } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); @@ -201,13 +187,15 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, mac_ctx_str, enc_ctx_str)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (trace_all_calls) { - dump_hex("mac_key_server", &session_ctx->mac_key_server()[0], - session_ctx->mac_key_server().size()); - dump_hex("mac_key_client", &session_ctx->mac_key_client()[0], - session_ctx->mac_key_client().size()); - dump_hex("enc_key", &session_ctx->encryption_key()[0], - session_ctx->encryption_key().size()); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("mac_key_server", &session_ctx->mac_key_server()[0], + session_ctx->mac_key_server().size()); + dump_hex("mac_key_client", &session_ctx->mac_key_client()[0], + session_ctx->mac_key_client().size()); + dump_hex("enc_key", &session_ctx->encryption_key()[0], + session_ctx->encryption_key().size()); + } } return OEMCrypto_SUCCESS; } @@ -219,9 +207,11 @@ OEMCryptoResult OEMCrypto_GenerateSignature( size_t message_length, uint8_t* signature, size_t* signature_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_GenerateSignature(\n"); - dump_hex("message", message, message_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GenerateSignature(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("message", message, message_length); + } } if (NO_ERROR != crypto_engine->ValidateKeybox()) { @@ -250,8 +240,10 @@ OEMCryptoResult OEMCrypto_GenerateSignature( message_length, signature, signature_length)) { - if (trace_all_calls) { - dump_hex("signature", signature, *signature_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("signature", signature, *signature_length); + } } return OEMCrypto_SUCCESS; } @@ -281,29 +273,33 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, const OEMCrypto_KeyObject* key_array, const uint8_t* pst, size_t pst_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,\n"); - dump_hex("message", message, message_length); - dump_hex("signature", signature, signature_length); - dump_hex("enc_mac_key_iv", enc_mac_key_iv, wvcdm::KEY_IV_SIZE); - dump_hex("enc_mac_keys", enc_mac_keys, 2*wvcdm::MAC_KEY_SIZE); - dump_hex("pst", pst, pst_length); - for (size_t i = 0; i < num_keys; i++) { - printf("key_array[%zu].key_id_length=%zu;\n", i, - key_array[i].key_id_length); - dump_array_part("key_array", i, "key_id", - key_array[i].key_id, key_array[i].key_id_length); - dump_array_part("key_array", i, "key_data_iv", - key_array[i].key_data_iv, wvcdm::KEY_IV_SIZE); - dump_array_part("key_array", i, "key_data", - key_array[i].key_data, key_array[i].key_data_length); - dump_array_part("key_array", i, "key_control_iv", - key_array[i].key_control_iv, wvcdm::KEY_IV_SIZE); - dump_array_part("key_array", i, "key_control", - key_array[i].key_control, wvcdm::KEY_IV_SIZE); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("message", message, message_length); + dump_hex("signature", signature, signature_length); + dump_hex("enc_mac_key_iv", enc_mac_key_iv, wvcdm::KEY_IV_SIZE); + dump_hex("enc_mac_keys", enc_mac_keys, 2*wvcdm::MAC_KEY_SIZE); + dump_hex("pst", pst, pst_length); + for (size_t i = 0; i < num_keys; i++) { + LOGV("key_array[%zu].key_id_length=%zu;\n", i, + key_array[i].key_id_length); + dump_array_part("key_array", i, "key_id", + key_array[i].key_id, key_array[i].key_id_length); + dump_array_part("key_array", i, "key_data_iv", + key_array[i].key_data_iv, wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_data", + key_array[i].key_data, key_array[i].key_data_length); + dump_array_part("key_array", i, "key_control_iv", + key_array[i].key_control_iv, wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_control", + key_array[i].key_control, wvcdm::KEY_IV_SIZE); + } } } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_LoadKeys(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; @@ -354,15 +350,16 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, } extern "C" -OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - const uint8_t* signature, - size_t signature_length, - size_t num_keys, - const OEMCrypto_KeyRefreshObject* key_array) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%zu)\n", +OEMCryptoResult OEMCrypto_RefreshKeys( + OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%zu)\n", num_keys); } @@ -415,7 +412,7 @@ OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, key_array[i].key_id + key_array[i].key_id_length); key_control.assign(key_array[i].key_control, key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); - if (key_array[i].key_control_iv == NULL ) { + if ( key_array[i].key_control_iv == NULL ) { key_control_iv.clear(); } else { key_control_iv.assign(key_array[i].key_control_iv, @@ -448,9 +445,12 @@ extern "C" OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,\n"); - dump_hex("key_id", key_id, key_id_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_SelectKey" + "(const OEMCrypto_SESSION session,\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("key_id", key_id, key_id_length); + } } #ifndef NDEBUG if (NO_ERROR != crypto_engine->ValidateKeybox()) { @@ -484,8 +484,9 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, size_t block_offset, const OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_DecryptCTR" + "(OEMCrypto_SESSION session,\n"); } wvoec_mock::BufferType buffer_type = kBufferTypeDirect; uint8_t* destination = NULL; @@ -541,8 +542,8 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, extern "C" OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, size_t keyBoxLength) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,\n"); } if (crypto_engine->keybox().InstallKeybox(keybox, keyBoxLength)) { return OEMCrypto_SUCCESS; @@ -552,8 +553,8 @@ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, extern "C" OEMCryptoResult OEMCrypto_IsKeyboxValid(void) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {\n"); } switch(crypto_engine->ValidateKeybox()) { case NO_ERROR: return OEMCrypto_SUCCESS; @@ -567,8 +568,8 @@ OEMCryptoResult OEMCrypto_IsKeyboxValid(void) { extern "C" OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,\n"); } std::vector dev_id_string = crypto_engine->keybox().device_id(); if (dev_id_string.empty()) { @@ -585,15 +586,17 @@ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, memset(deviceID, 0, *idLength); memcpy(deviceID, &dev_id_string[0], dev_id_len); *idLength = dev_id_len; - LOGD("[OEMCrypto_GetDeviceId(): success]"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_GetDeviceId(): success]"); + } return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,\n"); } size_t length = crypto_engine->keybox().key_data_length(); if (*keyDataLength < length) { @@ -604,14 +607,17 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, memset(keyData, 0, *keyDataLength); memcpy(keyData, crypto_engine->keybox().key_data(), length); *keyDataLength = length; - LOGD("[OEMCrypto_GetKeyData(): success]"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_GetKeyData(): success]"); + } return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetRandom" + "(uint8_t* randomData, size_t dataLength) {\n"); } if (!randomData) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -629,8 +635,8 @@ OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox, size_t* wrappedKeyBoxLength, const uint8_t* transportKey, size_t transportKeyLength) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,\n"); } if (!keybox || !wrappedKeybox || !wrappedKeyBoxLength || (keyBoxLength != *wrappedKeyBoxLength)) { @@ -654,13 +660,17 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey()\n"); - dump_hex("message", message, message_length); - dump_hex("signature", signature, signature_length); - printf("nonce = %08X;\n", *nonce); - dump_hex("enc_rsa_key", enc_rsa_key, enc_rsa_key_length); - dump_hex("enc_rsa_key_iv", enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls | kLoggingTraceNonce)) { + LOGI("-- OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey()\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("message", message, message_length); + dump_hex("signature", signature, signature_length); + } + LOGI("nonce = %08X;\n", *nonce); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("enc_rsa_key", enc_rsa_key, enc_rsa_key_length); + dump_hex("enc_rsa_key_iv", enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); + } } if (wrapped_rsa_key_length == NULL) { LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); @@ -673,7 +683,9 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) { - LOGW("[OEMCrypto_RewrapDeviceRSAKey(): Wrapped Keybox Short Buffer]"); + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGW("[OEMCrypto_RewrapDeviceRSAKey(): Wrapped Keybox Short Buffer]"); + } *wrapped_rsa_key_length = buffer_size; return OEMCrypto_ERROR_SHORT_BUFFER; } @@ -777,11 +789,13 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, result = OEMCrypto_ERROR_UNKNOWN_FAILURE; } } - if (trace_all_calls) { - dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length); - dump_hex("signature", wrapped->signature, sizeof(wrapped->signature)); - dump_hex("context", wrapped->context, sizeof(wrapped->context)); - dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length); + dump_hex("signature", wrapped->signature, sizeof(wrapped->signature)); + dump_hex("context", wrapped->context, sizeof(wrapped->context)); + dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); + } } return result; } @@ -796,12 +810,14 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, } const WrappedRSAKey* wrapped = reinterpret_cast(wrapped_rsa_key); - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n"); - dump_hex("wrapped_rsa_key", wrapped_rsa_key, wrapped_rsa_key_length); - dump_hex("signature", wrapped->signature, sizeof(wrapped->signature)); - dump_hex("context", wrapped->context, sizeof(wrapped->context)); - dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("wrapped_rsa_key", wrapped_rsa_key, wrapped_rsa_key_length); + dump_hex("signature", wrapped->signature, sizeof(wrapped->signature)); + dump_hex("context", wrapped->context, sizeof(wrapped->context)); + dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); + } } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); @@ -852,15 +868,19 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, } extern "C" -OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - uint8_t* signature, - size_t* signature_length, - RSA_Padding_Scheme padding_scheme) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n"); - dump_hex("message", message, message_length); +OEMCryptoResult OEMCrypto_GenerateRSASignature( + OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length, + RSA_Padding_Scheme padding_scheme) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("message", message, message_length); + dump_hex("message", message, message_length); + } } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_KEYBOX_INVALID]"); @@ -895,8 +915,10 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, signature, signature_length, padding_scheme)) { - if (trace_all_calls) { - dump_hex("signature", signature, *signature_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("signature", signature, *signature_length); + } } return OEMCrypto_SUCCESS; } @@ -912,13 +934,15 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( size_t mac_key_context_length, const uint8_t* enc_key_context, size_t enc_key_context_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n"); - dump_hex("enc_session_key", enc_session_key, enc_session_key_length); - dump_hex("mac_key_context", mac_key_context, - (size_t)mac_key_context_length); - dump_hex("enc_key_context", enc_key_context, - (size_t)enc_key_context_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("enc_session_key", enc_session_key, enc_session_key_length); + dump_hex("mac_key_context", mac_key_context, + (size_t)mac_key_context_length); + dump_hex("enc_key_context", enc_key_context, + (size_t)enc_key_context_length); + } } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); @@ -947,13 +971,15 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( if (!session_ctx->RSADeriveKeys(ssn_key_str, mac_ctx_str, enc_ctx_str)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (trace_all_calls) { - dump_hex("mac_key_server", &session_ctx->mac_key_server()[0], - session_ctx->mac_key_server().size()); - dump_hex("mac_key", &session_ctx->mac_key_client()[0], - session_ctx->mac_key_client().size()); - dump_hex("enc_key", &session_ctx->encryption_key()[0], - session_ctx->encryption_key().size()); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("mac_key_server", &session_ctx->mac_key_server()[0], + session_ctx->mac_key_server().size()); + dump_hex("mac_key", &session_ctx->mac_key_client()[0], + session_ctx->mac_key_client().size()); + dump_hex("enc_key", &session_ctx->encryption_key()[0], + session_ctx->encryption_key().size()); + } } return OEMCrypto_SUCCESS; } @@ -969,10 +995,11 @@ const char* OEMCrypto_SecurityLevel() { } extern "C" -OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current, - OEMCrypto_HDCP_Capability *maximum) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_GetHDCPCapability(%p, %p)\n", +OEMCryptoResult OEMCrypto_GetHDCPCapability( + OEMCrypto_HDCP_Capability *current, + OEMCrypto_HDCP_Capability *maximum) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetHDCPCapability(%p, %p)\n", current, maximum); } if (current == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -989,12 +1016,13 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { - - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_Generic_Encrypt( algorithm=%d\n", + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_Generic_Encrypt( algorithm=%d\n", algorithm); - dump_hex("in_buffer", in_buffer, buffer_length); - dump_hex("iv", iv, wvcdm::KEY_IV_SIZE); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("in_buffer", in_buffer, buffer_length); + dump_hex("iv", iv, wvcdm::KEY_IV_SIZE); + } } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_KEYBOX_INVALID]"); @@ -1013,8 +1041,10 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, OEMCryptoResult sts = session_ctx->Generic_Encrypt(in_buffer, buffer_length, iv, algorithm, out_buffer); - if (trace_all_calls) { - dump_hex("out_buffer", out_buffer, buffer_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("out_buffer", out_buffer, buffer_length); + } } return sts; } @@ -1026,11 +1056,13 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_Generic_Decrypt( algorithm=%d\n", + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_Generic_Decrypt( algorithm=%d\n", algorithm); - dump_hex("in_buffer", in_buffer, buffer_length); - dump_hex("iv", iv, wvcdm::KEY_IV_SIZE); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("in_buffer", in_buffer, buffer_length); + dump_hex("iv", iv, wvcdm::KEY_IV_SIZE); + } } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_KEYBOX_INVALID]"); @@ -1049,8 +1081,10 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, OEMCryptoResult sts = session_ctx->Generic_Decrypt(in_buffer, buffer_length, iv, algorithm, out_buffer); - if (trace_all_calls) { - dump_hex("out_buffer", out_buffer, buffer_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE){ + dump_hex("out_buffer", out_buffer, buffer_length); + } } return sts; } @@ -1062,10 +1096,12 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, OEMCrypto_Algorithm algorithm, uint8_t* signature, size_t* signature_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_Generic_Sign( algorithm=%d\n", + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_Generic_Sign( algorithm=%d\n", algorithm); - dump_hex("in_buffer", in_buffer, buffer_length); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("in_buffer", in_buffer, buffer_length); + } } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_Generic_Sign(): ERROR_KEYBOX_INVALID]"); @@ -1087,8 +1123,10 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, OEMCryptoResult sts = session_ctx->Generic_Sign(in_buffer, buffer_length, algorithm, signature, signature_length); - if (trace_all_calls) { - dump_hex("signature", signature, *signature_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("signature", signature, *signature_length); + } } return sts; } @@ -1100,11 +1138,13 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, OEMCrypto_Algorithm algorithm, const uint8_t* signature, size_t signature_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_Generic_Verify( algorithm=%d\n", + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_Generic_Verify( algorithm=%d\n", algorithm); - dump_hex("in_buffer", in_buffer, buffer_length); - dump_hex("signature", signature, signature_length); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("in_buffer", in_buffer, buffer_length); + dump_hex("signature", signature, signature_length); + } } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_Generic_Verify(): ERROR_KEYBOX_INVALID]"); @@ -1128,15 +1168,15 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, extern "C" bool OEMCrypto_SupportsUsageTable() { - if (trace_all_calls) { - printf("-- bool OEMCrypto_SupportsUsageTable(); // returns true.\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- bool OEMCrypto_SupportsUsageTable(); // returns true.\n"); } return true; } extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_UpdateUsageTable();\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_UpdateUsageTable();\n"); } return crypto_engine->usage_table()->UpdateTable(); } @@ -1144,9 +1184,11 @@ OEMCryptoResult OEMCrypto_UpdateUsageTable() { extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t *pst, size_t pst_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_DeactivateUsageEntry(\n"); - dump_hex("pst", pst, pst_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_DeactivateUsageEntry(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("pst", pst, pst_length); + } } std::vector pstv(pst, pst + pst_length); return crypto_engine->usage_table()->DeactivateEntry(pstv); @@ -1158,9 +1200,11 @@ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, size_t pst_length, OEMCrypto_PST_Report *buffer, size_t *buffer_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_ReportUsage(\n"); - dump_hex("pst", pst, pst_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_ReportUsage(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("pst", pst, pst_length); + } } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { @@ -1176,9 +1220,11 @@ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, OEMCryptoResult sts = entry->ReportUsage(session_ctx, pstv, buffer, buffer_length); crypto_engine->usage_table()->UpdateTable(); - if (trace_all_calls) { - dump_hex("usage buffer", reinterpret_cast(buffer), - *buffer_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("usage buffer", reinterpret_cast(buffer), + *buffer_length); + } } return sts; } @@ -1191,11 +1237,13 @@ OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session, size_t message_length, const uint8_t *signature, size_t signature_length) { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_DeleteUsageEntry(\n"); - dump_hex("pst", pst, pst_length); - dump_hex("message", message, message_length); - dump_hex("signature", signature, signature_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_DeleteUsageEntry(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("pst", pst, pst_length); + dump_hex("message", message, message_length); + dump_hex("signature", signature, signature_length); + } } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { @@ -1226,8 +1274,8 @@ OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session, extern "C" OEMCryptoResult OEMCrypto_DeleteUsageTable() { - if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_DeleteUsageTable()\n"); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_DeleteUsageTable()\n"); } if (crypto_engine->usage_table()->Clear()) { return OEMCrypto_SUCCESS; diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp index 2194c370..0260fdfe 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp @@ -3,14 +3,17 @@ // Mock implementation of OEMCrypto APIs // #include "oemcrypto_usage_table_mock.h" -#include "oemcrypto_engine_mock.h" #include +#include +#include #include "clock.h" #include "log.h" #include "file_store.h" #include "properties.h" +#include "oemcrypto_engine_mock.h" +#include "oemcrypto_logging.h" #include "openssl/aes.h" #include "openssl/rand.h" #include "openssl/sha.h" @@ -45,6 +48,7 @@ UsageTableEntry::UsageTableEntry(const StoredUsageEntry *buffer) { buffer->mac_key_server + wvcdm::MAC_KEY_SIZE); mac_key_client_.assign(buffer->mac_key_client, buffer->mac_key_client + wvcdm::MAC_KEY_SIZE); + session_ = NULL; } void UsageTableEntry::SaveToBuffer(StoredUsageEntry *buffer) { @@ -158,7 +162,9 @@ UsageTable::UsageTable(CryptoEngine *ce) { std::string filename = path + "UsageTable.dat"; if (!file.Exists(filename)) { - LOGI("UsageTable: No saved usage table. Creating new table."); + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + LOGI("UsageTable: No saved usage table. Creating new table."); + } return; } size_t file_size = file.FileSize(filename); @@ -222,9 +228,13 @@ UsageTable::UsageTable(CryptoEngine *ce) { file.Read(reinterpret_cast(&generation_), sizeof(int64_t)); file.Close(); if (stored_table->generation == generation_ + 1) { - LOGW("UsageTable: File is one generation old. Acceptable rollback."); + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + LOGW("UsageTable: File is one generation old. Acceptable rollback."); + } } else if (stored_table->generation == generation_ - 1) { - LOGW("UsageTable: File is one generation new. Acceptable rollback."); + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + LOGW("UsageTable: File is one generation new. Acceptable rollback."); + } // This might happen if the generation number was rolled back? } else if (stored_table->generation != generation_) { LOGE("UsageTable: Rollback detected. Clearing Usage Table. %lx -> %lx", @@ -241,7 +251,9 @@ UsageTable::UsageTable(CryptoEngine *ce) { new UsageTableEntry(&stored_table->entries[i].entry); table_[entry->pst_hash()] = entry; } - LOGI("UsageTable: loaded %d entryies.", stored_table->count); + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + LOGI("UsageTable: loaded %d entries.", stored_table->count); + } } bool UsageTable::SaveToFile() { @@ -308,7 +320,9 @@ bool UsageTable::SaveToFile() { std::string filename = path + "UsageTable.dat"; if (!file.Exists(filename)) { - LOGI("UsageTable: No saved usage table. Creating new table."); + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + LOGI("UsageTable: No saved usage table. Creating new table."); + } } if (!file.Open(filename, wvcdm::File::kCreate | wvcdm::File::kTruncate | diff --git a/libwvdrmengine/oemcrypto/mock/test/oemcrypto_logging_test.cpp b/libwvdrmengine/oemcrypto/mock/test/oemcrypto_logging_test.cpp new file mode 100644 index 00000000..9bf14160 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/test/oemcrypto_logging_test.cpp @@ -0,0 +1,138 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +#include "OEMCryptoCENC.h" + +#include +#include +#include "log.h" +#include "oemcrypto_logging.h" +#include "oemcrypto_mock.cpp" + +class OEMCryptoLoggingTest : public ::testing::Test { + protected: + OEMCryptoLoggingTest() {} + + void SetUp() { + ::testing::Test::SetUp(); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); + } + + void TearDown() { + OEMCrypto_Terminate(); + ::testing::Test::TearDown(); + } +}; + +TEST_F(OEMCryptoLoggingTest, TestDumpHexFunctions) { + uint8_t vector[] = { 0xFA, 0x11 , 0x28 , 0x33 }; + std::string buffer = ""; + wvoec_mock::dump_hex_helper(buffer, "name", vector, (size_t)4); + ASSERT_EQ(buffer, "name = \n wvcdm::a2b_hex(\"FA112833\");\n"); + + buffer = ""; + uint8_t vector2[] = { 0xFA, 0x11 , 0x28 , 0x33 , + 0xFA, 0x11 , 0x28 , 0x33 , 0xFA, 0x11 , + 0x28 , 0x33 , 0xFA, 0x11 , 0x28 , 0x33 , 0xFA, 0x11 , 0x28 , 0x33 , + 0xFA, 0x11 , 0x28 , 0x33 , 0x01, 0x14 , 0x28 , 0xAB, 0xFA, 0xCD , + 0xEF , 0x67, 0x01, 0x14 , 0x28 , 0xAB, 0xFA, 0xCD , 0xEF , 0x67 }; + wvoec_mock::dump_hex_helper(buffer, "name", vector2, (size_t)40); + + ASSERT_EQ(buffer, "name = \n wvcdm::a2b_hex(\"FA112833FA112833FA112833F" + "A112833FA112833FA112833011428ABFACDEF67\"\n \"" + "011428ABFACDEF67\");\n"); + + buffer = ""; + wvoec_mock::dump_array_part_helper(buffer, "array", + (size_t) 5, "name", vector2, (size_t) 40); + char* exp = "std::string s5_name = \n wvcdm::a2b_hex(\"FA112833FA112833F" + "A112833FA112833FA112833FA112833011428ABFACDEF67\"\n " + " \"011428ABFACDEF67\");\narray[5].name = message_ptr + me" + "ssage.find(s5_name.data());\n"; + ASSERT_EQ(buffer, exp); + + buffer = ""; + wvoec_mock::dump_array_part_helper(buffer, "array", (size_t) 5, + "name", NULL, (size_t) 40); + ASSERT_EQ(buffer, "array[5].name = NULL;\n"); +} + +TEST_F(OEMCryptoLoggingTest, TestChangeLoggingLevel) { + wvcdm::LogPriority default_logging_level = wvcdm::LOG_WARN; + wvoec_mock::SetLoggingLevel(1); + ASSERT_EQ(wvcdm::g_cutoff, default_logging_level); + + wvoec_mock::SetLoggingLevel(2); + ASSERT_EQ(wvcdm::g_cutoff, wvcdm::LOG_INFO); + + wvoec_mock::SetLoggingSettings( + wvcdm::LOG_WARN, + wvoec_mock::kLoggingDumpTraceAll); + ASSERT_EQ(wvcdm::g_cutoff, wvcdm::LOG_WARN); + ASSERT_EQ(wvoec_mock::LogCategoryEnabled( + wvoec_mock::kLoggingDumpTraceAll), true); + wvoec_mock::TurnOffLoggingForAllCategories(); + + wvoec_mock::SetLoggingLevel(wvcdm::LOG_VERBOSE); + ASSERT_EQ(wvcdm::g_cutoff, wvcdm::LOG_VERBOSE); + + wvoec_mock::SetLoggingLevel(1); +} + + namespace wvoec_mock { +TEST_F(OEMCryptoLoggingTest, TestChangeLoggingCategories) { + TurnOffLoggingForAllCategories(); + ASSERT_EQ(LogCategoryEnabled(kLoggingTraceDecryption | + kLoggingTraceOEMCryptoCalls), false); + + AddLoggingForCategories(kLoggingDumpKeyControlBlocks | + kLoggingDumpDerivedKeys); + ASSERT_EQ(LogCategoryEnabled(kLoggingDumpKeyControlBlocks), true); + ASSERT_EQ(LogCategoryEnabled(kLoggingTraceUsageTable), false); + ASSERT_EQ(LogCategoryEnabled(kLoggingDumpTraceAll), true); + + RemoveLoggingForCategories(kLoggingDumpKeyControlBlocks | + kLoggingTraceUsageTable); + ASSERT_EQ(LogCategoryEnabled(kLoggingDumpKeyControlBlocks), false); + + ASSERT_EQ(LogCategoryEnabled(kLoggingDumpDerivedKeys), true); + ASSERT_EQ(LogCategoryEnabled(kLoggingTraceUsageTable), false); + + TurnOffLoggingForAllCategories(); + bool flag = false; + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + flag = true; + } + ASSERT_EQ(flag, false); + + AddLoggingForCategories(kLoggingDumpTraceAll); + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + flag = true; + } + ASSERT_EQ(flag, true); + + ASSERT_EQ(LogCategoryEnabled(kLoggingTraceOEMCryptoCalls), true); + ASSERT_EQ(LogCategoryEnabled(kLoggingDumpContentKeys), true); + ASSERT_EQ(LogCategoryEnabled(kLoggingDumpKeyControlBlocks), true); + ASSERT_EQ(LogCategoryEnabled(kLoggingDumpDerivedKeys), true); + ASSERT_EQ(LogCategoryEnabled(kLoggingTraceNonce), true); + ASSERT_EQ(LogCategoryEnabled(kLoggingTraceDecryption), true); + ASSERT_EQ(LogCategoryEnabled(kLoggingTraceUsageTable), true); + ASSERT_EQ(LogCategoryEnabled(kLoggingDumpTraceAll), true); + + flag= false; + RemoveLoggingForCategories(kLoggingDumpKeyControlBlocks); + if ( LogCategoryEnabled(kLoggingDumpKeyControlBlocks) ) { + flag = true; + } + ASSERT_EQ(flag, false); +} + +} // namespace wvoec_mock + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + wvcdm::InitLogging(argc, argv); + + return RUN_ALL_TESTS(); +} + diff --git a/libwvdrmengine/oemcrypto/test/Makefile b/libwvdrmengine/oemcrypto/test/Makefile deleted file mode 100644 index 87f27b05..00000000 --- a/libwvdrmengine/oemcrypto/test/Makefile +++ /dev/null @@ -1,109 +0,0 @@ -# -# Builds oemcrypto_unittests -# -#PROJECTS_ROOT = ~projects -# -ifndef PROJECTS_ROOT -PROJECTS_ROOT = ../../../../.. -endif - -CDM_ROOT = $(PROJECTS_ROOT)/cdm -CDM_SRC_PATH = $(CDM_ROOT)/cdm -CDM_BASE_INCLUDE_PATH = $(CDM_SRC_PATH)/include - -EUREKA_ROOT = $(PROJECTS_ROOT)/eureka/eureka -CHROME_ROOT = $(EUREKA_ROOT)/src/chromium/src -# -# build outputs should go into Chrome repository, such as ../chromium/src/out -# or some local equivalent. -# WARNING: splitting outputs from CHROME_ROOT can lead to build errors -ifndef CHROME_ROOT -CHROME_ROOT = $(CDM_ROOT)/out -endif - -# TARGET_PLATFORM from {x86,eureka} -ifndef TARGET_PLATFORM -TARGET_PLATFORM = x86 -endif - -# TARGET_BUILD from {debug,release} -ifndef TARGET_BUILD -TARGET_BUILD = debug -endif - -ifeq ($(TARGET_PLATFORM),x86) - BUILDPLATFORM = out_x86_linux -else ifeq ($(TARGET_PLATFORM),eureka) - BUILDPLATFORM = out_arm_eureka -else - BUILDPLATFORM = UNKNOWN -endif - -ifeq ($(TARGET_BUILD),debug) - BUILDTYPE = Debug -else ifeq ($(TARGET_BUILD),release) - BUILDTYPE = Release -else - BUILDTYPE = UNKNOWN -endif - -BUILDPATH = $(CHROME_ROOT)/$(BUILDPLATFORM)/$(BUILDTYPE) -OBJPATH = $(BUILDPATH)/obj - -CHROME_THIRD_PARTY_LIBS = $(BUILDPATH)/obj/third_party - -# target image file name -TARGET_TEST_EXE = oemcrypto_unittests - -TARGET_OBJECTS = oemcrypto_test.o - -OBJECTDIR = $(OBJPATH)/oemcrypto_unittests - -INSTALLDIR = $(BUILDPATH) - -LIBGTEST_INCLUDE = $(CDM_SRC_PATH)/prebuilt/gtest/include -LIBGTEST_LIBS = $(CDM_SRC_PATH)/prebuilt/gtest/$(BUILDPLATFORM)/$(BUILDTYPE)/lib -LIBGTEST_LIBNAME = gtest - -INCLUDES = \ - -I$(LIBGTEST_INCLUDE) \ - -I$(CDM_BASE_INCLUDE_PATH) - -LIBDIRS = \ - -L$(INSTALLDIR) \ - -L$(LIBGTEST_LIBS) - -OBJECTS := $(patsubst %.o,$(OBJECTDIR)/%.o,$(TARGET_OBJECTS)) - -CXXFLAGS = -m64 -fPIC -W -Wall -g -DCDM_TEST -LINK = $(CXX) -MKDIR = mkdir -p - -$(INSTALLDIR)/$(TARGET_TEST_EXE): $(OBJECTDIR) $(INSTALLDIR) $(OBJECTS) - $(CXX) -v -fPIC -m64 $(OBJECTS) $(LIBDIRS) -loemcrypto_mock \ - -lcrypto -ldl -lrt -lpthread -l$(LIBGTEST_LIBNAME) -o $@ - @echo "[Unit test image: " $(INSTALLDIR)/$(TARGET_TEST_EXE) "]" - -$(OBJECTDIR)/%.o: %.cpp - $(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@ - -$(OBJECTDIR)/%.o: %.cc - $(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@ - -clean: - $(RM) -rf $(OBJECTDIR) - $(RM) -rf $(INSTALLDIR)/$(TARGET_TEST_EXE) - -$(OBJECTDIR): - @$(MKDIR) $@ - -$(INSTALLDIR): - @$(MKDIR) $@ - -.PHONY: $(OBJECTDIR) - -.PHONY: $(INSTALLDIR) - -.PHONY: clean - -.PHONY: test diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index c9dc0aa1..6d9e0252 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -89,6 +89,9 @@ struct PaddedPSTReport { uint8_t padding[256]; }; +// These are test keyboxes. They will not be accepted by production systems. +// By using known keyboxes for these tests, the results for a given set of +// inputs to a test are predictable and can be compared to the actual results. const wvoec_mock::WidevineKeybox kDefaultKeybox = { // Sample keybox used for test vectors { @@ -185,9 +188,8 @@ static wvoec_mock::WidevineKeybox kValidKeybox03 = { } }; -/* Note: Key 1 was 3072 bits. We are only generating 2048 bit keys, - so we do not need to test with 3072 bit keys. */ - +// A 2048-bit test RSA Private Key +// This is used to verify the functions that manipulate RSA keys. static const uint8_t kTestPKCS1RSAPrivateKey2_2048[] = { 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, @@ -339,7 +341,8 @@ static const uint8_t kTestPKCS1RSAPrivateKey2_2048[] = { 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 }; -// 2048 bit RSA key in PKCS#8 PrivateKeyInfo +// A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format +// Used to verify the functions that manipulate RSA keys. static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = { 0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, @@ -494,6 +497,8 @@ static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = { 0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 }; +// A 2048 bit RSA Public key +// Used to verify the functions that manipulate RSA keys. static const uint8_t kTestRSAPublicKey2_2048[] = { 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, @@ -530,6 +535,8 @@ static const uint8_t kTestRSAPublicKey2_2048[] = { 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01 }; +// A second 2048-bit RSA private key +// This is used to verify the functions that manipulate RSA keys. static const uint8_t kTestPKCS1RSAPrivateKey3_2048[] = { 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, @@ -681,7 +688,8 @@ static const uint8_t kTestPKCS1RSAPrivateKey3_2048[] = { 0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b, 0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8, 0xee, 0x24 }; -// 2048 bit RSA key in PKCS#8 PrivateKeyInfo +// A second 2048 bit RSA key in PKCS#8 PrivateKeyInfo format +// Used to verify the functions that manipulate RSA keys. static const uint8_t kTestRSAPKCS8PrivateKeyInfo3_2048[] = { 0x30, 0x82, 0x04, 0xbe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, @@ -837,6 +845,8 @@ static const uint8_t kTestRSAPKCS8PrivateKeyInfo3_2048[] = { 0x69, 0x0b, 0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8, 0xee, 0x24 }; +// A second 2048 bit RSA Public key +// Used to verify the functions that manipulate RSA keys. static const uint8_t kTestRSAPublicKey3_2048[] = { 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, @@ -938,8 +948,11 @@ class Session { void FillDefaultContext(vector* mac_context, vector* enc_context) { - /* These context strings are normally created by the CDM layer above from - a license request message. */ + /* Context strings + * These context strings are normally created by the CDM layer + * from a license request message. + * They are used to test MAC and ENC key generation. + */ *mac_context = wvcdm::a2b_hex( "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" @@ -963,6 +976,9 @@ class Session { OEMCrypto_GenerateDerivedKeys(session_id(), &mac_context[0], mac_context.size(), &enc_context[0], enc_context.size())); + + // Expected MAC and ENC keys generated from context strings + // with test keybox "installed". mac_key_server_ = wvcdm::a2b_hex( "3CFD60254786AF350B353B4FBB700AB382558400356866BA16C256BCD8C502BF"); mac_key_client_ = wvcdm::a2b_hex( @@ -1047,7 +1063,8 @@ class Session { license_.keys[i].control.control_bits = htonl(control); } memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length())); - // For the canned decryption content, The first key is: + + // The first key for the canned decryption content. vector key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D"); memcpy(license_.keys[0].key_data, &key[0], key.size()); } @@ -1166,6 +1183,7 @@ class Session { } // Set up our expected input and output + // This is dummy encrypted data. vector encryptedData = wvcdm::a2b_hex( "ec261c115f9d5cda1d5cc7d33c4e37362d1397c89efdd1da5f0065c4848b0462" "337ba14693735203c9b4184e362439c0cea5e5d1a628425eddf8a6bf9ba901ca" @@ -1177,6 +1195,7 @@ class Session { "fc14a9ab9647e6e31adabb72d792f0c9ba99dc3e9205657d28fc7771d64e6d4b"); vector encryptionIv = wvcdm::a2b_hex("719dbcb253b2ec702bb8c1b1bc2f3bc6"); + // This is the expected decrypted data. vector unencryptedData = wvcdm::a2b_hex( "19ef4361e16e6825b336e2012ad8ffc9ce176ab2256e1b98aa15b7877bd8c626" "fa40b2e88373457cbcf4f1b4b9793434a8ac03a708f85974cff01bddcbdd7a8e" @@ -1231,6 +1250,7 @@ class Session { rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048); } + // Dummy context for testing signature generation. vector context = wvcdm::a2b_hex( "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" @@ -1426,6 +1446,8 @@ class Session { &mac_context[0], mac_context.size(), &enc_context[0], enc_context.size())); + // Expected MAC and ENC keys generated from context strings + // with RSA certificate "installed". mac_key_server_ = wvcdm::a2b_hex( "1E451E59CB663DA1646194DD28880788ED8ED2EFF913CBD6A0D535D1D5A90381"); mac_key_client_ = wvcdm::a2b_hex( @@ -1815,7 +1837,7 @@ class DISABLED_TestKeybox : public OEMCryptoClientTest { } }; -TEST_F(DISABLED_TestKeybox, CheckSystemID) { +TEST_F(OEMCryptoClientTest, DISABLED_CheckSystemID) { OEMCryptoResult sts; uint8_t key_data[256]; size_t key_data_len = sizeof(key_data); @@ -1862,6 +1884,7 @@ TEST_F(DISABLED_TestKeybox, BadCRCKeybox) { InstallKeybox(keybox, false); sts = OEMCrypto_IsKeyboxValid(); ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); + InstallKeybox(kDefaultKeybox, true); } TEST_F(DISABLED_TestKeybox, BadMagicKeybox) { @@ -1871,6 +1894,7 @@ TEST_F(DISABLED_TestKeybox, BadMagicKeybox) { InstallKeybox(keybox, false); sts = OEMCrypto_IsKeyboxValid(); ASSERT_EQ(OEMCrypto_ERROR_BAD_MAGIC, sts); + InstallKeybox(kDefaultKeybox, true); } TEST_F(DISABLED_TestKeybox, BadDataKeybox) { @@ -1880,6 +1904,7 @@ TEST_F(DISABLED_TestKeybox, BadDataKeybox) { InstallKeybox(keybox, false); sts = OEMCrypto_IsKeyboxValid(); ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); + InstallKeybox(kDefaultKeybox, true); } TEST_F(DISABLED_TestKeybox, GenerateSignature) { @@ -1889,6 +1914,7 @@ TEST_F(DISABLED_TestKeybox, GenerateSignature) { s.GenerateDerivedKeys(); + // Dummy context for testing signature generation. vector context = wvcdm::a2b_hex( "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" @@ -2354,6 +2380,7 @@ TEST_F(DISABLED_TestKeybox, DecryptWithOffset) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); // Set up our expected input and output + // This is dummy encrypted data. vector encryptedData = wvcdm::a2b_hex( "c17055d4e3ab8e892b40ca2deed7cd46b406cd41d50f23d5877b36" "ad351887df2b3774dc413904afd958ba766cc6ab51a3ffd8f845296c5d8326ee" @@ -2365,6 +2392,7 @@ TEST_F(DISABLED_TestKeybox, DecryptWithOffset) { "4bcc7bd14746304fea100dc6465ab51241355bb19e6c2cfb2bb6bbf709765d13"); vector encryptionIv = wvcdm::a2b_hex( "c09454479a280829c946df3c22f25539"); + // This is the expected decrypted data. vector unencryptedData = wvcdm::a2b_hex( "f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8" "a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f" @@ -2450,6 +2478,7 @@ TEST_F(DISABLED_TestKeybox, DecryptWithNearWrap) { vector key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D"); vector encryptionIv = wvcdm::a2b_hex( "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); + // This is dummy decrypted data. vector unencryptedData = wvcdm::a2b_hex( "f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8" "a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f" @@ -2497,6 +2526,7 @@ TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); // Set up our expected input and output + // This is dummy decrypted data. vector unencryptedData = wvcdm::a2b_hex( "1558497b6d994be343ed1c6d6313e0537b843e9a9c0836d1e83fe33154191ce9" "a14d8d95bebaddc03bd471827170f527c0a166b9068b273d1bc57fbb13975ee4" @@ -2535,6 +2565,7 @@ TEST_F(DISABLED_TestKeybox, DecryptUnencryptedNoKey) { // CLear data should be copied even if there is no key selected. // Set up our expected input and output + // This is dummy decrypted data. vector unencryptedData = wvcdm::a2b_hex( "1558497b6d994be343ed1c6d6313e0537b843e9a9c0836d1e83fe33154191ce9" "a14d8d95bebaddc03bd471827170f527c0a166b9068b273d1bc57fbb13975ee4" @@ -4597,6 +4628,85 @@ TEST_P(DISABLED_UsageTableTest, EmptyTable) { } } +TEST_P(DISABLED_UsageTableTest, FiftyEntries) { + if (OEMCrypto_SupportsUsageTable()) { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); + Session s1; + s1.open(); + s1.GenerateDerivedKeys(); + std::string pst1 = "pst saved"; + s1.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s1.get_nonce(), pst1); + s1.EncryptAndSign(); + s1.LoadTestKeys(pst1, new_mac_keys_); + sleep(kShortSleep); + + cout << "Making 49\n"; + const size_t ENTRY_COUNT = 49;// API says should hold at least 50 entries. + Session sessions[ENTRY_COUNT]; + for (int i=0; istatus); + s.close(); + } + sleep(kShortSleep); + cout << "Making another 49\n"; + // If I add too many entries, it can delete the older ones first, except + // it shouldn't delete the one attached to an open session. (s1) + for (int i=0; istatus); + s.close(); + } + s1.close(); + s1.open(); // Make sure s1's entry is still in the table. + s1.GenerateReport(pst1); + EXPECT_EQ(kUnused, s1.pst_report()->status); + s1.close(); + } +} + TEST_P(DISABLED_UsageTableTest, DeleteUnusedEntry) { if (OEMCrypto_SupportsUsageTable()) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); @@ -5157,21 +5267,24 @@ TEST_P(DISABLED_UsageTableTest, TimingTest) { Session s1; Session s2; Session s3; - time_t loaded = time(NULL); LoadOfflineLicense(s1, pst1); + time_t loaded1 = time(NULL); LoadOfflineLicense(s2, pst2); + time_t loaded2 = time(NULL); LoadOfflineLicense(s3, pst3); + time_t loaded3 = time(NULL); sleep(kLongSleep); - time_t first_decrypt = time(NULL); s1.open(); s1.GenerateDerivedKeys(); s1.LoadTestKeys(pst1, new_mac_keys_); + time_t first_decrypt1 = time(NULL); s1.TestDecryptCTR(); s2.open(); s2.GenerateDerivedKeys(); s2.LoadTestKeys(pst2, new_mac_keys_); + time_t first_decrypt2 = time(NULL); s2.TestDecryptCTR(); sleep(kLongSleep); @@ -5204,34 +5317,36 @@ TEST_P(DISABLED_UsageTableTest, TimingTest) { s2.open(); s3.open(); sleep(kLongSleep); - time_t report_generated = time(NULL); + time_t report_generated1 = time(NULL); s1.GenerateReport(pst1); + time_t report_generated2 = time(NULL); s2.GenerateReport(pst2); + time_t report_generated3 = time(NULL); s3.GenerateReport(pst3); EXPECT_EQ(kInactive, s1.pst_report()->status); EXPECT_ALMOST( - report_generated - loaded, + report_generated1 - loaded1, wvcdm::htonll64(s1.pst_report()->seconds_since_license_received)); EXPECT_ALMOST( - report_generated - first_decrypt, + report_generated1 - first_decrypt1, wvcdm::htonll64(s1.pst_report()->seconds_since_first_decrypt)); - EXPECT_ALMOST(report_generated - second_decrypt, + EXPECT_ALMOST(report_generated1 - second_decrypt, wvcdm::htonll64(s1.pst_report()->seconds_since_last_decrypt)); EXPECT_EQ(kActive, s2.pst_report()->status); EXPECT_ALMOST( - report_generated - loaded, + report_generated2 - loaded2, wvcdm::htonll64(s2.pst_report()->seconds_since_license_received)); EXPECT_ALMOST( - report_generated - first_decrypt, + report_generated2 - first_decrypt2, wvcdm::htonll64(s2.pst_report()->seconds_since_first_decrypt)); - EXPECT_ALMOST(report_generated - third_decrypt, + EXPECT_ALMOST(report_generated2 - third_decrypt, wvcdm::htonll64(s2.pst_report()->seconds_since_last_decrypt)); EXPECT_EQ(kUnused, s3.pst_report()->status); EXPECT_ALMOST( - report_generated - loaded, + report_generated3 - loaded3, wvcdm::htonll64(s3.pst_report()->seconds_since_license_received)); // We don't expect first or last decrypt for unused report. } diff --git a/libwvdrmengine/test/demo/ExoPlayerDemo.apk b/libwvdrmengine/test/demo/ExoPlayerDemo.apk index 5c7867c2..5d60f6d2 100644 Binary files a/libwvdrmengine/test/demo/ExoPlayerDemo.apk and b/libwvdrmengine/test/demo/ExoPlayerDemo.apk differ diff --git a/libwvdrmengine/test/demo/readme.txt b/libwvdrmengine/test/demo/readme.txt index 67858a5f..a4ceec94 100644 --- a/libwvdrmengine/test/demo/readme.txt +++ b/libwvdrmengine/test/demo/readme.txt @@ -1,22 +1,12 @@ -ExoPlayerDemo.apk can be used to do end-to-end verification of Modular DRM. +ExoPlayerDemo.apk can be used to do end-to-end verification of your Modular DRM. To install, side load ExoPlayerDemo.apk app to your device: adb install ExoPlayerDemo.apk -To run, launch ExoPlayer, then choose the clip to play. The - Widevine-encrypted DASH CENC assets are in the "WIDEVINE DASH GTS" - section. - - These assets test various configurations of the Key Control Block (KCB) - with various protections and expirations: - - WV: HDCP not specified (KCB: Observe_HDCP=false) - WV: HDCP not required (KCB: Observe_HDCP=true && HDCP=not required && DataPath=normal) - WV: HDCP required (KCB: Observe_HDCP=true && HDCP=required && DataPath=normal) - WV: Secure video path required (KCB: Observe_HDCP=true && HDCP=not required && DataPath=secure) - WV: HDCP + secure video path required (KCB: Observe_HDCP=true && HDCP=required && DataPath=secure) - WV: 30s license duration (KCB: test timer expiration) +To run, launch ExoPlayer, then choose the Revenge (DASH CENC) clip, which is encrypted + using DASH Common Encryption. Then press "Play" to start playback. The other clips + in the list are clear (i.e. not encrypted). Notes: @@ -34,6 +24,5 @@ Notes: AudioTrack.getTimestamp API to do A/V sync. It will fall back to how it used to do things if the API isn't available. -- Exoplayer will retrieve a new license when playing "WV: 30s license duration", - after the license duration has expired. Integrators should verify by means - other than visual inspection that license duration is being enforced. +- The apk is still built against API level 18. The features above are accessed via reflection. + diff --git a/libwvdrmengine/test/gmock/README.android b/libwvdrmengine/test/gmock/README.android index f77a880c..e7053cc7 100644 --- a/libwvdrmengine/test/gmock/README.android +++ b/libwvdrmengine/test/gmock/README.android @@ -14,6 +14,13 @@ needs a different format of build script, and it needs to build against the OS' modified version of gTest, rather than the packaged version. Local Modifications: +--Tue Jun 3, 2014 (joeyparrish)-- + +Removed DOS newlines for the sake of the git-to-piper bridge. Piper converts +newlines automatically to unix style. If this change is not made in git, the +bridge detects differences and attempts to sync to piper, even when nothing +has changed in git. + --Mon Feb 4, 2014 (juce)-- Upgraded to gMock 1.7.0. This is required for compatibility with the upgrade @@ -59,4 +66,4 @@ Excluded the following files from being copied to Android repo: make/ msvc/ fused-src/ - gtest/ \ No newline at end of file + gtest/