diff --git a/libwvdrmengine/Android.mk b/libwvdrmengine/Android.mk index 32cb7ef3..342c9378 100644 --- a/libwvdrmengine/Android.mk +++ b/libwvdrmengine/Android.mk @@ -13,6 +13,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := libcdm_utils LOCAL_MODULE_CLASS := STATIC_LIBRARIES +LOCAL_STATIC_LIBRARIES := libcrypto_static + LOCAL_C_INCLUDES := \ vendor/widevine/libwvdrmengine/cdm/core/include \ vendor/widevine/libwvdrmengine/cdm/include \ @@ -28,6 +30,7 @@ LOCAL_SRC_FILES := \ $(CORE_SRC_DIR)/string_conversions.cpp \ $(SRC_DIR)/clock.cpp \ $(SRC_DIR)/file_store.cpp \ + $(SRC_DIR)/file_utils.cpp \ $(SRC_DIR)/lock.cpp \ $(SRC_DIR)/log.cpp \ $(SRC_DIR)/properties_android.cpp \ diff --git a/libwvdrmengine/build_and_run_all_unit_tests.sh b/libwvdrmengine/build_and_run_all_unit_tests.sh index 9e8e5f45..96071867 100755 --- a/libwvdrmengine/build_and_run_all_unit_tests.sh +++ b/libwvdrmengine/build_and_run_all_unit_tests.sh @@ -52,14 +52,16 @@ try_adb_push() { try_adb_push $OUT/system/bin/oemcrypto_test try_adb_push $OUT/system/bin/request_license_test try_adb_push $OUT/system/bin/cdm_extended_duration_test -try_adb_push $OUT/system/bin/max_res_engine_unittest try_adb_push $OUT/system/bin/policy_engine_unittest +try_adb_push $OUT/system/bin/policy_engine_constraints_unittest try_adb_push $OUT/system/bin/libwvdrmmediacrypto_test try_adb_push $OUT/system/bin/libwvdrmdrmplugin_test try_adb_push $OUT/system/bin/cdm_engine_test try_adb_push $OUT/system/bin/cdm_session_unittest try_adb_push $OUT/system/bin/file_store_unittest +try_adb_push $OUT/system/bin/file_utils_unittest try_adb_push $OUT/system/bin/license_unittest +try_adb_push $OUT/system/bin/license_keys_unittest try_adb_push $OUT/system/bin/initialization_data_unittest try_adb_push $OUT/system/bin/device_files_unittest try_adb_push $OUT/system/bin/timer_unittest diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index 5645d69e..21fc48b9 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -30,7 +30,7 @@ LOCAL_SRC_FILES := \ $(CORE_SRC_DIR)/device_files.cpp \ $(CORE_SRC_DIR)/initialization_data.cpp \ $(CORE_SRC_DIR)/license.cpp \ - $(CORE_SRC_DIR)/max_res_engine.cpp \ + $(CORE_SRC_DIR)/license_key_status.cpp \ $(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \ $(CORE_SRC_DIR)/policy_engine.cpp \ $(CORE_SRC_DIR)/privacy_crypto_openssl.cpp \ diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 64780c5e..d4582df2 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -7,6 +7,7 @@ #include "certificate_provisioning.h" #include "crypto_session.h" +#include "file_store.h" #include "initialization_data.h" #include "lock.h" #include "oemcrypto_adapter.h" @@ -26,26 +27,24 @@ typedef std::map CdmReleaseKeySetMap; class CdmEngine { public: - CdmEngine(); + CdmEngine(FileSystem* file_system); virtual ~CdmEngine(); // Session related methods virtual CdmResponseType OpenSession(const CdmKeySystem& key_system, CdmClientPropertySet* property_set, - const std::string& origin, const CdmSessionId& forced_session_id, WvCdmEventListener* event_listener); virtual CdmResponseType OpenSession(const CdmKeySystem& key_system, CdmClientPropertySet* property_set, - const std::string& origin, WvCdmEventListener* event_listener, CdmSessionId* session_id); virtual CdmResponseType CloseSession(const CdmSessionId& session_id); virtual bool IsOpenSession(const CdmSessionId& session_id); - virtual CdmResponseType OpenKeySetSession( - const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set, - const std::string& origin, WvCdmEventListener* event_listener); + virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id, + CdmClientPropertySet* property_set, + WvCdmEventListener* event_listener); virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); // License related methods @@ -93,38 +92,48 @@ class CdmEngine { // Query system information virtual CdmResponseType QueryStatus(SecurityLevel security_level, - const std::string& key, - std::string* value); + const std::string& query_token, + std::string* query_response); // Query session information virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id, - CdmQueryMap* key_info); + CdmQueryMap* query_response); virtual bool IsReleaseSession(const CdmSessionId& session_id); virtual bool IsOfflineSession(const CdmSessionId& session_id); // Query license information virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, - CdmQueryMap* key_info); + CdmQueryMap* query_response); - // Query session control information - virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, - CdmQueryMap* key_info); + // Query the types of usage permitted for the specified key. + virtual CdmResponseType QueryKeyAllowedUsage(const CdmSessionId& session_id, + const std::string& key_id, + CdmKeyAllowedUsage* key_usage); + + // Query the types of usage permitted for the specified key. + // Apply the query across all sessions. If the key is found in more than + // one session, return the allowed usage settings only if the usage settings + // are identical for each instance of the key. Otherwise, clear the settings + // and return KEY_CONFLICT_1. + virtual CdmResponseType QueryKeyAllowedUsage(const std::string& key_id, + CdmKeyAllowedUsage* key_usage); + + // Query OEMCrypto session ID + virtual CdmResponseType QueryOemCryptoSessionId( + const CdmSessionId& session_id, CdmQueryMap* query_response); // Provisioning related methods virtual CdmResponseType GetProvisioningRequest( CdmCertificateType cert_type, const std::string& cert_authority, - const std::string& origin, CdmProvisioningRequest* request, - std::string* default_url); + CdmProvisioningRequest* request, std::string* default_url); virtual CdmResponseType HandleProvisioningResponse( - const std::string& origin, const CdmProvisioningResponse& response, - std::string* cert, std::string* wrapped_key); + const CdmProvisioningResponse& response, std::string* cert, + std::string* wrapped_key); - virtual bool IsProvisioned(CdmSecurityLevel security_level, - const std::string& origin); + virtual bool IsProvisioned(CdmSecurityLevel security_level); - virtual CdmResponseType Unprovision(CdmSecurityLevel security_level, - const std::string& origin); + virtual CdmResponseType Unprovision(CdmSecurityLevel security_level); // Usage related methods for streaming licenses // Retrieve a random usage info from the list of all usage infos for this app @@ -147,6 +156,35 @@ class CdmEngine { virtual CdmResponseType Decrypt(const CdmSessionId& session_id, const CdmDecryptionParameters& parameters); + // Generic crypto operations - provides basic crypto operations that an + // application can use outside of content stream processing + + // Encrypts a buffer of app-level data. + virtual CdmResponseType GenericEncrypt( + const std::string& session_id, const std::string& in_buffer, + const std::string& key_id, const std::string& iv, + CdmEncryptionAlgorithm algorithm, std::string* out_buffer); + + // Decrypts a buffer of app-level data. + virtual CdmResponseType GenericDecrypt( + const std::string& session_id, const std::string& in_buffer, + const std::string& key_id, const std::string& iv, + CdmEncryptionAlgorithm algorithm, std::string* out_buffer); + + // Computes the signature for a message. + virtual CdmResponseType GenericSign(const std::string& session_id, + const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + std::string* signature); + + // Verifies the signature on a buffer of app-level data. + virtual CdmResponseType GenericVerify(const std::string& session_id, + const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + const std::string& signature); + virtual size_t SessionSize() const { return sessions_.size(); } // Is the key known to any session? @@ -170,7 +208,6 @@ class CdmEngine { // private methods CdmResponseType OpenSession(const CdmKeySystem& key_system, CdmClientPropertySet* property_set, - const std::string& origin, WvCdmEventListener* event_listener, const CdmSessionId* forced_session_id, CdmSessionId* session_id); @@ -190,6 +227,7 @@ class CdmEngine { CdmReleaseKeySetMap release_key_sets_; scoped_ptr cert_provisioning_; SecurityLevel cert_provisioning_requested_security_level_; + FileSystem* file_system_; static bool seeded_; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index 355c66f4..63c1908d 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -8,6 +8,7 @@ #include "crypto_session.h" #include "device_files.h" +#include "file_store.h" #include "initialization_data.h" #include "license.h" #include "oemcrypto_adapter.h" @@ -22,24 +23,7 @@ class WvCdmEventListener; class CdmSession { public: - CdmSession(const std::string& origin) - : initialized_(false), - origin_(origin), - crypto_session_(new CryptoSession), - file_handle_(new DeviceFiles), - license_received_(false), - is_offline_(false), - is_release_(false), - is_temporary_(false), - security_level_(kSecurityLevelUninitialized), - requested_security_level_(kLevelDefault), - is_initial_decryption_(true), - has_decrypted_since_last_report_(false), - is_initial_usage_update_(true), - is_usage_update_needed_(false), - mock_license_parser_in_use_(false), - mock_policy_engine_in_use_(false) {} - + CdmSession(FileSystem* file_system); virtual ~CdmSession(); virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set); @@ -63,13 +47,17 @@ class CdmSession { virtual CdmResponseType AddKey(const CdmKeyResponse& key_response); // Query session status - virtual CdmResponseType QueryStatus(CdmQueryMap* key_info); + virtual CdmResponseType QueryStatus(CdmQueryMap* query_response); // Query license information - virtual CdmResponseType QueryKeyStatus(CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyStatus(CdmQueryMap* query_response); - // Query session control info - virtual CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info); + // Query allowed usages for key + virtual CdmResponseType QueryKeyAllowedUsage(const std::string& key_id, + CdmKeyAllowedUsage* key_usage); + + // Query OEMCrypto session ID + virtual CdmResponseType QueryOemCryptoSessionId(CdmQueryMap* query_response); // Decrypt() - Accept encrypted buffer and return decrypted data. virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); @@ -131,11 +119,41 @@ class CdmSession { bool DeleteLicense(); + // Generate unique ID for each new session. + CdmSessionId GenerateSessionId(); + + // Generic crypto operations - provides basic crypto operations that an + // application can use outside of content stream processing + + // Encrypts a buffer of app-level data. + virtual CdmResponseType GenericEncrypt(const std::string& in_buffer, + const std::string& key_id, + const std::string& iv, + CdmEncryptionAlgorithm algorithm, + std::string* out_buffer); + + // Decrypts a buffer of app-level data. + virtual CdmResponseType GenericDecrypt(const std::string& in_buffer, + const std::string& key_id, + const std::string& iv, + CdmEncryptionAlgorithm algorithm, + std::string* out_buffer); + + // Computes the signature for a message. + virtual CdmResponseType GenericSign(const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + std::string* signature); + + // Verifies the signature on a buffer of app-level data. + virtual CdmResponseType GenericVerify(const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + const std::string& signature); + private: friend class CdmSessionTest; - // Generate unique ID for each new session. - CdmSessionId GenerateSessionId(); bool GenerateKeySetId(CdmKeySetId* key_set_id); CdmResponseType StoreLicense(); @@ -150,7 +168,6 @@ class CdmSession { // instance variables bool initialized_; CdmSessionId session_id_; - const std::string origin_; scoped_ptr license_parser_; scoped_ptr crypto_session_; scoped_ptr policy_engine_; diff --git a/libwvdrmengine/cdm/core/include/certificate_provisioning.h b/libwvdrmengine/cdm/core/include/certificate_provisioning.h index dd222828..22460c21 100644 --- a/libwvdrmengine/cdm/core/include/certificate_provisioning.h +++ b/libwvdrmengine/cdm/core/include/certificate_provisioning.h @@ -12,6 +12,7 @@ namespace wvcdm { class CdmSession; +class FileSystem; class CertificateProvisioning { public: @@ -26,7 +27,7 @@ class CertificateProvisioning { CdmProvisioningRequest* request, std::string* default_url); CdmResponseType HandleProvisioningResponse( - const std::string& origin, + FileSystem* file_system, const CdmProvisioningResponse& response, std::string* cert, std::string* wrapped_key); diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 847fa0cb..9f1893c8 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -103,6 +103,25 @@ class CryptoSession { virtual bool GetNumberOfOpenSessions(size_t* count); virtual bool GetMaxNumberOfSessions(size_t* max); + virtual CdmResponseType GenericEncrypt(const std::string& in_buffer, + const std::string& key_id, + const std::string& iv, + CdmEncryptionAlgorithm algorithm, + std::string* out_buffer); + virtual CdmResponseType GenericDecrypt(const std::string& in_buffer, + const std::string& key_id, + const std::string& iv, + CdmEncryptionAlgorithm algorithm, + std::string* out_buffer); + virtual CdmResponseType GenericSign(const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + std::string* signature); + virtual CdmResponseType GenericVerify(const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + const std::string& signature); + private: void Init(); void Terminate(); @@ -114,9 +133,17 @@ class CryptoSession { bool GenerateRsaSignature(const std::string& message, std::string* signature); size_t GetOffset(std::string message, std::string field); bool SetDestinationBufferType(); - bool SelectKey(const std::string& key_id); + static const OEMCrypto_Algorithm kInvalidAlgorithm = + static_cast(-1); + + OEMCrypto_Algorithm GenericSigningAlgorithm(CdmSigningAlgorithm algorithm); + OEMCrypto_Algorithm GenericEncryptionAlgorithm( + CdmEncryptionAlgorithm algorithm); + size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm); + + static const size_t kAes128BlockSize = 16; // Block size for AES_CBC_128 static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature static Lock crypto_lock_; static bool initialized_; @@ -130,7 +157,7 @@ class CryptoSession { bool is_destination_buffer_type_valid_; SecurityLevel requested_security_level_; - KeyId key_id_; + KeyId cached_key_id_; uint64_t request_id_base_; static uint64_t request_id_index_; diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 88dabae0..e60559e6 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -18,7 +18,7 @@ namespace wvcdm { -class File; +class FileSystem; class DeviceFiles { public: @@ -28,7 +28,7 @@ class DeviceFiles { kLicenseStateUnknown, } LicenseState; - DeviceFiles(); + DeviceFiles(FileSystem*); virtual ~DeviceFiles(); virtual bool Init(CdmSecurityLevel security_level); @@ -36,14 +36,12 @@ class DeviceFiles { return Init(security_level); } - virtual bool StoreCertificate(const std::string& origin, - const std::string& certificate, + virtual bool StoreCertificate(const std::string& certificate, const std::string& wrapped_private_key); - virtual bool RetrieveCertificate(const std::string& origin, - std::string* certificate, + virtual bool RetrieveCertificate(std::string* certificate, std::string* wrapped_private_key); - virtual bool HasCertificate(const std::string& origin); - virtual bool RemoveCertificate(const std::string& origin); + virtual bool HasCertificate(); + virtual bool RemoveCertificate(); virtual bool StoreLicense(const std::string& key_set_id, const LicenseState state, @@ -113,18 +111,11 @@ class DeviceFiles { bool RemoveFile(const std::string& name); ssize_t GetFileSize(const std::string& name); - // 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(); - - static std::string GetCertificateFileName(const std::string& origin); + static std::string GetCertificateFileName(); static std::string GetLicenseFileNameExtension(); static std::string GetUsageInfoFileName(const std::string& app_id); static std::string GetFileNameSafeHash(const std::string& input); - // For testing only: - void SetTestFile(File* file); #if defined(UNIT_TEST) FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel); FRIEND_TEST(DeviceCertificateStoreTest, StoreCertificate); @@ -135,7 +126,6 @@ class DeviceFiles { FRIEND_TEST(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem); FRIEND_TEST(DeviceFilesTest, RetrieveLicenses); FRIEND_TEST(DeviceFilesTest, AppParametersBackwardCompatibility); - FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility); FRIEND_TEST(DeviceFilesTest, StoreLicenses); FRIEND_TEST(DeviceFilesTest, UpdateLicenseState); FRIEND_TEST(DeviceFilesUsageInfoTest, Delete); @@ -153,12 +143,10 @@ class DeviceFiles { static std::set reserved_license_ids_; - scoped_ptr file_; + FileSystem* file_system_; CdmSecurityLevel security_level_; bool initialized_; - bool test_file_; - CORE_DISALLOW_COPY_AND_ASSIGN(DeviceFiles); }; diff --git a/libwvdrmengine/cdm/core/include/file_store.h b/libwvdrmengine/cdm/core/include/file_store.h index 8e77b4f1..6e28990f 100644 --- a/libwvdrmengine/cdm/core/include/file_store.h +++ b/libwvdrmengine/cdm/core/include/file_store.h @@ -15,39 +15,54 @@ namespace wvcdm { // File class. The implementation is platform dependent. class File { + public: + virtual ssize_t Read(char* buffer, size_t bytes); + virtual ssize_t Write(const char* buffer, size_t bytes); + virtual void Close(); + + protected: + class Impl; + + File(Impl*); + virtual ~File(); + + private: + Impl* impl_; + + friend class FileSystem; + CORE_DISALLOW_COPY_AND_ASSIGN(File); +}; + +class FileSystem { public: class Impl; // defines as bit flag enum OpenFlags { kNoFlags = 0, - kBinary = 1, - kCreate = 2, - kReadOnly = 4, // defaults to read and write access - kTruncate = 8 + kCreate = 1, + kReadOnly = 2, // defaults to read and write access + kTruncate = 4 }; - File(); - virtual ~File(); + FileSystem(); + FileSystem(const std::string& origin, void* extra_data); + virtual ~FileSystem(); - virtual bool Open(const std::string& file_path, int flags); - virtual ssize_t Read(char* buffer, size_t bytes); - virtual ssize_t Write(const char* buffer, size_t bytes); - virtual void Close(); + virtual File* Open(const std::string& file_path, int flags); virtual bool Exists(const std::string& file_path); virtual bool Remove(const std::string& file_path); - virtual bool Copy(const std::string& old_path, const std::string& new_path); - virtual bool List(const std::string& path, std::vector* files); - virtual bool CreateDirectory(const std::string dir_path); - virtual bool IsDirectory(const std::string& dir_path); - virtual bool IsRegularFile(const std::string& file_path); virtual ssize_t FileSize(const std::string& file_path); + const std::string& origin() const { return origin_; } + void SetOrigin(const std::string& origin); + private: Impl* impl_; + std::string origin_; - CORE_DISALLOW_COPY_AND_ASSIGN(File); + CORE_DISALLOW_COPY_AND_ASSIGN(FileSystem); }; } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/include/license_key_status.h b/libwvdrmengine/cdm/core/include/license_key_status.h new file mode 100644 index 00000000..cc81dd5a --- /dev/null +++ b/libwvdrmengine/cdm/core/include/license_key_status.h @@ -0,0 +1,139 @@ +// Copyright 2016 Google Inc. All Rights Reserved. + +#ifndef WVCDM_CORE_LICENSE_KEY_STATUS_H_ +#define WVCDM_CORE_LICENSE_KEY_STATUS_H_ + +#include + +#include "crypto_session.h" +#include "license_protocol.pb.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +class LicenseKeyStatus; + +// Holds all content and operator session keys for a session. +class LicenseKeys { + public: + LicenseKeys() {} + virtual ~LicenseKeys() { Clear(); } + + virtual bool Empty() { return keys_.empty(); } + + // Returns true if the key is a content key (not an operator session key) + virtual bool IsContentKey(const KeyId& key_id); + + // Returns true if the key is currently usable for content decryption. + virtual bool CanDecryptContent(const KeyId& key_id); + + // Returns the allowed usages for a key. + virtual bool GetAllowedUsage(const KeyId& key_id, + CdmKeyAllowedUsage* allowed_usage); + + // Applies a new status to each content key. + // Returns true if any statuses changed, and sets new_usable_keys to + // true if the status changes resulted in keys becoming usable. + virtual bool ApplyStatusChange(CdmKeyStatus new_status, + bool* new_usable_keys); + + // Populates the CdmKeyStatusMap with the current content keys. + virtual void ExtractKeyStatuses(CdmKeyStatusMap* content_keys); + + // Determines whether the specified key can be used under the current + // resolution and/or hdcp constraints. If no constraints have been applied + // to the key, returns true. + virtual bool MeetsConstraints(const KeyId& key_id); + + // Applies a resolution and/or hdcp change to each key, updating their + // useability under their constraints. + virtual void ApplyConstraints(uint32_t new_resolution, + CryptoSession::HdcpCapability new_hdcp_level); + + // Extracts the keys from a license and makes them available for + // querying usage and constraint settings. + virtual void SetFromLicense( + const video_widevine_server::sdk::License& license); + + private: + typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer; + typedef std::map::const_iterator + LicenseKeyStatusIterator; + + void Clear(); + + bool is_initialized_; + std::map keys_; + + CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeys); +}; + +// Holds the current license status of a key. +class LicenseKeyStatus { + friend class LicenseKeys; + + public: + // Returns true if the key is a content key (not an operator session key) + virtual bool IsContentKey() { return is_content_key_; } + + // Returns true if the key is currently usable for content decryption + virtual bool CanDecryptContent(); + + // Returns the usages allowed for this key. + virtual bool GetAllowedUsage(CdmKeyAllowedUsage* allowed_usage); + + // Returns the current status of the key. + virtual CdmKeyStatus GetKeyStatus() const { return key_status_; } + + // Applies a new status to this key. + // Returns true if the status changed, and sets new_usable_keys to + // true if the status changes resulted in the key becoming usable. + virtual bool ApplyStatusChange(CdmKeyStatus new_status, + bool* new_usable_keys); + + // Returns the current constraint status of this key. The result + // may change due to calls to ApplyConstraints(). + // Note: this will return true until the first call to ApplyConstraints(). + virtual bool MeetsConstraints() const { return meets_constraints_; } + + // Applies the given changes in resolution or HDCP settings. + virtual void ApplyConstraints( + uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level); + + protected: + typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer; + typedef KeyContainer::OperatorSessionKeyPermissions + OperatorSessionKeyPermissions; + typedef KeyContainer::OutputProtection OutputProtection; + typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint; + typedef ::google::protobuf::RepeatedPtrField + ConstraintList; + + LicenseKeyStatus(const KeyContainer& key); + + virtual ~LicenseKeyStatus() {} + + private: + + void ParseContentKey(const KeyContainer& key); + void ParseOperatorSessionKey(const KeyContainer& key); + + bool HasConstraints() { + return is_content_key_ && constraints_.size() != 0; + } + + void SetConstraints(const ConstraintList& constraints); + + bool is_content_key_; + CdmKeyStatus key_status_; + bool meets_constraints_; + CdmKeyAllowedUsage allowed_usage_; + CryptoSession::HdcpCapability default_hdcp_level_; + ConstraintList constraints_; + + CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeyStatus); +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_LICENSE_KEY_STATUS_H_ diff --git a/libwvdrmengine/cdm/core/include/max_res_engine.h b/libwvdrmengine/cdm/core/include/max_res_engine.h deleted file mode 100644 index 9a28d140..00000000 --- a/libwvdrmengine/cdm/core/include/max_res_engine.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. - -#ifndef WVCDM_CORE_MAX_RES_ENGINE_H_ -#define WVCDM_CORE_MAX_RES_ENGINE_H_ - -#include - -#include "crypto_session.h" -#include "license_protocol.pb.h" -#include "lock.h" -#include "scoped_ptr.h" -#include "wv_cdm_types.h" - -namespace wvcdm { - -class Clock; -class MaxResEngineTest; - -// Similar to the Policy Engine, this acts as an oracle that basically says -// "Yes(true) you may still decrypt or no(false) you may not decrypt this data -// anymore." -class MaxResEngine { - public: - explicit MaxResEngine(CryptoSession* crypto_session); - virtual ~MaxResEngine(); - - // The value returned is computed during the last call to SetLicense/ - // SetResolution/OnTimerEvent and may be out of sync depending on the amount - // of time elapsed. The current decryption status is not calculated when this - // function is called to avoid overhead in the decryption path. - virtual bool CanDecrypt(const KeyId& key_id); - - // SetLicense is used in handling the initial license response. It stores - // an exact copy of the key constraints from the license. - virtual void SetLicense(const video_widevine_server::sdk::License& license); - - // SetResolution is called when the current output resolution is updated by - // the decoder. The max-res engine will recalculate the current resolution - // constraints, (if any) which may affect the results for CanDecrypt(). - virtual void SetResolution(uint32_t width, uint32_t height); - - // OnTimerEvent is called when a timer fires. The max-res engine may check the - // current HDCP level using the crypto session, which may affect the results - // for CanDecrypt(). - virtual void OnTimerEvent(); - - private: - typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer; - typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection - OutputProtection; - typedef ::video_widevine_server::sdk::License::KeyContainer:: - VideoResolutionConstraint VideoResolutionConstraint; - typedef ::google::protobuf::RepeatedPtrField - ConstraintList; - - class KeyStatus { - public: - explicit KeyStatus(const ConstraintList& constraints); - KeyStatus(const ConstraintList& constraints, - const OutputProtection::HDCP& default_hdcp_level); - - bool can_decrypt() const { return can_decrypt_; } - - void Update(uint32_t res, - CryptoSession::HdcpCapability current_hdcp_level); - - private: - void Init(const ConstraintList& constraints); - - VideoResolutionConstraint* GetConstraintForRes(uint32_t res); - - static CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp( - const OutputProtection::HDCP& input); - - bool can_decrypt_; - - CryptoSession::HdcpCapability default_hdcp_level_; - ConstraintList constraints_; - }; - - typedef std::map::const_iterator KeyIterator; - - void Init(CryptoSession* crypto_session, Clock* clock); - - void DeleteAllKeys(); - - Lock status_lock_; - std::map keys_; - uint32_t current_resolution_; - int64_t next_check_time_; - - scoped_ptr clock_; - CryptoSession* crypto_session_; - - // For testing - friend class MaxResEngineTest; - MaxResEngine(CryptoSession* crypto_session, Clock* clock); - - CORE_DISALLOW_COPY_AND_ASSIGN(MaxResEngine); -}; - -} // wvcdm - -#endif // WVCDM_CORE_MAX_RES_ENGINE_H_ diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index 60e019f3..ccef771d 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -6,8 +6,8 @@ #include #include +#include "license_key_status.h" #include "license_protocol.pb.h" -#include "max_res_engine.h" #include "scoped_ptr.h" #include "wv_cdm_types.h" @@ -67,7 +67,10 @@ class PolicyEngine { virtual void NotifySessionExpiration(); - virtual CdmResponseType Query(CdmQueryMap* key_info); + virtual CdmResponseType Query(CdmQueryMap* query_response); + + virtual CdmResponseType QueryKeyAllowedUsage(const KeyId& key_id, + CdmKeyAllowedUsage* key_usage); virtual const LicenseIdentification& license_id() { return license_id_; } @@ -90,6 +93,14 @@ class PolicyEngine { private: friend class PolicyEngineTest; + friend class PolicyEngineConstraintsTest; + + void InitDevice(CryptoSession* crypto_session); + void CheckDevice(int64_t current_time); + + void SetDeviceResolution(uint32_t width, uint32_t height) { + current_resolution_ = width * height; + } typedef enum { kLicenseStateInitial, @@ -120,9 +131,9 @@ class PolicyEngine { // expiry time changes. void NotifyExpirationUpdate(); - // These setters are for testing only. Takes ownership of the pointers. + // set_clock() is for testing only. It alters ownership of the + // passed-in pointer. void set_clock(Clock* clock); - void set_max_res_engine(MaxResEngine* max_res_engine); LicenseState license_state_; @@ -154,9 +165,14 @@ class PolicyEngine { CdmSessionId session_id_; WvCdmEventListener* event_listener_; - scoped_ptr max_res_engine_; + // Keys associated with license - holds allowed usage, usage constraints, + // and current status (CdmKeyStatus) + scoped_ptr license_keys_; - std::map keys_status_; + // Device checks + int64_t next_device_check_; + uint32_t current_resolution_; + CryptoSession* crypto_session_; scoped_ptr clock_; diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index c6f04fdf..1fd57420 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -218,6 +218,33 @@ enum CdmResponseType { INVALID_PARAMETERS_ENG_5, DECRYPT_ERROR, INSUFFICIENT_OUTPUT_PROTECTION, + SESSION_NOT_FOUND_12, /* 180 */ + KEY_NOT_FOUND_1, + KEY_NOT_FOUND_2, + KEY_CONFLICT_1, + INVALID_PARAMETERS_ENG_6, + INVALID_PARAMETERS_ENG_7, /* 185 */ + INVALID_PARAMETERS_ENG_8, + INVALID_PARAMETERS_ENG_9, + INVALID_PARAMETERS_ENG_10, + INVALID_PARAMETERS_ENG_11, /* 190 */ + INVALID_PARAMETERS_ENG_12, + SESSION_NOT_FOUND_13, + SESSION_NOT_FOUND_14, + SESSION_NOT_FOUND_15, + SESSION_NOT_FOUND_16, /* 195 */ + KEY_NOT_FOUND_3, + KEY_NOT_FOUND_4, + KEY_NOT_FOUND_5, + KEY_NOT_FOUND_6, + KEY_ERROR_1, /* 200 */ + KEY_ERROR_2, + KEY_ERROR_3, + KEY_ERROR_4, + INVALID_PARAMETERS_ENG_13, + INVALID_PARAMETERS_ENG_14, /* 205 */ + INVALID_PARAMETERS_ENG_15, + INVALID_PARAMETERS_ENG_16, }; enum CdmKeyStatus { @@ -274,6 +301,59 @@ enum CdmCipherMode { kCipherModeCbc, }; +enum CdmEncryptionAlgorithm { + kEncryptionAlgorithmUnknown, + kEncryptionAlgorithmAesCbc128 +}; + +enum CdmSigningAlgorithm { + kSigningAlgorithmUnknown, + kSigningAlgorithmHmacSha256 +}; + +class CdmKeyAllowedUsage { + public: + CdmKeyAllowedUsage() { + Clear(); + } + + bool Valid() const { return valid_; } + void SetValid() { valid_ = true; } + + void Clear() { + decrypt_to_clear_buffer = false; + decrypt_to_secure_buffer = false; + generic_encrypt = false; + generic_decrypt = false; + generic_sign = false; + generic_verify = false; + valid_ = false; + } + + bool Equals(const CdmKeyAllowedUsage& other) { + if (!valid_ || !other.Valid() || + decrypt_to_clear_buffer != other.decrypt_to_clear_buffer || + decrypt_to_secure_buffer != other.decrypt_to_secure_buffer || + generic_encrypt != other.generic_encrypt || + generic_decrypt != other.generic_decrypt || + generic_sign != other.generic_sign || + generic_verify != other.generic_verify) { + return false; + } + return true; + } + + bool decrypt_to_clear_buffer; + bool decrypt_to_secure_buffer; + bool generic_encrypt; + bool generic_decrypt; + bool generic_sign; + bool generic_verify; + + private: + bool valid_; +}; + // For schemes that do not use pattern encryption (cenc and cbc1), encrypt // and skip should be set to 0. For those that do (cens and cbcs), it is // recommended that encrypt+skip bytes sum to 10 and for cbcs that a 1:9 diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index d130bef8..0352551e 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -15,7 +15,6 @@ #include "license_protocol.pb.h" #include "log.h" #include "properties.h" -#include "scoped_ptr.h" #include "string_conversions.h" #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" @@ -55,11 +54,13 @@ class UsagePropertySet : public CdmClientPropertySet { bool CdmEngine::seeded_ = false; -CdmEngine::CdmEngine() +CdmEngine::CdmEngine(FileSystem* file_system) : cert_provisioning_(NULL), cert_provisioning_requested_security_level_(kLevelDefault), + file_system_(file_system), usage_session_(NULL), last_usage_information_update_time_(0) { + assert(file_system); Properties::Init(); if (!seeded_) { Clock clock; @@ -79,25 +80,22 @@ CdmEngine::~CdmEngine() { CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system, CdmClientPropertySet* property_set, - const std::string& origin, const CdmSessionId& forced_session_id, WvCdmEventListener* event_listener) { - return OpenSession(key_system, property_set, origin, event_listener, + return OpenSession(key_system, property_set, event_listener, &forced_session_id, NULL); } CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system, CdmClientPropertySet* property_set, - const std::string& origin, WvCdmEventListener* event_listener, CdmSessionId* session_id) { - return OpenSession(key_system, property_set, origin, event_listener, - NULL, session_id); + return OpenSession(key_system, property_set, event_listener, NULL, + session_id); } CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system, CdmClientPropertySet* property_set, - const std::string& origin, WvCdmEventListener* event_listener, const CdmSessionId* forced_session_id, CdmSessionId* session_id) { @@ -119,7 +117,8 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system, } } - scoped_ptr new_session(new CdmSession(origin)); + scoped_ptr new_session(new CdmSession(file_system_)); + CdmResponseType sts = new_session->Init(property_set, forced_session_id, event_listener); if (sts != NO_ERROR) { @@ -141,7 +140,7 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system, CdmResponseType CdmEngine::OpenKeySetSession( const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set, - const std::string& origin, WvCdmEventListener* event_listener) { + WvCdmEventListener* event_listener) { LOGI("CdmEngine::OpenKeySetSession"); if (key_set_id.empty()) { @@ -150,9 +149,8 @@ CdmResponseType CdmEngine::OpenKeySetSession( } CdmSessionId session_id; - CdmResponseType sts = - OpenSession(KEY_SYSTEM, property_set, origin, event_listener, - NULL /* forced_session_id */, &session_id); + CdmResponseType sts = OpenSession(KEY_SYSTEM, property_set, event_listener, + NULL /* forced_session_id */, &session_id); if (sts != NO_ERROR) return sts; @@ -313,7 +311,9 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id, } CdmResponseType sts = iter->second->AddKey(key_data); - *key_set_id = iter->second->key_set_id(); + if (key_set_id) { + *key_set_id = iter->second->key_set_id(); + } switch (sts) { case KEY_ADDED: @@ -425,8 +425,8 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id, } CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, - const std::string& key, - std::string* value) { + const std::string& query_token, + std::string* query_response) { LOGI("CdmEngine::QueryStatus"); CryptoSession crypto_session; if (security_level == kLevel3) { @@ -434,36 +434,41 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, if (NO_ERROR != status) return INVALID_QUERY_STATUS; } - if (key == QUERY_KEY_SECURITY_LEVEL) { + if (!query_response) { + LOGE("CdmEngine::QueryStatus: no query response destination"); + return INVALID_PARAMETERS_ENG_6; + } + + if (query_token == QUERY_KEY_SECURITY_LEVEL) { CdmSecurityLevel security_level = crypto_session.GetSecurityLevel(); switch (security_level) { case kSecurityLevelL1: - *value = QUERY_VALUE_SECURITY_LEVEL_L1; + *query_response = QUERY_VALUE_SECURITY_LEVEL_L1; break; case kSecurityLevelL2: - *value = QUERY_VALUE_SECURITY_LEVEL_L2; + *query_response = QUERY_VALUE_SECURITY_LEVEL_L2; break; case kSecurityLevelL3: - *value = QUERY_VALUE_SECURITY_LEVEL_L3; + *query_response = QUERY_VALUE_SECURITY_LEVEL_L3; break; case kSecurityLevelUninitialized: case kSecurityLevelUnknown: - *value = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN; + *query_response = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN; break; default: LOGW("CdmEngine::QueryStatus: Unknown security level: %d", security_level); return UNKNOWN_ERROR; } - } else if (key == QUERY_KEY_DEVICE_ID) { + } else if (query_token == QUERY_KEY_DEVICE_ID) { std::string deviceId; if (!crypto_session.GetDeviceUniqueId(&deviceId)) { LOGW("CdmEngine::QueryStatus: GetDeviceUniqueId failed"); return UNKNOWN_ERROR; } - *value = deviceId; - } else if (key == QUERY_KEY_SYSTEM_ID) { + *query_response = deviceId; + } else if (query_token == QUERY_KEY_SYSTEM_ID) { uint32_t system_id; if (!crypto_session.GetSystemId(&system_id)) { LOGW("CdmEngine::QueryStatus: GetSystemId failed"); @@ -472,35 +477,36 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, std::ostringstream system_id_stream; system_id_stream << system_id; - *value = system_id_stream.str(); - } else if (key == QUERY_KEY_PROVISIONING_ID) { + *query_response = system_id_stream.str(); + } else if (query_token == QUERY_KEY_PROVISIONING_ID) { std::string provisioning_id; if (!crypto_session.GetProvisioningId(&provisioning_id)) { LOGW("CdmEngine::QueryStatus: GetProvisioningId failed"); return UNKNOWN_ERROR; } - *value = provisioning_id; - } else if (key == QUERY_KEY_CURRENT_HDCP_LEVEL || - key == QUERY_KEY_MAX_HDCP_LEVEL) { + *query_response = provisioning_id; + } else if (query_token == QUERY_KEY_CURRENT_HDCP_LEVEL || + query_token == QUERY_KEY_MAX_HDCP_LEVEL) { CryptoSession::HdcpCapability current_hdcp; CryptoSession::HdcpCapability max_hdcp; if (!crypto_session.GetHdcpCapabilities(¤t_hdcp, &max_hdcp)) { LOGW("CdmEngine::QueryStatus: GetHdcpCapabilities failed"); return UNKNOWN_ERROR; } - - *value = MapHdcpVersion(key == QUERY_KEY_CURRENT_HDCP_LEVEL ? current_hdcp - : max_hdcp); - } else if (key == QUERY_KEY_USAGE_SUPPORT) { + *query_response = + MapHdcpVersion(query_token == QUERY_KEY_CURRENT_HDCP_LEVEL ? + current_hdcp : max_hdcp); + } else if (query_token == QUERY_KEY_USAGE_SUPPORT) { bool supports_usage_reporting; if (!crypto_session.UsageInformationSupport(&supports_usage_reporting)) { LOGW("CdmEngine::QueryStatus: UsageInformationSupport failed"); return UNKNOWN_ERROR; } - *value = supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; - } else if (key == QUERY_KEY_NUMBER_OF_OPEN_SESSIONS) { + *query_response = + supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; + } else if (query_token == QUERY_KEY_NUMBER_OF_OPEN_SESSIONS) { size_t number_of_open_sessions; if (!crypto_session.GetNumberOfOpenSessions(&number_of_open_sessions)) { LOGW("CdmEngine::QueryStatus: GetNumberOfOpenSessions failed"); @@ -509,8 +515,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, std::ostringstream open_sessions_stream; open_sessions_stream << number_of_open_sessions; - *value = open_sessions_stream.str(); - } else if (key == QUERY_KEY_MAX_NUMBER_OF_SESSIONS) { + *query_response = open_sessions_stream.str(); + } else if (query_token == QUERY_KEY_MAX_NUMBER_OF_SESSIONS) { size_t maximum_number_of_sessions; if (!crypto_session.GetMaxNumberOfSessions(&maximum_number_of_sessions)) { LOGW("CdmEngine::QueryStatus: GetMaxNumberOfOpenSessions failed"); @@ -519,8 +525,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, std::ostringstream max_sessions_stream; max_sessions_stream << maximum_number_of_sessions; - *value = max_sessions_stream.str(); - } else if (key == QUERY_KEY_OEMCRYPTO_API_VERSION) { + *query_response = max_sessions_stream.str(); + } else if (query_token == QUERY_KEY_OEMCRYPTO_API_VERSION) { uint32_t api_version; if (!crypto_session.GetApiVersion(&api_version)) { LOGW("CdmEngine::QueryStatus: GetApiVersion failed"); @@ -529,10 +535,10 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, std::ostringstream api_version_stream; api_version_stream << api_version; - *value = api_version_stream.str(); + *query_response = api_version_stream.str(); } else { - LOGW("CdmEngine::QueryStatus: Unknown status requested, key = %s", - key.c_str()); + LOGW("CdmEngine::QueryStatus: Unknown status requested, token = %s", + query_token.c_str()); return INVALID_QUERY_KEY; } @@ -540,7 +546,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, } CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id, - CdmQueryMap* key_info) { + CdmQueryMap* query_response) { LOGI("CdmEngine::QuerySessionStatus"); CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { @@ -548,7 +554,7 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id, session_id.c_str()); return SESSION_NOT_FOUND_8; } - return iter->second->QueryStatus(key_info); + return iter->second->QueryStatus(query_response); } bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) { @@ -574,7 +580,7 @@ bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) { } CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id, - CdmQueryMap* key_info) { + CdmQueryMap* query_response) { LOGI("CdmEngine::QueryKeyStatus"); CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { @@ -582,19 +588,72 @@ CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id, session_id.c_str()); return SESSION_NOT_FOUND_9; } - return iter->second->QueryKeyStatus(key_info); + return iter->second->QueryKeyStatus(query_response); } -CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id, - CdmQueryMap* key_info) { - LOGI("CdmEngine::QueryKeyControlInfo"); +CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id, + const std::string& key_id, + CdmKeyAllowedUsage* key_usage) { + LOGI("CdmEngine::QueryKeyAllowedUsage"); + if (!key_usage) { + LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination"); + return INVALID_PARAMETERS_ENG_12; + } CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { - LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s", + LOGE("CdmEngine::QueryKeyAllowedUsage: session_id not found = %s", + session_id.c_str()); + return SESSION_NOT_FOUND_12; + } + return iter->second->QueryKeyAllowedUsage(key_id, key_usage); +} + +CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id, + CdmKeyAllowedUsage* key_usage) { + LOGI("CdmEngine::QueryKeyAllowedUsage (all sessions)"); + CdmResponseType session_sts; + CdmKeyAllowedUsage found_in_this_session; + bool found = false; + if (!key_usage) { + LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination"); + return INVALID_PARAMETERS_ENG_7; + } + key_usage->Clear(); + for (CdmSessionMap::iterator iter = sessions_.begin(); + iter != sessions_.end(); ++iter) { + session_sts = iter->second->QueryKeyAllowedUsage(key_id, + &found_in_this_session); + if (session_sts == NO_ERROR) { + if (found) { + // Found another key. If usage settings do not match, fail. + if (!key_usage->Equals(found_in_this_session)) { + key_usage->Clear(); + return KEY_CONFLICT_1; + } + } else { + *key_usage = found_in_this_session; + found = true; + } + } else if (session_sts != KEY_NOT_FOUND_1) { + LOGE("CdmEngine::QueryKeyAllowedUsage (all sessions) FAILED = %d", + session_sts); + key_usage->Clear(); + return session_sts; + } + } + return (found) ? NO_ERROR : KEY_NOT_FOUND_2; +} + +CdmResponseType CdmEngine::QueryOemCryptoSessionId( + const CdmSessionId& session_id, CdmQueryMap* query_response) { + LOGI("CdmEngine::QueryOemCryptoSessionId"); + CdmSessionMap::iterator iter = sessions_.find(session_id); + if (iter == sessions_.end()) { + LOGE("CdmEngine::QueryOemCryptoSessionId: session_id not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_10; } - return iter->second->QueryKeyControlInfo(key_info); + return iter->second->QueryOemCryptoSessionId(query_response); } /* @@ -606,8 +665,7 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id, */ CdmResponseType CdmEngine::GetProvisioningRequest( CdmCertificateType cert_type, const std::string& cert_authority, - const std::string& origin, CdmProvisioningRequest* request, - std::string* default_url) { + CdmProvisioningRequest* request, std::string* default_url) { if (!request) { LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters"); return INVALID_PROVISIONING_REQUEST_PARAM_1; @@ -624,7 +682,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest( } CdmResponseType ret = cert_provisioning_->GetProvisioningRequest( cert_provisioning_requested_security_level_, cert_type, cert_authority, - origin, request, default_url); + file_system_->origin(), request, default_url); if (ret != NO_ERROR) { cert_provisioning_.reset(NULL); // Release resources. } @@ -639,8 +697,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest( * Returns NO_ERROR for success and CdmResponseType error code if fails. */ CdmResponseType CdmEngine::HandleProvisioningResponse( - const std::string& origin, const CdmProvisioningResponse& response, - std::string* cert, std::string* wrapped_key) { + const CdmProvisioningResponse& response, std::string* cert, + std::string* wrapped_key) { if (response.empty()) { LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response."); cert_provisioning_.reset(NULL); @@ -672,7 +730,7 @@ CdmResponseType CdmEngine::HandleProvisioningResponse( return EMPTY_PROVISIONING_CERTIFICATE_2; } CdmSecurityLevel security_level = crypto_session.GetSecurityLevel(); - if (!IsProvisioned(security_level, origin)) { + if (!IsProvisioned(security_level)) { LOGE( "CdmEngine::HandleProvisioningResponse: provisioning object " "missing."); @@ -682,7 +740,7 @@ CdmResponseType CdmEngine::HandleProvisioningResponse( } CdmResponseType ret = cert_provisioning_->HandleProvisioningResponse( - origin, response, cert, wrapped_key); + file_system_, response, cert, wrapped_key); // Release resources only on success. It is possible that a provisioning // attempt was made after this one was requested but before the response was // received, which will cause this attempt to fail. Not releasing will @@ -691,28 +749,25 @@ CdmResponseType CdmEngine::HandleProvisioningResponse( return ret; } -bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level, - const std::string& origin) { - DeviceFiles handle; +bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level) { + DeviceFiles handle(file_system_); if (!handle.Init(security_level)) { LOGE("CdmEngine::IsProvisioned: unable to initialize device files"); return false; } - return handle.HasCertificate(origin); + return handle.HasCertificate(); } -CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level, - const std::string& origin) { - DeviceFiles handle; +CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) { + DeviceFiles handle(file_system_); if (!handle.Init(security_level)) { LOGE("CdmEngine::Unprovision: unable to initialize device files"); return UNPROVISION_ERROR_1; } - if (origin != EMPTY_ORIGIN) { - if (!handle.RemoveCertificate(origin)) { - LOGE("CdmEngine::Unprovision: unable to delete certificate for origin %s", - origin.c_str()); + if (!file_system_->origin().empty()) { + if (!handle.RemoveCertificate()) { + LOGE("CdmEngine::Unprovision: unable to delete certificate"); return UNPROVISION_ERROR_2; } return NO_ERROR; @@ -743,15 +798,19 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, if (NULL == usage_property_set_.get()) { usage_property_set_.reset(new UsagePropertySet()); } + if (!usage_info) { + LOGE("CdmEngine::GetUsageInfo: no usage info destination"); + return INVALID_PARAMETERS_ENG_8; + } usage_property_set_->set_security_level(kLevelDefault); usage_property_set_->set_app_id(app_id); - usage_session_.reset(new CdmSession(EMPTY_ORIGIN)); + usage_session_.reset(new CdmSession(file_system_)); CdmResponseType status = usage_session_->Init(usage_property_set_.get()); if (NO_ERROR != status) { LOGE("CdmEngine::GetUsageInfo: session init error"); return status; } - DeviceFiles handle; + DeviceFiles handle(file_system_); if (!handle.Init(usage_session_->GetSecurityLevel())) { LOGE("CdmEngine::GetUsageInfo: device file init error"); return GET_USAGE_INFO_ERROR_1; @@ -763,7 +822,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, &license_response)) { usage_property_set_->set_security_level(kLevel3); usage_property_set_->set_app_id(app_id); - usage_session_.reset(new CdmSession(EMPTY_ORIGIN)); + usage_session_.reset(new CdmSession(file_system_)); status = usage_session_->Init(usage_property_set_.get()); if (NO_ERROR != status) { LOGE("CdmEngine::GetUsageInfo: session init error"); @@ -808,6 +867,10 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, // Return a random usage report from a random security level SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3; CdmResponseType status = UNKNOWN_ERROR; + if (!usage_info) { + LOGE("CdmEngine::GetUsageInfo: no usage info destination"); + return INVALID_PARAMETERS_ENG_9; + } do { status = GetUsageInfo(app_id, security_level, usage_info); @@ -833,7 +896,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, usage_property_set_->set_security_level(requested_security_level); usage_property_set_->set_app_id(app_id); - usage_session_.reset(new CdmSession(EMPTY_ORIGIN)); + usage_session_.reset(new CdmSession(file_system_)); CdmResponseType status = usage_session_->Init(usage_property_set_.get()); if (NO_ERROR != status) { @@ -841,7 +904,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, return status; } - DeviceFiles handle; + DeviceFiles handle(file_system_); if (!handle.Init(usage_session_->GetSecurityLevel())) { LOGE("CdmEngine::GetUsageInfo: unable to initialize device files"); return GET_USAGE_INFO_ERROR_3; @@ -853,12 +916,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, return GET_USAGE_INFO_ERROR_4; } + if (!usage_info) { + LOGE("CdmEngine::GetUsageInfo: no usage info destination"); + return INVALID_PARAMETERS_ENG_10; + } if (0 == license_info.size()) { usage_info->resize(0); return NO_ERROR; } - std::string server_url; usage_info->resize(kUsageReportsPerRequest); uint32_t index = rand() % license_info.size(); @@ -901,7 +967,7 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) { CdmResponseType status = NO_ERROR; for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) { - DeviceFiles handle; + DeviceFiles handle(file_system_); if (handle.Init(static_cast(j))) { std::vector provider_session_tokens; if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) { @@ -914,7 +980,7 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) { ? kLevel3 : kLevelDefault; usage_property_set_->set_security_level(security_level); - usage_session_.reset(new CdmSession(EMPTY_ORIGIN)); + usage_session_.reset(new CdmSession(file_system_)); usage_session_->Init(usage_property_set_.get()); CdmResponseType status2 = usage_session_-> DeleteMultipleUsageInformation(provider_session_tokens); @@ -966,7 +1032,12 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id, return SESSION_NOT_FOUND_11; } - DeviceFiles handle; + if (!release_message) { + LOGE("CdmEngine::LoadUsageSession: no release message destination"); + return INVALID_PARAMETERS_ENG_11; + } + + DeviceFiles handle(file_system_); if (!handle.Init(iter->second->GetSecurityLevel())) { LOGE("CdmEngine::LoadUsageSession: unable to initialize device files"); return LOAD_USAGE_INFO_FILE_ERROR; @@ -1061,6 +1132,61 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id, return session_iter->second->Decrypt(parameters); } +CdmResponseType CdmEngine::GenericEncrypt( + const std::string& session_id, const std::string& in_buffer, + const std::string& key_id, const std::string& iv, + CdmEncryptionAlgorithm algorithm, std::string* out_buffer) { + CdmSessionMap::iterator iter = sessions_.find(session_id); + if (iter == sessions_.end()) { + LOGE("CdmEngine::GenericEncrypt: session_id not found = %s ", + session_id.c_str()); + return SESSION_NOT_FOUND_13; + } + return iter->second->GenericEncrypt(in_buffer, key_id, iv, algorithm, + out_buffer); +} + +CdmResponseType CdmEngine::GenericDecrypt( + const std::string& session_id, const std::string& in_buffer, + const std::string& key_id, const std::string& iv, + CdmEncryptionAlgorithm algorithm, + std::string* out_buffer) { + CdmSessionMap::iterator iter = sessions_.find(session_id); + if (iter == sessions_.end()) { + LOGE("CdmEngine::GenericDecrypt: session_id not found = %s ", + session_id.c_str()); + return SESSION_NOT_FOUND_14; + } + return iter->second->GenericDecrypt(in_buffer, key_id, iv, algorithm, + out_buffer); +} + +CdmResponseType CdmEngine::GenericSign( + const std::string& session_id, const std::string& message, + const std::string& key_id, CdmSigningAlgorithm algorithm, + std::string* signature) { + CdmSessionMap::iterator iter = sessions_.find(session_id); + if (iter == sessions_.end()) { + LOGE("CdmEngine::GenericSign: session_id not found = %s ", + session_id.c_str()); + return SESSION_NOT_FOUND_15; + } + return iter->second->GenericSign(message, key_id, algorithm, signature); +} + +CdmResponseType CdmEngine::GenericVerify( + const std::string& session_id, const std::string& message, + const std::string& key_id, CdmSigningAlgorithm algorithm, + const std::string& signature) { + CdmSessionMap::iterator iter = sessions_.find(session_id); + if (iter == sessions_.end()) { + LOGE("CdmEngine::GenericVerify: session_id not found = %s ", + session_id.c_str()); + return SESSION_NOT_FOUND_16; + } + return iter->second->GenericVerify(message, key_id, algorithm, signature); +} + bool CdmEngine::IsKeyLoaded(const KeyId& key_id) { for (CdmSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { @@ -1083,7 +1209,8 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id, CdmSessionMap::iterator session_iter = sessions_.end(); int64_t seconds_remaining = 0; - for (CdmSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { + for (CdmSessionMap::iterator iter = sessions_.begin(); + iter != sessions_.end(); ++iter) { CdmSessionId local_session_id = iter->second->session_id(); if (Properties::GetSessionSharingId(local_session_id) == session_sharing_id) { @@ -1196,9 +1323,8 @@ void CdmEngine::DeleteAllUsageReportsUponFactoryReset() { Properties::GetDeviceFilesBasePath(kSecurityLevelL3, &device_base_path_level3); - File file; - if (!file.Exists(device_base_path_level1) && - !file.Exists(device_base_path_level3)) { + if (!file_system_->Exists(device_base_path_level1) && + !file_system_->Exists(device_base_path_level3)) { scoped_ptr crypto_session(new CryptoSession()); CdmResponseType status = crypto_session->Open( cert_provisioning_requested_security_level_); diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index bcd2663f..6d3b8b80 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -10,8 +10,6 @@ #include "cdm_engine.h" #include "clock.h" -#include "crypto_session.h" -#include "device_files.h" #include "file_store.h" #include "log.h" #include "properties.h" @@ -25,6 +23,23 @@ const size_t kKeySetIdLength = 14; namespace wvcdm { +CdmSession::CdmSession(FileSystem* file_system) : + initialized_(false), + crypto_session_(new CryptoSession), + file_handle_(new DeviceFiles(file_system)), + license_received_(false), + is_offline_(false), + is_release_(false), + is_temporary_(false), + security_level_(kSecurityLevelUninitialized), + requested_security_level_(kLevelDefault), + is_initial_decryption_(true), + has_decrypted_since_last_report_(false), + is_initial_usage_update_(true), + is_usage_update_needed_(false), + mock_license_parser_in_use_(false), + mock_policy_engine_in_use_(false) {} + CdmSession::~CdmSession() { if (!key_set_id_.empty()) { // Unreserve the license ID. @@ -45,6 +60,7 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set, LOGE("CdmSession::Init: Failed due to previous initialization"); return SESSION_INIT_ERROR_2; } + if (cdm_client_property_set && cdm_client_property_set->security_level() == QUERY_VALUE_SECURITY_LEVEL_L3) { @@ -63,7 +79,7 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set, std::string token; if (Properties::use_certificates_as_identification()) { std::string wrapped_key; - if (!file_handle_->RetrieveCertificate(origin_, &token, &wrapped_key) || + if (!file_handle_->RetrieveCertificate(&token, &wrapped_key) || !crypto_session_->LoadCertificatePrivateKey(wrapped_key)) { return NEED_PROVISIONING; } @@ -301,7 +317,7 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { } } -CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) { +CdmResponseType CdmSession::QueryStatus(CdmQueryMap* query_response) { if (crypto_session_.get() == NULL) { LOGE("CdmSession::QueryStatus: Invalid crypto session"); return INVALID_CRYPTO_SESSION_3; @@ -314,17 +330,20 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) { switch (security_level_) { case kSecurityLevelL1: - (*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1; + (*query_response)[QUERY_KEY_SECURITY_LEVEL] = + QUERY_VALUE_SECURITY_LEVEL_L1; break; case kSecurityLevelL2: - (*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L2; + (*query_response)[QUERY_KEY_SECURITY_LEVEL] = + QUERY_VALUE_SECURITY_LEVEL_L2; break; case kSecurityLevelL3: - (*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3; + (*query_response)[QUERY_KEY_SECURITY_LEVEL] = + QUERY_VALUE_SECURITY_LEVEL_L3; break; case kSecurityLevelUninitialized: case kSecurityLevelUnknown: - (*key_info)[QUERY_KEY_SECURITY_LEVEL] = + (*query_response)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN; break; default: @@ -333,24 +352,30 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) { return NO_ERROR; } -CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) { - return policy_engine_->Query(key_info); +CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* query_response) { + return policy_engine_->Query(query_response); } -CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) { +CdmResponseType CdmSession::QueryKeyAllowedUsage( + const std::string& key_id, CdmKeyAllowedUsage* key_usage) { + return policy_engine_->QueryKeyAllowedUsage(key_id, key_usage); +} + +CdmResponseType CdmSession::QueryOemCryptoSessionId( + CdmQueryMap* query_response) { if (crypto_session_.get() == NULL) { - LOGW("CdmSession::QueryKeyControlInfo: Invalid crypto session"); + LOGW("CdmSession::QueryOemCryptoSessionId: Invalid crypto session"); return INVALID_CRYPTO_SESSION_4; } if (!crypto_session_->IsOpen()) { - LOGW("CdmSession::QueryKeyControlInfo: Crypto session not open"); + LOGW("CdmSession::QueryOemCryptoSessionId: Crypto session not open"); return CRYPTO_SESSION_OPEN_ERROR_4; } std::stringstream ss; ss << crypto_session_->oec_session_id(); - (*key_info)[QUERY_KEY_OEMCRYPTO_SESSION_ID] = ss.str(); + (*query_response)[QUERY_KEY_OEMCRYPTO_SESSION_ID] = ss.str(); return NO_ERROR; } @@ -545,6 +570,11 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) { policy_engine_->GetLastPlaybackTime(), app_parameters_); } +CdmResponseType CdmSession::ReleaseCrypto() { + crypto_session_->Close(); + return NO_ERROR; +} + bool CdmSession::DeleteLicense() { if (!is_offline_ && license_parser_->provider_session_token().empty()) return false; @@ -596,11 +626,52 @@ CdmResponseType CdmSession::UpdateUsageInformation() { return crypto_session_->UpdateUsageInformation(); } -CdmResponseType CdmSession::ReleaseCrypto() { - crypto_session_->Close(); - return NO_ERROR; +CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer, + const std::string& key_id, + const std::string& iv, + CdmEncryptionAlgorithm algorithm, + std::string* out_buffer) { + if (!out_buffer) { + LOGE("CdmSession::GenericEncrypt: No output destination provided"); + return INVALID_PARAMETERS_ENG_6; + } + return crypto_session_->GenericEncrypt(in_buffer, key_id, iv, algorithm, + out_buffer); } +CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer, + const std::string& key_id, + const std::string& iv, + CdmEncryptionAlgorithm algorithm, + std::string* out_buffer) { + if (!out_buffer) { + LOGE("CdmSession::GenericDecrypt: No output destination provided"); + return INVALID_PARAMETERS_ENG_7; + } + return crypto_session_->GenericDecrypt(in_buffer, key_id, iv, algorithm, + out_buffer); +} + +CdmResponseType CdmSession::GenericSign(const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + std::string* signature) { + if (!signature) { + LOGE("CdmSession::GenericSign: No output destination provided"); + return INVALID_PARAMETERS_ENG_8; + } + return crypto_session_->GenericSign(message, key_id, algorithm, signature); +} + +CdmResponseType CdmSession::GenericVerify(const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + const std::string& signature) { + return crypto_session_->GenericVerify(message, key_id, algorithm, signature); +} + +// For testing only - takes ownership of pointers + void CdmSession::set_license_parser(CdmLicense* license_parser) { license_parser_.reset(license_parser); mock_license_parser_in_use_ = true; diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index 61c27077..a6b2c10a 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -189,7 +189,7 @@ bool CertificateProvisioning::ParseJsonResponse( * Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails. */ CdmResponseType CertificateProvisioning::HandleProvisioningResponse( - const std::string& origin, const CdmProvisioningResponse& response, + FileSystem* file_system, const CdmProvisioningResponse& response, std::string* cert, std::string* wrapped_key) { // Extracts signed response from JSON string, decodes base64 signed response const std::string kMessageStart = "\"signedResponse\": \""; @@ -259,12 +259,12 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse( const std::string& device_certificate = provisioning_response.device_certificate(); - DeviceFiles handle; + DeviceFiles handle(file_system); if (!handle.Init(crypto_session_.GetSecurityLevel())) { LOGE("HandleProvisioningResponse: failed to init DeviceFiles"); return CERT_PROVISIONING_RESPONSE_ERROR_7; } - if (!handle.StoreCertificate(origin, device_certificate, wrapped_rsa_key)) { + if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) { LOGE("HandleProvisioningResponse: failed to save provisioning certificate"); return CERT_PROVISIONING_RESPONSE_ERROR_8; } diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index d17dab5a..2d20fb89 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -56,17 +56,19 @@ void CryptoSession::Init() { LOGV("CryptoSession::Init"); AutoLock auto_lock(crypto_lock_); session_count_ += 1; - if (initialized_) return; - OEMCryptoResult sts = OEMCrypto_Initialize(); - if (OEMCrypto_SUCCESS != sts) { - LOGE("OEMCrypto_Initialize failed: %d", sts); - return; + if (!initialized_) { + OEMCryptoResult sts = OEMCrypto_Initialize(); + if (OEMCrypto_SUCCESS != sts) { + LOGE("OEMCrypto_Initialize failed: %d", sts); + return; + } + initialized_ = true; } - initialized_ = true; } void CryptoSession::Terminate() { - LOGV("CryptoSession::Terminate"); + LOGE("CryptoSession::Terminate: initialized_=%d, session_count_=%d", + initialized_, session_count_); AutoLock auto_lock(crypto_lock_); if (session_count_ > 0) { session_count_ -= 1; @@ -252,16 +254,15 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_); open_ = true; } else if (OEMCrypto_ERROR_TOO_MANY_SESSIONS == sts) { - LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts, - session_count_, (int)initialized_); + LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", + sts, session_count_, (int)initialized_); return INSUFFICIENT_CRYPTO_RESOURCES; } if (!open_) { - LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts, - session_count_, (int)initialized_); + LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", + sts, session_count_, (int)initialized_); return UNKNOWN_ERROR; } - OEMCrypto_GetRandom(reinterpret_cast(&request_id_base_), sizeof(request_id_base_)); ++request_id_index_; @@ -507,12 +508,22 @@ bool CryptoSession::RefreshKeys(const std::string& message, } bool CryptoSession::SelectKey(const std::string& key_id) { + // Crypto session lock already locked. + if (!cached_key_id_.empty() && cached_key_id_ == key_id) { + // Already using the desired key. + return true; + } + + cached_key_id_ = key_id; + const uint8_t* key_id_string = - reinterpret_cast(key_id.data()); + reinterpret_cast(cached_key_id_.data()); OEMCryptoResult sts = - OEMCrypto_SelectKey(oec_session_id_, key_id_string, key_id.size()); + OEMCrypto_SelectKey(oec_session_id_, key_id_string, + cached_key_id_.size()); if (OEMCrypto_SUCCESS != sts) { + cached_key_id_.clear(); return false; } return true; @@ -567,78 +578,76 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message, bool CryptoSession::GenerateSignature(const std::string& message, std::string* signature) { LOGV("GenerateSignature: id=%ld", (uint32_t)oec_session_id_); - if (!signature) return false; + if (!signature) { + LOGE("GenerateSignature: null signature string"); + return false; + } + OEMCryptoResult sts; 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) { - LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); - return false; - } - - // Retry with proper-sized signature buffer - signature->resize(length); + // At most two attempts. + // The first attempt may fail due to buffer too short + for (int i = 0; i < 2; ++i) { sts = OEMCrypto_GenerateSignature( oec_session_id_, reinterpret_cast(message.data()), message.size(), reinterpret_cast(const_cast(signature->data())), &length); - if (OEMCrypto_SUCCESS != sts) { - LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); - return false; + if (OEMCrypto_SUCCESS == sts) { + // Trim signature buffer and done + signature->resize(length); + return true; } + if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { + break; + } + + // Retry with proper-sized signature buffer + signature->resize(length); } - // Trim signature buffer - signature->resize(length); - - return true; + LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); + return false; } bool CryptoSession::GenerateRsaSignature(const std::string& message, std::string* signature) { LOGV("GenerateRsaSignature: id=%ld", (uint32_t)oec_session_id_); - if (!signature) return false; + if (!signature) { + LOGE("GenerateRsaSignature: null signature string"); + return false; + } + OEMCryptoResult sts; signature->resize(kRsaSignatureLength); 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) { - if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { - LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts); - return false; - } - - // Retry with proper-sized signature buffer - signature->resize(length); + // At most two attempts. + // The first attempt may fail due to buffer too short + for (int i = 0; i < 2; ++i) { 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) { - LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts); - return false; + if (OEMCrypto_SUCCESS == sts) { + // Trim signature buffer and done + signature->resize(length); + return true; } + if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { + break; + } + + // Retry with proper-sized signature buffer + signature->resize(length); } - // Trim signature buffer - signature->resize(length); - - return true; + LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts); + return false; } CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { @@ -693,12 +702,8 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { AutoLock auto_lock(crypto_lock_); // Check if key needs to be selected if (params.is_encrypted) { - if (key_id_ != *params.key_id) { - if (SelectKey(*params.key_id)) { - key_id_ = *params.key_id; - } else { - return NEED_KEY; - } + if (!SelectKey(*params.key_id)) { + return NEED_KEY; } } sts = OEMCrypto_DecryptCENC( @@ -1101,4 +1106,200 @@ bool CryptoSession::GetMaxNumberOfSessions(size_t* max) { return true; } +CdmResponseType CryptoSession::GenericEncrypt(const std::string& in_buffer, + const std::string& key_id, + const std::string& iv, + CdmEncryptionAlgorithm algorithm, + std::string* out_buffer) { + LOGV("GenericEncrypt: id=%ld", (uint32_t)oec_session_id_); + if (!out_buffer) return INVALID_PARAMETERS_ENG_9; + + OEMCrypto_Algorithm oec_algorithm = GenericEncryptionAlgorithm(algorithm); + if (iv.size() != GenericEncryptionBlockSize(algorithm) || + oec_algorithm == kInvalidAlgorithm) { + return INVALID_PARAMETERS_ENG_13; + } + + if (out_buffer->size() < in_buffer.size()) { + out_buffer->resize(in_buffer.size()); + } + + AutoLock auto_lock(crypto_lock_); + if (!SelectKey(key_id)) { + return KEY_ERROR_1; + } + + OEMCryptoResult sts = OEMCrypto_Generic_Encrypt( + oec_session_id_, reinterpret_cast(in_buffer.data()), + in_buffer.size(), reinterpret_cast(iv.data()), + oec_algorithm, + reinterpret_cast(const_cast(out_buffer->data()))); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenericEncrypt: OEMCrypto_Generic_Encrypt err=%d", sts); + if (OEMCrypto_ERROR_KEY_EXPIRED == sts || + OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { + return KEY_NOT_FOUND_3; + } else { + return UNKNOWN_ERROR; + } + } + return NO_ERROR; +} + +CdmResponseType CryptoSession::GenericDecrypt(const std::string& in_buffer, + const std::string& key_id, + const std::string& iv, + CdmEncryptionAlgorithm algorithm, + std::string* out_buffer) { + LOGV("GenericDecrypt: id=%ld", (uint32_t)oec_session_id_); + if (!out_buffer) return INVALID_PARAMETERS_ENG_10; + + OEMCrypto_Algorithm oec_algorithm = GenericEncryptionAlgorithm(algorithm); + if (iv.size() != GenericEncryptionBlockSize(algorithm) || + oec_algorithm == kInvalidAlgorithm) { + return INVALID_PARAMETERS_ENG_14; + } + + if (out_buffer->size() < in_buffer.size()) { + out_buffer->resize(in_buffer.size()); + } + + AutoLock auto_lock(crypto_lock_); + if (!SelectKey(key_id)) { + return KEY_ERROR_2; + } + + OEMCryptoResult sts = OEMCrypto_Generic_Decrypt( + oec_session_id_, reinterpret_cast(in_buffer.data()), + in_buffer.size(), reinterpret_cast(iv.data()), + oec_algorithm, + reinterpret_cast(const_cast(out_buffer->data()))); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenericDecrypt: OEMCrypto_Generic_Decrypt err=%d", sts); + if (OEMCrypto_ERROR_KEY_EXPIRED == sts || + OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { + return KEY_NOT_FOUND_4; + } else { + return UNKNOWN_ERROR; + } + } + return NO_ERROR; +} + +CdmResponseType CryptoSession::GenericSign(const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + std::string* signature) { + LOGV("GenericSign: id=%ld", (uint32_t)oec_session_id_); + if (!signature) { + LOGE("GenerateSign: null signature string"); + return INVALID_PARAMETERS_ENG_11; + } + + OEMCrypto_Algorithm oec_algorithm = GenericSigningAlgorithm(algorithm); + if (oec_algorithm == kInvalidAlgorithm) { + return INVALID_PARAMETERS_ENG_15; + } + + OEMCryptoResult sts; + size_t length = signature->size(); + + AutoLock auto_lock(crypto_lock_); + if (!SelectKey(key_id)) { + return KEY_ERROR_3; + } + + // At most two attempts. + // The first attempt may fail due to buffer too short + for (int i = 0; i < 2; ++i) { + sts = OEMCrypto_Generic_Sign( + oec_session_id_, reinterpret_cast(message.data()), + message.size(), oec_algorithm, + reinterpret_cast(const_cast(signature->data())), + &length); + + if (OEMCrypto_SUCCESS == sts) { + // Trim signature buffer and done + signature->resize(length); + return NO_ERROR; + } + if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { + break; + } + + // Retry with proper-sized return buffer + signature->resize(length); + } + + LOGE("GenericSign: OEMCrypto_Generic_Sign err=%d", sts); + if (OEMCrypto_ERROR_KEY_EXPIRED == sts || + OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { + return KEY_NOT_FOUND_5; + } else { + return UNKNOWN_ERROR; + } +} + +CdmResponseType CryptoSession::GenericVerify(const std::string& message, + const std::string& key_id, + CdmSigningAlgorithm algorithm, + const std::string& signature) { + LOGV("GenericVerify: id=%ld", (uint32_t)oec_session_id_); + + OEMCrypto_Algorithm oec_algorithm = GenericSigningAlgorithm(algorithm); + if (oec_algorithm == kInvalidAlgorithm) { + return INVALID_PARAMETERS_ENG_16; + } + + AutoLock auto_lock(crypto_lock_); + if (!SelectKey(key_id)) { + return KEY_ERROR_4; + } + + OEMCryptoResult sts = OEMCrypto_Generic_Verify( + oec_session_id_, reinterpret_cast(message.data()), + message.size(), oec_algorithm, + reinterpret_cast(signature.data()), signature.size()); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenericVerify: OEMCrypto_Generic_Verify err=%d", sts); + if (OEMCrypto_ERROR_KEY_EXPIRED == sts || + OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { + return KEY_NOT_FOUND_6; + } else { + return UNKNOWN_ERROR; + } + } + return NO_ERROR; +} + +OEMCrypto_Algorithm CryptoSession::GenericSigningAlgorithm( + CdmSigningAlgorithm algorithm) { + if (kSigningAlgorithmHmacSha256 == algorithm) { + return OEMCrypto_HMAC_SHA256; + } else { + return kInvalidAlgorithm; + } +} + +OEMCrypto_Algorithm CryptoSession::GenericEncryptionAlgorithm( + CdmEncryptionAlgorithm algorithm) { + if (kEncryptionAlgorithmAesCbc128 == algorithm) { + return OEMCrypto_AES_CBC_128_NO_PADDING; + } else { + return kInvalidAlgorithm; + } +} + +size_t CryptoSession::GenericEncryptionBlockSize( + CdmEncryptionAlgorithm algorithm) { + if (kEncryptionAlgorithmAesCbc128 == algorithm) { + return kAes128BlockSize; + } else { + return 0; + } +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index b2bdb210..e08d8666 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -34,19 +34,13 @@ using video_widevine_client::sdk::UsageInfo_ProviderSession; namespace { -const char kCertificateFileNamePrefix[] = "cert"; -const char kCertificateFileNameExt[] = ".bin"; +const char kCertificateFileName[] = "cert.bin"; +const char kHlsAttributesFileNameExt[] = ".hal"; const char kUsageInfoFileNamePrefix[] = "usage"; const char kUsageInfoFileNameExt[] = ".bin"; const char kLicenseFileNameExt[] = ".lic"; const char kEmptyFileName[] = ""; const char kWildcard[] = "*"; -const char kDirectoryDelimiter = '/'; -const char* kSecurityLevelPathCompatibilityExclusionList[] = { - "ay64.dat", "ay64.dat2", "ay64.dat3"}; -size_t kSecurityLevelPathCompatibilityExclusionListSize = - sizeof(kSecurityLevelPathCompatibilityExclusionList) / - sizeof(*kSecurityLevelPathCompatibilityExclusionList); bool Hash(const std::string& data, std::string* hash) { if (!hash) return false; @@ -66,30 +60,30 @@ namespace wvcdm { // static std::set DeviceFiles::reserved_license_ids_; -DeviceFiles::DeviceFiles() - : file_(NULL), +DeviceFiles::DeviceFiles(FileSystem* file_system) + : file_system_(file_system), security_level_(kSecurityLevelUninitialized), - initialized_(false), - test_file_(false) {} + initialized_(false) {} -DeviceFiles::~DeviceFiles() { - if (test_file_) file_.release(); -} +DeviceFiles::~DeviceFiles() {} bool DeviceFiles::Init(CdmSecurityLevel security_level) { + if (!file_system_) { + LOGD("DeviceFiles::Init: Invalid FileSystem given."); + return false; + } + std::string path; if (!Properties::GetDeviceFilesBasePath(security_level, &path)) { LOGW("DeviceFiles::Init: Unsupported security level %d", security_level); return false; } - if (!test_file_) file_.reset(new File()); security_level_ = security_level; initialized_ = true; return true; } -bool DeviceFiles::StoreCertificate(const std::string& origin, - const std::string& certificate, +bool DeviceFiles::StoreCertificate(const std::string& certificate, const std::string& wrapped_private_key) { if (!initialized_) { LOGW("DeviceFiles::StoreCertificate: not initialized"); @@ -109,23 +103,18 @@ bool DeviceFiles::StoreCertificate(const std::string& origin, std::string serialized_file; file.SerializeToString(&serialized_file); - return StoreFileWithHash(GetCertificateFileName(origin), serialized_file); + return StoreFileWithHash(GetCertificateFileName(), serialized_file); } -bool DeviceFiles::RetrieveCertificate(const std::string& origin, - std::string* certificate, +bool DeviceFiles::RetrieveCertificate(std::string* certificate, std::string* wrapped_private_key) { if (!initialized_) { LOGW("DeviceFiles::RetrieveCertificate: not initialized"); return false; } - if (Properties::security_level_path_backward_compatibility_support()) { - SecurityLevelPathBackwardCompatibility(); - } - video_widevine_client::sdk::File file; - if (!RetrieveHashedFile(GetCertificateFileName(origin), &file)) { + if (!RetrieveHashedFile(GetCertificateFileName(), &file)) { return false; } @@ -151,22 +140,22 @@ bool DeviceFiles::RetrieveCertificate(const std::string& origin, return true; } -bool DeviceFiles::HasCertificate(const std::string& origin) { +bool DeviceFiles::HasCertificate() { if (!initialized_) { LOGW("DeviceFiles::HasCertificate: not initialized"); return false; } - return FileExists(GetCertificateFileName(origin)); + return FileExists(GetCertificateFileName()); } -bool DeviceFiles::RemoveCertificate(const std::string& origin) { +bool DeviceFiles::RemoveCertificate() { if (!initialized_) { LOGW("DeviceFiles::RemoveCertificate: not initialized"); return false; } - return RemoveFile(GetCertificateFileName(origin)); + return RemoveFile(GetCertificateFileName()); } bool DeviceFiles::StoreLicense( @@ -536,11 +525,6 @@ bool DeviceFiles::RetrieveUsageInfoByKeySetId( bool DeviceFiles::StoreFileWithHash(const std::string& name, const std::string& serialized_file) { - if (!file_.get()) { - LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle"); - return false; - } - // calculate SHA hash std::string hash; if (!Hash(serialized_file, &hash)) { @@ -561,30 +545,23 @@ bool DeviceFiles::StoreFileWithHash(const std::string& name, bool DeviceFiles::StoreFileRaw(const std::string& name, const std::string& serialized_file) { - if (!file_.get()) { - LOGW("DeviceFiles::StoreFileRaw: Invalid file handle"); - return false; - } - std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { LOGW("DeviceFiles::StoreFileRaw: Unable to get base path"); return false; } - if (!file_->IsDirectory(path)) { - if (!file_->CreateDirectory(path)) return false; - } - path += name; - if (!file_->Open(path, File::kCreate | File::kTruncate | File::kBinary)) { + File* file = + file_system_->Open(path, FileSystem::kCreate | FileSystem::kTruncate); + if (!file) { LOGW("DeviceFiles::StoreFileRaw: File open failed: %s", path.c_str()); return false; } - ssize_t bytes = file_->Write(serialized_file.data(), serialized_file.size()); - file_->Close(); + ssize_t bytes = file->Write(serialized_file.data(), serialized_file.size()); + file->Close(); if (bytes != static_cast(serialized_file.size())) { LOGW( @@ -599,16 +576,12 @@ bool DeviceFiles::StoreFileRaw(const std::string& name, return true; } -bool DeviceFiles::RetrieveHashedFile(const std::string& name, - video_widevine_client::sdk::File* file) { +bool DeviceFiles::RetrieveHashedFile( + const std::string& name, + video_widevine_client::sdk::File* deserialized_file) { std::string serialized_file; - if (!file_.get()) { - LOGW("DeviceFiles::RetrieveHashedFile: Invalid file handle"); - return false; - } - - if (!file) { + if (!deserialized_file) { LOGW("DeviceFiles::RetrieveHashedFile: Unspecified file parameter"); return false; } @@ -621,29 +594,30 @@ bool DeviceFiles::RetrieveHashedFile(const std::string& name, path += name; - if (!file_->Exists(path)) { + if (!file_system_->Exists(path)) { LOGW("DeviceFiles::RetrieveHashedFile: %s does not exist", path.c_str()); return false; } - ssize_t bytes = file_->FileSize(path); + ssize_t bytes = file_system_->FileSize(path); if (bytes <= 0) { LOGW("DeviceFiles::RetrieveHashedFile: File size invalid: %s", path.c_str()); // Remove the corrupted file so the caller will not get the same error // when trying to access the file repeatedly, causing the system to stall. - file_->Remove(path); + file_system_->Remove(path); return false; } - if (!file_->Open(path, File::kReadOnly | File::kBinary)) { + File* file = file_system_->Open(path, FileSystem::kReadOnly); + if (!file) { return false; } std::string serialized_hash_file; serialized_hash_file.resize(bytes); - bytes = file_->Read(&serialized_hash_file[0], serialized_hash_file.size()); - file_->Close(); + bytes = file->Read(&serialized_hash_file[0], serialized_hash_file.size()); + file->Close(); if (bytes != static_cast(serialized_hash_file.size())) { LOGW("DeviceFiles::RetrieveHashedFile: read failed"); @@ -669,11 +643,11 @@ bool DeviceFiles::RetrieveHashedFile(const std::string& name, LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch"); // Remove the corrupted file so the caller will not get the same error // when trying to access the file repeatedly, causing the system to stall. - file_->Remove(path); + file_system_->Remove(path); return false; } - if (!file->ParseFromString(hash_file.file())) { + if (!deserialized_file->ParseFromString(hash_file.file())) { LOGW("DeviceFiles::RetrieveHashedFile: Unable to parse file"); return false; } @@ -681,11 +655,6 @@ bool DeviceFiles::RetrieveHashedFile(const std::string& name, } bool DeviceFiles::FileExists(const std::string& name) { - if (!file_.get()) { - LOGW("DeviceFiles::FileExists: Invalid file handle"); - return false; - } - std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { LOGW("DeviceFiles::FileExists: Unable to get base path"); @@ -693,15 +662,10 @@ bool DeviceFiles::FileExists(const std::string& name) { } path += name; - return file_->Exists(path); + return file_system_->Exists(path); } bool DeviceFiles::RemoveFile(const std::string& name) { - if (!file_.get()) { - LOGW("DeviceFiles::RemoveFile: Invalid file handle"); - return false; - } - std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { LOGW("DeviceFiles::RemoveFile: Unable to get base path"); @@ -709,15 +673,10 @@ bool DeviceFiles::RemoveFile(const std::string& name) { } path += name; - return file_->Remove(path); + return file_system_->Remove(path); } ssize_t DeviceFiles::GetFileSize(const std::string& name) { - if (!file_.get()) { - LOGW("DeviceFiles::GetFileSize: Invalid file handle"); - return -1; - } - std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { LOGW("DeviceFiles::GetFileSize: Unable to get base path"); @@ -725,79 +684,11 @@ ssize_t DeviceFiles::GetFileSize(const std::string& name) { } path += name; - return file_->FileSize(path); + return file_system_->FileSize(path); } -void DeviceFiles::SecurityLevelPathBackwardCompatibility() { - std::string path; - if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { - LOGW( - "DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to " - "get base path"); - return; - } - - std::vector security_dirs; - if (!Properties::GetSecurityLevelDirectories(&security_dirs)) { - LOGW( - "DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to " - "get security directories"); - return; - } - - size_t pos = std::string::npos; - for (size_t i = 0; i < security_dirs.size(); ++i) { - pos = path.find(security_dirs[i]); - if (pos != std::string::npos && pos > 0 && - pos == path.size() - security_dirs[i].size() && - path[pos - 1] == kDirectoryDelimiter) { - break; - } - } - - if (pos == std::string::npos) { - LOGV( - "DeviceFiles::SecurityLevelPathBackwardCompatibility: Security level " - "specific path not found. Check properties?"); - return; - } - - std::string from_dir(path, 0, pos); - - std::vector files; - if (!file_->List(from_dir, &files)) { - return; - } - - for (size_t i = 0; i < files.size(); ++i) { - std::string from = from_dir + files[i]; - bool exclude = false; - for (size_t j = 0; j < kSecurityLevelPathCompatibilityExclusionListSize; - ++j) { - if (files[i] == kSecurityLevelPathCompatibilityExclusionList[j]) { - exclude = true; - break; - } - } - if (exclude) continue; - if (!file_->IsRegularFile(from)) continue; - - for (size_t j = 0; j < security_dirs.size(); ++j) { - std::string to_dir = from_dir + security_dirs[j]; - if (!file_->Exists(to_dir)) file_->CreateDirectory(to_dir); - std::string to = to_dir + files[i]; - file_->Copy(from, to); - } - file_->Remove(from); - } -} - -std::string DeviceFiles::GetCertificateFileName(const std::string& origin) { - std::string hash; - if (origin != EMPTY_ORIGIN) { - hash = GetFileNameSafeHash(origin); - } - return kCertificateFileNamePrefix + hash + kCertificateFileNameExt; +std::string DeviceFiles::GetCertificateFileName() { + return kCertificateFileName; } std::string DeviceFiles::GetLicenseFileNameExtension() { @@ -820,9 +711,4 @@ std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) { return wvcdm::Base64SafeEncode(hash); } -void DeviceFiles::SetTestFile(File* file) { - file_.reset(file); - test_file_ = true; -} - } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/license_key_status.cpp b/libwvdrmengine/cdm/core/src/license_key_status.cpp new file mode 100644 index 00000000..d467687f --- /dev/null +++ b/libwvdrmengine/cdm/core/src/license_key_status.cpp @@ -0,0 +1,285 @@ +// Copyright 2016 Google Inc. All Rights Reserved. + +#include "license_key_status.h" + +#include + +#include "log.h" + +namespace { +// License protocol aliases +typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer; +typedef KeyContainer::OutputProtection OutputProtection; +typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint; +typedef ::google::protobuf::RepeatedPtrField + ConstraintList; + +// Map the HDCP protection associated with a key in the license to +// an equivalent OEMCrypto HDCP protection level +wvcdm::CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp( + const OutputProtection::HDCP& input) { + switch (input) { + case OutputProtection::HDCP_NONE: + return HDCP_NONE; + case OutputProtection::HDCP_V1: + return HDCP_V1; + case OutputProtection::HDCP_V2: + return HDCP_V2; + case OutputProtection::HDCP_V2_1: + return HDCP_V2_1; + case OutputProtection::HDCP_V2_2: + return HDCP_V2_2; + case OutputProtection::HDCP_NO_DIGITAL_OUTPUT: + return HDCP_NO_DIGITAL_OUTPUT; + default: + LOGE("ContentKeyStatus::ProtobufHdcpToOemCryptoHdcp: " + "Unknown HDCP Level: input=%d, returning HDCP_NO_DIGITAL_OUTPUT", + input); + return HDCP_NO_DIGITAL_OUTPUT; + } +} + +// Returns the constraint from a set of constraints that matches the +// specified resolution, or null if none match +VideoResolutionConstraint* GetConstraintForRes( + uint32_t res, ConstraintList& constraints_from_key) { + typedef ConstraintList::pointer_iterator Iterator; + for (Iterator i = constraints_from_key.pointer_begin(); + i != constraints_from_key.pointer_end(); ++i) { + VideoResolutionConstraint* constraint = *i; + if (constraint->has_min_resolution_pixels() && + constraint->has_max_resolution_pixels() && + res >= constraint->min_resolution_pixels() && + res <= constraint->max_resolution_pixels()) { + return constraint; + } + } + return NULL; +} + +} // namespace + +namespace wvcdm { + +bool LicenseKeys::IsContentKey(const std::string& key_id) { + if (keys_.count(key_id) > 0) { + return keys_[key_id]->IsContentKey(); + } else { + return false; + } +} + +bool LicenseKeys::CanDecryptContent(const std::string& key_id) { + if (keys_.count(key_id) > 0) { + return keys_[key_id]->CanDecryptContent(); + } else { + return false; + } +} + +bool LicenseKeys::GetAllowedUsage(const KeyId& key_id, + CdmKeyAllowedUsage* allowed_usage) { + if (keys_.count(key_id) > 0) { + return keys_[key_id]->GetAllowedUsage(allowed_usage); + } else { + return false; + } +} + +bool LicenseKeys::ApplyStatusChange(CdmKeyStatus new_status, + bool* new_usable_keys) { + bool keys_changed = false; + bool newly_usable = false; + *new_usable_keys = false; + for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) { + bool usable; + if (it->second->ApplyStatusChange(new_status, &usable)) { + newly_usable |= usable; + keys_changed = true; + } + } + *new_usable_keys = newly_usable; + return keys_changed; +} + +void LicenseKeys::ExtractKeyStatuses(CdmKeyStatusMap* content_keys) { + for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) { + if (it->second->IsContentKey()) { + const KeyId key_id = it->first; + CdmKeyStatus key_status = it->second->GetKeyStatus(); + (*content_keys)[key_id] = key_status; + } + } +} + +bool LicenseKeys::MeetsConstraints(const KeyId& key_id) { + if (keys_.count(key_id) > 0) { + return keys_[key_id]->MeetsConstraints(); + } else { + // If a Key ID is unknown to us, we don't know of any constraints for it, + // so never block decryption. + return true; + } +} + +void LicenseKeys::ApplyConstraints( + uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) { + for (LicenseKeyStatusIterator i = keys_.begin(); i != keys_.end(); ++i) { + i->second->ApplyConstraints(new_resolution, new_hdcp_level); + } +} + +void LicenseKeys::SetFromLicense( + const video_widevine_server::sdk::License& license) { + this->Clear(); + for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) { + const KeyContainer& key = license.key(key_index); + if (key.has_id() && (key.type() == KeyContainer::CONTENT || + key.type() == KeyContainer::OPERATOR_SESSION)) { + const KeyId& key_id = key.id(); + keys_[key_id] = new LicenseKeyStatus(key); + } + } +} + +LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key) : + is_content_key_(false), + key_status_(kKeyStatusInternalError), + meets_constraints_(true), + default_hdcp_level_(HDCP_NONE) { + + allowed_usage_.Clear(); + constraints_.Clear(); + + if (key.type() == KeyContainer::CONTENT) { + ParseContentKey(key); + } else if (key.type() == KeyContainer::OPERATOR_SESSION) { + ParseOperatorSessionKey(key); + } +} + +void LicenseKeyStatus::ParseContentKey(const KeyContainer& key) { + is_content_key_ = true; + if (key.has_level() && + ((key.level() == KeyContainer::HW_SECURE_DECODE) || + (key.level() == KeyContainer::HW_SECURE_ALL))) { + allowed_usage_.decrypt_to_clear_buffer = false; + allowed_usage_.decrypt_to_secure_buffer = true; + } else { + allowed_usage_.decrypt_to_clear_buffer = true; + allowed_usage_.decrypt_to_secure_buffer = true; + } + allowed_usage_.SetValid(); + + if (key.video_resolution_constraints_size() > 0) { + SetConstraints(key.video_resolution_constraints()); + } + + if (key.has_required_protection()) { + default_hdcp_level_ = + ProtobufHdcpToOemCryptoHdcp(key.required_protection().hdcp()); + } +} + +void LicenseKeyStatus::ParseOperatorSessionKey(const KeyContainer& key) { + is_content_key_ = false; + if (key.has_operator_session_key_permissions()) { + OperatorSessionKeyPermissions permissions = + key.operator_session_key_permissions(); + if (permissions.has_allow_encrypt()) + allowed_usage_.generic_encrypt = permissions.allow_encrypt(); + if (permissions.has_allow_decrypt()) + allowed_usage_.generic_decrypt = permissions.allow_decrypt(); + if (permissions.has_allow_sign()) + allowed_usage_.generic_sign = permissions.allow_sign(); + if (permissions.has_allow_signature_verify()) + allowed_usage_.generic_verify = permissions.allow_signature_verify(); + } else { + allowed_usage_.generic_encrypt = false; + allowed_usage_.generic_decrypt = false; + allowed_usage_.generic_sign = false; + allowed_usage_.generic_verify = false; + } + allowed_usage_.SetValid(); +} + +void LicenseKeys::Clear() { + for (LicenseKeyStatusIterator i = keys_.begin(); i != keys_.end(); ++i) { + delete i->second; + } + keys_.clear(); +} + +bool LicenseKeyStatus::CanDecryptContent() { + return is_content_key_ && key_status_ == kKeyStatusUsable; +} + +bool LicenseKeyStatus::GetAllowedUsage(CdmKeyAllowedUsage* allowed_usage) { + if (NULL == allowed_usage) + return false; + *allowed_usage = allowed_usage_; + return true; +} + +bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status, + bool* new_usable_key) { + *new_usable_key = false; + if (!is_content_key_) { + return false; + } + CdmKeyStatus updated_status = new_status; + if (updated_status == kKeyStatusUsable) { + if (!MeetsConstraints()) { + updated_status = kKeyStatusOutputNotAllowed; + } + } + if (key_status_ != updated_status) { + key_status_ = updated_status; + if (updated_status == kKeyStatusUsable) { + *new_usable_key = true; + } + return true; + } + return false; +} + +// If the key has constraints, find the constraint that applies. +// If none found, then the constraint test fails. +// If a constraint is found, verify that the device's current HDCP +// level is sufficient. If the constraint has an HDCP setting, use it, +// If the key has no constraints, or if the constraint has no HDCP +// requirement, use the key's default HDCP setting to check against the +// device's current HDCP level. +void LicenseKeyStatus::ApplyConstraints( + uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) { + + VideoResolutionConstraint* current_constraint = NULL; + if (HasConstraints()) { + current_constraint = GetConstraintForRes(new_resolution, constraints_); + if (NULL == current_constraint) { + meets_constraints_ = false; + return; + } + } + + CryptoSession::HdcpCapability desired_hdcp_level; + if (current_constraint && current_constraint->has_required_protection()) { + desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp( + current_constraint->required_protection().hdcp()); + } else { + desired_hdcp_level = default_hdcp_level_; + } + + meets_constraints_ = (new_hdcp_level >= desired_hdcp_level); +} + +void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) { + if (!is_content_key_) { + return; + } + constraints_.Clear(); + constraints_.MergeFrom(constraints); + meets_constraints_ = true; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/license_protocol.proto b/libwvdrmengine/cdm/core/src/license_protocol.proto index be398202..f411b6b6 100644 --- a/libwvdrmengine/cdm/core/src/license_protocol.proto +++ b/libwvdrmengine/cdm/core/src/license_protocol.proto @@ -461,7 +461,7 @@ message EncryptedClientIdentification { optional bytes encrypted_client_id = 3; // Initialization vector needed to decrypt encrypted_client_id. optional bytes encrypted_client_id_iv = 4; - // AES-128 privacy key, encrytped with the service public public key using + // AES-128 privacy key, encrypted with the service public public key using // RSA-OAEP. optional bytes encrypted_privacy_key = 5; } diff --git a/libwvdrmengine/cdm/core/src/max_res_engine.cpp b/libwvdrmengine/cdm/core/src/max_res_engine.cpp deleted file mode 100644 index fbc28a81..00000000 --- a/libwvdrmengine/cdm/core/src/max_res_engine.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. - -#include "max_res_engine.h" - -#include "clock.h" -#include "log.h" - -namespace { - -const int64_t kHdcpCheckInterval = 10; -const uint32_t kNoResolution = 0; - -} // namespace - -namespace wvcdm { - -MaxResEngine::MaxResEngine(CryptoSession* crypto_session) { - Init(crypto_session, new Clock()); -} - -MaxResEngine::MaxResEngine(CryptoSession* crypto_session, Clock* clock) { - Init(crypto_session, clock); -} - -MaxResEngine::~MaxResEngine() { - AutoLock lock(status_lock_); - DeleteAllKeys(); -} - -bool MaxResEngine::CanDecrypt(const KeyId& key_id) { - AutoLock lock(status_lock_); - if (keys_.count(key_id) > 0) { - return keys_[key_id]->can_decrypt(); - } else { - // If a Key ID is unknown to us, we don't know of any constraints for it, - // so never block decryption. - return true; - } -} - -void MaxResEngine::Init(CryptoSession* crypto_session, Clock* clock) { - AutoLock lock(status_lock_); - current_resolution_ = kNoResolution; - clock_.reset(clock); - next_check_time_ = clock_->GetCurrentTime(); - crypto_session_ = crypto_session; -} - -void MaxResEngine::SetLicense( - const video_widevine_server::sdk::License& license) { - AutoLock lock(status_lock_); - DeleteAllKeys(); - for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) { - const KeyContainer& key = license.key(key_index); - if (key.type() == KeyContainer::CONTENT && key.has_id() && - key.video_resolution_constraints_size() > 0) { - const ConstraintList& constraints = key.video_resolution_constraints(); - const KeyId& key_id = key.id(); - if (key.has_required_protection()) { - keys_[key_id] = - new KeyStatus(constraints, key.required_protection().hdcp()); - } else { - keys_[key_id] = new KeyStatus(constraints); - } - } - } -} - -void MaxResEngine::SetResolution(uint32_t width, uint32_t height) { - AutoLock lock(status_lock_); - current_resolution_ = width * height; -} - -void MaxResEngine::OnTimerEvent() { - AutoLock lock(status_lock_); - int64_t current_time = clock_->GetCurrentTime(); - if (!keys_.empty() && current_resolution_ != kNoResolution && - current_time >= next_check_time_) { - CryptoSession::HdcpCapability current_hdcp_level; - CryptoSession::HdcpCapability ignored; - if (!crypto_session_->GetHdcpCapabilities(¤t_hdcp_level, &ignored)) { - current_hdcp_level = HDCP_NONE; - } - for (KeyIterator i = keys_.begin(); i != keys_.end(); ++i) { - i->second->Update(current_resolution_, current_hdcp_level); - } - next_check_time_ = current_time + kHdcpCheckInterval; - } -} - -void MaxResEngine::DeleteAllKeys() { - // This helper method assumes that status_lock_ is already held. - for (KeyIterator i = keys_.begin(); i != keys_.end(); ++i) delete i->second; - keys_.clear(); -} - -MaxResEngine::KeyStatus::KeyStatus(const ConstraintList& constraints) - : default_hdcp_level_(HDCP_NONE) { - Init(constraints); -} - -MaxResEngine::KeyStatus::KeyStatus( - const ConstraintList& constraints, - const OutputProtection::HDCP& default_hdcp_level) - : default_hdcp_level_(ProtobufHdcpToOemCryptoHdcp(default_hdcp_level)) { - Init(constraints); -} - -void MaxResEngine::KeyStatus::Init(const ConstraintList& constraints) { - constraints_.Clear(); - constraints_.MergeFrom(constraints); - can_decrypt_ = true; -} - -void MaxResEngine::KeyStatus::Update( - uint32_t res, CryptoSession::HdcpCapability current_hdcp_level) { - VideoResolutionConstraint* current_constraint = GetConstraintForRes(res); - - if (current_constraint == NULL) { - can_decrypt_ = false; - return; - } - - CryptoSession::HdcpCapability desired_hdcp_level; - if (current_constraint->has_required_protection()) { - desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp( - current_constraint->required_protection().hdcp()); - } else { - desired_hdcp_level = default_hdcp_level_; - } - can_decrypt_ = (current_hdcp_level >= desired_hdcp_level); -} - -MaxResEngine::VideoResolutionConstraint* -MaxResEngine::KeyStatus::GetConstraintForRes(uint32_t res) { - typedef ConstraintList::pointer_iterator Iterator; - for (Iterator i = constraints_.pointer_begin(); - i != constraints_.pointer_end(); ++i) { - VideoResolutionConstraint* constraint = *i; - if (constraint->has_min_resolution_pixels() && - constraint->has_max_resolution_pixels() && - res >= constraint->min_resolution_pixels() && - res <= constraint->max_resolution_pixels()) { - return constraint; - } - } - return NULL; -} - -CryptoSession::HdcpCapability -MaxResEngine::KeyStatus::ProtobufHdcpToOemCryptoHdcp( - const OutputProtection::HDCP& input) { - switch (input) { - case OutputProtection::HDCP_NONE: - return HDCP_NONE; - case OutputProtection::HDCP_V1: - return HDCP_V1; - case OutputProtection::HDCP_V2: - return HDCP_V2; - case OutputProtection::HDCP_V2_1: - return HDCP_V2_1; - case OutputProtection::HDCP_V2_2: - return HDCP_V2_2; - case OutputProtection::HDCP_NO_DIGITAL_OUTPUT: - return HDCP_NO_DIGITAL_OUTPUT; - default: - LOGE("MaxResEngine::KeyStatus::ProtobufHdcpToOemCryptoHdcp: " - "Unknown HDCP Level"); - return HDCP_NO_DIGITAL_OUTPUT; - } -} - -} // wvcdm diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index 9d253201..06c716a8 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -452,21 +452,23 @@ class Adapter { } return true; } - wvcdm::File file; + wvcdm::FileSystem file_system; std::string filename; if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) { LOGW("Bad Level 1 Keybox. Falling Back to L3."); level1_.Terminate(); return false; } - ssize_t size = file.FileSize(filename); - if (size <= 0 || !file.Open(filename, file.kBinary | file.kReadOnly)) { + ssize_t size = file_system.FileSize(filename); + wvcdm::File* file = file_system.Open(filename, file_system.kReadOnly); + if (size <= 0 || !file) { LOGW("Could not open %s. Falling Back to L3.", filename.c_str()); level1_.Terminate(); return false; } uint8_t keybox[size]; - ssize_t size_read = file.Read(reinterpret_cast(keybox), size); + ssize_t size_read = file->Read(reinterpret_cast(keybox), size); + file->Close(); if (level1_.InstallKeybox(keybox, size) != OEMCrypto_SUCCESS) { LOGE("Could NOT install keybox from %s. Falling Back to L3.", filename.c_str()); @@ -625,7 +627,6 @@ namespace wvcdm { OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, SecurityLevel level) { - if (!kAdapter) return OEMCrypto_ERROR_OPEN_SESSION_FAILED; return kAdapter->OpenSession(session, level); } diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index ee24fede..6d885a21 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -3,10 +3,7 @@ #include "policy_engine.h" #include - #include -#include -#include #include "clock.h" #include "log.h" @@ -17,6 +14,13 @@ using video_widevine_server::sdk::License; +namespace { + +const int64_t kHdcpCheckInterval = 10; +const uint32_t kNoResolution = 0; + +} // namespace + namespace wvcdm { PolicyEngine::PolicyEngine(CdmSessionId session_id, @@ -32,18 +36,43 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id, policy_max_duration_seconds_(0), session_id_(session_id), event_listener_(event_listener), - max_res_engine_(new MaxResEngine(crypto_session)), - clock_(new Clock) {} + license_keys_(new LicenseKeys), + clock_(new Clock) { + InitDevice(crypto_session); +} PolicyEngine::~PolicyEngine() {} bool PolicyEngine::CanDecrypt(const KeyId& key_id) { - if (keys_status_.find(key_id) == keys_status_.end()) { + if (license_keys_->IsContentKey(key_id)) { + return license_keys_->CanDecryptContent(key_id); + } else { LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.", b2a_hex(key_id).c_str()); return false; } - return keys_status_[key_id] == kKeyStatusUsable; +} + +void PolicyEngine::InitDevice(CryptoSession* crypto_session) { + current_resolution_ = kNoResolution; + next_device_check_ = 0; + crypto_session_ = crypto_session; +} + +void PolicyEngine::CheckDevice(int64_t current_time) { + if (current_time < next_device_check_) { + return; + } + + if (!license_keys_->Empty() && current_resolution_ != kNoResolution) { + CryptoSession::HdcpCapability current_hdcp_level; + CryptoSession::HdcpCapability ignored; + if (!crypto_session_->GetHdcpCapabilities(¤t_hdcp_level, &ignored)) { + current_hdcp_level = HDCP_NONE; + } + license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level); + next_device_check_ = current_time + kHdcpCheckInterval; + } } void PolicyEngine::OnTimerEvent() { @@ -57,7 +86,8 @@ void PolicyEngine::OnTimerEvent() { return; } - max_res_engine_->OnTimerEvent(); + // Check device conditions that affect playability (HDCP, resolution) + CheckDevice(current_time); bool renewal_needed = false; @@ -110,17 +140,8 @@ void PolicyEngine::SetLicense(const License& license) { license_id_.Clear(); license_id_.CopyFrom(license.id()); policy_.Clear(); - - // Extract content key ids. - keys_status_.clear(); - for (int key_index = 0; key_index < license.key_size(); ++key_index) { - const License::KeyContainer& key = license.key(key_index); - if (key.type() == License::KeyContainer::CONTENT && key.has_id()) - keys_status_[key.id()] = kKeyStatusInternalError; - } - + license_keys_->SetFromLicense(license); UpdateLicense(license); - max_res_engine_->SetLicense(license); } void PolicyEngine::SetLicenseForRelease(const License& license) { @@ -130,7 +151,6 @@ void PolicyEngine::SetLicenseForRelease(const License& license) { // Expire any old keys. NotifyKeysChange(kKeyStatusExpired); - UpdateLicense(license); } @@ -220,7 +240,7 @@ void PolicyEngine::DecryptionEvent() { } void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) { - max_res_engine_->SetResolution(width, height); + SetDeviceResolution(width, height); } void PolicyEngine::NotifySessionExpiration() { @@ -228,35 +248,46 @@ void PolicyEngine::NotifySessionExpiration() { NotifyKeysChange(kKeyStatusExpired); } -CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) { +CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) { std::stringstream ss; int64_t current_time = clock_->GetCurrentTime(); if (license_state_ == kLicenseStateInitial) { - key_info->clear(); + query_response->clear(); return NO_ERROR; } - (*key_info)[QUERY_KEY_LICENSE_TYPE] = + (*query_response)[QUERY_KEY_LICENSE_TYPE] = license_id_.type() == video_widevine_server::sdk::STREAMING ? QUERY_VALUE_STREAMING : QUERY_VALUE_OFFLINE; - (*key_info)[QUERY_KEY_PLAY_ALLOWED] = + (*query_response)[QUERY_KEY_PLAY_ALLOWED] = policy_.can_play() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; - (*key_info)[QUERY_KEY_PERSIST_ALLOWED] = + (*query_response)[QUERY_KEY_PERSIST_ALLOWED] = policy_.can_persist() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; - (*key_info)[QUERY_KEY_RENEW_ALLOWED] = + (*query_response)[QUERY_KEY_RENEW_ALLOWED] = policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; ss << GetLicenseDurationRemaining(current_time); - (*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str(); + (*query_response)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str(); 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(); + (*query_response)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str(); + (*query_response)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url(); return NO_ERROR; } +CdmResponseType PolicyEngine::QueryKeyAllowedUsage( + const KeyId& key_id, CdmKeyAllowedUsage* key_usage) { + if (NULL == key_usage) { + return INVALID_PARAMETERS_ENG_12; + } + if (license_keys_->GetAllowedUsage(key_id, key_usage)) { + return NO_ERROR; + } + return KEY_NOT_FOUND_1; +} + bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) { if (playback_start_time_ == 0) return false; @@ -351,25 +382,14 @@ bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) { } void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) { - bool keys_changed = false; + bool keys_changed; bool has_new_usable_key = false; - for (std::map::iterator it = keys_status_.begin(); - it != keys_status_.end(); ++it) { - const KeyId key_id = it->first; - CdmKeyStatus& key_status = it->second; - CdmKeyStatus updated_status = new_status; - if (updated_status == kKeyStatusUsable) { - if (!max_res_engine_->CanDecrypt(key_id)) - updated_status = kKeyStatusOutputNotAllowed; - } - if (key_status != updated_status) { - key_status = updated_status; - if (updated_status == kKeyStatusUsable) has_new_usable_key = true; - keys_changed = true; - } - } - if (keys_changed && event_listener_) { - event_listener_->OnSessionKeysChange(session_id_, keys_status_, + keys_changed = license_keys_->ApplyStatusChange(new_status, + &has_new_usable_key); + if (event_listener_ && keys_changed) { + CdmKeyStatusMap content_keys; + license_keys_->ExtractKeyStatuses(&content_keys); + event_listener_->OnSessionKeysChange(session_id_, content_keys, has_new_usable_key); } } @@ -387,8 +407,4 @@ void PolicyEngine::NotifyExpirationUpdate() { void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); } -void PolicyEngine::set_max_res_engine(MaxResEngine* max_res_engine) { - max_res_engine_.reset(max_res_engine); -} - -} // wvcdm +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index e47217ba..aa44c262 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -4,6 +4,7 @@ // This is because we need a valid RSA certificate, and will attempt to connect // to the provisioning server to request one if we don't. +#include #include #include @@ -43,6 +44,8 @@ const std::string kWebmMimeType = "video/webm"; class WvCdmEngineTest : public testing::Test { public: + WvCdmEngineTest() : cdm_engine_(&file_system_) {} + static void SetUpTestCase() { ConfigTestEnv config(kContentProtectionUatServer); g_client_auth.assign(config.client_auth()); @@ -52,19 +55,16 @@ class WvCdmEngineTest : public testing::Test { g_key_id_pssh.assign(a2bs_hex(config.key_id())); // Extract the key ID from the PSSH box. - InitializationData extractor(CENC_INIT_DATA_FORMAT, - g_key_id_pssh); + InitializationData extractor(CENC_INIT_DATA_FORMAT, g_key_id_pssh); g_key_id_unwrapped = extractor.data(); } virtual void SetUp() { CdmResponseType status = - cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, - &session_id_); + cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_); if (status == NEED_PROVISIONING) { Provision(); - status = cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, - &session_id_); + status = cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_); } ASSERT_EQ(NO_ERROR, status); ASSERT_NE("", session_id_) << "Could not open CDM session."; @@ -81,15 +81,15 @@ class WvCdmEngineTest : public testing::Test { std::string cert_authority; std::string cert, wrapped_key; ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest( - cert_type, cert_authority, EMPTY_ORIGIN, - &prov_request, &provisioning_server_url)); + cert_type, cert_authority, &prov_request, + &provisioning_server_url)); UrlRequest url_request(provisioning_server_url); + EXPECT_TRUE(url_request.is_connected()); url_request.PostCertRequestInQueryString(prov_request); std::string message; bool ok = url_request.GetResponse(&message); EXPECT_TRUE(ok); - ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(EMPTY_ORIGIN, - message, &cert, + ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(message, &cert, &wrapped_key)); } @@ -113,9 +113,8 @@ class WvCdmEngineTest : public testing::Test { void GenerateRenewalRequest() { CdmKeyRequest request; - - EXPECT_EQ(KEY_MESSAGE, cdm_engine_.GenerateRenewalRequest( - session_id_, &request)); + EXPECT_EQ(KEY_MESSAGE, + cdm_engine_.GenerateRenewalRequest(session_id_, &request)); key_msg_ = request.message; server_url_ = request.url; @@ -177,6 +176,7 @@ class WvCdmEngineTest : public testing::Test { EXPECT_EQ(KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp)); } + FileSystem file_system_; CdmEngine cdm_engine_; std::string key_msg_; std::string session_id_; diff --git a/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp index 5e060df9..0a7390b9 100644 --- a/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp @@ -88,13 +88,12 @@ const std::string kWrappedKey = a2bs_hex( "E74C92B44F9205D22262FB47948654229DE1920F8EDF96A19A88A1CA1552F8856FB4CBF83B" "AA3348419159D207F65FCE9C1A500C6818"); -const std::string kTestOrigin = "com.google"; - class MockDeviceFiles : public DeviceFiles { public: + MockDeviceFiles() : DeviceFiles(NULL) {} + MOCK_METHOD1(Init, bool(CdmSecurityLevel)); - MOCK_METHOD3(RetrieveCertificate, bool(const std::string&, std::string*, - std::string*)); + MOCK_METHOD2(RetrieveCertificate, bool(std::string*, std::string*)); }; class MockCryptoSession : public CryptoSession { @@ -136,7 +135,7 @@ using ::testing::StrEq; class CdmSessionTest : public ::testing::Test { protected: virtual void SetUp() { - cdm_session_.reset(new CdmSession(kTestOrigin)); + cdm_session_.reset(new CdmSession(NULL)); // Inject testing mocks. license_parser_ = new MockCdmLicense(cdm_session_->session_id()); cdm_session_->set_license_parser(license_parser_); @@ -165,9 +164,8 @@ TEST_F(CdmSessionTest, InitWithCertificate) { .InSequence(crypto_session_seq) .WillOnce(Return(level)); EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); - EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(), - NotNull())) - .WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey), + EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey), Return(true))); EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey))) .InSequence(crypto_session_seq) @@ -213,9 +211,8 @@ TEST_F(CdmSessionTest, ReInitFail) { .InSequence(crypto_session_seq) .WillOnce(Return(level)); EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); - EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(), - NotNull())) - .WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey), + EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey), Return(true))); EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey))) .InSequence(crypto_session_seq) @@ -249,8 +246,7 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) { .InSequence(crypto_session_seq) .WillOnce(Return(level)); EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); - EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(), - NotNull())) + EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull())) .WillOnce(Return(false)); Properties::set_use_certificates_as_identification(true); diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index d246e13f..21d7ee39 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -1412,19 +1412,24 @@ const std::string kTestOrigin = "com.google"; class MockFile : public File { public: - MOCK_METHOD2(Open, bool(const std::string&, int flags)); - MOCK_METHOD0(Close, void()); + MockFile() : File(NULL) {} + ~MockFile() {} MOCK_METHOD2(Read, ssize_t(char*, size_t)); MOCK_METHOD2(Write, ssize_t(const char*, size_t)); + MOCK_METHOD0(Close, void()); +}; + +class MockFileSystem : public FileSystem { + public: + MockFileSystem() {} + ~MockFileSystem() {} + + MOCK_METHOD2(Open, File*(const std::string&, int flags)); + MOCK_METHOD0(IsFactoryReset, bool()); MOCK_METHOD1(Exists, bool(const std::string&)); MOCK_METHOD1(Remove, bool(const std::string&)); - MOCK_METHOD2(Copy, bool(const std::string&, const std::string&)); - MOCK_METHOD2(List, bool(const std::string&, std::vector*)); - MOCK_METHOD1(CreateDirectory, bool(const std::string)); - MOCK_METHOD1(IsDirectory, bool(const std::string&)); - MOCK_METHOD1(IsRegularFile, bool(const std::string&)); MOCK_METHOD1(FileSize, ssize_t(const std::string&)); }; @@ -1490,21 +1495,9 @@ class DeviceFilesTest : public ::testing::Test { class DeviceFilesStoreTest : public DeviceFilesTest, public ::testing::WithParamInterface {}; -struct CertStorageVariant { - CertStorageVariant(bool dir_exists_value, const std::string& origin_value) - : dir_exists(dir_exists_value), origin(origin_value) {} +class DeviceCertificateStoreTest : public DeviceFilesTest {}; - const bool dir_exists; - const std::string origin; -}; - -class DeviceCertificateStoreTest - : public DeviceFilesTest, - public ::testing::WithParamInterface {}; - -class DeviceCertificateTest - : public DeviceFilesTest, - public ::testing::WithParamInterface {}; +class DeviceCertificateTest : public DeviceFilesTest {}; class DeviceFilesSecurityLevelTest : public DeviceFilesTest, @@ -1513,8 +1506,7 @@ class DeviceFilesSecurityLevelTest class DeviceFilesUsageInfoTest : public DeviceFilesTest, public ::testing::WithParamInterface {}; -MATCHER(IsCreateFileFlagSet, "") { return File::kCreate & arg; } -MATCHER(IsBinaryFileFlagSet, "") { return File::kBinary & arg; } +MATCHER(IsCreateFileFlagSet, "") { return FileSystem::kCreate & arg; } MATCHER_P(IsStrEq, str, "") { // Estimating the length of data. We can have gmock provide length // as well as pointer to data but that will introduce a dependency on tr1 @@ -1586,105 +1578,78 @@ MATCHER_P7(Contains, str1, str2, str3, str4, str5, str6, map7, "") { data.find(str6) != std::string::npos && map7_entries_present); } -TEST_P(DeviceCertificateStoreTest, StoreCertificate) { - MockFile file; - CertStorageVariant params = GetParam(); +TEST_F(DeviceCertificateStoreTest, StoreCertificate) { + MockFileSystem file_system; std::string certificate(GenerateRandomData(kCertificateLen)); std::string wrapped_private_key(GenerateRandomData(kWrappedKeyLen)); std::string device_certificate_path = - device_base_path_ + DeviceFiles::GetCertificateFileName(params.origin); + device_base_path_ + DeviceFiles::GetCertificateFileName(); - EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) - .WillOnce(Return(params.dir_exists)); - if (params.dir_exists) { - EXPECT_CALL(file, CreateDirectory(_)).Times(0); - } else { - EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path_))) - .WillOnce(Return(true)); - } - - EXPECT_CALL(file, Open(StrEq(device_certificate_path), - AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet()))) - .WillOnce(Return(true)); + MockFile file; + EXPECT_CALL(file_system, + Open(StrEq(device_certificate_path), IsCreateFileFlagSet())) + .WillOnce(Return(&file)); EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0), Gt(certificate.size() + wrapped_private_key.size()))) .WillOnce(ReturnArg<1>()); EXPECT_CALL(file, Close()).Times(1); EXPECT_CALL(file, Read(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); - EXPECT_TRUE(device_files.StoreCertificate(params.origin, certificate, - wrapped_private_key)); + EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key)); } -INSTANTIATE_TEST_CASE_P( - StoreCertificate, DeviceCertificateStoreTest, - ::testing::Values(CertStorageVariant(true, EMPTY_ORIGIN), - CertStorageVariant(true, kTestOrigin), - CertStorageVariant(false, EMPTY_ORIGIN), - CertStorageVariant(false, kTestOrigin))); - -TEST_P(DeviceCertificateTest, ReadCertificate) { - MockFile file; - std::string origin = GetParam(); +TEST_F(DeviceCertificateTest, ReadCertificate) { + MockFileSystem file_system; std::string device_certificate_path = - device_base_path_ + DeviceFiles::GetCertificateFileName(origin); + device_base_path_ + DeviceFiles::GetCertificateFileName(); std::string data = a2bs_hex(kTestCertificateFileData); - EXPECT_CALL(file, Exists(StrEq(device_certificate_path))) + MockFile file; + EXPECT_CALL(file_system, Exists(StrEq(device_certificate_path))) .WillOnce(Return(true)); - EXPECT_CALL(file, FileSize(StrEq(device_certificate_path))) + EXPECT_CALL(file_system, FileSize(StrEq(device_certificate_path))) .WillOnce(Return(data.size())); - EXPECT_CALL(file, Open(StrEq(device_certificate_path), IsBinaryFileFlagSet())) - .WillOnce(Return(true)); + EXPECT_CALL(file_system, Open(StrEq(device_certificate_path), _)) + .WillOnce(Return(&file)); EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))) .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), Return(data.size()))); EXPECT_CALL(file, Close()).Times(1); EXPECT_CALL(file, Write(_, _)).Times(0); - if (Properties::security_level_path_backward_compatibility_support()) { - EXPECT_CALL(file, List(_, NotNull())).WillOnce(Return(false)); - } - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); std::string certificate, wrapped_private_key; - ASSERT_TRUE(device_files.RetrieveCertificate(origin, &certificate, - &wrapped_private_key)); + ASSERT_TRUE( + device_files.RetrieveCertificate(&certificate, &wrapped_private_key)); EXPECT_EQ(kTestCertificate, b2a_hex(certificate)); EXPECT_EQ(kTestWrappedPrivateKey, b2a_hex(wrapped_private_key)); } -TEST_P(DeviceCertificateTest, HasCertificate) { - MockFile file; - std::string origin = GetParam(); +TEST_F(DeviceCertificateTest, HasCertificate) { + MockFileSystem file_system; std::string device_certificate_path = - device_base_path_ + DeviceFiles::GetCertificateFileName(origin); + device_base_path_ + DeviceFiles::GetCertificateFileName(); - EXPECT_CALL(file, Exists(StrEq(device_certificate_path))) + EXPECT_CALL(file_system, Exists(StrEq(device_certificate_path))) .WillOnce(Return(false)) .WillOnce(Return(true)); - EXPECT_CALL(file, Open(_, _)).Times(0); + EXPECT_CALL(file_system, Open(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); ASSERT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); // MockFile returns false. - EXPECT_FALSE(device_files.HasCertificate(origin)); + EXPECT_FALSE(device_files.HasCertificate()); // MockFile returns true. - EXPECT_TRUE(device_files.HasCertificate(origin)); + EXPECT_TRUE(device_files.HasCertificate()); } -INSTANTIATE_TEST_CASE_P(CertificateUseTests, DeviceCertificateTest, - ::testing::Values(EMPTY_ORIGIN, kTestOrigin)); - TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) { - MockFile file; + MockFileSystem file_system; std::string certificate(GenerateRandomData(kCertificateLen)); std::string wrapped_private_key(GenerateRandomData(kWrappedKeyLen)); @@ -1693,55 +1658,39 @@ TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) { ASSERT_TRUE( Properties::GetDeviceFilesBasePath(security_level, &device_base_path)); std::string device_certificate_path = - device_base_path + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN); + device_base_path + DeviceFiles::GetCertificateFileName(); - EXPECT_CALL(file, IsDirectory(StrEq(device_base_path))) - .WillOnce(Return(false)); - EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path))) - .WillOnce(Return(true)); - - EXPECT_CALL(file, Open(StrEq(device_certificate_path), - AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet()))) - .WillOnce(Return(true)); + MockFile file; + EXPECT_CALL(file_system, + Open(StrEq(device_certificate_path), IsCreateFileFlagSet())) + .WillOnce(Return(&file)); EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0), Gt(certificate.size() + wrapped_private_key.size()))) .WillOnce(ReturnArg<1>()); EXPECT_CALL(file, Close()).Times(1); EXPECT_CALL(file, Read(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(security_level)); - device_files.SetTestFile(&file); - EXPECT_TRUE(device_files.StoreCertificate(EMPTY_ORIGIN, certificate, - wrapped_private_key)); + EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key)); } INSTANTIATE_TEST_CASE_P(SecurityLevel, DeviceFilesSecurityLevelTest, ::testing::Values(kSecurityLevelL1, kSecurityLevelL3)); TEST_P(DeviceFilesStoreTest, StoreLicense) { - MockFile file; + MockFileSystem file_system; size_t license_num = 0; std::string license_path = device_base_path_ + license_test_data[license_num].key_set_id + DeviceFiles::GetLicenseFileNameExtension(); - bool dir_exists = GetParam(); - EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) - .WillOnce(Return(dir_exists)); - if (dir_exists) { - EXPECT_CALL(file, CreateDirectory(_)).Times(0); - } else { - EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path_))) - .WillOnce(Return(true)); - } - CdmAppParameterMap app_parameters = GetAppParameters(license_test_data[license_num].app_parameters); - EXPECT_CALL(file, Open(StrEq(license_path), - AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet()))) - .WillOnce(Return(true)); + MockFile file; + EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet())) + .WillOnce(Return(&file)); EXPECT_CALL( file, Write(Contains(license_test_data[license_num].pssh_data, license_test_data[license_num].key_request, @@ -1755,9 +1704,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) { EXPECT_CALL(file, Close()).Times(1); EXPECT_CALL(file, Read(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); EXPECT_TRUE(device_files.StoreLicense( license_test_data[license_num].key_set_id, license_test_data[license_num].license_state, @@ -1774,11 +1722,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) { INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest, ::testing::Bool()); TEST_F(DeviceFilesTest, StoreLicenses) { + MockFileSystem file_system; MockFile file; - EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) - .Times(kNumberOfLicenses) - .WillRepeatedly(Return(true)); - EXPECT_CALL(file, CreateDirectory(_)).Times(0); for (size_t i = 0; i < kNumberOfLicenses; ++i) { std::string license_path = device_base_path_ + @@ -1788,9 +1733,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) { CdmAppParameterMap app_parameters = GetAppParameters(license_test_data[i].app_parameters); - EXPECT_CALL(file, Open(StrEq(license_path), - AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet()))) - .WillOnce(Return(true)); + EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet())) + .WillOnce(Return(&file)); EXPECT_CALL(file, Write(Contains(license_test_data[i].pssh_data, license_test_data[i].key_request, @@ -1805,9 +1749,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) { EXPECT_CALL(file, Close()).Times(kNumberOfLicenses); EXPECT_CALL(file, Read(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); for (size_t i = 0; i < kNumberOfLicenses; i++) { CdmAppParameterMap app_parameters = GetAppParameters(license_test_data[i].app_parameters); @@ -1825,6 +1768,7 @@ TEST_F(DeviceFilesTest, StoreLicenses) { } TEST_F(DeviceFilesTest, RetrieveLicenses) { + MockFileSystem file_system; MockFile file; for (size_t i = 0; i < kNumberOfLicenses; ++i) { @@ -1834,10 +1778,12 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) { size_t size = license_test_data[i].file_data.size(); - EXPECT_CALL(file, Exists(StrEq(license_path))).WillOnce(Return(true)); - EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size)); - EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet())) + EXPECT_CALL(file_system, Exists(StrEq(license_path))) .WillOnce(Return(true)); + EXPECT_CALL(file_system, FileSize(StrEq(license_path))) + .WillOnce(Return(size)); + EXPECT_CALL(file_system, Open(StrEq(license_path), _)) + .WillOnce(Return(&file)); EXPECT_CALL(file, Read(NotNull(), Eq(size))) .WillOnce( DoAll(SetArrayArgument<0>(license_test_data[i].file_data.begin(), @@ -1847,9 +1793,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) { EXPECT_CALL(file, Close()).Times(kNumberOfLicenses); EXPECT_CALL(file, Write(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); CdmInitData pssh_data; CdmKeyMessage key_request; CdmKeyResponse key_response; @@ -1886,7 +1831,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) { } TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) { - MockFile file; + MockFileSystem file_system; LicenseInfo* test_data = &license_app_parameters_backwards_compatibility_test_data; @@ -1895,10 +1840,12 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) { size_t size = test_data->file_data.size(); - EXPECT_CALL(file, Exists(StrEq(license_path))).WillOnce(Return(true)); - EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size)); - EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet())) - .WillOnce(Return(true)); + MockFile file; + EXPECT_CALL(file_system, Exists(StrEq(license_path))).WillOnce(Return(true)); + EXPECT_CALL(file_system, FileSize(StrEq(license_path))) + .WillOnce(Return(size)); + EXPECT_CALL(file_system, Open(StrEq(license_path), _)) + .WillOnce(Return(&file)); EXPECT_CALL(file, Read(NotNull(), Eq(size))) .WillOnce(DoAll(SetArrayArgument<0>(test_data->file_data.begin(), test_data->file_data.end()), @@ -1907,9 +1854,8 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) { EXPECT_CALL(file, Close()).Times(1); EXPECT_CALL(file, Write(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); DeviceFiles::LicenseState license_state; CdmInitData pssh_data; CdmKeyMessage key_request; @@ -1936,102 +1882,16 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) { EXPECT_EQ(0u, app_parameters.size()); } -TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) { - if (!Properties::security_level_path_backward_compatibility_support()) { - return; - } - - MockFile file; - std::vector security_dirs; - EXPECT_TRUE(Properties::GetSecurityLevelDirectories(&security_dirs)); - - size_t pos = std::string::npos; - for (size_t i = 0; i < security_dirs.size(); ++i) { - pos = device_base_path_.rfind(security_dirs[i]); - if (std::string::npos != pos) break; - } - - EXPECT_NE(std::string::npos, pos); - - std::string base_path(device_base_path_, 0, pos); - std::vector old_files; - std::string new_path; - for (size_t i = 0; i < security_dirs.size(); ++i) { - old_files.push_back(security_dirs[i]); - new_path = base_path + security_dirs[i]; - EXPECT_CALL(file, IsRegularFile(StrEq(new_path))).WillOnce(Return(false)); - EXPECT_CALL(file, Exists(StrEq(new_path))) - .WillOnce(Return(false)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(file, CreateDirectory(StrEq(new_path))).WillOnce(Return(true)); - } - - std::string old_path = - base_path + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN); - old_files.push_back(DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN)); - EXPECT_CALL(file, IsRegularFile(StrEq(old_path))).WillOnce(Return(true)); - EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true)); - for (size_t i = 0; i < security_dirs.size(); ++i) { - new_path = base_path + security_dirs[i] + - DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN); - EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path))) - .WillOnce(Return(true)); - } - - for (size_t j = 0; j < kNumberOfLicenses; ++j) { - std::string file_name = license_test_data[j].key_set_id + - DeviceFiles::GetLicenseFileNameExtension(); - old_path = base_path + file_name; - old_files.push_back(file_name); - EXPECT_CALL(file, IsRegularFile(StrEq(old_path))).WillOnce(Return(true)); - EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true)); - for (size_t i = 0; i < security_dirs.size(); ++i) { - new_path = base_path + security_dirs[i] + file_name; - EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path))) - .WillOnce(Return(true)); - } - } - - EXPECT_CALL(file, List(StrEq(base_path), NotNull())) - .WillOnce(DoAll(SetArgPointee<1>(old_files), Return(true))); - - std::string data = a2bs_hex(kTestCertificateFileData); - - new_path = - device_base_path_ + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN); - EXPECT_CALL(file, Exists(StrEq(new_path))).WillOnce(Return(true)); - EXPECT_CALL(file, FileSize(_)).WillOnce(Return(data.size())); - EXPECT_CALL(file, Open(_, _)).WillOnce(Return(true)); - EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))) - .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), - Return(data.size()))); - EXPECT_CALL(file, Close()).Times(1); - EXPECT_CALL(file, Write(_, _)).Times(0); - - DeviceFiles device_files; - EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); - - Properties::Init(); - std::string certificate, wrapped_private_key; - ASSERT_TRUE(device_files.RetrieveCertificate(EMPTY_ORIGIN, &certificate, - &wrapped_private_key)); -} - TEST_F(DeviceFilesTest, UpdateLicenseState) { - MockFile file; + MockFileSystem file_system; std::string license_path = device_base_path_ + license_update_test_data[0].key_set_id + DeviceFiles::GetLicenseFileNameExtension(); - EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) + MockFile file; + EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet())) .Times(2) - .WillRepeatedly(Return(true)); - EXPECT_CALL(file, CreateDirectory(_)).Times(0); - EXPECT_CALL(file, Open(StrEq(license_path), - AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet()))) - .Times(2) - .WillRepeatedly(Return(true)); + .WillRepeatedly(Return(&file)); EXPECT_CALL(file, Write(IsStrEq(license_update_test_data[0].file_data), Eq(license_update_test_data[0].file_data.size()))) .WillOnce(ReturnArg<1>()); @@ -2041,9 +1901,8 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) { EXPECT_CALL(file, Close()).Times(2); EXPECT_CALL(file, Read(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); EXPECT_TRUE(device_files.StoreLicense( license_update_test_data[0].key_set_id, license_update_test_data[0].license_state, @@ -2072,32 +1931,33 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) { } TEST_F(DeviceFilesTest, DeleteLicense) { - MockFile file; + MockFileSystem file_system; std::string license_path = device_base_path_ + license_test_data[0].key_set_id + DeviceFiles::GetLicenseFileNameExtension(); size_t size = license_test_data[0].file_data.size(); - EXPECT_CALL(file, Exists(StrEq(license_path))) + MockFile file; + EXPECT_CALL(file_system, Exists(StrEq(license_path))) .Times(2) .WillOnce(Return(true)) .WillOnce(Return(false)); - EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size)); - EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet())) - .WillOnce(Return(true)); + EXPECT_CALL(file_system, FileSize(StrEq(license_path))) + .WillOnce(Return(size)); + EXPECT_CALL(file_system, Open(StrEq(license_path), _)) + .WillOnce(Return(&file)); EXPECT_CALL(file, Read(NotNull(), Eq(size))) .WillOnce( DoAll(SetArrayArgument<0>(license_test_data[0].file_data.begin(), license_test_data[0].file_data.end()), Return(size))); - EXPECT_CALL(file, Remove(StrEq(license_path))).WillOnce(Return(true)); + EXPECT_CALL(file_system, Remove(StrEq(license_path))).WillOnce(Return(true)); EXPECT_CALL(file, Close()).Times(1); EXPECT_CALL(file, Write(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); DeviceFiles::LicenseState license_state; CdmInitData pssh_data; CdmKeyMessage key_request; @@ -2135,17 +1995,11 @@ TEST_F(DeviceFilesTest, DeleteLicense) { TEST_F(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem) { // Validate that ReserveLicenseIds does not touch the file system. - MockFile file; - EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))).Times(0); - EXPECT_CALL(file, CreateDirectory(_)).Times(0); - EXPECT_CALL(file, Open(_, _)).Times(0); - EXPECT_CALL(file, Write(_, _)).Times(0); - EXPECT_CALL(file, Close()).Times(0); - EXPECT_CALL(file, Read(_, _)).Times(0); + MockFileSystem file_system; + EXPECT_CALL(file_system, Open(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); for (size_t i = 0; i < kNumberOfLicenses; i++) { EXPECT_TRUE(device_files.ReserveLicenseId(license_test_data[i].key_set_id)); // Validate that the license IDs are actually reserved. @@ -2156,6 +2010,7 @@ TEST_F(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem) { } TEST_P(DeviceFilesUsageInfoTest, Read) { + MockFileSystem file_system; MockFile file; std::string app_id; // TODO(fredgc): add tests with multiple app_ids. std::string path = @@ -2167,28 +2022,24 @@ TEST_P(DeviceFilesUsageInfoTest, Read) { data = kUsageInfoTestData[index].file_data; } if (index >= 0) { - EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(true)); - EXPECT_CALL(file, FileSize(StrEq(path))) + EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true)); + EXPECT_CALL(file_system, FileSize(StrEq(path))) .WillRepeatedly(Return(data.size())); - EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet())) - .WillOnce(Return(true)); + EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))) .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), Return(data.size()))); EXPECT_CALL(file, Close()).Times(1); } else { - EXPECT_CALL(file, Exists(StrEq(path))) - .WillRepeatedly(Return(false)); - EXPECT_CALL(file, FileSize(_)).Times(0); - EXPECT_CALL(file, Open(_, _)).Times(0); - EXPECT_CALL(file, Close()).Times(0); + EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(false)); + EXPECT_CALL(file_system, FileSize(_)).Times(0); + EXPECT_CALL(file_system, Open(_, _)).Times(0); } EXPECT_CALL(file, Write(_, _)).Times(0); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); std::vector > license_info; ASSERT_TRUE(device_files.RetrieveUsageInfo(app_id, &license_info)); @@ -2212,7 +2063,7 @@ TEST_P(DeviceFilesUsageInfoTest, Read) { } TEST_P(DeviceFilesUsageInfoTest, Store) { - MockFile file; + MockFileSystem file_system; std::string app_id; // TODO(fredgc): multiple app ids. std::string pst(GenerateRandomData(kProviderSessionTokenLen)); std::string license_request(GenerateRandomData(kLicenseRequestLen)); @@ -2226,24 +2077,24 @@ TEST_P(DeviceFilesUsageInfoTest, Store) { if (index >= 0) { data = kUsageInfoTestData[index].file_data; } - EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) - .WillRepeatedly(Return(true)); - EXPECT_CALL(file, CreateDirectory(_)).Times(0); - EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(index >= 0)); + MockFile file; + EXPECT_CALL(file_system, Exists(StrEq(path))) + .WillRepeatedly(Return(index >= 0)); if (index >= 0) { - EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size())); - EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet())) + EXPECT_CALL(file_system, FileSize(StrEq(path))) + .WillOnce(Return(data.size())); + EXPECT_CALL(file_system, Open(StrEq(path), _)) .Times(2) - .WillRepeatedly(Return(true)); + .WillRepeatedly(Return(&file)); EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))) .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), Return(data.size()))); EXPECT_CALL(file, Close()).Times(2); } else { - EXPECT_CALL(file, FileSize(_)).Times(0); - EXPECT_CALL(file, Open(_, _)).Times(1).WillOnce(Return(true)); - EXPECT_CALL(file, Close()).Times(1); + EXPECT_CALL(file_system, FileSize(_)).Times(0); + EXPECT_CALL(file_system, Open(_, _)).Times(1).WillOnce(Return(&file)); + EXPECT_CALL(file, Close()); } EXPECT_CALL(file, @@ -2253,9 +2104,8 @@ TEST_P(DeviceFilesUsageInfoTest, Store) { key_set_id.size()))) .WillOnce(ReturnArg<1>()); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); ASSERT_TRUE( device_files.StoreUsageInfo(pst, license_request, license, app_id, @@ -2263,6 +2113,7 @@ TEST_P(DeviceFilesUsageInfoTest, Store) { } TEST_P(DeviceFilesUsageInfoTest, Delete) { + MockFileSystem file_system; MockFile file; std::string app_id; // TODO(fredgc): expand tests. std::string path = @@ -2282,24 +2133,19 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) { } } - EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) - .WillRepeatedly(Return(true)); - EXPECT_CALL(file, CreateDirectory(_)).Times(0); + EXPECT_CALL(file_system, Exists(StrEq(path))).WillOnce(Return(index >= 0)); - EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(index >= 0)); - - EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size())); + EXPECT_CALL(file_system, FileSize(StrEq(path))).WillOnce(Return(data.size())); if (index >= 1) { - EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet())) + EXPECT_CALL(file_system, Open(StrEq(path), _)) .Times(2) - .WillRepeatedly(Return(true)); + .WillRepeatedly(Return(&file)); EXPECT_CALL(file, Write(Contains(prev_pst, prev_license, prev_data.size()), Gt(prev_pst.size() + prev_license.size()))) .WillOnce(ReturnArg<1>()); EXPECT_CALL(file, Close()).Times(2); } else { - EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet())) - .WillOnce(Return(true)); + EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); EXPECT_CALL(file, Write(_, _)).Times(0); EXPECT_CALL(file, Close()).Times(1); } @@ -2307,9 +2153,8 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) { .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), Return(data.size()))); - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); if (index >= 1) { ASSERT_TRUE(device_files.DeleteUsageInfo(app_id, pst)); @@ -2319,36 +2164,33 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) { } TEST_P(DeviceFilesUsageInfoTest, DeleteAll) { + MockFileSystem file_system; MockFile file; std::string app_id; // TODO(fredgc): expand tests. std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id); int index = GetParam(); - EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) - .WillRepeatedly(Return(true)); - EXPECT_CALL(file, CreateDirectory(_)).Times(0); EXPECT_CALL(file, Write(_, _)).Times(0); std::string data; if (index < 0) { - EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(false)); + EXPECT_CALL(file_system, Exists(StrEq(path))).WillOnce(Return(false)); } else { data = kUsageInfoTestData[index].file_data; - EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(true)); - EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size())); - EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet())) - .WillOnce(Return(true)); + EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true)); + EXPECT_CALL(file_system, FileSize(StrEq(path))) + .WillOnce(Return(data.size())); + EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))) .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), Return(data.size()))); EXPECT_CALL(file, Close()).Times(1); - EXPECT_CALL(file, Remove(StrEq(path))).WillOnce(Return(true)); + EXPECT_CALL(file_system, Remove(StrEq(path))).WillOnce(Return(true)); } - DeviceFiles device_files; + DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - device_files.SetTestFile(&file); std::vector psts; ASSERT_TRUE(device_files.DeleteAllUsageInfoForApp(app_id, &psts)); diff --git a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp index 3eee8251..48bfcd17 100644 --- a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp @@ -1,9 +1,8 @@ // Copyright 2013 Google Inc. All Rights Reserved. #include -#include "device_files.h" + #include "file_store.h" -#include "properties.h" #include "test_vectors.h" namespace wvcdm { @@ -18,20 +17,12 @@ const std::string kWildcard = "*"; class FileTest : public testing::Test { protected: - virtual void SetUp() { CreateTestDir(); } + FileTest() {} + virtual void TearDown() { RemoveTestDir(); } - void CreateTestDir() { - File file; - if (!file.Exists(test_vectors::kTestDir)) { - EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir)); - } - EXPECT_TRUE(file.Exists(test_vectors::kTestDir)); - } - void RemoveTestDir() { - File file; - EXPECT_TRUE(file.Remove(test_vectors::kTestDir)); + EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir)); } std::string GenerateRandomData(uint32_t len) { @@ -41,64 +32,52 @@ class FileTest : public testing::Test { } return data; } + + FileSystem file_system; }; TEST_F(FileTest, FileExists) { - File file; - 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) { - File file; - std::string dir_wo_delimiter = - test_vectors::kTestDir.substr(0, test_vectors::kTestDir.size() - 1); - if (file.Exists(dir_wo_delimiter)) EXPECT_TRUE(file.Remove(dir_wo_delimiter)); - EXPECT_FALSE(file.Exists(dir_wo_delimiter)); - EXPECT_TRUE(file.CreateDirectory(dir_wo_delimiter)); - EXPECT_TRUE(file.Exists(dir_wo_delimiter)); - EXPECT_TRUE(file.Remove(dir_wo_delimiter)); - EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir)); - EXPECT_TRUE(file.Exists(test_vectors::kTestDir)); - EXPECT_TRUE(file.Remove(test_vectors::kTestDir)); + EXPECT_TRUE(file_system.Exists(test_vectors::kExistentFile)); + EXPECT_TRUE(file_system.Exists(test_vectors::kExistentDir)); + EXPECT_FALSE(file_system.Exists(test_vectors::kNonExistentFile)); + EXPECT_FALSE(file_system.Exists(test_vectors::kNonExistentDir)); } TEST_F(FileTest, RemoveDir) { - File file; - EXPECT_TRUE(file.Remove(test_vectors::kTestDir)); - EXPECT_FALSE(file.Exists(test_vectors::kTestDir)); + EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir)); + EXPECT_FALSE(file_system.Exists(test_vectors::kTestDir)); } TEST_F(FileTest, OpenFile) { std::string path = test_vectors::kTestDir + kTestFileName; - File handle; - EXPECT_TRUE(handle.Remove(path)); + EXPECT_TRUE(file_system.Remove(path)); - File file; - EXPECT_TRUE(file.Open(path, File::kCreate)); - file.Close(); + File* file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + file->Close(); - EXPECT_TRUE(handle.Exists(path)); + EXPECT_TRUE(file_system.Exists(path)); } TEST_F(FileTest, RemoveDirAndFile) { std::string path = test_vectors::kTestDir + kTestFileName; - File file; - EXPECT_TRUE(file.Open(path, File::kCreate)); - file.Close(); - EXPECT_TRUE(file.Exists(path)); - EXPECT_TRUE(file.Remove(path)); - EXPECT_FALSE(file.Exists(path)); + File* file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + file->Close(); - EXPECT_TRUE(file.Open(path, File::kCreate)); - file.Close(); - EXPECT_TRUE(file.Exists(path)); + EXPECT_TRUE(file_system.Exists(path)); + EXPECT_TRUE(file_system.Remove(path)); + EXPECT_FALSE(file_system.Exists(path)); + + file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + file->Close(); + + EXPECT_TRUE(file_system.Exists(path)); RemoveTestDir(); - EXPECT_FALSE(file.Exists(test_vectors::kTestDir)); - EXPECT_FALSE(file.Exists(path)); + EXPECT_FALSE(file_system.Exists(test_vectors::kTestDir)); + EXPECT_FALSE(file_system.Exists(path)); } TEST_F(FileTest, RemoveWildcardFiles) { @@ -107,159 +86,53 @@ TEST_F(FileTest, RemoveWildcardFiles) { std::string wildcard_path = test_vectors::kTestDir + kWildcard + kTestFileNameExt; - File file; - EXPECT_TRUE(file.Open(path1, File::kCreate)); - file.Close(); - EXPECT_TRUE(file.Open(path2, File::kCreate)); - file.Close(); - EXPECT_TRUE(file.Exists(path1)); - EXPECT_TRUE(file.Exists(path2)); - EXPECT_TRUE(file.Remove(wildcard_path)); - EXPECT_FALSE(file.Exists(path1)); - EXPECT_FALSE(file.Exists(path2)); -} + File* file = file_system.Open(path1, FileSystem::kCreate); + ASSERT_TRUE(file); + file->Close(); + file = file_system.Open(path2, FileSystem::kCreate); + ASSERT_TRUE(file); + file->Close(); -TEST_F(FileTest, IsDir) { - std::string path = test_vectors::kTestDir + kTestFileName; - File file; - EXPECT_TRUE(file.Open(path, File::kCreate)); - file.Close(); - - EXPECT_TRUE(file.Exists(path)); - EXPECT_TRUE(file.Exists(test_vectors::kTestDir)); - EXPECT_FALSE(file.IsDirectory(path)); - EXPECT_TRUE(file.IsDirectory(test_vectors::kTestDir)); -} - -TEST_F(FileTest, IsRegularFile) { - std::string path = test_vectors::kTestDir + kTestFileName; - File file; - EXPECT_TRUE(file.Open(path, File::kCreate)); - file.Close(); - - EXPECT_TRUE(file.Exists(path)); - EXPECT_TRUE(file.Exists(test_vectors::kTestDir)); - EXPECT_TRUE(file.IsRegularFile(path)); - EXPECT_FALSE(file.IsRegularFile(test_vectors::kTestDir)); + EXPECT_TRUE(file_system.Exists(path1)); + EXPECT_TRUE(file_system.Exists(path2)); + EXPECT_TRUE(file_system.Remove(wildcard_path)); + EXPECT_FALSE(file_system.Exists(path1)); + EXPECT_FALSE(file_system.Exists(path2)); } TEST_F(FileTest, FileSize) { std::string path = test_vectors::kTestDir + kTestFileName; - File file; - file.Remove(path); + file_system.Remove(path); std::string write_data = GenerateRandomData(600); - File wr_file; - EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary)); - EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size())); - wr_file.Close(); - EXPECT_TRUE(file.Exists(path)); + File* file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + EXPECT_TRUE(file->Write(write_data.data(), write_data.size())); + file->Close(); + EXPECT_TRUE(file_system.Exists(path)); - EXPECT_EQ(static_cast(write_data.size()), file.FileSize(path)); -} - -TEST_F(FileTest, WriteReadTextFile) { - std::string path = test_vectors::kTestDir + kTestFileName; - File file; - file.Remove(path); - - std::string write_data = "This is a test"; - File wr_file; - EXPECT_TRUE(wr_file.Open(path, File::kCreate)); - EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size())); - wr_file.Close(); - EXPECT_TRUE(file.Exists(path)); - - std::string read_data; - read_data.resize(file.FileSize(path)); - File rd_file; - EXPECT_TRUE(rd_file.Open(path, File::kReadOnly)); - EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size())); - rd_file.Close(); - EXPECT_EQ(write_data, read_data); + EXPECT_EQ(static_cast(write_data.size()), + file_system.FileSize(path)); } TEST_F(FileTest, WriteReadBinaryFile) { std::string path = test_vectors::kTestDir + kTestFileName; - File file; - file.Remove(path); + file_system.Remove(path); std::string write_data = GenerateRandomData(600); - File wr_file; - EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary)); - EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size())); - wr_file.Close(); - EXPECT_TRUE(file.Exists(path)); + File* file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + EXPECT_TRUE(file->Write(write_data.data(), write_data.size())); + file->Close(); + EXPECT_TRUE(file_system.Exists(path)); std::string read_data; - read_data.resize(file.FileSize(path)); - File rd_file; - EXPECT_TRUE(rd_file.Open(path, File::kReadOnly)); - EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size())); - rd_file.Close(); + read_data.resize(file_system.FileSize(path)); + file = file_system.Open(path, FileSystem::kReadOnly); + ASSERT_TRUE(file); + EXPECT_TRUE(file->Read(&read_data[0], read_data.size())); + file->Close(); EXPECT_EQ(write_data, read_data); } -TEST_F(FileTest, CopyFile) { - std::string path = test_vectors::kTestDir + kTestFileName; - File file; - file.Remove(path); - - std::string write_data = GenerateRandomData(600); - File wr_file; - EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary)); - EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size())); - wr_file.Close(); - ASSERT_TRUE(file.Exists(path)); - - std::string path_copy = test_vectors::kTestDir + kTestFileName2; - EXPECT_FALSE(file.Exists(path_copy)); - EXPECT_TRUE(file.Copy(path, path_copy)); - - std::string read_data; - read_data.resize(file.FileSize(path_copy)); - File rd_file; - EXPECT_TRUE(rd_file.Open(path_copy, File::kReadOnly)); - EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size())); - rd_file.Close(); - EXPECT_EQ(write_data, read_data); - EXPECT_EQ(file.FileSize(path), file.FileSize(path_copy)); -} - -TEST_F(FileTest, ListEmptyDirectory) { - std::vector files; - File file; - EXPECT_TRUE(file.List(test_vectors::kTestDir, &files)); - EXPECT_EQ(0u, files.size()); -} - -TEST_F(FileTest, ListFiles) { - File file; - std::string path = test_vectors::kTestDir + kTestDirName; - EXPECT_TRUE(file.CreateDirectory(path)); - - path = test_vectors::kTestDir + kTestFileName; - std::string write_data = GenerateRandomData(600); - EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary)); - EXPECT_TRUE(file.Write(write_data.data(), write_data.size())); - file.Close(); - EXPECT_TRUE(file.Exists(path)); - - path = test_vectors::kTestDir + kTestFileName2; - write_data = GenerateRandomData(600); - EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary)); - EXPECT_TRUE(file.Write(write_data.data(), write_data.size())); - file.Close(); - EXPECT_TRUE(file.Exists(path)); - - std::vector files; - EXPECT_TRUE(file.List(test_vectors::kTestDir, &files)); - EXPECT_EQ(3u, files.size()); - - for (size_t i = 0; i < files.size(); ++i) { - EXPECT_TRUE(files[i] == kTestDirName || files[i] == kTestFileName || - files[i] == kTestFileName2); - } -} - } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/generic_crypto_unittest.cpp b/libwvdrmengine/cdm/core/test/generic_crypto_unittest.cpp new file mode 100644 index 00000000..571a2af4 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/generic_crypto_unittest.cpp @@ -0,0 +1,416 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// These tests are for the generic crypto operations. They call on the +// CdmEngine class and exercise the classes below it as well. In +// particular, we assume that the OEMCrypo layer works, and has a valid keybox. +// This is because we need a valid RSA certificate, and will attempt to connect +// to the provisioning server to request one if we don't. + +#include +#include +#include + +#include "cdm_engine.h" + +#include "license_request.h" +#include "log.h" +#include "oec_session_util.h" +#include "../../oemcrypto/mock/src/oemcrypto_key_mock.h" +#include "string_conversions.h" +#include "url_request.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_types.h" + +namespace { + +const std::string kKeySystem = "com.widevine.alpha"; + +} // namespace + +namespace wvcdm { + +class WvGenericOperationsTest : public testing::Test { + public: + virtual void SetUp() { + ::testing::Test::SetUp(); + + // Load test keybox. This keybox will be used by any CryptoSession + // created by the CDM under test. + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox()); + + // Perform CdmEngine setup + cdm_engine_ = new CdmEngine(&file_system_); + + CdmResponseType status = + cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_); + if (status == NEED_PROVISIONING) { + Provision(); + status = cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_); + } + ASSERT_EQ(NO_ERROR, status); + ASSERT_NE("", session_id_) << "Could not open CDM session."; + ASSERT_TRUE(cdm_engine_->IsOpenSession(session_id_)); + + // Get OEMCrypto session ID from the CDM + CdmQueryMap query; + cdm_engine_->QueryOemCryptoSessionId(session_id_, &query); + std::istringstream parse_int; + parse_int.str(query[QUERY_KEY_OEMCRYPTO_SESSION_ID]); + parse_int >> oec_session_id_; + + // Construct and install keys into the CDM's OEMCrypto session. + OecSessionSetup(oec_session_id_); + EncryptAndLoadKeys(); + } + + virtual void TearDown() { + oec_util_session_.close(); + cdm_engine_->CloseSession(session_id_); + // OEMCrypto_Terminate() will be performed during the test class's + // destruction (specifically by the CryptoSession destructor) + } + + void OecSessionSetup(uint32_t oec_session_id) { + buffer_size_ = 160; + oec_util_session_.SetSessionId(oec_session_id); + oec_util_session_.GenerateTestSessionKeys(); + MakeFourKeys(); + } + + enum GenericKeyType { + kGenericEncrypt = 0, + kGenericDecrypt = 1, + kGenericSign = 2, + kGenericVerify = 3 + }; + + virtual void MakeFourKeys( + uint32_t duration = wvoec::kDuration, uint32_t control = 0, + uint32_t nonce = 0, const std::string& pst = "") { + ASSERT_NO_FATAL_FAILURE( + oec_util_session_.FillSimpleMessage(duration, control, nonce, pst)); + oec_util_session_.license().keys[kGenericEncrypt].control.control_bits |= + htonl(wvoec_mock::kControlAllowEncrypt); + oec_util_session_.license().keys[kGenericDecrypt].control.control_bits |= + htonl(wvoec_mock::kControlAllowDecrypt); + oec_util_session_.license().keys[kGenericSign].control.control_bits |= + htonl(wvoec_mock::kControlAllowSign); + oec_util_session_.license().keys[kGenericVerify].control.control_bits |= + htonl(wvoec_mock::kControlAllowVerify); + + oec_util_session_.license().keys[kGenericSign].key_data_length = + wvcdm::MAC_KEY_SIZE; + oec_util_session_.license().keys[kGenericVerify].key_data_length = + wvcdm::MAC_KEY_SIZE; + + clear_buffer_.assign(buffer_size_, 0); + for (size_t i = 0; i < clear_buffer_.size(); i++) { + clear_buffer_[i] = 1 + i % 250; + } + for (size_t i = 0; i < wvcdm::KEY_IV_SIZE; i++) { + iv_[i] = i; + } + } + + std::string GetKeyId(GenericKeyType type) { + std::string key_id; + size_t key_id_length = oec_util_session_.license().keys[0].key_id_length; + key_id.assign( + &(oec_util_session_.license().keys[type].key_id[0]), + &(oec_util_session_.license().keys[type].key_id[key_id_length])); + return key_id; + } + + std::string GetClearBuffer() { + std::string buffer; + size_t buffer_length = clear_buffer_.size(); + buffer.assign(&clear_buffer_[0], &clear_buffer_[buffer_length]); + return buffer; + } + + std::string GetEncryptedBuffer() { + std::string buffer; + size_t buffer_length = encrypted_buffer_.size(); + buffer.assign(&encrypted_buffer_[0], &encrypted_buffer_[buffer_length]); + return buffer; + } + + std::string GetIvBlock() { + std::string buffer; + size_t buffer_length = wvcdm::KEY_IV_SIZE; + buffer.assign(&iv_[0], &iv_[buffer_length]); + return buffer; + } + + std::string GetSignatureBuffer() { + std::string buffer; + buffer.resize(SHA256_DIGEST_LENGTH); + return buffer; + } + + void EncryptAndLoadKeys() { + ASSERT_NO_FATAL_FAILURE(oec_util_session_.EncryptAndSign()); + oec_util_session_.LoadTestKeys(); + } + + protected: + void Provision() { + CdmProvisioningRequest prov_request; + std::string provisioning_server_url; + CdmCertificateType cert_type = kCertificateWidevine; + std::string cert_authority; + std::string cert, wrapped_key; + ASSERT_EQ(NO_ERROR, + cdm_engine_->GetProvisioningRequest( + cert_type, cert_authority, &prov_request, + &provisioning_server_url)); + UrlRequest url_request(provisioning_server_url); + EXPECT_TRUE(url_request.is_connected()); + url_request.PostCertRequestInQueryString(prov_request); + std::string message; + bool ok = url_request.GetResponse(&message); + EXPECT_TRUE(ok); + ASSERT_EQ(NO_ERROR, + cdm_engine_->HandleProvisioningResponse(message, &cert, + &wrapped_key)); + } + + // This CryptoSession object handles Initialization and Termination + // calls on OEMCrypto for the duration of the test. CryptoSessions + // created by the CDM will share the OEMCrypto state of this CryptoSession, + // including, for example, a test keybox. + CryptoSession crypto_session_; + + FileSystem file_system_; + CdmEngine* cdm_engine_; + std::string key_msg_; + std::string session_id_; + std::string server_url_; + uint32_t oec_session_id_; + wvoec::Session oec_util_session_; + size_t buffer_size_; + vector clear_buffer_; + vector encrypted_buffer_; + uint8_t iv_[wvcdm::KEY_IV_SIZE]; +}; + +TEST_F(WvGenericOperationsTest, NormalSessionOpenClose) { + wvoec::Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_F(WvGenericOperationsTest, GenerateSessionKeys) { + wvoec::Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.GenerateTestSessionKeys()); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_F(WvGenericOperationsTest, GenericEncryptNoKey) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string out_buffer = GetEncryptedBuffer(); + std::string iv = GetIvBlock(); + + // No key + KeyId key_id("xyz"); + cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv, + wvcdm::kEncryptionAlgorithmAesCbc128, + &out_buffer); + EXPECT_EQ(KEY_ERROR_1, cdm_sts); +} + +TEST_F(WvGenericOperationsTest, GenericEncryptKeyNotAllowed) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string out_buffer = GetEncryptedBuffer(); + std::string iv = GetIvBlock(); + + // Wrong key + std::string key_id = GetKeyId(kGenericDecrypt); + + cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv, + wvcdm::kEncryptionAlgorithmAesCbc128, + &out_buffer); + EXPECT_EQ(UNKNOWN_ERROR, cdm_sts); +} + +TEST_F(WvGenericOperationsTest, GenericEncryptGood) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string out_buffer = GetEncryptedBuffer(); + std::string iv = GetIvBlock(); + + // Good key + std::string key_id = GetKeyId(kGenericEncrypt); + + cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv, + wvcdm::kEncryptionAlgorithmAesCbc128, + &out_buffer); + EXPECT_EQ(NO_ERROR, cdm_sts); +} + +TEST_F(WvGenericOperationsTest, GenericDecryptKeyNotAllowed) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string out_buffer = GetEncryptedBuffer(); + std::string iv = GetIvBlock(); + + // Wrong key + std::string key_id = GetKeyId(kGenericEncrypt); + + cdm_sts = cdm_engine_->GenericDecrypt(session_id_, in_buffer, key_id, iv, + wvcdm::kEncryptionAlgorithmAesCbc128, + &out_buffer); + EXPECT_EQ(UNKNOWN_ERROR, cdm_sts); +} + +TEST_F(WvGenericOperationsTest, GenericDecryptGood) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string out_buffer = GetEncryptedBuffer(); + std::string iv = GetIvBlock(); + + // Good key + std::string key_id = GetKeyId(kGenericDecrypt); + + cdm_sts = cdm_engine_->GenericDecrypt(session_id_, in_buffer, key_id, iv, + wvcdm::kEncryptionAlgorithmAesCbc128, + &out_buffer); + EXPECT_EQ(NO_ERROR, cdm_sts); +} + +TEST_F(WvGenericOperationsTest, GenericSignKeyNotAllowed) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string signature_buffer; + + // Wrong key + std::string key_id = GetKeyId(kGenericVerify); + + cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id, + wvcdm::kSigningAlgorithmHmacSha256, + &signature_buffer); + EXPECT_EQ(UNKNOWN_ERROR, cdm_sts); +} + +TEST_F(WvGenericOperationsTest, GenericSignGood) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string signature_buffer; + + // Good key + std::string key_id = GetKeyId(kGenericSign); + + cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id, + wvcdm::kSigningAlgorithmHmacSha256, + &signature_buffer); + EXPECT_EQ(NO_ERROR, cdm_sts); +} + +TEST_F(WvGenericOperationsTest, GenericVerifyKeyNotAllowed) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string signature_buffer = GetSignatureBuffer(); + + // Wrong key + std::string key_id = GetKeyId(kGenericSign); + + cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id, + wvcdm::kSigningAlgorithmHmacSha256, + signature_buffer); + EXPECT_EQ(UNKNOWN_ERROR, cdm_sts); +} + +TEST_F(WvGenericOperationsTest, GenericVerifyGood) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string signature_buffer = GetSignatureBuffer(); + + // Good key - signature not set. + std::string key_id = GetKeyId(kGenericVerify); + + cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id, + wvcdm::kSigningAlgorithmHmacSha256, + signature_buffer); + // OEMCrypto error is OEMCrypto_ERROR_SIGNATURE_FAILURE + EXPECT_EQ(UNKNOWN_ERROR, cdm_sts); +} + +class WvGenericOperationsDataTest : public WvGenericOperationsTest { + public: + // Construct keys for encrypt/decrypt and for sign/verify + virtual void MakeFourKeys( + uint32_t duration = wvoec::kDuration, uint32_t control = 0, + uint32_t nonce = 0, const std::string& pst = "") { + ASSERT_NO_FATAL_FAILURE( + oec_util_session_.FillSimpleMessage(duration, control, nonce, pst)); + oec_util_session_.license().keys[kGenericEncrypt].control.control_bits |= + htonl(wvoec_mock::kControlAllowEncrypt | + wvoec_mock::kControlAllowDecrypt); + oec_util_session_.license().keys[kGenericSign].control.control_bits |= + htonl(wvoec_mock::kControlAllowSign | wvoec_mock::kControlAllowVerify); + + oec_util_session_.license().keys[kGenericSign].key_data_length = + wvcdm::MAC_KEY_SIZE; + + clear_buffer_.assign(buffer_size_, 0); + for (size_t i = 0; i < clear_buffer_.size(); i++) { + clear_buffer_[i] = 1 + i % 250; + } + for (size_t i = 0; i < wvcdm::KEY_IV_SIZE; i++) { + iv_[i] = i; + } + } +}; + +TEST_F(WvGenericOperationsDataTest, GenericEncryptDecrypt) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string encrypted_buffer = GetEncryptedBuffer(); + std::string iv = GetIvBlock(); + + // Encrypt + std::string key_id = GetKeyId(kGenericEncrypt); + + cdm_sts = cdm_engine_->GenericEncrypt( + session_id_, in_buffer, key_id, iv, wvcdm::kEncryptionAlgorithmAesCbc128, + &encrypted_buffer); + + EXPECT_EQ(NO_ERROR, cdm_sts); + + // Decrypt, use same key as encrypt. + key_id = GetKeyId(kGenericEncrypt); + + std::string final_buffer; + final_buffer.resize(in_buffer.size()); + + cdm_sts = cdm_engine_->GenericDecrypt( + session_id_, encrypted_buffer, key_id, iv, + wvcdm::kEncryptionAlgorithmAesCbc128, &final_buffer); + + EXPECT_EQ(NO_ERROR, cdm_sts); + EXPECT_EQ(0, in_buffer.compare(final_buffer)); +} + +TEST_F(WvGenericOperationsDataTest, GenericSignVerify) { + CdmResponseType cdm_sts; + std::string in_buffer = GetClearBuffer(); + std::string signature_buffer = GetSignatureBuffer(); + + // Signing key + std::string key_id = GetKeyId(kGenericSign); + cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id, + wvcdm::kSigningAlgorithmHmacSha256, + &signature_buffer); + EXPECT_EQ(NO_ERROR, cdm_sts); + + // Verify signature, use same key as sign. + key_id = GetKeyId(kGenericSign); + cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id, + wvcdm::kSigningAlgorithmHmacSha256, + signature_buffer); + EXPECT_EQ(NO_ERROR, cdm_sts); +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp b/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp new file mode 100644 index 00000000..34ecf62b --- /dev/null +++ b/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp @@ -0,0 +1,888 @@ +// Copyright 2016 Google Inc. All Rights Reserved. + +#include +#include +#include +#include "license.h" +#include "license_key_status.h" + +namespace wvcdm { + +namespace { + +static const uint32_t dev_lo_res = 200; +static const uint32_t dev_hi_res = 400; +static const uint32_t dev_top_res = 800; + +static const uint32_t key_lo_res_min = 151; +static const uint32_t key_lo_res_max = 300; +static const uint32_t key_hi_res_min = 301; +static const uint32_t key_hi_res_max = 450; +static const uint32_t key_top_res_min = 451; +static const uint32_t key_top_res_max = 650; + +// Content Keys +static const KeyId ck_sw_crypto = "c_key_SW_SECURE_CRYPTO"; +static const KeyId ck_sw_decode = "c_key_SW_SECURE_DECODE"; +static const KeyId ck_hw_crypto = "c_key_HW_SECURE_CRYPTO"; +static const KeyId ck_hw_decode = "c_key_HW_SECURE_DECODE"; +static const KeyId ck_hw_secure = "c_key_HW_SECURE_ALL"; + +// Operator Session Keys +static const KeyId osk_decrypt = "os_key_generic_decrypt"; +static const KeyId osk_encrypt = "os_key_generic_encrypt"; +static const KeyId osk_sign = "os_key_generic_sign"; +static const KeyId osk_verify = "os_key_generic_verify"; +static const KeyId osk_encrypt_decrypt = "os_key_generic_encrypt_decrypt"; +static const KeyId osk_sign_verify = "os_key_generic_sign_verify"; +static const KeyId osk_all = "os_key_generic_all"; + +// HDCP test keys +static const KeyId ck_sw_crypto_NO_HDCP = "ck_sw_crypto_NO_HDCP"; +static const KeyId ck_hw_secure_NO_HDCP = "ck_hw_secure_NO_HDCP"; +static const KeyId ck_sw_crypto_HDCP_V2_1 = "ck_sw_crypto_HDCP_V2_1"; +static const KeyId ck_hw_secure_HDCP_V2_1 = "ck_hw_secure_HDCP_V2_1"; +static const KeyId ck_sw_crypto_HDCP_NO_OUTPUT = "ck_sw_crypto_HDCP_NO_OUT"; +static const KeyId ck_hw_secure_HDCP_NO_OUTPUT = "ck_hw_secure_HDCP_NO_OUT"; + +// Constraint test keys +static const KeyId ck_NO_HDCP_lo_res = "ck_NO_HDCP_lo_res"; +static const KeyId ck_HDCP_NO_OUTPUT_hi_res = "ck_HDCP_NO_OUTPUT_hi_res"; +static const KeyId ck_HDCP_V2_1_max_res = "ck_HDCP_V2_1_max_res"; +static const KeyId ck_NO_HDCP_dual_res = "ck_NO_HDCP_dual_res"; + +} // namespace + +// protobuf generated classes. +using video_widevine_server::sdk::License; +using video_widevine_server::sdk::LicenseIdentification; +using video_widevine_server::sdk::STREAMING; +using video_widevine_server::sdk::OFFLINE; + +typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer; +typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint; + +class LicenseKeysTest : public ::testing::Test { + protected: + + enum KeyFlag { + kKeyFlagNull, + kKeyFlagFalse, + kKeyFlagTrue + }; + + static const KeyFlag kEncryptNull = kKeyFlagNull; + static const KeyFlag kEncryptFalse = kKeyFlagFalse; + static const KeyFlag kEncryptTrue = kKeyFlagTrue; + static const KeyFlag kDecryptNull = kKeyFlagNull; + static const KeyFlag kDecryptFalse = kKeyFlagFalse; + static const KeyFlag kDecryptTrue = kKeyFlagTrue; + static const KeyFlag kSignNull = kKeyFlagNull; + static const KeyFlag kSignFalse = kKeyFlagFalse; + static const KeyFlag kSignTrue = kKeyFlagTrue; + static const KeyFlag kVerifyNull = kKeyFlagNull; + static const KeyFlag kVerifyFalse = kKeyFlagFalse; + static const KeyFlag kVerifyTrue = kKeyFlagTrue; + + static const KeyFlag kContentSecureFalse = kKeyFlagFalse; + static const KeyFlag kContentSecureTrue = kKeyFlagTrue; + static const KeyFlag kContentClearFalse = kKeyFlagFalse; + static const KeyFlag kContentClearTrue = kKeyFlagTrue; + + virtual void SetUp() { + LicenseIdentification* id = license_.mutable_id(); + id->set_version(1); + id->set_type(STREAMING); + } + + virtual void AddContentKey( + const KeyId& key_id, bool set_level = false, + KeyContainer::SecurityLevel level = KeyContainer::SW_SECURE_CRYPTO, + bool set_hdcp = false, KeyContainer::OutputProtection::HDCP hdcp_value = + KeyContainer::OutputProtection::HDCP_NONE, + bool set_constraints = false, + std::vector* constraints = NULL) { + KeyContainer* key = license_.add_key(); + key->set_type(KeyContainer::CONTENT); + if (set_level) { + key->set_level(level); + } + if (set_hdcp) { + KeyContainer::OutputProtection* pro = key->mutable_required_protection(); + pro->set_hdcp(hdcp_value); + } + if (set_constraints) { + for (std::vector::iterator + it = constraints->begin(); it != constraints->end(); ++it) { + VideoResolutionConstraint* constraint = + key->add_video_resolution_constraints(); + constraint->set_min_resolution_pixels(it->min_resolution_pixels()); + constraint->set_max_resolution_pixels(it->max_resolution_pixels()); + constraint->mutable_required_protection()-> + set_hdcp(it->required_protection().hdcp()); + } + } + key->set_id(key_id); + } + + virtual void AddOperatorSessionKey( + const KeyId& key_id, bool set_perms = false, + KeyFlag encrypt = kKeyFlagNull, KeyFlag decrypt = kKeyFlagNull, + KeyFlag sign = kKeyFlagNull, KeyFlag verify = kKeyFlagNull) { + KeyContainer* non_content_key = license_.add_key(); + non_content_key->set_type(KeyContainer::OPERATOR_SESSION); + non_content_key->set_id(key_id); + if (set_perms) { + KeyContainer::OperatorSessionKeyPermissions* permissions = + non_content_key->mutable_operator_session_key_permissions(); + if (encrypt != kKeyFlagNull) { + permissions->set_allow_encrypt(encrypt == kKeyFlagTrue); + } + if (decrypt != kKeyFlagNull) { + permissions->set_allow_decrypt(decrypt == kKeyFlagTrue); + } + if (sign != kKeyFlagNull) { + permissions->set_allow_sign(sign == kKeyFlagTrue); + } + if (verify != kKeyFlagNull) { + permissions->set_allow_signature_verify(verify == kKeyFlagTrue); + } + } + } + + virtual void AddSigningKey(const KeyId& key_id) { + KeyContainer* key = license_.add_key(); + key->set_type(KeyContainer::SIGNING); + key->set_id(key_id); + } + + virtual void ExpectAllowedUsageContent( + const CdmKeyAllowedUsage& key_usage, KeyFlag secure, KeyFlag clear) { + EXPECT_EQ(key_usage.decrypt_to_secure_buffer, secure == kKeyFlagTrue); + EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == kKeyFlagTrue); + EXPECT_FALSE(key_usage.generic_encrypt); + EXPECT_FALSE(key_usage.generic_decrypt); + EXPECT_FALSE(key_usage.generic_sign); + EXPECT_FALSE(key_usage.generic_verify); + } + + virtual void ExpectAllowedUsageOperator( + const CdmKeyAllowedUsage& key_usage, KeyFlag encrypt, KeyFlag decrypt, + KeyFlag sign, KeyFlag verify) { + EXPECT_FALSE(key_usage.decrypt_to_secure_buffer); + EXPECT_FALSE(key_usage.decrypt_to_clear_buffer); + EXPECT_EQ(key_usage.generic_encrypt, encrypt == kKeyFlagTrue); + EXPECT_EQ(key_usage.generic_decrypt, decrypt == kKeyFlagTrue); + EXPECT_EQ(key_usage.generic_sign, sign == kKeyFlagTrue); + EXPECT_EQ(key_usage.generic_verify, verify == kKeyFlagTrue); + } + + virtual int NumContentKeys() { + return content_key_count_; + } + + virtual void StageContentKeys() { + content_key_count_ = 0; + AddContentKey(ck_sw_crypto, true, KeyContainer::SW_SECURE_CRYPTO); + content_key_count_++; + AddContentKey(ck_sw_decode, true, KeyContainer::SW_SECURE_DECODE); + content_key_count_++; + AddContentKey(ck_hw_crypto, true, KeyContainer::HW_SECURE_CRYPTO); + content_key_count_++; + AddContentKey(ck_hw_decode, true, KeyContainer::HW_SECURE_DECODE); + content_key_count_++; + AddContentKey(ck_hw_secure, true, KeyContainer::HW_SECURE_ALL); + content_key_count_++; + license_keys_.SetFromLicense(license_); + } + + virtual void StageOperatorSessionKeys() { + AddOperatorSessionKey(osk_decrypt, true, + kEncryptNull, kDecryptTrue, kSignNull, kVerifyNull); + AddOperatorSessionKey(osk_encrypt, true, + kEncryptTrue, kDecryptNull, kSignNull, kVerifyNull); + AddOperatorSessionKey(osk_sign, true, + kEncryptNull, kDecryptNull, kSignTrue, kVerifyNull); + AddOperatorSessionKey(osk_verify, true, + kEncryptNull, kDecryptNull, kSignNull, kVerifyTrue); + AddOperatorSessionKey(osk_encrypt_decrypt, true, + kEncryptTrue, kDecryptTrue, kSignNull, kVerifyNull); + AddOperatorSessionKey(osk_sign_verify, true, + kEncryptNull, kDecryptNull, kSignTrue, kVerifyTrue); + AddOperatorSessionKey(osk_all, true, + kEncryptTrue, kDecryptTrue, kSignTrue, kVerifyTrue); + license_keys_.SetFromLicense(license_); + } + + virtual void StageHdcpKeys() { + content_key_count_ = 0; + AddContentKey(ck_sw_crypto_NO_HDCP, true, KeyContainer::SW_SECURE_CRYPTO, + true, KeyContainer::OutputProtection::HDCP_NONE); + content_key_count_++; + AddContentKey(ck_hw_secure_NO_HDCP, true, KeyContainer::HW_SECURE_ALL, true, + KeyContainer::OutputProtection::HDCP_NONE); + content_key_count_++; + + AddContentKey(ck_sw_crypto_HDCP_V2_1, true, KeyContainer::SW_SECURE_CRYPTO, + true, KeyContainer::OutputProtection::HDCP_V2_1); + content_key_count_++; + AddContentKey(ck_hw_secure_HDCP_V2_1, true, KeyContainer::HW_SECURE_ALL, + true, KeyContainer::OutputProtection::HDCP_V2_1); + content_key_count_++; + + AddContentKey(ck_sw_crypto_HDCP_NO_OUTPUT, true, + KeyContainer::SW_SECURE_CRYPTO, true, + KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT); + content_key_count_++; + AddContentKey(ck_hw_secure_HDCP_NO_OUTPUT, true, + KeyContainer::HW_SECURE_ALL, true, + KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT); + content_key_count_++; + license_keys_.SetFromLicense(license_); + } + + virtual void AddConstraint( + std::vector& constraints, uint32_t min_res, + uint32_t max_res, bool set_hdcp = false, + KeyContainer::OutputProtection::HDCP hdcp = + KeyContainer::OutputProtection::HDCP_NONE) { + VideoResolutionConstraint constraint; + constraint.set_min_resolution_pixels(min_res); + constraint.set_max_resolution_pixels(max_res); + if (set_hdcp) { + constraint.mutable_required_protection()->set_hdcp(hdcp); + } + constraints.push_back(constraint); + } + + virtual void StageConstraintKeys() { + content_key_count_ = 0; + + std::vector constraints; + + AddConstraint(constraints, key_lo_res_min, key_lo_res_max); + + AddContentKey(ck_NO_HDCP_lo_res, true, KeyContainer::SW_SECURE_CRYPTO, true, + KeyContainer::OutputProtection::HDCP_NONE, true, + &constraints); + content_key_count_++; + + constraints.clear(); + AddConstraint(constraints, key_hi_res_min, key_hi_res_max); + + AddContentKey( + ck_HDCP_NO_OUTPUT_hi_res, true, KeyContainer::SW_SECURE_CRYPTO, true, + KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT, true, + &constraints); + content_key_count_++; + + constraints.clear(); + AddConstraint(constraints, key_top_res_min, key_top_res_max); + + AddContentKey(ck_HDCP_V2_1_max_res, true, KeyContainer::SW_SECURE_CRYPTO, + true, KeyContainer::OutputProtection::HDCP_V2_1, true, + &constraints); + content_key_count_++; + + constraints.clear(); + AddConstraint(constraints, key_lo_res_min, key_lo_res_max); + AddConstraint(constraints, key_hi_res_min, key_hi_res_max, + KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT); + + AddContentKey(ck_NO_HDCP_dual_res, true, KeyContainer::HW_SECURE_ALL, true, + KeyContainer::OutputProtection::HDCP_NONE, true, + &constraints); + content_key_count_++; + + license_keys_.SetFromLicense(license_); + } + + virtual void ExpectKeyStatusesEqual(CdmKeyStatusMap& key_status_map, + CdmKeyStatus expected_status) { + for (CdmKeyStatusMap::iterator it = key_status_map.begin(); + it != key_status_map.end(); ++it) { + EXPECT_TRUE(it->second == expected_status); + } + } + + virtual void ExpectKeyStatusEqual(CdmKeyStatusMap& key_status_map, + const KeyId& key_id, + CdmKeyStatus expected_status) { + for (CdmKeyStatusMap::iterator it = key_status_map.begin(); + it != key_status_map.end(); ++it) { + if (key_id == it->first) { + EXPECT_TRUE(it->second == expected_status); + } + } + } + + + int content_key_count_; + LicenseKeys license_keys_; + License license_; +}; + +TEST_F(LicenseKeysTest, Empty) { + EXPECT_TRUE(license_keys_.Empty()); +} + +TEST_F(LicenseKeysTest, NotEmpty) { + const KeyId c_key = "content_key"; + AddContentKey(c_key); + license_keys_.SetFromLicense(license_); + EXPECT_FALSE(license_keys_.Empty()); +} + +TEST_F(LicenseKeysTest, BadKeyId) { + const KeyId c_key = "content_key"; + const KeyId os_key = "op_sess_key"; + const KeyId unk_key = "unknown_key"; + CdmKeyAllowedUsage allowed_usage; + AddContentKey(c_key); + AddOperatorSessionKey(os_key); + license_keys_.SetFromLicense(license_); + EXPECT_FALSE(license_keys_.IsContentKey(unk_key)); + EXPECT_FALSE(license_keys_.CanDecryptContent(unk_key)); + EXPECT_TRUE(license_keys_.MeetsConstraints(unk_key)); + EXPECT_FALSE(license_keys_.GetAllowedUsage(unk_key, &allowed_usage)); +} + +TEST_F(LicenseKeysTest, SigningKey) { + const KeyId c_key = "content_key"; + const KeyId os_key = "op_sess_key"; + const KeyId sign_key = "signing_key"; + CdmKeyAllowedUsage allowed_usage; + AddSigningKey(sign_key); + AddContentKey(c_key); + AddOperatorSessionKey(os_key); + license_keys_.SetFromLicense(license_); + EXPECT_FALSE(license_keys_.IsContentKey(sign_key)); + EXPECT_FALSE(license_keys_.CanDecryptContent(sign_key)); + EXPECT_TRUE(license_keys_.MeetsConstraints(sign_key)); + EXPECT_FALSE(license_keys_.GetAllowedUsage(sign_key, &allowed_usage)); +} + +TEST_F(LicenseKeysTest, ContentKey) { + const KeyId c_key = "content_key"; + AddContentKey(c_key); + EXPECT_FALSE(license_keys_.IsContentKey(c_key)); + + license_keys_.SetFromLicense(license_); + EXPECT_TRUE(license_keys_.IsContentKey(c_key)); +} + +TEST_F(LicenseKeysTest, OperatorSessionKey) { + const KeyId os_key = "op_sess_key"; + EXPECT_FALSE(license_keys_.IsContentKey(os_key)); + AddOperatorSessionKey(os_key); + + license_keys_.SetFromLicense(license_); + EXPECT_FALSE(license_keys_.IsContentKey(os_key)); +} + +TEST_F(LicenseKeysTest, CanDecrypt) { + const KeyId os_key = "op_sess_key"; + const KeyId c_key = "content_key"; + EXPECT_FALSE(license_keys_.CanDecryptContent(c_key)); + EXPECT_FALSE(license_keys_.CanDecryptContent(os_key)); + AddOperatorSessionKey(os_key); + AddContentKey(c_key); + license_keys_.SetFromLicense(license_); + EXPECT_FALSE(license_keys_.CanDecryptContent(c_key)); + EXPECT_FALSE(license_keys_.CanDecryptContent(os_key)); + bool new_usable_keys = false; + bool any_change = false; + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(c_key)); + EXPECT_FALSE(license_keys_.CanDecryptContent(os_key)); + + any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired, + &new_usable_keys); + EXPECT_TRUE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_FALSE(license_keys_.CanDecryptContent(c_key)); + EXPECT_FALSE(license_keys_.CanDecryptContent(os_key)); +} + +TEST_F(LicenseKeysTest, AllowedUsageNull) { + const KeyId os_key = "op_sess_key"; + const KeyId c_key = "content_key"; + const KeyId sign_key = "signing_key"; + AddOperatorSessionKey(os_key); + AddContentKey(c_key); + AddSigningKey(sign_key); + license_keys_.SetFromLicense(license_); + CdmKeyAllowedUsage usage_1; + EXPECT_FALSE(license_keys_.GetAllowedUsage(sign_key, &usage_1)); + + CdmKeyAllowedUsage usage_2; + EXPECT_TRUE(license_keys_.GetAllowedUsage(c_key, &usage_2)); + ExpectAllowedUsageContent(usage_2, kContentClearTrue, kContentSecureTrue); + + CdmKeyAllowedUsage usage_3; + EXPECT_TRUE(license_keys_.GetAllowedUsage(os_key, &usage_3)); + ExpectAllowedUsageContent(usage_3, kContentClearFalse, kContentSecureFalse); +} + +TEST_F(LicenseKeysTest, AllowedUsageContent) { + StageContentKeys(); + CdmKeyAllowedUsage u_sw_crypto; + EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_crypto, &u_sw_crypto)); + ExpectAllowedUsageContent(u_sw_crypto, kContentSecureTrue, kContentClearTrue); + + CdmKeyAllowedUsage u_sw_decode; + EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_decode, &u_sw_decode)); + ExpectAllowedUsageContent(u_sw_decode, kContentSecureTrue, kContentClearTrue); + + CdmKeyAllowedUsage u_hw_crypto; + EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_crypto, &u_hw_crypto)); + ExpectAllowedUsageContent(u_hw_crypto, kContentSecureTrue, kContentClearTrue); + + CdmKeyAllowedUsage u_hw_decode; + EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_decode, &u_hw_decode)); + ExpectAllowedUsageContent(u_hw_decode, kContentSecureTrue, + kContentClearFalse); + + CdmKeyAllowedUsage u_hw_secure; + EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_secure, &u_hw_secure)); + ExpectAllowedUsageContent(u_hw_secure, kContentSecureTrue, + kContentClearFalse); +} + +TEST_F(LicenseKeysTest, AllowedUsageOperatorSession) { + StageOperatorSessionKeys(); + CdmKeyAllowedUsage u_encrypt; + EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_encrypt, &u_encrypt)); + ExpectAllowedUsageOperator(u_encrypt, kEncryptTrue, kDecryptFalse, kSignFalse, + kVerifyFalse); + + CdmKeyAllowedUsage u_decrypt; + EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_decrypt, &u_decrypt)); + ExpectAllowedUsageOperator(u_decrypt, kEncryptFalse, kDecryptTrue, kSignFalse, + kVerifyFalse); + + CdmKeyAllowedUsage u_sign; + EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_sign, &u_sign)); + ExpectAllowedUsageOperator(u_sign, kEncryptFalse, kDecryptFalse, kSignTrue, + kVerifyFalse); + + CdmKeyAllowedUsage u_verify; + EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_verify, &u_verify)); + ExpectAllowedUsageOperator(u_verify, kEncryptFalse, kDecryptFalse, kSignFalse, + kVerifyTrue); + + CdmKeyAllowedUsage u_encrypt_decrypt; + EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_encrypt_decrypt, + &u_encrypt_decrypt)); + ExpectAllowedUsageOperator(u_encrypt_decrypt, kEncryptTrue, kDecryptTrue, + kSignFalse, kVerifyFalse); + + CdmKeyAllowedUsage u_sign_verify; + EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_sign_verify, &u_sign_verify)); + ExpectAllowedUsageOperator(u_sign_verify, kEncryptFalse, kDecryptFalse, + kSignTrue, kVerifyTrue); + + CdmKeyAllowedUsage u_all; + EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_all, &u_all)); + ExpectAllowedUsageOperator(u_all, kEncryptTrue, kDecryptTrue, kSignTrue, + kVerifyTrue); +} + +TEST_F(LicenseKeysTest, ExtractKeyStatuses) { + CdmKeyStatusMap key_status_map; + StageOperatorSessionKeys(); + license_keys_.ExtractKeyStatuses(&key_status_map); + EXPECT_EQ(0, key_status_map.size()); + StageContentKeys(); + license_keys_.ExtractKeyStatuses(&key_status_map); + EXPECT_EQ(content_key_count_, key_status_map.size()); + ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError); +} + +TEST_F(LicenseKeysTest, KeyStatusChanges) { + bool new_usable_keys = false; + bool any_change = false; + CdmKeyStatusMap key_status_map; + StageOperatorSessionKeys(); + StageContentKeys(); + + license_keys_.ExtractKeyStatuses(&key_status_map); + EXPECT_EQ(content_key_count_, key_status_map.size()); + ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError); + + // change to pending + any_change = license_keys_.ApplyStatusChange(kKeyStatusPending, + &new_usable_keys); + EXPECT_TRUE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + EXPECT_EQ(content_key_count_, key_status_map.size()); + ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending); + + // change to pending (again) + any_change = license_keys_.ApplyStatusChange(kKeyStatusPending, + &new_usable_keys); + EXPECT_FALSE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + EXPECT_EQ(content_key_count_, key_status_map.size()); + ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending); + + // change to usable + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + EXPECT_EQ(content_key_count_, key_status_map.size()); + ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable); + + // change to usable (again) + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + EXPECT_FALSE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + EXPECT_EQ(content_key_count_, key_status_map.size()); + ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable); + + // change to expired + any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired, + &new_usable_keys); + EXPECT_TRUE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + EXPECT_EQ(content_key_count_, key_status_map.size()); + ExpectKeyStatusesEqual(key_status_map, kKeyStatusExpired); +} + +TEST_F(LicenseKeysTest, HdcpChanges) { + bool new_usable_keys = false; + bool any_change = false; + CdmKeyStatusMap key_status_map; + StageHdcpKeys(); + + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT)); + + license_keys_.ApplyConstraints(100, HDCP_NONE); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1, + kKeyStatusOutputNotAllowed); + + license_keys_.ApplyConstraints(100, HDCP_V1); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_FALSE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_NO_HDCP, kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1, + kKeyStatusOutputNotAllowed); + + license_keys_.ApplyConstraints(100, HDCP_V2_2); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1, + kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT, + kKeyStatusOutputNotAllowed); + + license_keys_.ApplyConstraints(100, HDCP_NO_DIGITAL_OUTPUT); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1, + kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT, + kKeyStatusUsable); + + license_keys_.ApplyConstraints(100, HDCP_NONE); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, + kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT, + kKeyStatusOutputNotAllowed); +} + +TEST_F(LicenseKeysTest, ConstraintChanges) { + bool new_usable_keys = false; + bool any_change = false; + CdmKeyStatusMap key_status_map; + StageConstraintKeys(); + + // No constraints set by device + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res)); + + // Low-res device, no HDCP support + license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1, + kKeyStatusOutputNotAllowed); + + // Hi-res device, HDCP_V1 support + license_keys_.ApplyConstraints(dev_hi_res, HDCP_V1); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res)); + + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_NO_HDCP, kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1, + kKeyStatusOutputNotAllowed); + + // Lo-res device, HDCP V2.2 support + license_keys_.ApplyConstraints(dev_lo_res, HDCP_V2_2); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1, + kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT, + kKeyStatusOutputNotAllowed); + + // Hi-res device, Maximal HDCP support + license_keys_.ApplyConstraints(dev_hi_res, HDCP_NO_DIGITAL_OUTPUT); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res)); + + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1, + kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT, + kKeyStatusUsable); + + // Lo-res device, no HDCP support + license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_TRUE(new_usable_keys); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res)); + + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res)); + EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, + kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT, + kKeyStatusOutputNotAllowed); + + // Too-high-res -- all keys rejected + license_keys_.ApplyConstraints(dev_top_res, HDCP_NONE); + any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, + &new_usable_keys); + + EXPECT_TRUE(any_change); + EXPECT_FALSE(new_usable_keys); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res)); + EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res)); + + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res)); + EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res)); + + license_keys_.ExtractKeyStatuses(&key_status_map); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, + kKeyStatusUsable); + ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT, + kKeyStatusOutputNotAllowed); +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/max_res_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/max_res_engine_unittest.cpp deleted file mode 100644 index 5d03a659..00000000 --- a/libwvdrmengine/cdm/core/test/max_res_engine_unittest.cpp +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. - -#include -#include -#include "crypto_session.h" -#include "license.h" -#include "max_res_engine.h" -#include "mock_clock.h" -#include "scoped_ptr.h" -#include "wv_cdm_types.h" - -namespace wvcdm { - -typedef ::video_widevine_server::sdk::License License; -typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer; -typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection - OutputProtection; -typedef ::video_widevine_server::sdk::License::KeyContainer:: - VideoResolutionConstraint VideoResolutionConstraint; -typedef ::google::protobuf::RepeatedPtrField KeyList; -typedef ::google::protobuf::RepeatedPtrField - ConstraintList; - -using namespace testing; - -namespace { - -const KeyId kKeyId1 = "357adc89f1673433c36c621f1b5c41ee"; -const KeyId kKeyId2 = "3d25f819250789ecfc9ed48cc99af164"; -const KeyId kKeyId3 = "fe3cf6b69e76c9a1c877922e1a661707"; -const KeyId kKeyId4 = "29a321b9886658078f916fdd41d6f570"; -const KeyId kKeyId5 = "cc5b031bcde371031c06822d935b9a63"; -const KeyId kKeyId6 = "90ac1332e4efc8acbaf929c8d321f50c"; - -const uint32_t kMinRes1 = 0; -const uint32_t kMaxRes1 = 2000; -const uint32_t kTargetRes1 = (kMinRes1 + kMaxRes1) / 2; -const uint32_t kMinRes2 = kMaxRes1; -const uint32_t kMaxRes2 = 4000; -const uint32_t kTargetRes2 = (kMinRes2 + kMaxRes2) / 2; -const uint32_t kTargetRes3 = kMaxRes2 + 1000; - -const OutputProtection::HDCP kHdcpDefault = OutputProtection::HDCP_V2; -const OutputProtection::HDCP kHdcpConstraint = OutputProtection::HDCP_V2_1; - -const int64_t kHdcpInterval = 10; - -class HdcpOnlyMockCryptoSession : public CryptoSession { - public: - MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*)); -}; - -} // namespace - -ACTION_P2(IncrementAndReturnPointee, p, a) { - *p += a; - return *p; -} - -class MaxResEngineTest : public Test { - protected: - virtual void SetUp() { - mock_clock_ = new NiceMock(); - current_time_ = 0; - - ON_CALL(*mock_clock_, GetCurrentTime()) - .WillByDefault( - IncrementAndReturnPointee(¤t_time_, kHdcpInterval)); - - max_res_engine_.reset(new MaxResEngine(&crypto_session_, mock_clock_)); - - KeyList* keys = license_.mutable_key(); - - // Key 1 - Content key w/ ID, no HDCP, no constraints - { - KeyContainer* key1 = keys->Add(); - key1->set_type(KeyContainer::CONTENT); - key1->set_id(kKeyId1); - } - - // Key 2 - Content key w/ ID, HDCP, no constraints - { - KeyContainer* key2 = keys->Add(); - key2->set_type(KeyContainer::CONTENT); - key2->set_id(kKeyId2); - key2->mutable_required_protection()->set_hdcp(kHdcpDefault); - } - - // Key 3 - Content key w/ ID, no HDCP, constraints - { - KeyContainer* key3 = keys->Add(); - key3->set_type(KeyContainer::CONTENT); - key3->set_id(kKeyId3); - AddConstraints(key3->mutable_video_resolution_constraints()); - } - - // Key 4 - Content key w/ ID, HDCP, constraints - { - KeyContainer* key4 = keys->Add(); - key4->set_type(KeyContainer::CONTENT); - key4->set_id(kKeyId4); - key4->mutable_required_protection()->set_hdcp(kHdcpDefault); - AddConstraints(key4->mutable_video_resolution_constraints()); - } - - // Key 5 - Content key w/o ID, HDCP, constraints - { - KeyContainer* key5 = keys->Add(); - key5->set_type(KeyContainer::CONTENT); - key5->mutable_required_protection()->set_hdcp(kHdcpDefault); - AddConstraints(key5->mutable_video_resolution_constraints()); - } - - // Key 6 - Non-content key - { - KeyContainer* key6 = keys->Add(); - key6->set_type(KeyContainer::OPERATOR_SESSION); - } - } - - void AddConstraints(ConstraintList* constraints) { - // Constraint 1 - Low-res and no HDCP - { - VideoResolutionConstraint* constraint1 = constraints->Add(); - constraint1->set_min_resolution_pixels(kMinRes1); - constraint1->set_max_resolution_pixels(kMaxRes1); - } - - // Constraint 2 - High-res and stricter HDCP - { - VideoResolutionConstraint* constraint2 = constraints->Add(); - constraint2->set_min_resolution_pixels(kMinRes2); - constraint2->set_max_resolution_pixels(kMaxRes2); - constraint2->mutable_required_protection()->set_hdcp(kHdcpConstraint); - } - } - - MockClock* mock_clock_; - int64_t current_time_; - StrictMock crypto_session_; - scoped_ptr max_res_engine_; - License license_; -}; - -TEST_F(MaxResEngineTest, IsPermissiveByDefault) { - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); - - max_res_engine_->OnTimerEvent(); - - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); -} - -TEST_F(MaxResEngineTest, IsPermissiveWithoutALicense) { - max_res_engine_->SetResolution(1, kTargetRes1); - max_res_engine_->OnTimerEvent(); - - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); -} - -TEST_F(MaxResEngineTest, IsPermissiveWithoutAResolution) { - max_res_engine_->SetLicense(license_); - max_res_engine_->OnTimerEvent(); - - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); -} - -TEST_F(MaxResEngineTest, HandlesResolutionsBasedOnConstraints) { - EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), - Return(true))); - - max_res_engine_->SetLicense(license_); - - max_res_engine_->SetResolution(1, kTargetRes1); - max_res_engine_->OnTimerEvent(); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); - - max_res_engine_->SetResolution(1, kTargetRes2); - max_res_engine_->OnTimerEvent(); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); - - max_res_engine_->SetResolution(1, kTargetRes3); - max_res_engine_->OnTimerEvent(); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); -} - -TEST_F(MaxResEngineTest, RequestsHdcpImmediatelyAndOnlyAfterInterval) { - int64_t start_time = current_time_; - - { - InSequence calls; - EXPECT_CALL(*mock_clock_, GetCurrentTime()).WillOnce(Return(start_time)); - EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) - .WillOnce( - DoAll(SetArgPointee<0>(HDCP_V2_2), - Return(true))); - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(start_time + kHdcpInterval / 2)) - .WillOnce(Return(start_time + kHdcpInterval)); - EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) - .WillOnce( - DoAll(SetArgPointee<0>(HDCP_V2_2), - Return(true))); - } - - max_res_engine_->SetLicense(license_); - max_res_engine_->SetResolution(1, kTargetRes1); - max_res_engine_->OnTimerEvent(); - max_res_engine_->OnTimerEvent(); - max_res_engine_->OnTimerEvent(); -} - -TEST_F(MaxResEngineTest, DoesNotRequestHdcpWithoutALicense) { - EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0); - - max_res_engine_->OnTimerEvent(); -} - -TEST_F(MaxResEngineTest, HandlesConstraintOverridingHdcp) { - EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(HDCP_V2), - Return(true))); - - max_res_engine_->SetLicense(license_); - - max_res_engine_->SetResolution(1, kTargetRes1); - max_res_engine_->OnTimerEvent(); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); - - max_res_engine_->SetResolution(1, kTargetRes2); - max_res_engine_->OnTimerEvent(); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); -} - -TEST_F(MaxResEngineTest, HandlesNoHdcp) { - EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(HDCP_NONE), - Return(true))); - - max_res_engine_->SetLicense(license_); - - max_res_engine_->SetResolution(1, kTargetRes1); - max_res_engine_->OnTimerEvent(); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); - - max_res_engine_->SetResolution(1, kTargetRes2); - max_res_engine_->OnTimerEvent(); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); -} - -TEST_F(MaxResEngineTest, IgnoresHdcpWithoutAResolution) { - EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0); - - max_res_engine_->SetLicense(license_); - max_res_engine_->OnTimerEvent(); - - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5)); - EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6)); -} - -} // wvcdm diff --git a/libwvdrmengine/cdm/core/test/policy_engine_constraints_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_constraints_unittest.cpp new file mode 100644 index 00000000..44b1956b --- /dev/null +++ b/libwvdrmengine/cdm/core/test/policy_engine_constraints_unittest.cpp @@ -0,0 +1,430 @@ +// Copyright 2016 Google Inc. All Rights Reserved. + +#include +#include +#include "crypto_session.h" +#include "license.h" +#include "policy_engine.h" +#include "mock_clock.h" +#include "scoped_ptr.h" +#include "wv_cdm_event_listener.h" +#include "wv_cdm_types.h" + +// protobuf generated classes. +using video_widevine_server::sdk::License; +using video_widevine_server::sdk::License_Policy; +using video_widevine_server::sdk::STREAMING; + +namespace wvcdm { + +typedef ::video_widevine_server::sdk::License License; +typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer; +typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection + OutputProtection; +typedef ::video_widevine_server::sdk::License::KeyContainer:: + VideoResolutionConstraint VideoResolutionConstraint; +typedef ::google::protobuf::RepeatedPtrField KeyList; +typedef ::google::protobuf::RepeatedPtrField + ConstraintList; + +using namespace testing; + +namespace { + +const CdmSessionId kSessionId = "mock_session_id"; + +const KeyId kKeyId1 = "1111kKeyId1"; +const KeyId kKeyId2 = "2222kKeyId2"; +const KeyId kKeyId3 = "3333kKeyId3"; +const KeyId kKeyId4 = "4444kKeyId4"; +const KeyId kKeyId5 = "5555kKeyId5"; +const KeyId kKeyId6 = "6666kKeyId6"; + +const uint32_t kMinRes1 = 0; +const uint32_t kMaxRes1 = 2000; +const uint32_t kMinRes2 = kMaxRes1; +const uint32_t kMaxRes2 = 4000; +const uint32_t kTargetRes1 = (kMinRes1 + kMaxRes1) / 2; +const uint32_t kTargetRes2 = (kMinRes2 + kMaxRes2) / 2; +const uint32_t kTargetRes3 = kMaxRes2 + 1000; + +const int64_t kRentalDuration = 604800; // 7 days +const int64_t kPlaybackDuration = 172800; // 48 hours +const int64_t kStreamingLicenseDuration = 300; + +const OutputProtection::HDCP kHdcpV2 = OutputProtection::HDCP_V2; +const OutputProtection::HDCP kHdcpV2_1 = OutputProtection::HDCP_V2_1; + +// should match kHdcpCheckInterval in policy_engine.cpp +const int64_t kHdcpInterval = 10; + +class HdcpOnlyMockCryptoSession : public CryptoSession { + public: + MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*)); +}; + +class MockCdmEventListener : public WvCdmEventListener { + public: + MOCK_METHOD1(OnSessionRenewalNeeded, void(const CdmSessionId& session_id)); + MOCK_METHOD3(OnSessionKeysChange, void(const CdmSessionId& session_id, + const CdmKeyStatusMap& keys_status, + bool has_new_usable_key)); + MOCK_METHOD2(OnExpirationUpdate, void(const CdmSessionId& session_id, + int64_t new_expiry_time_seconds)); +}; + +} // namespace + +class PolicyEngineConstraintsTest : public Test { + protected: + virtual void SetUp() { + mock_clock_ = new NiceMock(); + current_time_ = 0; + + policy_engine_.reset(new PolicyEngine(kSessionId, &mock_event_listener_, + &crypto_session_)); + InjectMockClock(); + + SetupLicense(); + } + + void AddConstraints(ConstraintList* constraints) { + // Constraint 1 - Low-res and no HDCP + { + VideoResolutionConstraint* c_lo_res_no_hdcp = constraints->Add(); + c_lo_res_no_hdcp->set_min_resolution_pixels(kMinRes1); + c_lo_res_no_hdcp->set_max_resolution_pixels(kMaxRes1); + } + + // Constraint 2 - High-res and stricter HDCP + { + VideoResolutionConstraint* c_hi_res_hdcp_v2 = constraints->Add(); + c_hi_res_hdcp_v2->set_min_resolution_pixels(kMinRes2); + c_hi_res_hdcp_v2->set_max_resolution_pixels(kMaxRes2); + c_hi_res_hdcp_v2->mutable_required_protection()->set_hdcp(kHdcpV2_1); + } + } + + void SetupLicense() { + license_.set_license_start_time(current_time_); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(1); + id->set_type(STREAMING); + + License_Policy* policy = license_.mutable_policy(); + policy = license_.mutable_policy(); + policy->set_can_play(true); + policy->set_can_persist(false); + policy->set_rental_duration_seconds(kRentalDuration); + policy->set_playback_duration_seconds(kPlaybackDuration); + policy->set_license_duration_seconds(kStreamingLicenseDuration); + + KeyList* keys = license_.mutable_key(); + + // Key 1 - Content key w/ ID, no HDCP, no constraints + { + KeyContainer* key1 = keys->Add(); + key1->set_type(KeyContainer::CONTENT); + key1->set_id(kKeyId1); + } + + // Key 2 - Content key w/ ID, HDCP, no constraints + { + KeyContainer* key2 = keys->Add(); + key2->set_type(KeyContainer::CONTENT); + key2->set_id(kKeyId2); + key2->mutable_required_protection()->set_hdcp(kHdcpV2); + } + + // Key 3 - Content key w/ ID, no HDCP, constraints + { + KeyContainer* key3 = keys->Add(); + key3->set_type(KeyContainer::CONTENT); + key3->set_id(kKeyId3); + AddConstraints(key3->mutable_video_resolution_constraints()); + } + + // Key 4 - Content key w/ ID, HDCP, constraints + { + KeyContainer* key4 = keys->Add(); + key4->set_type(KeyContainer::CONTENT); + key4->set_id(kKeyId4); + key4->mutable_required_protection()->set_hdcp(kHdcpV2); + AddConstraints(key4->mutable_video_resolution_constraints()); + } + + // Key 5 - Content key w/o ID, HDCP, constraints + { + KeyContainer* key5 = keys->Add(); + key5->set_type(KeyContainer::CONTENT); + key5->mutable_required_protection()->set_hdcp(kHdcpV2); + AddConstraints(key5->mutable_video_resolution_constraints()); + } + + // Key 6 - Non-content key + { + KeyContainer* key6 = keys->Add(); + key6->set_type(KeyContainer::OPERATOR_SESSION); + } + } + + void InjectMockClock() { + mock_clock_ = new MockClock(); + policy_engine_->set_clock(mock_clock_); + } + + void ExpectSessionKeysChange(CdmKeyStatus expected_key_status, + bool expected_has_new_usable_key) { + EXPECT_CALL(mock_event_listener_, + OnSessionKeysChange( + kSessionId, UnorderedElementsAre( + Pair(kKeyId1, expected_key_status), + Pair(kKeyId2, expected_key_status), + Pair(kKeyId3, expected_key_status), + Pair(kKeyId4, expected_key_status)), + expected_has_new_usable_key)); + } + + void ExpectSessionKeysChanges(const KeyId& k1, CdmKeyStatus expected_1, + const KeyId& k2, CdmKeyStatus expected_2, + const KeyId& k3, CdmKeyStatus expected_3, + const KeyId& k4, CdmKeyStatus expected_4, + bool expected_has_new_usable_key) { + EXPECT_CALL(mock_event_listener_, + OnSessionKeysChange( + kSessionId, UnorderedElementsAre( + Pair(k1, expected_1), + Pair(k2, expected_2), + Pair(k3, expected_3), + Pair(k4, expected_4)), + expected_has_new_usable_key)); + } + + scoped_ptr policy_engine_; + MockClock* mock_clock_; + int64_t current_time_; + StrictMock crypto_session_; + StrictMock mock_event_listener_; + License license_; +}; + +TEST_F(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()).Times(2); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + + policy_engine_->SetLicense(license_); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6)); +} + +TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) { + { + Sequence time; + for (int i=0; i<4; ++i) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time) + .WillOnce(Return(i * 10)); + } + } + { + Sequence key_change; + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable, + kKeyId2, kKeyStatusUsable, + kKeyId3, kKeyStatusOutputNotAllowed, + kKeyId4, kKeyStatusOutputNotAllowed, false); + } + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _)); + EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(true))); + + + policy_engine_->SetLicense(license_); + policy_engine_->NotifyResolution(1, kTargetRes1); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6)); + + policy_engine_->NotifyResolution(1, kTargetRes2); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6)); + + policy_engine_->NotifyResolution(1, kTargetRes3); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6)); +} + +TEST_F(PolicyEngineConstraintsTest, + RequestsHdcpImmediatelyAndOnlyAfterInterval) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(0)) + .WillOnce(Return(5)); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + + int64_t start_time = current_time_ + 5; + { + InSequence calls; + EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) + .WillOnce( + DoAll(SetArgPointee<0>(HDCP_V2_2), + Return(true))); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(start_time + kHdcpInterval / 2)) + .WillOnce(Return(start_time + kHdcpInterval)); + EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) + .WillOnce( + DoAll(SetArgPointee<0>(HDCP_V2_2), + Return(true))); + } + + policy_engine_->NotifyResolution(1, kTargetRes1); + policy_engine_->SetLicense(license_); + policy_engine_->OnTimerEvent(); + policy_engine_->OnTimerEvent(); + policy_engine_->OnTimerEvent(); +} + +TEST_F(PolicyEngineConstraintsTest, DoesNotRequestHdcpWithoutALicense) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(0)); + EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0); + + policy_engine_->OnTimerEvent(); +} + +TEST_F(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) { + { + Sequence time; + for (int i=0; i<3; ++i) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time) + .WillOnce(Return(i * 10)); + } + } + { + Sequence key_change; + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable, + kKeyId2, kKeyStatusUsable, + kKeyId3, kKeyStatusOutputNotAllowed, + kKeyId4, kKeyStatusOutputNotAllowed, false); + } + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _)); + EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_V2), + Return(true))); + + policy_engine_->SetLicense(license_); + policy_engine_->NotifyResolution(1, kTargetRes1); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6)); + + policy_engine_->NotifyResolution(1, kTargetRes2); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6)); +} + +TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) { + { + Sequence time; + for (int i=0; i<3; ++i) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time) + .WillOnce(Return(i * 10)); + } + } + { + Sequence key_change; + ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable, + kKeyId2, kKeyStatusOutputNotAllowed, + kKeyId3, kKeyStatusUsable, + kKeyId4, kKeyStatusOutputNotAllowed, false); + ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable, + kKeyId2, kKeyStatusOutputNotAllowed, + kKeyId3, kKeyStatusOutputNotAllowed, + kKeyId4, kKeyStatusOutputNotAllowed, false); + } + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NONE), + Return(true))); + + policy_engine_->SetLicense(license_); + + policy_engine_->NotifyResolution(1, kTargetRes1); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId2)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6)); + + policy_engine_->NotifyResolution(1, kTargetRes2); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId2)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6)); +} + +TEST_F(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) { + { + Sequence time; + for (int i=0; i<2; ++i) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time) + .WillOnce(Return(i * 10)); + } + } + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _)); + EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0); + + policy_engine_->SetLicense(license_); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3)); + EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5)); + EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6)); +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index 68b30b15..a74dcc3e 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -42,6 +42,7 @@ const char* kRenewalServerUrl = const KeyId kKeyId = "357adc89f1673433c36c621f1b5c41ee"; const KeyId kAnotherKeyId = "another_key_id"; const KeyId kSomeRandomKeyId = "some_random_key_id"; +const KeyId kUnknownKeyId = "some_random_unknown_key_id"; const CdmSessionId kSessionId = "mock_session_id"; int64_t GetLicenseRenewalDelay(int64_t license_duration) { @@ -60,13 +61,6 @@ class MockCdmEventListener : public WvCdmEventListener { int64_t new_expiry_time_seconds)); }; -class MockMaxResEngine : public MaxResEngine { - public: - MockMaxResEngine() : MaxResEngine(NULL) {} - - MOCK_METHOD1(CanDecrypt, bool(const KeyId& key_id)); -}; - } // namespace // protobuf generated classes. @@ -138,11 +132,6 @@ class PolicyEngineTest : public ::testing::Test { policy_engine_->set_clock(mock_clock_); } - void InjectMockMaxResEngine() { - mock_max_res_engine_ = new MockMaxResEngine(); - policy_engine_->set_max_res_engine(mock_max_res_engine_); - } - void ExpectSessionKeysChange(CdmKeyStatus expected_key_status, bool expected_has_new_usable_key) { EXPECT_CALL(mock_event_listener_, @@ -165,7 +154,6 @@ class PolicyEngineTest : public ::testing::Test { StrictMock mock_event_listener_; MockClock* mock_clock_; - MockMaxResEngine* mock_max_res_engine_; scoped_ptr policy_engine_; License license_; MockFunction check_; @@ -1036,56 +1024,204 @@ TEST_F(PolicyEngineTest, MultipleKeysInLicense) { EXPECT_FALSE(policy_engine_->CanDecrypt(kSomeRandomKeyId)); } -TEST_F(PolicyEngineTest, KeysOutputNotAllowedByMaxResEngine) { +class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest { + protected: + enum KeyFlag { + kKeyFlagNull, + kKeyFlagFalse, + kKeyFlagTrue + }; + + static const KeyFlag kEncryptNull = kKeyFlagNull; + static const KeyFlag kEncryptFalse = kKeyFlagFalse; + static const KeyFlag kEncryptTrue = kKeyFlagTrue; + static const KeyFlag kDecryptNull = kKeyFlagNull; + static const KeyFlag kDecryptFalse = kKeyFlagFalse; + static const KeyFlag kDecryptTrue = kKeyFlagTrue; + static const KeyFlag kSignNull = kKeyFlagNull; + static const KeyFlag kSignFalse = kKeyFlagFalse; + static const KeyFlag kSignTrue = kKeyFlagTrue; + static const KeyFlag kVerifyNull = kKeyFlagNull; + static const KeyFlag kVerifyFalse = kKeyFlagFalse; + static const KeyFlag kVerifyTrue = kKeyFlagTrue; + + static const KeyFlag kContentSecureFalse = kKeyFlagFalse; + static const KeyFlag kContentSecureTrue = kKeyFlagTrue; + static const KeyFlag kContentClearFalse = kKeyFlagFalse; + static const KeyFlag kContentClearTrue = kKeyFlagTrue; + + + void ExpectAllowedContentKeySettings(const CdmKeyAllowedUsage& key_usage, + KeyFlag secure, KeyFlag clear) { + EXPECT_EQ(key_usage.decrypt_to_secure_buffer, secure == kKeyFlagTrue); + EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == kKeyFlagTrue); + EXPECT_FALSE(key_usage.generic_encrypt); + EXPECT_FALSE(key_usage.generic_decrypt); + EXPECT_FALSE(key_usage.generic_sign); + EXPECT_FALSE(key_usage.generic_verify); + } + + void ExpectAllowedOperatorKeySettings(const CdmKeyAllowedUsage& key_usage, + KeyFlag encrypt, KeyFlag decrypt, + KeyFlag sign, KeyFlag verify) { + EXPECT_FALSE(key_usage.decrypt_to_secure_buffer); + EXPECT_FALSE(key_usage.decrypt_to_clear_buffer); + EXPECT_EQ(key_usage.generic_encrypt, encrypt == kKeyFlagTrue); + EXPECT_EQ(key_usage.generic_decrypt, decrypt == kKeyFlagTrue); + EXPECT_EQ(key_usage.generic_sign, sign == kKeyFlagTrue); + EXPECT_EQ(key_usage.generic_verify, verify == kKeyFlagTrue); + } + + void ExpectSecureContentKey(const KeyId& key_id) { + CdmKeyAllowedUsage key_usage; + EXPECT_EQ(NO_ERROR, + policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage)); + + ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue, + kContentSecureFalse); + } + + void ExpectLessSecureContentKey(const KeyId& key_id) { + CdmKeyAllowedUsage key_usage; + EXPECT_EQ(NO_ERROR, + policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage)); + + ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue, + kContentSecureTrue); + } + + void ExpectOperatorSessionKey(const KeyId& key_id, KeyFlag encrypt, + KeyFlag decrypt, KeyFlag sign, KeyFlag verify) { + CdmKeyAllowedUsage key_usage; + EXPECT_EQ(NO_ERROR, + policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage)); + + ExpectAllowedOperatorKeySettings(key_usage, encrypt, decrypt, sign, verify); + } + + void AddOperatorSessionKey(const KeyId& key_id, KeyFlag encrypt, + KeyFlag decrypt, KeyFlag sign, KeyFlag verify) { + License::KeyContainer* non_content_key = license_.add_key(); + non_content_key->set_type(License::KeyContainer::OPERATOR_SESSION); + non_content_key->set_id(key_id); + License::KeyContainer::OperatorSessionKeyPermissions* permissions = + non_content_key->mutable_operator_session_key_permissions(); + if (encrypt != kKeyFlagNull) { + permissions->set_allow_encrypt(encrypt == kKeyFlagTrue); + } + if (decrypt != kKeyFlagNull) { + permissions->set_allow_decrypt(decrypt == kKeyFlagTrue); + } + if (sign != kKeyFlagNull) { + permissions->set_allow_sign(sign == kKeyFlagTrue); + } + if (verify != kKeyFlagNull) { + permissions->set_allow_signature_verify(verify == kKeyFlagTrue); + } + } +}; + +TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageBasic) { + + const KeyId kGenericKeyId = "oper_session_key"; + license_.clear_key(); + + // most secure License::KeyContainer* content_key = license_.add_key(); content_key->set_type(License::KeyContainer::CONTENT); content_key->set_id(kKeyId); + content_key->set_level(License::KeyContainer::HW_SECURE_ALL); + + // generic operator session key (sign) + AddOperatorSessionKey(kGenericKeyId, kEncryptNull, kDecryptNull, kSignTrue, + kVerifyNull); + + License::KeyContainer* content_key_without_id = license_.add_key(); + content_key_without_id->set_type(License::KeyContainer::CONTENT); + + // default level - less secure License::KeyContainer* another_content_key = license_.add_key(); another_content_key->set_type(License::KeyContainer::CONTENT); another_content_key->set_id(kAnotherKeyId); EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(kLicenseStartTime + 1)) - .WillOnce(Return(kLicenseStartTime + 5)) - .WillOnce(Return(kLicenseStartTime + 8)) - .WillOnce(Return(kLicenseStartTime + 10)); + .WillOnce(Return(kLicenseStartTime + 1)); - InjectMockMaxResEngine(); - EXPECT_CALL(*mock_max_res_engine_, CanDecrypt(kKeyId)) - .WillOnce(Return(false)) - .WillOnce(Return(true)) - .WillOnce(Return(false)) - .WillOnce(Return(true)); - EXPECT_CALL(*mock_max_res_engine_, CanDecrypt(kAnotherKeyId)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(true)) - .WillOnce(Return(true)); - - InSequence s; - ExpectSessionKeysChange(kKeyStatusOutputNotAllowed, - kKeyStatusOutputNotAllowed, false); - EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _)); - ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusOutputNotAllowed, true); - ExpectSessionKeysChange(kKeyStatusOutputNotAllowed, kKeyStatusUsable, true); ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _)); policy_engine_->SetLicense(license_); - EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId)); - EXPECT_FALSE(policy_engine_->CanDecrypt(kAnotherKeyId)); - policy_engine_->OnTimerEvent(); - EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId)); - EXPECT_FALSE(policy_engine_->CanDecrypt(kAnotherKeyId)); + ExpectSecureContentKey(kKeyId); + ExpectLessSecureContentKey(kAnotherKeyId); + ExpectOperatorSessionKey(kGenericKeyId, kEncryptNull, kDecryptNull, + kSignTrue, kVerifyNull); - policy_engine_->OnTimerEvent(); - EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId)); - EXPECT_TRUE(policy_engine_->CanDecrypt(kAnotherKeyId)); + CdmKeyAllowedUsage key_usage; + EXPECT_EQ(KEY_NOT_FOUND_1, + policy_engine_->QueryKeyAllowedUsage(kUnknownKeyId, &key_usage)); +} - policy_engine_->OnTimerEvent(); - EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId)); - EXPECT_TRUE(policy_engine_->CanDecrypt(kAnotherKeyId)); +TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageGeneric) { + + const KeyId kGenericEncryptKeyId = "oper_session_key_1"; + const KeyId kGenericDecryptKeyId = "oper_session_key_2"; + const KeyId kGenericSignKeyId = "oper_session_key_3"; + const KeyId kGenericVerifyKeyId = "oper_session_key_4"; + const KeyId kGenericFullKeyId = "oper_session_key_5"; + const KeyId kGenericExplicitKeyId = "oper_session_key_6"; + + license_.clear_key(); + + // more secure + License::KeyContainer* content_key = license_.add_key(); + content_key->set_type(License::KeyContainer::CONTENT); + content_key->set_id(kKeyId); + content_key->set_level(License::KeyContainer::HW_SECURE_DECODE); + + // less secure + License::KeyContainer* another_content_key = license_.add_key(); + another_content_key->set_type(License::KeyContainer::CONTENT); + another_content_key->set_id(kAnotherKeyId); + another_content_key->set_level(License::KeyContainer::HW_SECURE_CRYPTO); + + // generic operator session keys + AddOperatorSessionKey(kGenericSignKeyId, kEncryptNull, kDecryptNull, + kSignTrue, kVerifyNull); + AddOperatorSessionKey(kGenericEncryptKeyId, kEncryptTrue, kDecryptNull, + kSignNull, kVerifyNull); + AddOperatorSessionKey(kGenericDecryptKeyId, kEncryptNull, kDecryptTrue, + kSignNull, kVerifyNull); + AddOperatorSessionKey(kGenericVerifyKeyId, kEncryptNull, kDecryptNull, + kSignNull, kVerifyTrue); + AddOperatorSessionKey(kGenericFullKeyId, kEncryptTrue, kDecryptTrue, + kSignTrue, kVerifyTrue); + AddOperatorSessionKey(kGenericExplicitKeyId, kEncryptFalse, kDecryptTrue, + kSignFalse, kVerifyTrue); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)); + + ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _)); + + policy_engine_->SetLicense(license_); + + ExpectSecureContentKey(kKeyId); + ExpectLessSecureContentKey(kAnotherKeyId); + ExpectOperatorSessionKey(kGenericEncryptKeyId, kEncryptTrue, kDecryptFalse, + kSignFalse, kVerifyFalse); + ExpectOperatorSessionKey(kGenericDecryptKeyId, kEncryptFalse, kDecryptTrue, + kSignFalse, kVerifyFalse); + ExpectOperatorSessionKey(kGenericSignKeyId, kEncryptFalse, kDecryptFalse, + kSignTrue, kVerifyFalse); + ExpectOperatorSessionKey(kGenericVerifyKeyId, kEncryptFalse, kDecryptFalse, + kSignFalse, kVerifyTrue); + ExpectOperatorSessionKey(kGenericFullKeyId, kEncryptTrue, kDecryptTrue, + kSignTrue, kVerifyTrue); + ExpectOperatorSessionKey(kGenericExplicitKeyId, kEncryptFalse, kDecryptTrue, + kSignFalse, kVerifyTrue); } class PolicyEngineQueryTest : public PolicyEngineTest { diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index 10181c82..2d470d44 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -26,59 +26,74 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { break; case DEVICE_REVOKED: *os << "DEVICE_REVOKED"; break; - case INSUFFICIENT_CRYPTO_RESOURCES: *os << "INSUFFICIENT_CRYPTO_RESOURCES"; + case INSUFFICIENT_CRYPTO_RESOURCES: *os << "INSUFFICIENT_CRYPTO_RESOURCES"; break; case ADD_KEY_ERROR: *os << "ADD_KEY_ERROR"; break; - case CERT_PROVISIONING_GET_KEYBOX_ERROR_1: *os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_1"; + case CERT_PROVISIONING_GET_KEYBOX_ERROR_1: + *os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_1"; break; - case CERT_PROVISIONING_GET_KEYBOX_ERROR_2: *os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_2"; + case CERT_PROVISIONING_GET_KEYBOX_ERROR_2: + *os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_2"; break; - case CERT_PROVISIONING_INVALID_CERT_TYPE: *os << "CERT_PROVISIONING_INVALID_CERT_TYPE"; + case CERT_PROVISIONING_INVALID_CERT_TYPE: + *os << "CERT_PROVISIONING_INVALID_CERT_TYPE"; break; - case CERT_PROVISIONING_REQUEST_ERROR_1: *os << "CERT_PROVISIONING_REQUEST_ERROR_1"; + case CERT_PROVISIONING_REQUEST_ERROR_1: + *os << "CERT_PROVISIONING_REQUEST_ERROR_1"; break; - case CERT_PROVISIONING_REQUEST_ERROR_2: *os << "CERT_PROVISIONING_REQUEST_ERROR_2"; + case CERT_PROVISIONING_REQUEST_ERROR_2: + *os << "CERT_PROVISIONING_REQUEST_ERROR_2"; break; - case CERT_PROVISIONING_REQUEST_ERROR_3: *os << "CERT_PROVISIONING_REQUEST_ERROR_3"; + case CERT_PROVISIONING_REQUEST_ERROR_3: + *os << "CERT_PROVISIONING_REQUEST_ERROR_3"; break; - case CERT_PROVISIONING_REQUEST_ERROR_4: *os << "CERT_PROVISIONING_REQUEST_ERROR_4"; + case CERT_PROVISIONING_REQUEST_ERROR_4: + *os << "CERT_PROVISIONING_REQUEST_ERROR_4"; break; - case CERT_PROVISIONING_RESPONSE_ERROR_1: *os << "CERT_PROVISIONING_RESPONSE_ERROR_1"; + case CERT_PROVISIONING_RESPONSE_ERROR_1: + *os << "CERT_PROVISIONING_RESPONSE_ERROR_1"; break; - case CERT_PROVISIONING_RESPONSE_ERROR_2: *os << "CERT_PROVISIONING_RESPONSE_ERROR_2"; + case CERT_PROVISIONING_RESPONSE_ERROR_2: + *os << "CERT_PROVISIONING_RESPONSE_ERROR_2"; break; - case CERT_PROVISIONING_RESPONSE_ERROR_3: *os << "CERT_PROVISIONING_RESPONSE_ERROR_3"; + case CERT_PROVISIONING_RESPONSE_ERROR_3: + *os << "CERT_PROVISIONING_RESPONSE_ERROR_3"; break; - case CERT_PROVISIONING_RESPONSE_ERROR_4: *os << "CERT_PROVISIONING_RESPONSE_ERROR_4"; + case CERT_PROVISIONING_RESPONSE_ERROR_4: + *os << "CERT_PROVISIONING_RESPONSE_ERROR_4"; break; - case CERT_PROVISIONING_RESPONSE_ERROR_5: *os << "CERT_PROVISIONING_RESPONSE_ERROR_5"; + case CERT_PROVISIONING_RESPONSE_ERROR_5: + *os << "CERT_PROVISIONING_RESPONSE_ERROR_5"; break; - case CERT_PROVISIONING_RESPONSE_ERROR_6: *os << "CERT_PROVISIONING_RESPONSE_ERROR_6"; + case CERT_PROVISIONING_RESPONSE_ERROR_6: + *os << "CERT_PROVISIONING_RESPONSE_ERROR_6"; break; - case CERT_PROVISIONING_RESPONSE_ERROR_7: *os << "CERT_PROVISIONING_RESPONSE_ERROR_7"; + case CERT_PROVISIONING_RESPONSE_ERROR_7: + *os << "CERT_PROVISIONING_RESPONSE_ERROR_7"; break; - case CERT_PROVISIONING_RESPONSE_ERROR_8: *os << "CERT_PROVISIONING_RESPONSE_ERROR_8"; + case CERT_PROVISIONING_RESPONSE_ERROR_8: + *os << "CERT_PROVISIONING_RESPONSE_ERROR_8"; break; - case CRYPTO_SESSION_OPEN_ERROR_1: *os << "CRYPTO_SESSION_OPEN_ERROR_1"; + case CRYPTO_SESSION_OPEN_ERROR_1: *os << "CRYPTO_SESSION_OPEN_ERROR_1"; break; - case CRYPTO_SESSION_OPEN_ERROR_2: *os << "CRYPTO_SESSION_OPEN_ERROR_2"; + case CRYPTO_SESSION_OPEN_ERROR_2: *os << "CRYPTO_SESSION_OPEN_ERROR_2"; break; - case CRYPTO_SESSION_OPEN_ERROR_3: *os << "CRYPTO_SESSION_OPEN_ERROR_3"; + case CRYPTO_SESSION_OPEN_ERROR_3: *os << "CRYPTO_SESSION_OPEN_ERROR_3"; break; - case CRYPTO_SESSION_OPEN_ERROR_4: *os << "CRYPTO_SESSION_OPEN_ERROR_4"; + case CRYPTO_SESSION_OPEN_ERROR_4: *os << "CRYPTO_SESSION_OPEN_ERROR_4"; break; - case CRYPTO_SESSION_OPEN_ERROR_5: *os << "CRYPTO_SESSION_OPEN_ERROR_5"; + case CRYPTO_SESSION_OPEN_ERROR_5: *os << "CRYPTO_SESSION_OPEN_ERROR_5"; break; case DECRYPT_NOT_READY: *os << "DECRYPT_NOT_READY"; break; - case DEVICE_CERTIFICATE_ERROR_1: *os << "DEVICE_CERTIFICATE_ERROR_1"; + case DEVICE_CERTIFICATE_ERROR_1: *os << "DEVICE_CERTIFICATE_ERROR_1"; break; - case DEVICE_CERTIFICATE_ERROR_2: *os << "DEVICE_CERTIFICATE_ERROR_2"; + case DEVICE_CERTIFICATE_ERROR_2: *os << "DEVICE_CERTIFICATE_ERROR_2"; break; - case DEVICE_CERTIFICATE_ERROR_3: *os << "DEVICE_CERTIFICATE_ERROR_3"; + case DEVICE_CERTIFICATE_ERROR_3: *os << "DEVICE_CERTIFICATE_ERROR_3"; break; - case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4"; + case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4"; break; case EMPTY_KEY_DATA_1: *os << "EMPTY_KEY_DATA_1"; break; @@ -96,25 +111,27 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { break; case EMPTY_LICENSE_RENEWAL: *os << "EMPTY_LICENSE_RENEWAL"; break; - case EMPTY_LICENSE_RESPONSE_1: *os << "EMPTY_LICENSE_RESPONSE_1"; + case EMPTY_LICENSE_RESPONSE_1: *os << "EMPTY_LICENSE_RESPONSE_1"; break; - case EMPTY_LICENSE_RESPONSE_2: *os << "EMPTY_LICENSE_RESPONSE_2"; + case EMPTY_LICENSE_RESPONSE_2: *os << "EMPTY_LICENSE_RESPONSE_2"; break; - case EMPTY_PROVISIONING_CERTIFICATE_1: *os << "EMPTY_PROVISIONING_CERTIFICATE_1"; + case EMPTY_PROVISIONING_CERTIFICATE_1: + *os << "EMPTY_PROVISIONING_CERTIFICATE_1"; break; - case EMPTY_PROVISIONING_RESPONSE: *os << "EMPTY_PROVISIONING_RESPONSE"; + case EMPTY_PROVISIONING_RESPONSE: *os << "EMPTY_PROVISIONING_RESPONSE"; break; case EMPTY_SESSION_ID: *os << "EMPTY_SESSION_ID"; break; - case GENERATE_DERIVED_KEYS_ERROR: *os << "GENERATE_DERIVED_KEYS_ERROR"; + case GENERATE_DERIVED_KEYS_ERROR: *os << "GENERATE_DERIVED_KEYS_ERROR"; break; - case LICENSE_RENEWAL_NONCE_GENERATION_ERROR: *os << "LICENSE_RENEWAL_NONCE_GENERATION_ERROR"; + case LICENSE_RENEWAL_NONCE_GENERATION_ERROR: + *os << "LICENSE_RENEWAL_NONCE_GENERATION_ERROR"; break; - case GENERATE_USAGE_REPORT_ERROR: *os << "GENERATE_USAGE_REPORT_ERROR"; + case GENERATE_USAGE_REPORT_ERROR: *os << "GENERATE_USAGE_REPORT_ERROR"; break; case GET_LICENSE_ERROR: *os << "GET_LICENSE_ERROR"; break; - case GET_RELEASED_LICENSE_ERROR: *os << "GET_RELEASED_LICENSE_ERROR"; + case GET_RELEASED_LICENSE_ERROR: *os << "GET_RELEASED_LICENSE_ERROR"; break; case GET_USAGE_INFO_ERROR_1: *os << "GET_USAGE_INFO_ERROR_1"; break; @@ -126,51 +143,60 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { break; case INIT_DATA_NOT_FOUND: *os << "INIT_DATA_NOT_FOUND"; break; - case INVALID_CRYPTO_SESSION_1: *os << "INVALID_CRYPTO_SESSION_1"; + case INVALID_CRYPTO_SESSION_1: *os << "INVALID_CRYPTO_SESSION_1"; break; - case INVALID_CRYPTO_SESSION_2: *os << "INVALID_CRYPTO_SESSION_2"; + case INVALID_CRYPTO_SESSION_2: *os << "INVALID_CRYPTO_SESSION_2"; break; - case INVALID_CRYPTO_SESSION_3: *os << "INVALID_CRYPTO_SESSION_3"; + case INVALID_CRYPTO_SESSION_3: *os << "INVALID_CRYPTO_SESSION_3"; break; - case INVALID_CRYPTO_SESSION_4: *os << "INVALID_CRYPTO_SESSION_4"; + case INVALID_CRYPTO_SESSION_4: *os << "INVALID_CRYPTO_SESSION_4"; break; - case INVALID_CRYPTO_SESSION_5: *os << "INVALID_CRYPTO_SESSION_5"; + case INVALID_CRYPTO_SESSION_5: *os << "INVALID_CRYPTO_SESSION_5"; break; - case INVALID_DECRYPT_PARAMETERS_ENG_1: *os << "INVALID_DECRYPT_PARAMETERS_ENG_1"; + case INVALID_DECRYPT_PARAMETERS_ENG_1: + *os << "INVALID_DECRYPT_PARAMETERS_ENG_1"; break; - case INVALID_DECRYPT_PARAMETERS_ENG_2: *os << "INVALID_DECRYPT_PARAMETERS_ENG_2"; + case INVALID_DECRYPT_PARAMETERS_ENG_2: + *os << "INVALID_DECRYPT_PARAMETERS_ENG_2"; break; - case INVALID_DECRYPT_PARAMETERS_ENG_3: *os << "INVALID_DECRYPT_PARAMETERS_ENG_3"; + case INVALID_DECRYPT_PARAMETERS_ENG_3: + *os << "INVALID_DECRYPT_PARAMETERS_ENG_3"; break; - case INVALID_DECRYPT_PARAMETERS_ENG_4: *os << "INVALID_DECRYPT_PARAMETERS_ENG_4"; + case INVALID_DECRYPT_PARAMETERS_ENG_4: + *os << "INVALID_DECRYPT_PARAMETERS_ENG_4"; break; - case INVALID_DEVICE_CERTIFICATE_TYPE: *os << "INVALID_DEVICE_CERTIFICATE_TYPE"; + case INVALID_DEVICE_CERTIFICATE_TYPE: + *os << "INVALID_DEVICE_CERTIFICATE_TYPE"; break; case INVALID_KEY_SYSTEM: *os << "INVALID_KEY_SYSTEM"; break; - case INVALID_LICENSE_RESPONSE: *os << "INVALID_LICENSE_RESPONSE"; + case INVALID_LICENSE_RESPONSE: *os << "INVALID_LICENSE_RESPONSE"; break; case INVALID_LICENSE_TYPE: *os << "INVALID_LICENSE_TYPE"; break; - case INVALID_PARAMETERS_ENG_1: *os << "INVALID_PARAMETERS_ENG_1"; + case INVALID_PARAMETERS_ENG_1: *os << "INVALID_PARAMETERS_ENG_1"; break; - case INVALID_PARAMETERS_ENG_2: *os << "INVALID_PARAMETERS_ENG_2"; + case INVALID_PARAMETERS_ENG_2: *os << "INVALID_PARAMETERS_ENG_2"; break; - case INVALID_PARAMETERS_ENG_3: *os << "INVALID_PARAMETERS_ENG_3"; + case INVALID_PARAMETERS_ENG_3: *os << "INVALID_PARAMETERS_ENG_3"; break; - case INVALID_PARAMETERS_ENG_4: *os << "INVALID_PARAMETERS_ENG_4"; + case INVALID_PARAMETERS_ENG_4: *os << "INVALID_PARAMETERS_ENG_4"; break; - case INVALID_PARAMETERS_LIC_1: *os << "INVALID_PARAMETERS_LIC_1"; + case INVALID_PARAMETERS_LIC_1: *os << "INVALID_PARAMETERS_LIC_1"; break; - case INVALID_PARAMETERS_LIC_2: *os << "INVALID_PARAMETERS_LIC_2"; + case INVALID_PARAMETERS_LIC_2: *os << "INVALID_PARAMETERS_LIC_2"; break; - case INVALID_PROVISIONING_PARAMETERS_1: *os << "INVALID_PROVISIONING_PARAMETERS_1"; + case INVALID_PROVISIONING_PARAMETERS_1: + *os << "INVALID_PROVISIONING_PARAMETERS_1"; break; - case INVALID_PROVISIONING_PARAMETERS_2: *os << "INVALID_PROVISIONING_PARAMETERS_2"; + case INVALID_PROVISIONING_PARAMETERS_2: + *os << "INVALID_PROVISIONING_PARAMETERS_2"; break; - case INVALID_PROVISIONING_REQUEST_PARAM_1: *os << "INVALID_PROVISIONING_REQUEST_PARAM_1"; + case INVALID_PROVISIONING_REQUEST_PARAM_1: + *os << "INVALID_PROVISIONING_REQUEST_PARAM_1"; break; - case INVALID_PROVISIONING_REQUEST_PARAM_2: *os << "INVALID_PROVISIONING_REQUEST_PARAM_2"; + case INVALID_PROVISIONING_REQUEST_PARAM_2: + *os << "INVALID_PROVISIONING_REQUEST_PARAM_2"; break; case INVALID_QUERY_KEY: *os << "INVALID_QUERY_KEY"; break; @@ -188,22 +214,28 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { break; case LICENSE_ID_NOT_FOUND: *os << "LICENSE_ID_NOT_FOUND"; break; - case LICENSE_PARSER_INIT_ERROR: *os << "LICENSE_PARSER_INIT_ERROR"; + case LICENSE_PARSER_INIT_ERROR: *os << "LICENSE_PARSER_INIT_ERROR"; break; - case LICENSE_PARSER_NOT_INITIALIZED_1: *os << "LICENSE_PARSER_NOT_INITIALIZED_1"; + case LICENSE_PARSER_NOT_INITIALIZED_1: + *os << "LICENSE_PARSER_NOT_INITIALIZED_1"; break; - case LICENSE_PARSER_NOT_INITIALIZED_2: *os << "LICENSE_PARSER_NOT_INITIALIZED_2"; + case LICENSE_PARSER_NOT_INITIALIZED_2: + *os << "LICENSE_PARSER_NOT_INITIALIZED_2"; break; - case LICENSE_PARSER_NOT_INITIALIZED_3: *os << "LICENSE_PARSER_NOT_INITIALIZED_3"; + case LICENSE_PARSER_NOT_INITIALIZED_3: + *os << "LICENSE_PARSER_NOT_INITIALIZED_3"; break; - case LICENSE_RESPONSE_NOT_SIGNED: *os << "LICENSE_RESPONSE_NOT_SIGNED"; + case LICENSE_RESPONSE_NOT_SIGNED: *os << "LICENSE_RESPONSE_NOT_SIGNED"; break; break; - case LICENSE_RESPONSE_PARSE_ERROR_1: *os << "LICENSE_RESPONSE_PARSE_ERROR_1"; + case LICENSE_RESPONSE_PARSE_ERROR_1: + *os << "LICENSE_RESPONSE_PARSE_ERROR_1"; break; - case LICENSE_RESPONSE_PARSE_ERROR_2: *os << "LICENSE_RESPONSE_PARSE_ERROR_2"; + case LICENSE_RESPONSE_PARSE_ERROR_2: + *os << "LICENSE_RESPONSE_PARSE_ERROR_2"; break; - case LICENSE_RESPONSE_PARSE_ERROR_3: *os << "LICENSE_RESPONSE_PARSE_ERROR_3"; + case LICENSE_RESPONSE_PARSE_ERROR_3: + *os << "LICENSE_RESPONSE_PARSE_ERROR_3"; break; case LOAD_KEY_ERROR: *os << "LOAD_KEY_ERROR"; break; @@ -211,31 +243,34 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { break; case REFRESH_KEYS_ERROR: *os << "REFRESH_KEYS_ERROR"; break; - case RELEASE_ALL_USAGE_INFO_ERROR_1: *os << "RELEASE_ALL_USAGE_INFO_ERROR_1"; + case RELEASE_ALL_USAGE_INFO_ERROR_1: + *os << "RELEASE_ALL_USAGE_INFO_ERROR_1"; break; - case RELEASE_ALL_USAGE_INFO_ERROR_2: *os << "RELEASE_ALL_USAGE_INFO_ERROR_2"; + case RELEASE_ALL_USAGE_INFO_ERROR_2: + *os << "RELEASE_ALL_USAGE_INFO_ERROR_2"; break; case RELEASE_KEY_ERROR: *os << "RELEASE_KEY_ERROR"; break; - case RELEASE_KEY_REQUEST_ERROR: *os << "RELEASE_KEY_REQUEST_ERROR"; + case RELEASE_KEY_REQUEST_ERROR: *os << "RELEASE_KEY_REQUEST_ERROR"; break; case RELEASE_LICENSE_ERROR_1: *os << "RELEASE_LICENSE_ERROR_1"; break; case RELEASE_LICENSE_ERROR_2: *os << "RELEASE_LICENSE_ERROR_2"; break; - case RELEASE_USAGE_INFO_ERROR: *os << "RELEASE_USAGE_INFO_ERROR"; + case RELEASE_USAGE_INFO_ERROR: *os << "RELEASE_USAGE_INFO_ERROR"; break; case RENEW_KEY_ERROR_1: *os << "RENEW_KEY_ERROR_1"; break; case RENEW_KEY_ERROR_2: *os << "RENEW_KEY_ERROR_2"; break; - case LICENSE_RENEWAL_SIGNING_ERROR: *os << "LICENSE_RENEWAL_SIGNING_ERROR"; + case LICENSE_RENEWAL_SIGNING_ERROR: *os << "LICENSE_RENEWAL_SIGNING_ERROR"; break; - case RESTORE_OFFLINE_LICENSE_ERROR_2: *os << "RESTORE_OFFLINE_LICENSE_ERROR_2"; + case RESTORE_OFFLINE_LICENSE_ERROR_2: + *os << "RESTORE_OFFLINE_LICENSE_ERROR_2"; break; case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2"; break; - case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR"; + case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR"; break; case SESSION_NOT_FOUND_1: *os << "SESSION_NOT_FOUND_1"; break; @@ -257,7 +292,7 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { break; case SESSION_NOT_FOUND_10: *os << "SESSION_NOT_FOUND_10"; break; - case SESSION_NOT_FOUND_FOR_DECRYPT: *os << "SESSION_NOT_FOUND_FOR_DECRYPT"; + case SESSION_NOT_FOUND_FOR_DECRYPT: *os << "SESSION_NOT_FOUND_FOR_DECRYPT"; break; case SESSION_KEYS_NOT_FOUND: *os << "SESSION_KEYS_NOT_FOUND"; break; @@ -284,68 +319,134 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR: *os << "LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR"; break; - case EMPTY_PROVISIONING_CERTIFICATE_2: *os << "EMPTY_PROVISIONING_CERTIFICATE_2"; + case EMPTY_PROVISIONING_CERTIFICATE_2: + *os << "EMPTY_PROVISIONING_CERTIFICATE_2"; break; - case PARSE_SERVICE_CERTIFICATE_ERROR: *os << "PARSE_SERVICE_CERTIFICATE_ERROR"; + case PARSE_SERVICE_CERTIFICATE_ERROR: + *os << "PARSE_SERVICE_CERTIFICATE_ERROR"; break; - case SERVICE_CERTIFICATE_TYPE_ERROR: *os << "SERVICE_CERTIFICATE_TYPE_ERROR"; + case SERVICE_CERTIFICATE_TYPE_ERROR: + *os << "SERVICE_CERTIFICATE_TYPE_ERROR"; break; - case CLIENT_ID_GENERATE_RANDOM_ERROR: *os << "CLIENT_ID_GENERATE_RANDOM_ERROR"; + case CLIENT_ID_GENERATE_RANDOM_ERROR: + *os << "CLIENT_ID_GENERATE_RANDOM_ERROR"; break; - case CLIENT_ID_AES_INIT_ERROR: *os << "CLIENT_ID_AES_INIT_ERROR"; + case CLIENT_ID_AES_INIT_ERROR: *os << "CLIENT_ID_AES_INIT_ERROR"; break; - case CLIENT_ID_AES_ENCRYPT_ERROR: *os << "CLIENT_ID_AES_ENCRYPT_ERROR"; + case CLIENT_ID_AES_ENCRYPT_ERROR: *os << "CLIENT_ID_AES_ENCRYPT_ERROR"; break; - case CLIENT_ID_RSA_INIT_ERROR: *os << "CLIENT_ID_RSA_INIT_ERROR"; + case CLIENT_ID_RSA_INIT_ERROR: *os << "CLIENT_ID_RSA_INIT_ERROR"; break; - case CLIENT_ID_RSA_ENCRYPT_ERROR: *os << "CLIENT_ID_RSA_ENCRYPT_ERROR"; + case CLIENT_ID_RSA_ENCRYPT_ERROR: *os << "CLIENT_ID_RSA_ENCRYPT_ERROR"; break; case INVALID_QUERY_STATUS: *os << "INVALID_QUERY_STATUS"; break; - case LICENSE_PARSER_NOT_INITIALIZED_4: *os << "LICENSE_PARSER_NOT_INITIALIZED_4"; + case LICENSE_PARSER_NOT_INITIALIZED_4: + *os << "LICENSE_PARSER_NOT_INITIALIZED_4"; break; - case INVALID_PARAMETERS_LIC_3: *os << "INVALID_PARAMETERS_LIC_3"; + case INVALID_PARAMETERS_LIC_3: *os << "INVALID_PARAMETERS_LIC_3"; break; - case INVALID_PARAMETERS_LIC_4: *os << "INVALID_PARAMETERS_LIC_4"; + case INVALID_PARAMETERS_LIC_4: *os << "INVALID_PARAMETERS_LIC_4"; break; - case INVALID_PARAMETERS_LIC_6: *os << "INVALID_PARAMETERS_LIC_6"; + case INVALID_PARAMETERS_LIC_6: *os << "INVALID_PARAMETERS_LIC_6"; break; - case INVALID_PARAMETERS_LIC_7: *os << "INVALID_PARAMETERS_LIC_7"; + case INVALID_PARAMETERS_LIC_7: *os << "INVALID_PARAMETERS_LIC_7"; break; case LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR: *os << "LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR"; break; - case CENC_INIT_DATA_UNAVAILABLE: *os << "CENC_INIT_DATA_UNAVAILABLE"; + case CENC_INIT_DATA_UNAVAILABLE: *os << "CENC_INIT_DATA_UNAVAILABLE"; break; - case PREPARE_CENC_CONTENT_ID_FAILED: *os << "PREPARE_CENC_CONTENT_ID_FAILED"; + case PREPARE_CENC_CONTENT_ID_FAILED: + *os << "PREPARE_CENC_CONTENT_ID_FAILED"; break; - case WEBM_INIT_DATA_UNAVAILABLE: *os << "WEBM_INIT_DATA_UNAVAILABLE"; + case WEBM_INIT_DATA_UNAVAILABLE: *os << "WEBM_INIT_DATA_UNAVAILABLE"; break; - case PREPARE_WEBM_CONTENT_ID_FAILED: *os << "PREPARE_WEBM_CONTENT_ID_FAILED"; + case PREPARE_WEBM_CONTENT_ID_FAILED: + *os << "PREPARE_WEBM_CONTENT_ID_FAILED"; break; - case UNSUPPORTED_INIT_DATA_FORMAT: *os << "UNSUPPORTED_INIT_DATA_FORMAT"; + case UNSUPPORTED_INIT_DATA_FORMAT: *os << "UNSUPPORTED_INIT_DATA_FORMAT"; break; - case LICENSE_REQUEST_NONCE_GENERATION_ERROR: *os << "LICENSE_REQUEST_NONCE_GENERATION_ERROR"; + case LICENSE_REQUEST_NONCE_GENERATION_ERROR: + *os << "LICENSE_REQUEST_NONCE_GENERATION_ERROR"; break; - case LICENSE_REQUEST_SIGNING_ERROR: *os << "LICENSE_REQUEST_SIGNING_ERROR"; + case LICENSE_REQUEST_SIGNING_ERROR: *os << "LICENSE_REQUEST_SIGNING_ERROR"; break; case EMPTY_LICENSE_REQUEST: *os << "EMPTY_LICENSE_REQUEST"; break; - case DUPLICATE_SESSION_ID_SPECIFIED: *os << "DUPLICATE_SESSION_ID_SPECIFIED"; + case DUPLICATE_SESSION_ID_SPECIFIED: + *os << "DUPLICATE_SESSION_ID_SPECIFIED"; break; - case LICENSE_RENEWAL_PROHIBITED: *os << "LICENSE_RENEWAL_PROHIBITED"; + case LICENSE_RENEWAL_PROHIBITED: *os << "LICENSE_RENEWAL_PROHIBITED"; break; - case SESSION_FILE_HANDLE_INIT_ERROR: *os << "SESSION_FILE_HANDLE_INIT_ERROR"; + case SESSION_FILE_HANDLE_INIT_ERROR: + *os << "SESSION_FILE_HANDLE_INIT_ERROR"; break; case INCORRECT_CRYPTO_MODE: *os << "INCORRECT_CRYPTO_MODE"; break; + case INVALID_PARAMETERS_ENG_5: *os << "INVALID_PARAMETERS_ENG_5"; + break; case DECRYPT_ERROR: *os << "DECRYPT_ERROR"; break; case INSUFFICIENT_OUTPUT_PROTECTION: *os << "INSUFFICIENT_OUTPUT_PROTECTION"; break; + case SESSION_NOT_FOUND_12: *os << "SESSION_NOT_FOUND_12"; + break; + case KEY_NOT_FOUND_1: *os << "KEY_NOT_FOUND_1"; + break; + case KEY_NOT_FOUND_2: *os << "KEY_NOT_FOUND_2"; + break; + case KEY_CONFLICT_1: *os << "KEY_CONFLICT_1"; + break; + case INVALID_PARAMETERS_ENG_6: *os << "INVALID_PARAMETERS_ENG_6"; + break; + case INVALID_PARAMETERS_ENG_7: *os << "INVALID_PARAMETERS_ENG_7"; + break; + case INVALID_PARAMETERS_ENG_8: *os << "INVALID_PARAMETERS_ENG_8"; + break; + case INVALID_PARAMETERS_ENG_9: *os << "INVALID_PARAMETERS_ENG_9"; + break; + case INVALID_PARAMETERS_ENG_10: *os << "INVALID_PARAMETERS_ENG_10"; + break; + case INVALID_PARAMETERS_ENG_11: *os << "INVALID_PARAMETERS_ENG_11"; + break; + case INVALID_PARAMETERS_ENG_12: *os << "INVALID_PARAMETERS_ENG_12"; + break; + case SESSION_NOT_FOUND_13: *os << "SESSION_NOT_FOUND_13"; + break; + case SESSION_NOT_FOUND_14: *os << "SESSION_NOT_FOUND_14"; + break; + case SESSION_NOT_FOUND_15: *os << "SESSION_NOT_FOUND_15"; + break; + case SESSION_NOT_FOUND_16: *os << "SESSION_NOT_FOUND_16"; + break; + case KEY_NOT_FOUND_3: *os << "KEY_NOT_FOUND_3"; + break; + case KEY_NOT_FOUND_4: *os << "KEY_NOT_FOUND_4"; + break; + case KEY_NOT_FOUND_5: *os << "KEY_NOT_FOUND_5"; + break; + case KEY_NOT_FOUND_6: *os << "KEY_NOT_FOUND_6"; + break; + case KEY_ERROR_1: *os << "KEY_ERROR_1"; + break; + case KEY_ERROR_2: *os << "KEY_ERROR_2"; + break; + case KEY_ERROR_3: *os << "KEY_ERROR_3"; + break; + case KEY_ERROR_4: *os << "KEY_ERROR_4"; + break; + case INVALID_PARAMETERS_ENG_13: *os << "INVALID_PARAMETERS_ENG_13"; + break; + case INVALID_PARAMETERS_ENG_14: *os << "INVALID_PARAMETERS_ENG_14"; + break; + case INVALID_PARAMETERS_ENG_15: *os << "INVALID_PARAMETERS_ENG_15"; + break; + case INVALID_PARAMETERS_ENG_16: *os << "INVALID_PARAMETERS_ENG_16"; + break; default: - *os << "Unknown CdmResponseType"; + *os << "Unknown CdmResponseType"; break; } } @@ -368,7 +469,7 @@ void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os) { void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os) { switch (value) { - case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized"; + case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized"; break; case kSecurityLevelL1: *os << "kSecurityLevelL1"; break; @@ -391,7 +492,7 @@ void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os) { case kCertificateX509: *os << "kCertificateX509"; break; default: - *os << "Unknown CdmCertificateType"; + *os << "Unknown CdmCertificateType"; break; } }; diff --git a/libwvdrmengine/cdm/include/file_utils.h b/libwvdrmengine/cdm/include/file_utils.h new file mode 100644 index 00000000..7e5ae9dc --- /dev/null +++ b/libwvdrmengine/cdm/include/file_utils.h @@ -0,0 +1,33 @@ +// Copyright 2016 Google Inc. All Rights Reserved. + +#include +#include + +#include "wv_cdm_types.h" + +namespace wvcdm { + +const char kCurrentDirectory[] = "."; +const char kParentDirectory[] = ".."; +const char kDirectoryDelimiter = '/'; +const char kWildcard[] = "*"; +bool IsCurrentOrParentDirectory(char* dir); + +class FileUtils { + public: + static bool Exists(const std::string& src); + static bool Remove(const std::string& src); + static bool Copy(const std::string& src, const std::string& dest); + static bool List(const std::string& path, std::vector* files); + static bool IsRegularFile(const std::string& path); + static bool IsDirectory(const std::string& path); + static bool CreateDirectory(const std::string& path); + + // 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. + static void SecurityLevelPathBackwardCompatibility( + CdmSecurityLevel security_level); +}; + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 50ae230c..d576d398 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -3,9 +3,12 @@ #ifndef CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ #define CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ +#include + #include #include +#include "file_store.h" #include "lock.h" #include "timer.h" #include "wv_cdm_types.h" @@ -71,9 +74,9 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, CdmQueryMap* key_info); - // Query session control information - virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, - CdmQueryMap* key_info); + // Query OEMCrypto session ID + virtual CdmResponseType QueryOemCryptoSessionId(const CdmSessionId& session_id, + CdmQueryMap* response); // Provisioning related methods virtual CdmResponseType GetProvisioningRequest( @@ -118,6 +121,19 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { virtual bool IsValidServiceCertificate(const std::string& certificate); private: + struct CdmInfo { + CdmInfo(); + + FileSystem file_system; + UniquePtr cdm_engine; + }; + + // Finds the CdmEngine instance for the given origin, creating one if needed. + CdmEngine* EnsureCdmForOrigin(const std::string& origin); + // Finds the CdmEngine instance for the given session id, returning NULL if + // not found. + CdmEngine* GetCdmForSessionId(const std::string& session_id); + uint32_t GenerateSessionSharingId(); // timer related methods to drive policy decisions @@ -130,7 +146,10 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { Timer policy_timer_; // instance variables - UniquePtr cdm_engine_; + // This manages the lifetime of the CDM instances. + std::map cdms_; + // This contains weak pointers to the CDM instances contained in |cdms_|. + std::map cdm_by_session_id_; CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule); }; diff --git a/libwvdrmengine/cdm/src/file_store.cpp b/libwvdrmengine/cdm/src/file_store.cpp index ceb906e2..572f8eb6 100644 --- a/libwvdrmengine/cdm/src/file_store.cpp +++ b/libwvdrmengine/cdm/src/file_store.cpp @@ -13,68 +13,69 @@ #include #include +#include "file_utils.h" #include "log.h" +#include "string_conversions.h" -namespace { -const char kCurrentDirectory[] = "."; -const char kParentDirectory[] = ".."; -const char kDirectoryDelimiter = '/'; -const char kWildcard[] = "*"; -bool IsCurrentOrParentDirectory(char* dir) { - return strcmp(dir, kCurrentDirectory) == 0 || - strcmp(dir, kParentDirectory) == 0; -} -} // namespace +#include +#include namespace wvcdm { +namespace { +const char kCertificateFileNamePrefix[] = "cert"; +const char kCertificateFileNameExt[] = ".bin"; +const char kCertificateFileName[] = "cert.bin"; + +std::string GetFileNameSafeHash(const std::string& input) { + std::vector hash(MD5_DIGEST_LENGTH); + const unsigned char* input_ptr = + reinterpret_cast(input.data()); + MD5(input_ptr, input.size(), &hash[0]); + return wvcdm::Base64SafeEncode(hash); +} + +std::string GetFileNameForOrigin(const std::string path, + const std::string origin) { + std::string file_name = path; + std::string dir_path; + const size_t delimiter_pos = path.rfind(kDirectoryDelimiter); + if (delimiter_pos != std::string::npos) { + dir_path = file_name.substr(0, delimiter_pos); + file_name = path.substr(delimiter_pos + 1); + } + + if (file_name == kCertificateFileName && !origin.empty()) { + const std::string hash = GetFileNameSafeHash(origin); + file_name = kCertificateFileNamePrefix + hash + kCertificateFileNameExt; + } + + if (dir_path.empty()) + return file_name; + else + return dir_path + kDirectoryDelimiter + file_name; +} +} // namespace + class File::Impl { public: - Impl() : file_(NULL) {} - Impl(const std::string& file_path) : file_(NULL), file_path_(file_path) {} + Impl(FILE* file, const std::string& file_path) + : file_(file), file_path_(file_path) {} virtual ~Impl() {} FILE* file_; std::string file_path_; }; -File::File() : impl_(new File::Impl()) {} +File::File(Impl* impl) : impl_(impl) {} File::~File() { Close(); delete impl_; } -bool File::Open(const std::string& name, int flags) { - std::string open_flags; - - // ensure only owners has access - mode_t old_mask = umask(077); - if (((flags & File::kTruncate) && Exists(name)) || - ((flags & File::kCreate) && !Exists(name))) { - FILE* fp = fopen(name.c_str(), "w+"); - if (fp) { - fclose(fp); - } - } - - if (flags & File::kBinary) { - open_flags = (flags & File::kReadOnly) ? "rb" : "rb+"; - } else { - open_flags = (flags & File::kReadOnly) ? "r" : "r+"; - } - - impl_->file_ = fopen(name.c_str(), open_flags.c_str()); - if (!impl_->file_) { - LOGW("File::Open: fopen failed: %d", errno); - } - impl_->file_path_ = name; - umask(old_mask); - return impl_->file_ != NULL; -} - void File::Close() { - if (impl_->file_){ + if (impl_ && impl_->file_) { fflush(impl_->file_); fsync(fileno(impl_->file_)); fclose(impl_->file_); @@ -83,7 +84,7 @@ void File::Close() { } ssize_t File::Read(char* buffer, size_t bytes) { - if (impl_->file_) { + if (impl_ && impl_->file_) { size_t len = fread(buffer, sizeof(char), bytes, impl_->file_); if (len == 0) { LOGW("File::Read: fread failed: %d", errno); @@ -95,7 +96,7 @@ ssize_t File::Read(char* buffer, size_t bytes) { } ssize_t File::Write(const char* buffer, size_t bytes) { - if (impl_->file_) { + if (impl_ && impl_->file_) { size_t len = fwrite(buffer, sizeof(char), bytes, impl_->file_); if (len == 0) { LOGW("File::Write: fwrite failed: %d", errno); @@ -106,194 +107,62 @@ ssize_t File::Write(const char* buffer, size_t bytes) { return -1; } -bool File::Exists(const std::string& path) { - struct stat buf; - int res = stat(path.c_str(), &buf) == 0; - if (!res) { - LOGV("File::Exists: stat failed: %d", errno); - } - return res; +class FileSystem::Impl {}; + +FileSystem::FileSystem() : FileSystem("", NULL) {} +FileSystem::FileSystem(const std::string& origin, void* /* extra_data */) + : origin_(origin) { + FileUtils::SecurityLevelPathBackwardCompatibility(kSecurityLevelL1); + FileUtils::SecurityLevelPathBackwardCompatibility(kSecurityLevelL3); } -bool File::Remove(const std::string& path) { - if (IsDirectory(path)) { - // Handle directory deletion - DIR* dir; - if ((dir = opendir(path.c_str())) != NULL) { - // first remove files and dir within it - struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (!IsCurrentOrParentDirectory(entry->d_name)) { - std::string path_to_remove = path + kDirectoryDelimiter; - path_to_remove += entry->d_name; - if (!Remove(path_to_remove)) { - closedir(dir); - return false; - } - } - } - closedir(dir); - } - if (rmdir(path.c_str())) { - LOGW("File::Remove: rmdir failed: %d", errno); - return false; - } - return true; - } else { - size_t wildcard_pos = path.find(kWildcard); - if (wildcard_pos == std::string::npos) { - // Handle file deletion - if (unlink(path.c_str()) && (errno != ENOENT)) { - LOGW("File::Remove: unlink failed: %d", errno); - return false; - } - } else { - // Handle wildcard specified file deletion - size_t delimiter_pos = path.rfind(kDirectoryDelimiter, wildcard_pos); - if (delimiter_pos == std::string::npos) { - LOGW("File::Remove: unable to find path delimiter before wildcard"); - return false; - } +FileSystem::~FileSystem() {} - DIR* dir; - std::string dir_path = path.substr(0, delimiter_pos); - if ((dir = opendir(dir_path.c_str())) == NULL) { - LOGW("File::Remove: directory open failed for wildcard"); - return false; - } +File* FileSystem::Open(const std::string& in_name, int flags) { + std::string open_flags; - struct dirent* entry; - std::string ext = path.substr(wildcard_pos + 1); + std::string name = GetFileNameForOrigin(in_name, origin_); - while ((entry = readdir(dir)) != NULL) { - size_t filename_len = strlen(entry->d_name); - if (filename_len > ext.size()) { - if (strcmp(entry->d_name + filename_len - ext.size(), ext.c_str()) == - 0) { - std::string file_path_to_remove = - dir_path + kDirectoryDelimiter + entry->d_name; - if (!Remove(file_path_to_remove)) { - closedir(dir); - return false; - } - } - } - } - closedir(dir); - } - return true; - } -} - -bool File::Copy(const std::string& src, const std::string& dest) { - struct stat stat_buf; - if (stat(src.c_str(), &stat_buf)) { - LOGV("File::Copy: file %s stat error: %d", src.c_str(), errno); - return false; + // create the enclosing directory if it does not exist + size_t delimiter_pos = name.rfind(kDirectoryDelimiter); + if (delimiter_pos != std::string::npos) { + std::string dir_path = name.substr(0, delimiter_pos); + if ((flags & FileSystem::kCreate) && !Exists(dir_path)) + FileUtils::CreateDirectory(dir_path); } - int fd_src = open(src.c_str(), O_RDONLY); - if (fd_src < 0) { - LOGW("File::Copy: unable to open file %s: %d", src.c_str(), errno); - return false; - } - - int fd_dest = open(dest.c_str(), O_WRONLY | O_CREAT, stat_buf.st_mode); - if (fd_dest < 0) { - LOGW("File::Copy: unable to open file %s: %d", dest.c_str(), errno); - close(fd_src); - return false; - } - - off_t offset = 0; - bool status = true; - if (sendfile(fd_dest, fd_src, &offset, stat_buf.st_size) < 0) { - LOGV("File::Copy: unable to copy %s to %s: %d", src.c_str(), dest.c_str(), - errno); - status = false; - } - - close(fd_src); - close(fd_dest); - return status; -} - -bool File::List(const std::string& path, std::vector* files) { - if (NULL == files) { - LOGV("File::List: files destination not provided"); - return false; - } - - if (!Exists(path)) { - LOGV("File::List: path %s does not exist: %d", path.c_str(), errno); - return false; - } - - DIR* dir = opendir(path.c_str()); - if (dir == NULL) { - LOGW("File::List: unable to open directory %s: %d", path.c_str(), errno); - return false; - } - - files->clear(); - struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (!IsCurrentOrParentDirectory(entry->d_name)) { - files->push_back(entry->d_name); + // ensure only owners has access + mode_t old_mask = umask(077); + if (((flags & FileSystem::kTruncate) && Exists(name)) || + ((flags & FileSystem::kCreate) && !Exists(name))) { + FILE* fp = fopen(name.c_str(), "w+"); + if (fp) { + fclose(fp); } } - closedir(dir); - return true; -} + open_flags = (flags & FileSystem::kReadOnly) ? "rb" : "rb+"; -bool File::CreateDirectory(std::string path) { - size_t size = path.size(); - if ((size == 1) && (path[0] == kDirectoryDelimiter)) return true; - - if (size <= 1) return false; - - size_t pos = path.find(kDirectoryDelimiter, 1); - while (pos < size) { - path[pos] = '\0'; - if (mkdir(path.c_str(), 0700) != 0) { - if (errno != EEXIST) { - LOGW("File::CreateDirectory: mkdir failed: %d\n", errno); - return false; - } - } - path[pos] = kDirectoryDelimiter; - pos = path.find(kDirectoryDelimiter, pos + 1); + FILE* file = fopen(name.c_str(), open_flags.c_str()); + umask(old_mask); + if (!file) { + LOGW("File::Open: fopen failed: %d", errno); + return NULL; } - if (path[size - 1] != kDirectoryDelimiter) { - if (mkdir(path.c_str(), 0700) != 0) { - if (errno != EEXIST) { - LOGW("File::CreateDirectory: mkdir failed: %d\n", errno); - return false; - } - } - } - return true; + return new File(new File::Impl(file, name)); } -bool File::IsDirectory(const std::string& path) { - struct stat buf; - if (stat(path.c_str(), &buf) == 0) - return buf.st_mode & S_IFDIR; - else - return false; +bool FileSystem::Exists(const std::string& path) { + return FileUtils::Exists(GetFileNameForOrigin(path, origin_)); } -bool File::IsRegularFile(const std::string& path) { - struct stat buf; - if (stat(path.c_str(), &buf) == 0) - return buf.st_mode & S_IFREG; - else - return false; +bool FileSystem::Remove(const std::string& path) { + return FileUtils::Remove(GetFileNameForOrigin(path, origin_)); } -ssize_t File::FileSize(const std::string& path) { +ssize_t FileSystem::FileSize(const std::string& in_path) { + std::string path = GetFileNameForOrigin(in_path, origin_); struct stat buf; if (stat(path.c_str(), &buf) == 0) return buf.st_size; @@ -301,4 +170,6 @@ ssize_t File::FileSize(const std::string& path) { return -1; } +void FileSystem::SetOrigin(const std::string& origin) { origin_ = origin; } + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/src/file_utils.cpp b/libwvdrmengine/cdm/src/file_utils.cpp new file mode 100644 index 00000000..05fd7782 --- /dev/null +++ b/libwvdrmengine/cdm/src/file_utils.cpp @@ -0,0 +1,282 @@ +// Copyright 2016 Google Inc. All Rights Reserved. + +#include "file_utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "file_store.h" +#include "log.h" +#include "properties.h" + +namespace { +const char* kSecurityLevelPathCompatibilityExclusionList[] = { + "ay64.dat", "ay64.dat2", "ay64.dat3"}; +size_t kSecurityLevelPathCompatibilityExclusionListSize = + sizeof(kSecurityLevelPathCompatibilityExclusionList) / + sizeof(*kSecurityLevelPathCompatibilityExclusionList); +} // namespace + +namespace wvcdm { + +bool IsCurrentOrParentDirectory(char* dir) { + return strcmp(dir, kCurrentDirectory) == 0 || + strcmp(dir, kParentDirectory) == 0; +} + +bool FileUtils::Exists(const std::string& path) { + struct stat buf; + int res = stat(path.c_str(), &buf) == 0; + if (!res) { + LOGV("File::Exists: stat failed: %d", errno); + } + return res; +} + +bool FileUtils::Remove(const std::string& path) { + if (FileUtils::IsDirectory(path)) { + // Handle directory deletion + DIR* dir; + if ((dir = opendir(path.c_str())) != NULL) { + // first remove files and dir within it + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (!IsCurrentOrParentDirectory(entry->d_name)) { + std::string path_to_remove = path + kDirectoryDelimiter; + path_to_remove += entry->d_name; + if (!Remove(path_to_remove)) { + closedir(dir); + return false; + } + } + } + closedir(dir); + } + if (rmdir(path.c_str())) { + LOGW("File::Remove: rmdir failed: %d", errno); + return false; + } + return true; + } else { + size_t wildcard_pos = path.find(kWildcard); + if (wildcard_pos == std::string::npos) { + // Handle file deletion + if (unlink(path.c_str()) && (errno != ENOENT)) { + LOGW("File::Remove: unlink failed: %d", errno); + return false; + } + } else { + // Handle wildcard specified file deletion + size_t delimiter_pos = path.rfind(kDirectoryDelimiter, wildcard_pos); + if (delimiter_pos == std::string::npos) { + LOGW("File::Remove: unable to find path delimiter before wildcard"); + return false; + } + + DIR* dir; + std::string dir_path = path.substr(0, delimiter_pos); + if ((dir = opendir(dir_path.c_str())) == NULL) { + LOGW("File::Remove: directory open failed for wildcard"); + return false; + } + + struct dirent* entry; + std::string ext = path.substr(wildcard_pos + 1); + + while ((entry = readdir(dir)) != NULL) { + size_t filename_len = strlen(entry->d_name); + if (filename_len > ext.size()) { + if (strcmp(entry->d_name + filename_len - ext.size(), ext.c_str()) == + 0) { + std::string file_path_to_remove = + dir_path + kDirectoryDelimiter + entry->d_name; + if (!Remove(file_path_to_remove)) { + closedir(dir); + return false; + } + } + } + } + closedir(dir); + } + return true; + } +} + +bool FileUtils::Copy(const std::string& src, const std::string& dest) { + struct stat stat_buf; + if (stat(src.c_str(), &stat_buf)) { + LOGV("File::Copy: file %s stat error: %d", src.c_str(), errno); + return false; + } + + int fd_src = open(src.c_str(), O_RDONLY); + if (fd_src < 0) { + LOGW("File::Copy: unable to open file %s: %d", src.c_str(), errno); + return false; + } + + int fd_dest = open(dest.c_str(), O_WRONLY | O_CREAT, stat_buf.st_mode); + if (fd_dest < 0) { + LOGW("File::Copy: unable to open file %s: %d", dest.c_str(), errno); + close(fd_src); + return false; + } + + off_t offset = 0; + bool status = true; + if (sendfile(fd_dest, fd_src, &offset, stat_buf.st_size) < 0) { + LOGV("File::Copy: unable to copy %s to %s: %d", src.c_str(), dest.c_str(), + errno); + status = false; + } + + close(fd_src); + close(fd_dest); + return status; +} + +bool FileUtils::List(const std::string& path, std::vector* files) { + if (NULL == files) { + LOGV("File::List: files destination not provided"); + return false; + } + + if (!FileUtils::Exists(path)) { + LOGV("File::List: path %s does not exist: %d", path.c_str(), errno); + return false; + } + + DIR* dir = opendir(path.c_str()); + if (dir == NULL) { + LOGW("File::List: unable to open directory %s: %d", path.c_str(), errno); + return false; + } + + files->clear(); + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (!IsCurrentOrParentDirectory(entry->d_name)) { + files->push_back(entry->d_name); + } + } + closedir(dir); + + return true; +} + +bool FileUtils::IsRegularFile(const std::string& path) { + struct stat buf; + if (stat(path.c_str(), &buf) == 0) + return buf.st_mode & S_IFREG; + else + return false; +} + +bool FileUtils::IsDirectory(const std::string& path) { + struct stat buf; + if (stat(path.c_str(), &buf) == 0) + return buf.st_mode & S_IFDIR; + else + return false; +} + +bool FileUtils::CreateDirectory(const std::string& path_in) { + std::string path = path_in; + size_t size = path.size(); + if ((size == 1) && (path[0] == kDirectoryDelimiter)) return true; + + if (size <= 1) return false; + + size_t pos = path.find(kDirectoryDelimiter, 1); + while (pos < size) { + path[pos] = '\0'; + if (mkdir(path.c_str(), 0700) != 0) { + if (errno != EEXIST) { + LOGW("File::CreateDirectory: mkdir failed: %d\n", errno); + return false; + } + } + path[pos] = kDirectoryDelimiter; + pos = path.find(kDirectoryDelimiter, pos + 1); + } + + if (path[size - 1] != kDirectoryDelimiter) { + if (mkdir(path.c_str(), 0700) != 0) { + if (errno != EEXIST) { + LOGW("File::CreateDirectory: mkdir failed: %d\n", errno); + return false; + } + } + } + return true; +} + +void FileUtils::SecurityLevelPathBackwardCompatibility( + CdmSecurityLevel security_level) { + std::string path; + if (!Properties::GetDeviceFilesBasePath(security_level, &path)) { + LOGW("SecurityLevelPathBackwardCompatibility: Unable to get base path"); + return; + } + + std::vector security_dirs; + if (!Properties::GetSecurityLevelDirectories(&security_dirs)) { + LOGW("SecurityLevelPathBackwardCompatibility: Unable to get security " + "directories"); + return; + } + + size_t pos = std::string::npos; + for (size_t i = 0; i < security_dirs.size(); ++i) { + pos = path.find(security_dirs[i]); + if (pos != std::string::npos && pos > 0 && + pos == path.size() - security_dirs[i].size() && + path[pos - 1] == kDirectoryDelimiter) { + break; + } + } + + if (pos == std::string::npos) { + LOGV("SecurityLevelPathBackwardCompatibility: Security level specific path " + "not found. Check properties?"); + return; + } + + std::string from_dir(path, 0, pos); + + std::vector files; + if (!FileUtils::List(from_dir, &files)) { + return; + } + + for (size_t i = 0; i < files.size(); ++i) { + std::string from = from_dir + files[i]; + bool exclude = false; + for (size_t j = 0; j < kSecurityLevelPathCompatibilityExclusionListSize; + ++j) { + if (files[i] == kSecurityLevelPathCompatibilityExclusionList[j]) { + exclude = true; + break; + } + } + if (exclude) continue; + if (!FileUtils::IsRegularFile(from)) continue; + + for (size_t j = 0; j < security_dirs.size(); ++j) { + std::string to_dir = from_dir + security_dirs[j]; + if (!FileUtils::Exists(to_dir)) FileUtils::CreateDirectory(to_dir); + std::string to = to_dir + files[i]; + FileUtils::Copy(from, to); + } + FileUtils::Remove(from); + } +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 9865dff9..86799978 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -19,8 +19,7 @@ namespace wvcdm { Lock WvContentDecryptionModule::session_sharing_id_generation_lock_; -WvContentDecryptionModule::WvContentDecryptionModule() - : cdm_engine_(new CdmEngine()) {} +WvContentDecryptionModule::WvContentDecryptionModule() {} WvContentDecryptionModule::~WvContentDecryptionModule() { DisablePolicyTimer(true); @@ -52,19 +51,31 @@ CdmResponseType WvContentDecryptionModule::OpenSession( property_set->set_session_sharing_id(GenerateSessionSharingId()); } - return cdm_engine_->OpenSession(key_system, property_set, origin, - event_listener, session_id); + CdmEngine* cdm_engine = EnsureCdmForOrigin(origin); + CdmResponseType sts = cdm_engine->OpenSession(key_system, property_set, + event_listener, session_id); + if (sts == NO_ERROR) { + cdm_by_session_id_[*session_id] = cdm_engine; + } + return sts; } CdmResponseType WvContentDecryptionModule::CloseSession( const CdmSessionId& session_id) { - CdmResponseType sts = cdm_engine_->CloseSession(session_id); + CdmEngine* cdm_engine = GetCdmForSessionId(session_id); + // TODO(rfrias): Avoid reusing the error codes from CdmEngine. + if (!cdm_engine) return SESSION_NOT_FOUND_1; + CdmResponseType sts = cdm_engine->CloseSession(session_id); + if (sts == NO_ERROR) { + cdm_by_session_id_.erase(session_id); + } DisablePolicyTimer(false); return sts; } bool WvContentDecryptionModule::IsOpenSession(const CdmSessionId& session_id) { - return cdm_engine_->IsOpenSession(session_id); + CdmEngine* cdm_engine = GetCdmForSessionId(session_id); + return cdm_engine && cdm_engine->IsOpenSession(session_id); } CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( @@ -73,21 +84,24 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( const CdmLicenseType license_type, CdmAppParameterMap& app_parameters, CdmClientPropertySet* property_set, const std::string& origin, CdmKeyRequest* key_request) { + CdmEngine* cdm_engine = EnsureCdmForOrigin(origin); CdmResponseType sts; if (license_type == kLicenseTypeRelease) { - sts = cdm_engine_->OpenKeySetSession(key_set_id, property_set, origin, - NULL); + sts = cdm_engine->OpenKeySetSession(key_set_id, property_set, NULL); if (sts != NO_ERROR) return sts; + cdm_by_session_id_[key_set_id] = cdm_engine; } InitializationData initialization_data(init_data_type, init_data); - sts = cdm_engine_->GenerateKeyRequest( + sts = cdm_engine->GenerateKeyRequest( session_id, key_set_id, initialization_data, license_type, app_parameters, key_request); switch(license_type) { case kLicenseTypeRelease: - if (sts != KEY_MESSAGE) - cdm_engine_->CloseKeySetSession(key_set_id); + if (sts != KEY_MESSAGE) { + cdm_engine->CloseKeySetSession(key_set_id); + cdm_by_session_id_.erase(key_set_id); + } break; default: if (sts == KEY_MESSAGE) @@ -101,16 +115,23 @@ CdmResponseType WvContentDecryptionModule::AddKey( const CdmSessionId& session_id, const CdmKeyResponse& key_data, CdmKeySetId* key_set_id) { - CdmResponseType sts = cdm_engine_->AddKey(session_id, key_data, key_set_id); - if (sts == KEY_ADDED && session_id.empty()) // license type release - cdm_engine_->CloseKeySetSession(*key_set_id); + CdmEngine* cdm_engine = session_id.empty() ? GetCdmForSessionId(*key_set_id) + : GetCdmForSessionId(session_id); + if (!cdm_engine) return SESSION_NOT_FOUND_3; + CdmResponseType sts = cdm_engine->AddKey(session_id, key_data, key_set_id); + if (sts == KEY_ADDED && session_id.empty()) { // license type release + cdm_engine->CloseKeySetSession(*key_set_id); + cdm_by_session_id_.erase(*key_set_id); + } return sts; } CdmResponseType WvContentDecryptionModule::RestoreKey( const CdmSessionId& session_id, const CdmKeySetId& key_set_id) { - CdmResponseType sts = cdm_engine_->RestoreKey(session_id, key_set_id); + CdmEngine* cdm_engine = GetCdmForSessionId(session_id); + if (!cdm_engine) return SESSION_NOT_FOUND_4; + CdmResponseType sts = cdm_engine->RestoreKey(session_id, key_set_id); if (sts == KEY_ADDED) EnablePolicyTimer(); return sts; @@ -118,29 +139,38 @@ CdmResponseType WvContentDecryptionModule::RestoreKey( CdmResponseType WvContentDecryptionModule::RemoveKeys( const CdmSessionId& session_id) { - return cdm_engine_->RemoveKeys(session_id); + CdmEngine* cdm_engine = GetCdmForSessionId(session_id); + if (!cdm_engine) return SESSION_NOT_FOUND_5; + return cdm_engine->RemoveKeys(session_id); } CdmResponseType WvContentDecryptionModule::QueryStatus( SecurityLevel security_level, const std::string& key, std::string* value) { - return cdm_engine_->QueryStatus(security_level, key, value); + CdmEngine* cdm_engine = EnsureCdmForOrigin(EMPTY_ORIGIN); + return cdm_engine->QueryStatus(security_level, key, value); } CdmResponseType WvContentDecryptionModule::QuerySessionStatus( const CdmSessionId& session_id, CdmQueryMap* key_info) { - return cdm_engine_->QuerySessionStatus(session_id, key_info); + CdmEngine* cdm_engine = GetCdmForSessionId(session_id); + if (!cdm_engine) return SESSION_NOT_FOUND_8; + return cdm_engine->QuerySessionStatus(session_id, key_info); } CdmResponseType WvContentDecryptionModule::QueryKeyStatus( const CdmSessionId& session_id, CdmQueryMap* key_info) { - return cdm_engine_->QueryKeyStatus(session_id, key_info); + CdmEngine* cdm_engine = GetCdmForSessionId(session_id); + if (!cdm_engine) return SESSION_NOT_FOUND_9; + return cdm_engine->QueryKeyStatus(session_id, key_info); } -CdmResponseType WvContentDecryptionModule::QueryKeyControlInfo( - const CdmSessionId& session_id, CdmQueryMap* key_info) { - return cdm_engine_->QueryKeyControlInfo(session_id, key_info); +CdmResponseType WvContentDecryptionModule::QueryOemCryptoSessionId( + const CdmSessionId& session_id, CdmQueryMap* response) { + CdmEngine* cdm_engine = GetCdmForSessionId(session_id); + if (!cdm_engine) return SESSION_NOT_FOUND_10; + return cdm_engine->QueryOemCryptoSessionId(session_id, response); } CdmResponseType WvContentDecryptionModule::GetProvisioningRequest( @@ -149,8 +179,9 @@ CdmResponseType WvContentDecryptionModule::GetProvisioningRequest( const std::string& origin, CdmProvisioningRequest* request, std::string* default_url) { - return cdm_engine_->GetProvisioningRequest(cert_type, cert_authority, origin, - request, default_url); + CdmEngine* cdm_engine = EnsureCdmForOrigin(origin); + return cdm_engine->GetProvisioningRequest(cert_type, cert_authority, request, + default_url); } CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse( @@ -158,59 +189,70 @@ CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse( CdmProvisioningResponse& response, std::string* cert, std::string* wrapped_key) { - return cdm_engine_->HandleProvisioningResponse(origin, response, cert, - wrapped_key); + CdmEngine* cdm_engine = EnsureCdmForOrigin(origin); + return cdm_engine->HandleProvisioningResponse(response, cert, wrapped_key); } CdmResponseType WvContentDecryptionModule::Unprovision( - CdmSecurityLevel level, - const std::string& origin) { - return cdm_engine_->Unprovision(level, origin); + CdmSecurityLevel level, const std::string& origin) { + CdmEngine* cdm_engine = EnsureCdmForOrigin(origin); + return cdm_engine->Unprovision(level); } CdmResponseType WvContentDecryptionModule::GetUsageInfo( const std::string& app_id, CdmUsageInfo* usage_info) { - return cdm_engine_->GetUsageInfo(app_id, usage_info); + CdmEngine* cdm_engine = EnsureCdmForOrigin(EMPTY_ORIGIN); + return cdm_engine->GetUsageInfo(app_id, usage_info); } CdmResponseType WvContentDecryptionModule::GetUsageInfo( const std::string& app_id, const CdmSecureStopId& ssid, CdmUsageInfo* usage_info) { - return cdm_engine_->GetUsageInfo(app_id, ssid, usage_info); + CdmEngine* cdm_engine = EnsureCdmForOrigin(EMPTY_ORIGIN); + return cdm_engine->GetUsageInfo(app_id, ssid, usage_info); } CdmResponseType WvContentDecryptionModule::ReleaseAllUsageInfo( const std::string& app_id) { - return cdm_engine_->ReleaseAllUsageInfo(app_id); + CdmEngine* cdm_engine = EnsureCdmForOrigin(EMPTY_ORIGIN); + return cdm_engine->ReleaseAllUsageInfo(app_id); } CdmResponseType WvContentDecryptionModule::ReleaseUsageInfo( const CdmUsageInfoReleaseMessage& message) { - return cdm_engine_->ReleaseUsageInfo(message); + CdmEngine* cdm_engine = EnsureCdmForOrigin(EMPTY_ORIGIN); + return cdm_engine->ReleaseUsageInfo(message); } CdmResponseType WvContentDecryptionModule::Decrypt( const CdmSessionId& session_id, bool validate_key_id, const CdmDecryptionParameters& parameters) { + // First find the CdmEngine that has the given session_id. If we are using + // key sharing, the shared session will still be in the same CdmEngine. + CdmEngine* cdm_engine = GetCdmForSessionId(session_id); + if (!cdm_engine) return SESSION_NOT_FOUND_FOR_DECRYPT; + CdmSessionId local_session_id = session_id; if (validate_key_id && Properties::GetSessionSharingId(session_id) != 0) { - bool status = cdm_engine_->FindSessionForKey(*parameters.key_id, + bool status = cdm_engine->FindSessionForKey(*parameters.key_id, &local_session_id); if (!status) { LOGE("WvContentDecryptionModule::Decrypt: unable to find session"); - return NEED_KEY; + return SESSION_NOT_FOUND_FOR_DECRYPT; } } - return cdm_engine_->Decrypt(local_session_id, parameters); + return cdm_engine->Decrypt(local_session_id, parameters); } void WvContentDecryptionModule::NotifyResolution(const CdmSessionId& session_id, uint32_t width, uint32_t height) { - cdm_engine_->NotifyResolution(session_id, width, height); + CdmEngine* cdm_engine = GetCdmForSessionId(session_id); + if (!cdm_engine) return; + cdm_engine->NotifyResolution(session_id, width, height); } bool WvContentDecryptionModule::IsValidServiceCertificate( @@ -218,6 +260,27 @@ bool WvContentDecryptionModule::IsValidServiceCertificate( return CdmLicense::VerifySignedServiceCertificate(certificate) == NO_ERROR; } +WvContentDecryptionModule::CdmInfo::CdmInfo() + : cdm_engine(new CdmEngine(&file_system)) {} + +CdmEngine* WvContentDecryptionModule::EnsureCdmForOrigin( + const std::string& origin) { + if (cdms_.find(origin) == cdms_.end()) { + // Will create a new instance using the default constructor. + cdms_[origin].file_system.SetOrigin(origin); + } + + return cdms_[origin].cdm_engine.get(); +} + +CdmEngine* WvContentDecryptionModule::GetCdmForSessionId( + const std::string& session_id) { + // Use find to avoid creating empty entries when not found. + auto it = cdm_by_session_id_.find(session_id); + if (it == cdm_by_session_id_.end()) return NULL; + return it->second; +} + void WvContentDecryptionModule::EnablePolicyTimer() { AutoLock auto_lock(policy_timer_lock_); if (!policy_timer_.IsRunning()) @@ -226,12 +289,25 @@ void WvContentDecryptionModule::EnablePolicyTimer() { void WvContentDecryptionModule::DisablePolicyTimer(bool force) { AutoLock auto_lock(policy_timer_lock_); - if ((cdm_engine_->SessionSize() == 0 || force) && policy_timer_.IsRunning()) + bool has_sessions = false; + for (auto it = cdms_.begin(); it != cdms_.end();) { + if (it->second.cdm_engine->SessionSize() != 0) { + has_sessions = true; + ++it; + } else { + // The CDM is no longer used for this origin, delete it. + it = cdms_.erase(it); + } + } + + if ((!has_sessions || force) && policy_timer_.IsRunning()) policy_timer_.Stop(); } void WvContentDecryptionModule::OnTimerEvent() { - cdm_engine_->OnTimerEvent(); + for (auto it = cdms_.begin(); it != cdms_.end(); ++it) { + it->second.cdm_engine->OnTimerEvent(); + } } uint32_t WvContentDecryptionModule::GenerateSessionSharingId() { diff --git a/libwvdrmengine/cdm/test/Android.mk b/libwvdrmengine/cdm/test/Android.mk index dba9f488..b83a3134 100644 --- a/libwvdrmengine/cdm/test/Android.mk +++ b/libwvdrmengine/cdm/test/Android.mk @@ -31,6 +31,10 @@ test_name := file_store_unittest test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk +test_name := file_utils_unittest +test_src_dir := . +include $(LOCAL_PATH)/unit-test.mk + test_name := http_socket_test test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk @@ -43,7 +47,7 @@ test_name := license_unittest test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk -test_name := max_res_engine_unittest +test_name := license_keys_unittest test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk @@ -51,6 +55,10 @@ test_name := policy_engine_unittest test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk +test_name := policy_engine_constraints_unittest +test_src_dir := ../core/test +include $(LOCAL_PATH)/unit-test.mk + test_name := request_license_test test_src_dir := . include $(LOCAL_PATH)/unit-test.mk diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index e9dae268..5008a7e1 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -795,10 +795,9 @@ TEST_F(WvCdmExtendedDurationTest, UsageOverflowTest) { TestWvCdmClientPropertySet* property_set = NULL; CdmSecurityLevel security_level = GetDefaultSecurityLevel(); - DeviceFiles handle; + FileSystem file_system; + DeviceFiles handle(&file_system); EXPECT_TRUE(handle.Init(security_level)); - File file; - handle.SetTestFile(&file); std::vector provider_session_tokens; EXPECT_TRUE(handle.DeleteAllUsageInfoForApp("", &provider_session_tokens)); @@ -831,8 +830,9 @@ TEST_F(WvCdmExtendedDurationTest, UsageOverflowTest) { 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 " - << static_cast(status) ; break; + default: + FAIL() << "GetUsageInfo failed with error " << static_cast(status); + break; } } } diff --git a/libwvdrmengine/cdm/test/file_utils_unittest.cpp b/libwvdrmengine/cdm/test/file_utils_unittest.cpp new file mode 100644 index 00000000..bf2d7a1e --- /dev/null +++ b/libwvdrmengine/cdm/test/file_utils_unittest.cpp @@ -0,0 +1,148 @@ +// Copyright 2016 Google Inc. All Rights Reserved. + +#include + +#include "file_store.h" +#include "file_utils.h" +#include "test_vectors.h" + +namespace wvcdm { + +namespace { +const std::string kTestDirName = "test_dir"; +const std::string kTestFileName = "test.txt"; +const std::string kTestFileName2 = "test2.txt"; +const std::string kTestFileNameExt = ".txt"; +const std::string kWildcard = "*"; +} // namespace + +class FileUtilsTest : public testing::Test { + protected: + FileUtilsTest() {} + virtual ~FileUtilsTest() {} + + virtual void SetUp() { CreateTestDir(); } + virtual void TearDown() { RemoveTestDir(); } + + void CreateTestDir() { + if (!file_system.Exists(test_vectors::kTestDir)) { + EXPECT_TRUE(FileUtils::CreateDirectory(test_vectors::kTestDir)); + } + EXPECT_TRUE(file_system.Exists(test_vectors::kTestDir)); + } + + void RemoveTestDir() { + EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir)); + } + + std::string GenerateRandomData(uint32_t len) { + std::string data(len, 0); + for (size_t i = 0; i < len; i++) { + data[i] = rand() % 256; + } + return data; + } + + FileSystem file_system; +}; + +TEST_F(FileUtilsTest, CreateDirectory) { + std::string dir_wo_delimiter = + test_vectors::kTestDir.substr(0, test_vectors::kTestDir.size() - 1); + if (file_system.Exists(dir_wo_delimiter)) + EXPECT_TRUE(file_system.Remove(dir_wo_delimiter)); + EXPECT_FALSE(file_system.Exists(dir_wo_delimiter)); + EXPECT_TRUE(FileUtils::CreateDirectory(dir_wo_delimiter)); + EXPECT_TRUE(file_system.Exists(dir_wo_delimiter)); + EXPECT_TRUE(file_system.Remove(dir_wo_delimiter)); + EXPECT_TRUE(FileUtils::CreateDirectory(test_vectors::kTestDir)); + EXPECT_TRUE(file_system.Exists(test_vectors::kTestDir)); + EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir)); +} + +TEST_F(FileUtilsTest, IsDir) { + std::string path = test_vectors::kTestDir + kTestFileName; + File* file = file_system.Open(path, FileSystem::kCreate); + EXPECT_TRUE(file); + file->Close(); + + EXPECT_TRUE(file_system.Exists(path)); + EXPECT_TRUE(file_system.Exists(test_vectors::kTestDir)); + EXPECT_FALSE(FileUtils::IsDirectory(path)); + EXPECT_TRUE(FileUtils::IsDirectory(test_vectors::kTestDir)); +} + +TEST_F(FileUtilsTest, IsRegularFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + File* file = file_system.Open(path, FileSystem::kCreate); + EXPECT_TRUE(file); + file->Close(); + + EXPECT_TRUE(file_system.Exists(path)); + EXPECT_TRUE(file_system.Exists(test_vectors::kTestDir)); + EXPECT_TRUE(FileUtils::IsRegularFile(path)); + EXPECT_FALSE(FileUtils::IsRegularFile(test_vectors::kTestDir)); +} + +TEST_F(FileUtilsTest, CopyFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + file_system.Remove(path); + + std::string write_data = GenerateRandomData(600); + File* wr_file = file_system.Open(path, FileSystem::kCreate); + EXPECT_TRUE(wr_file); + EXPECT_TRUE(wr_file->Write(write_data.data(), write_data.size())); + wr_file->Close(); + ASSERT_TRUE(file_system.Exists(path)); + + std::string path_copy = test_vectors::kTestDir + kTestFileName2; + EXPECT_FALSE(file_system.Exists(path_copy)); + EXPECT_TRUE(FileUtils::Copy(path, path_copy)); + + std::string read_data; + read_data.resize(file_system.FileSize(path_copy)); + File* rd_file = file_system.Open(path_copy, FileSystem::kReadOnly); + EXPECT_TRUE(rd_file); + EXPECT_TRUE(rd_file->Read(&read_data[0], read_data.size())); + rd_file->Close(); + EXPECT_EQ(write_data, read_data); + EXPECT_EQ(file_system.FileSize(path), file_system.FileSize(path_copy)); +} + +TEST_F(FileUtilsTest, ListEmptyDirectory) { + std::vector files; + EXPECT_TRUE(FileUtils::List(test_vectors::kTestDir, &files)); + EXPECT_EQ(0u, files.size()); +} + +TEST_F(FileUtilsTest, ListFiles) { + std::string path = test_vectors::kTestDir + kTestDirName; + EXPECT_TRUE(FileUtils::CreateDirectory(path)); + + path = test_vectors::kTestDir + kTestFileName; + std::string write_data = GenerateRandomData(600); + File* file = file_system.Open(path, FileSystem::kCreate); + EXPECT_TRUE(file); + EXPECT_TRUE(file->Write(write_data.data(), write_data.size())); + file->Close(); + EXPECT_TRUE(file_system.Exists(path)); + + path = test_vectors::kTestDir + kTestFileName2; + write_data = GenerateRandomData(600); + file = file_system.Open(path, FileSystem::kCreate); + EXPECT_TRUE(file); + EXPECT_TRUE(file->Write(write_data.data(), write_data.size())); + file->Close(); + EXPECT_TRUE(file_system.Exists(path)); + + std::vector files; + EXPECT_TRUE(FileUtils::List(test_vectors::kTestDir, &files)); + EXPECT_EQ(3u, files.size()); + + for (size_t i = 0; i < files.size(); ++i) { + EXPECT_TRUE(files[i] == kTestDirName || files[i] == kTestFileName || + files[i] == kTestFileName2); + } +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 0c4d1023..1ec5b43b 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -11,6 +11,7 @@ #include "config_test_env.h" #include "device_files.h" #include "file_store.h" +#include "file_utils.h" #include "license_protocol.pb.h" #include "license_request.h" #include "log.h" @@ -47,6 +48,8 @@ const int kHttpOk = 200; const int kHttpBadRequest = 400; const int kHttpInternalServerError = 500; +const char kOrigin[] = "com.example"; + // Protobuf generated classes using video_widevine_server::sdk::LicenseIdentification; using video_widevine_server::sdk::LicenseRequest_ContentIdentification; @@ -1136,6 +1139,10 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { } void Provision(SecurityLevel level) { + Provision(EMPTY_ORIGIN, level); + } + + void Provision(const std::string& origin, SecurityLevel level) { TestWvCdmClientPropertySet property_set_L3; TestWvCdmClientPropertySet* property_set = NULL; @@ -1145,7 +1152,7 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { } CdmResponseType status = decryptor_.OpenSession( - g_key_system, property_set, EMPTY_ORIGIN, NULL, &session_id_); + g_key_system, property_set, origin, NULL, &session_id_); switch (status) { case NO_ERROR: decryptor_.CloseSession(session_id_); @@ -1162,7 +1169,7 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { std::string cert_authority, cert, wrapped_key; status = decryptor_.GetProvisioningRequest(cert_type, cert_authority, - EMPTY_ORIGIN, &key_msg_, + origin, &key_msg_, &provisioning_server_url); EXPECT_EQ(wvcdm::NO_ERROR, status); if (NO_ERROR != status) return; @@ -1172,7 +1179,7 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { GetCertRequestResponse(g_config->provisioning_server_url()); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(wvcdm::NO_ERROR, - decryptor_.HandleProvisioningResponse(EMPTY_ORIGIN, response, + decryptor_.HandleProvisioningResponse(origin, response, &cert, &wrapped_key)); EXPECT_EQ(0, static_cast(cert.size())); EXPECT_EQ(0, static_cast(wrapped_key.size())); @@ -1238,20 +1245,57 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { decryptor_.CloseSession(session_id_); } +TEST_F(WvCdmRequestLicenseTest, PerOriginProvisioningTest) { + EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL3, kOrigin)); + + // Verify the empty origin is provisioned. + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, + &session_id_)); + decryptor_.CloseSession(session_id_); + + // The other origin should not be provisioned. + EXPECT_EQ( + wvcdm::NEED_PROVISIONING, + decryptor_.OpenSession(g_key_system, NULL, kOrigin, NULL, &session_id_)); +} + +TEST_F(WvCdmRequestLicenseTest, PerOriginProvisioningSupportsOldPaths) { + // This is the current file name scheme. This test exists to ensure we do + // not break existing clients if we decide to change the naming/paths of + // certificates. + const char kOldFileName[] = "cert0LF0GfM75iqlNA_sByaQMA==.bin"; + ASSERT_EQ("com.example", kOrigin); + + // Unprovision the device and delete all files. + EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL3, kOrigin)); + EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL3, EMPTY_ORIGIN)); + Provision(kOrigin, kLevel3); + + std::string base_path; + ASSERT_TRUE(Properties::GetDeviceFilesBasePath(kSecurityLevelL3, &base_path)); + + // Make sure that the cert exists. + std::vector files; + ASSERT_TRUE(FileUtils::List(base_path, &files)); + ASSERT_EQ(1u, files.size()); + EXPECT_EQ(kOldFileName, files[0]); + + // Reprovision the empty origin. + Provision(EMPTY_ORIGIN, kLevel3); +} + TEST_F(WvCdmRequestLicenseTest, UnprovisionTest) { CdmSecurityLevel security_level = GetDefaultSecurityLevel(); - DeviceFiles handle; + FileSystem file_system; + DeviceFiles handle(&file_system); EXPECT_TRUE(handle.Init(security_level)); - File file; - handle.SetTestFile(&file); std::string certificate; std::string wrapped_private_key; - EXPECT_TRUE(handle.RetrieveCertificate(EMPTY_ORIGIN, &certificate, - &wrapped_private_key)); + EXPECT_TRUE(handle.RetrieveCertificate(&certificate, &wrapped_private_key)); EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(security_level, EMPTY_ORIGIN)); - EXPECT_FALSE(handle.RetrieveCertificate(EMPTY_ORIGIN, &certificate, - &wrapped_private_key)); + EXPECT_FALSE(handle.RetrieveCertificate(&certificate, &wrapped_private_key)); } TEST_F(WvCdmRequestLicenseTest, ProvisioningInterposedRetryTest) { @@ -1437,10 +1481,9 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) { TestWvCdmClientPropertySet property_set; property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); - File file; - DeviceFiles handle; + FileSystem file_system; + DeviceFiles handle(&file_system); EXPECT_TRUE(handle.Init(kSecurityLevelL3)); - handle.SetTestFile(&file); EXPECT_TRUE(handle.DeleteAllFiles()); EXPECT_EQ(NEED_PROVISIONING, @@ -2151,10 +2194,9 @@ TEST_P(WvCdmUsageTest, WithClientId) { CdmSecurityLevel security_level = GetDefaultSecurityLevel(); std::string app_id = ""; - DeviceFiles handle; + FileSystem file_system; + DeviceFiles handle(&file_system); EXPECT_TRUE(handle.Init(security_level)); - File file; - handle.SetTestFile(&file); std::vector psts; EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(app_id, &psts)); @@ -2269,10 +2311,9 @@ TEST_F(WvCdmRequestLicenseTest, UsageInfoRetryTest) { CdmSecurityLevel security_level = GetDefaultSecurityLevel(); std::string app_id = ""; - DeviceFiles handle; + FileSystem file_system; + DeviceFiles handle(&file_system); EXPECT_TRUE(handle.Init(security_level)); - File file; - handle.SetTestFile(&file); std::vector psts; EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(app_id, &psts)); @@ -2351,10 +2392,9 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) { } CdmSecurityLevel security_level = GetDefaultSecurityLevel(); - DeviceFiles handle; + FileSystem file_system; + DeviceFiles handle(&file_system); EXPECT_TRUE(handle.Init(security_level)); - File file; - handle.SetTestFile(&file); std::vector psts; EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(usage_info_data->app_id, &psts)); @@ -2425,10 +2465,9 @@ TEST_F(WvCdmRequestLicenseTest, UsageReleaseAllTest) { Provision(kLevelDefault); CdmSecurityLevel security_level = GetDefaultSecurityLevel(); - DeviceFiles handle; + FileSystem file_system; + DeviceFiles handle(&file_system); EXPECT_TRUE(handle.Init(security_level)); - File file; - handle.SetTestFile(&file); std::vector psts; EXPECT_TRUE(handle.DeleteAllUsageInfoForApp("", &psts)); @@ -2739,7 +2778,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatusL3) { EXPECT_LE(10u, api_version); } -TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) { +TEST_F(WvCdmRequestLicenseTest, QueryOemCryptoSessionId) { Unprovision(); Provision(kLevelDefault); @@ -2750,7 +2789,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) { CdmQueryMap query_info; CdmQueryMap::iterator itr; EXPECT_EQ(wvcdm::NO_ERROR, - decryptor_.QueryKeyControlInfo(session_id_, &query_info)); + decryptor_.QueryOemCryptoSessionId(session_id_, &query_info)); uint32_t oem_crypto_session_id; itr = query_info.find(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID); @@ -2797,14 +2836,16 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) { if (std::string::npos != pos) break; } + // Delete any old files. EXPECT_NE(std::string::npos, pos); std::string old_base_path(base_path, 0, pos); - File file; + FileSystem file_system; for (size_t i = 0; i < security_dirs.size(); i++) { std::string path = old_base_path + kPathDelimiter + security_dirs[i]; - file.Remove(path); + file_system.Remove(path); } + // Provision the device to create any required files. decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, &session_id_); std::string provisioning_server_url; CdmCertificateType cert_type = kCertificateWidevine; @@ -2821,9 +2862,10 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) { decryptor_.CloseSession(session_id_); std::vector files; - EXPECT_TRUE(file.List(base_path, &files)); + EXPECT_TRUE(FileUtils::List(base_path, &files)); size_t number_of_files = files.size(); + // Create an offline license to create license files. decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, &session_id_); GenerateKeyRequest(key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, client_auth, false); @@ -2831,23 +2873,30 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) { EXPECT_FALSE(key_set_id_.empty()); decryptor_.CloseSession(session_id_); - EXPECT_TRUE(file.List(base_path, &files)); + EXPECT_TRUE(FileUtils::List(base_path, &files)); int number_of_new_files = files.size() - number_of_files; // There should be a new license file, and maybe a new usage table // file. EXPECT_LE(1, number_of_new_files); EXPECT_GE(2, number_of_new_files); + // Move files to the old location. for (size_t i = 0; i < files.size(); ++i) { std::string from = base_path + files[i]; - if (file.IsRegularFile(from)) { + if (FileUtils::IsRegularFile(from)) { std::string to = old_base_path + files[i]; - EXPECT_TRUE(file.Copy(from, to)); + EXPECT_TRUE(FileUtils::Copy(from, to)); } } - EXPECT_TRUE(file.Remove(base_path)); + EXPECT_TRUE(file_system.Remove(base_path)); + + //// Setup complete to earlier version (non-security level based) path. + + // Create a new file system. This will cause the files to be moved over. + // Normally this will happen when the plugin is created, but since we are + // reusing the same decryptor, we need it to update here. + FileSystem dummy_file_system; - // Setup complete to earlier version (non-security level based) path. // Restore persistent license, retrieve L1, L3 streaming licenses to verify session_id_.clear(); decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, &session_id_); diff --git a/libwvdrmengine/cdm/test/unit-test.mk b/libwvdrmengine/cdm/test/unit-test.mk index 0794d5f4..5db3bc50 100644 --- a/libwvdrmengine/cdm/test/unit-test.mk +++ b/libwvdrmengine/cdm/test/unit-test.mk @@ -18,7 +18,6 @@ LOCAL_SRC_FILES := \ ../core/test/url_request.cpp LOCAL_C_INCLUDES := \ - external/openssl/include \ vendor/widevine/libwvdrmengine/android/cdm/test \ vendor/widevine/libwvdrmengine/cdm/core/include \ vendor/widevine/libwvdrmengine/cdm/core/test \ diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index 6ee98bb8..7d997308 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -185,8 +185,38 @@ enum { kLoadUsageInfoMissing = ERROR_DRM_VENDOR_MIN + 171, kSessionFileHandleInitError = ERROR_DRM_VENDOR_MIN + 172, kIncorrectCryptoMode = ERROR_DRM_VENDOR_MIN + 173, - kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 173, kInvalidParametersEng5 = ERROR_DRM_VENDOR_MIN + 174, + kSessionNotFound12 = ERROR_DRM_VENDOR_MIN + 175, + kKeyNotFound1 = ERROR_DRM_VENDOR_MIN + 176, + kKeyNotFound2 = ERROR_DRM_VENDOR_MIN + 177, + kKeyConflict1 = ERROR_DRM_VENDOR_MIN + 178, + kInvalidParametersEng6 = ERROR_DRM_VENDOR_MIN + 179, + kInvalidParametersEng7 = ERROR_DRM_VENDOR_MIN + 180, + kInvalidParametersEng8 = ERROR_DRM_VENDOR_MIN + 181, + kInvalidParametersEng9 = ERROR_DRM_VENDOR_MIN + 182, + kInvalidParametersEng10 = ERROR_DRM_VENDOR_MIN + 183, + kInvalidParametersEng11 = ERROR_DRM_VENDOR_MIN + 184, + kInvalidParametersEng12 = ERROR_DRM_VENDOR_MIN + 185, + kSessionNotFound13 = ERROR_DRM_VENDOR_MIN + 186, + kSessionNotFound14 = ERROR_DRM_VENDOR_MIN + 187, + kSessionNotFound15 = ERROR_DRM_VENDOR_MIN + 188, + kSessionNotFound16 = ERROR_DRM_VENDOR_MIN + 189, + kKeyNotFound3 = ERROR_DRM_VENDOR_MIN + 190, + kKeyNotFound4 = ERROR_DRM_VENDOR_MIN + 191, + kKeyNotFound5 = ERROR_DRM_VENDOR_MIN + 192, + kKeyNotFound6 = ERROR_DRM_VENDOR_MIN + 193, + kKeyError1 = ERROR_DRM_VENDOR_MIN + 194, + kKeyError2 = ERROR_DRM_VENDOR_MIN + 195, + kKeyError3 = ERROR_DRM_VENDOR_MIN + 196, + kKeyError4 = ERROR_DRM_VENDOR_MIN + 197, + kInvalidParametersEng13 = ERROR_DRM_VENDOR_MIN + 198, + kInvalidParametersEng14 = ERROR_DRM_VENDOR_MIN + 199, + kInvalidParametersEng15 = ERROR_DRM_VENDOR_MIN + 200, + kInvalidParametersEng16 = ERROR_DRM_VENDOR_MIN + 201, + + // This should always follow the last error code. + // The offset value should be updated each time a new error code is added. + kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 201, // Used by crypto test mode kErrorTestMode = ERROR_DRM_VENDOR_MAX, diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index f983bfd8..ee26ef34 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -363,6 +363,38 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) { return android::ERROR_DRM_CANNOT_HANDLE; case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION: return android::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION; + case wvcdm::SESSION_NOT_FOUND_13: + return kSessionNotFound13; + case wvcdm::SESSION_NOT_FOUND_14: + return kSessionNotFound14; + case wvcdm::SESSION_NOT_FOUND_15: + return kSessionNotFound15; + case wvcdm::SESSION_NOT_FOUND_16: + return kSessionNotFound16; + case wvcdm::KEY_NOT_FOUND_3: + return kKeyNotFound3; + case wvcdm::KEY_NOT_FOUND_4: + return kKeyNotFound4; + case wvcdm::KEY_NOT_FOUND_5: + return kKeyNotFound5; + case wvcdm::KEY_NOT_FOUND_6: + return kKeyNotFound6; + case wvcdm::KEY_ERROR_1: + return kKeyError1; + case wvcdm::KEY_ERROR_2: + return kKeyError2; + case wvcdm::KEY_ERROR_3: + return kKeyError3; + case wvcdm::KEY_ERROR_4: + return kKeyError4; + case wvcdm::INVALID_PARAMETERS_ENG_13: + return kInvalidParametersEng13; + case wvcdm::INVALID_PARAMETERS_ENG_14: + return kInvalidParametersEng14; + case wvcdm::INVALID_PARAMETERS_ENG_15: + return kInvalidParametersEng15; + case wvcdm::INVALID_PARAMETERS_ENG_16: + return kInvalidParametersEng16; case wvcdm::UNUSED_1: case wvcdm::UNUSED_2: case wvcdm::UNUSED_3: diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index ed3b398d..29998ddc 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -36,7 +36,7 @@ using wvcdm::CdmSessionId; using wvcdm::CdmResponseType; using wvcdm::WvContentDecryptionModule; -const OEMCrypto_Algorithm kInvalidCrytpoAlgorithm = +const OEMCrypto_Algorithm kInvalidCryptoAlgorithm = static_cast(-1); class WVDrmPlugin : public android::DrmPlugin, @@ -155,13 +155,13 @@ class WVDrmPlugin : public android::DrmPlugin, public: CryptoSession() : mOecSessionId(-1), - mCipherAlgorithm(kInvalidCrytpoAlgorithm), - mMacAlgorithm(kInvalidCrytpoAlgorithm) {} + mCipherAlgorithm(kInvalidCryptoAlgorithm), + mMacAlgorithm(kInvalidCryptoAlgorithm) {} CryptoSession(OEMCrypto_SESSION sessionId) : mOecSessionId(sessionId), - mCipherAlgorithm(kInvalidCrytpoAlgorithm), - mMacAlgorithm(kInvalidCrytpoAlgorithm) {} + mCipherAlgorithm(kInvalidCryptoAlgorithm), + mMacAlgorithm(kInvalidCryptoAlgorithm) {} OEMCrypto_SESSION oecSessionId() const { return mOecSessionId; } diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index 0722826f..fafd8a91 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -111,7 +111,7 @@ status_t WVDrmPlugin::openSession(Vector& sessionId) { // Construct a CryptoSession CdmQueryMap info; - res = mCDM->QueryKeyControlInfo(cdmSessionId, &info); + res = mCDM->QueryOemCryptoSessionId(cdmSessionId, &info); if (isCdmResponseTypeSuccess(res) && info.count(QUERY_KEY_OEMCRYPTO_SESSION_ID)) { @@ -688,7 +688,7 @@ status_t WVDrmPlugin::encrypt(const Vector& sessionId, const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId]; - if (cryptoSession.cipherAlgorithm() == kInvalidCrytpoAlgorithm) { + if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) { return android::NO_INIT; } @@ -726,7 +726,7 @@ status_t WVDrmPlugin::decrypt(const Vector& sessionId, const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId]; - if (cryptoSession.cipherAlgorithm() == kInvalidCrytpoAlgorithm) { + if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) { return android::NO_INIT; } @@ -763,7 +763,7 @@ status_t WVDrmPlugin::sign(const Vector& sessionId, const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId]; - if (cryptoSession.macAlgorithm() == kInvalidCrytpoAlgorithm) { + if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) { return android::NO_INIT; } @@ -817,7 +817,7 @@ status_t WVDrmPlugin::verify(const Vector& sessionId, const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId]; - if (cryptoSession.macAlgorithm() == kInvalidCrytpoAlgorithm) { + if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) { return android::NO_INIT; } diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index db623ef0..a98c20b3 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -66,8 +66,8 @@ class MockCDM : public WvContentDecryptionModule { MOCK_METHOD2(QueryKeyStatus, CdmResponseType(const CdmSessionId&, CdmQueryMap*)); - MOCK_METHOD2(QueryKeyControlInfo, CdmResponseType(const CdmSessionId&, - CdmQueryMap*)); + MOCK_METHOD2(QueryOemCryptoSessionId, CdmResponseType(const CdmSessionId&, + CdmQueryMap*)); MOCK_METHOD5(GetProvisioningRequest, CdmResponseType(CdmCertificateType, const std::string&, @@ -197,7 +197,7 @@ TEST_F(WVDrmPluginTest, OpensSessions) { Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); @@ -454,7 +454,7 @@ TEST_F(WVDrmPluginTest, HandlesPrivacyCertCaseOfAddKey) { Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); EXPECT_CALL(*cdm, CloseSession(_)) @@ -968,7 +968,7 @@ TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) { .WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId), Return(wvcdm::NO_ERROR))); - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); @@ -1047,7 +1047,7 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) { .WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId), Return(wvcdm::NO_ERROR))); - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); @@ -1107,7 +1107,7 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) { .WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId), Return(wvcdm::NO_ERROR))); - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); @@ -1169,7 +1169,7 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) { .WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId), Return(wvcdm::NO_ERROR))); - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); @@ -1241,7 +1241,7 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) { .WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId), Return(wvcdm::NO_ERROR))); - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); @@ -1274,7 +1274,7 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) { .WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId), Return(wvcdm::NO_ERROR))); - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); @@ -1308,10 +1308,10 @@ TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) { .WillOnce(DoAll(SetArgPointee<4>(cdmSessionId2), Return(wvcdm::NO_ERROR))); - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId1, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId1, _)) .WillOnce(Invoke(setSessionIdOnMap<4>)); - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId2, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId2, _)) .WillOnce(Invoke(setSessionIdOnMap<5>)); EXPECT_CALL(*cdm, CloseSession(_)) @@ -1458,7 +1458,7 @@ TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) { Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); EXPECT_CALL(*cdm, CloseSession(_)) @@ -1498,7 +1498,7 @@ TEST_F(WVDrmPluginTest, CanSetAppId) { Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); EXPECT_CALL(*cdm, CloseSession(_)) @@ -1535,7 +1535,7 @@ TEST_P(WVDrmPluginOriginTest, CanSetOrigin) { // Provide expected mock behavior { // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); // Provide expected behavior when plugin closes a session @@ -1585,7 +1585,7 @@ TEST_F(WVDrmPluginTest, CanSetSecurityLevel) { Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); EXPECT_CALL(*cdm, CloseSession(_)) @@ -1670,7 +1670,7 @@ TEST_F(WVDrmPluginTest, CanSetPrivacyMode) { Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); EXPECT_CALL(*cdm, CloseSession(_)) @@ -1727,7 +1727,7 @@ TEST_F(WVDrmPluginTest, CanSetServiceCertificate) { Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); EXPECT_CALL(*cdm, CloseSession(_)) @@ -1780,7 +1780,7 @@ TEST_F(WVDrmPluginTest, CanSetSessionSharing) { Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); EXPECT_CALL(*cdm, CloseSession(_)) @@ -1839,7 +1839,7 @@ TEST_F(WVDrmPluginTest, AllowsStoringOfSessionSharingId) { Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info - EXPECT_CALL(*cdm, QueryKeyControlInfo(cdmSessionId, _)) + EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _)) .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); EXPECT_CALL(*cdm, CloseSession(_)) diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index 73f16132..ae23c5c9 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -397,7 +397,8 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session); * OEMCrypto_CloseSession * * Description: - * Closes the crypto security engine session and frees any associated resources. + * Closes the crypto security engine session and frees any associated + * resources. * * Parameters: * session (in) - handle for the session to be closed. diff --git a/libwvdrmengine/oemcrypto/mock/Android.mk b/libwvdrmengine/oemcrypto/mock/Android.mk index a751c18c..2b8f89fe 100644 --- a/libwvdrmengine/oemcrypto/mock/Android.mk +++ b/libwvdrmengine/oemcrypto/mock/Android.mk @@ -21,8 +21,6 @@ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/src \ vendor/widevine/libwvdrmengine/cdm/core/include \ vendor/widevine/libwvdrmengine/third_party/stringencoders/src \ - external/openssl/include \ - external/openssl/include/openssl \ LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index 72ba87d1..c41b85ea 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -1182,9 +1182,12 @@ bool SessionContext::IsUsageEntryValid() { void SessionContext::ReleaseUsageEntry() { usage_entry_ = NULL; } -CryptoEngine::CryptoEngine() - : current_session_(NULL), use_test_keybox_(false), - usage_table_(new UsageTable(this)), rsa_key_(NULL) { +CryptoEngine::CryptoEngine(wvcdm::FileSystem* file_system) + : current_session_(NULL), + use_test_keybox_(false), + file_system_(file_system), + usage_table_(new UsageTable(this)), + rsa_key_(NULL) { ERR_load_crypto_strings(); if (!supports_keybox() && !LoadPkcs8RsaKey(kPrivateKey, kPrivateKeySize)) { diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index f9d8fc1e..dfa75335 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -12,6 +12,7 @@ #include +#include "file_store.h" #include "lock.h" #include "oemcrypto_key_mock.h" #include "oemcrypto_keybox_mock.h" @@ -231,7 +232,7 @@ class SessionContext { class CryptoEngine { public: - CryptoEngine(); + CryptoEngine(wvcdm::FileSystem* file_system); ~CryptoEngine(); bool Initialized() { return true; } @@ -267,6 +268,7 @@ class CryptoEngine { OEMCrypto_HDCP_Capability maximum_hdcp_capability(); UsageTable* usage_table() { return usage_table_; } + wvcdm::FileSystem* file_system() { return file_system_; } bool local_display(); bool closed_platform(); bool supports_storage(); @@ -284,6 +286,7 @@ class CryptoEngine { WvTestKeybox test_keybox_; bool use_test_keybox_; wvcdm::Lock session_table_lock_; + wvcdm::FileSystem* file_system_; UsageTable* usage_table_; RSA* rsa_key_; // If no keybox, this is baked in certificate. diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index f4004381..1606ab9b 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -17,6 +17,7 @@ #include #include #include +#include "file_store.h" #include "log.h" #include "oemcrypto_engine_mock.h" #include "oemcrypto_logging.h" @@ -45,7 +46,9 @@ OEMCryptoResult OEMCrypto_Initialize(void) { LOGI("------------------------- OEMCrypto_Initialize(void)\n"); } - crypto_engine = new CryptoEngine; + // NOTE: This requires a compatible Filesystem implementation. + wvcdm::FileSystem* fs = new wvcdm::FileSystem(); + crypto_engine = new CryptoEngine(fs); if (!crypto_engine || !crypto_engine->Initialized()) { LOGE("[OEMCrypto_Initialize(): failed]"); @@ -1321,17 +1324,17 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (NO_ERROR != crypto_engine->ValidateKeybox()) { - LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_KEYBOX_INVALID]"); + LOGE("[OEMCrypto_Generic_Enrcypt(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_INVALID_SESSION]"); + LOGE("[OEMCrypto_Generic_Encrypt(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } if (in_buffer == NULL || buffer_length == 0 || iv == NULL || out_buffer == NULL) { - LOGE("[OEMCrypto_Generic_Enrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + LOGE("[OEMCrypto_Generic_Encrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } OEMCryptoResult sts = diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp index d1b2ed40..d0fd6154 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp @@ -144,7 +144,8 @@ UsageTable::UsageTable(CryptoEngine *ce) { table_.clear(); // Load saved table. - wvcdm::File file; + wvcdm::FileSystem *file_system = ce->file_system(); + wvcdm::File *file; std::string path; // Note: this path is OK for a real implementation, but using security level 1 // would be better. @@ -155,14 +156,14 @@ UsageTable::UsageTable(CryptoEngine *ce) { } std::string filename = path + "UsageTable.dat"; - if (!file.Exists(filename)) { + if (!file_system->Exists(filename)) { if (LogCategoryEnabled(kLoggingTraceUsageTable)) { LOGI("UsageTable: No saved usage table. Creating new table."); } return; } - size_t file_size = file.FileSize(filename); + size_t file_size = file_system->FileSize(filename); std::vector encrypted_buffer(file_size); std::vector buffer(file_size); StoredUsageTable *stored_table = @@ -170,12 +171,13 @@ UsageTable::UsageTable(CryptoEngine *ce) { StoredUsageTable *encrypted_table = reinterpret_cast(&encrypted_buffer[0]); - if (!file.Open(filename, wvcdm::File::kReadOnly | wvcdm::File::kBinary)) { + file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly); + if (!file) { LOGE("UsageTable: File open failed: %s", path.c_str()); return; } - file.Read(reinterpret_cast(&encrypted_buffer[0]), file_size); - file.Close(); + file->Read(reinterpret_cast(&encrypted_buffer[0]), file_size); + file->Close(); // First, verify the signature of the usage table file. std::vector &key = ce_->real_keybox().device_key(); @@ -213,15 +215,15 @@ UsageTable::UsageTable(CryptoEngine *ce) { // a file in user space. It should be stored in secure memory. For the // reference implementation, we'll just pretend this is secure. std::string filename2 = path + "GenerationNumber.dat"; - if (!file.Exists(filename2) || - !file.Open(filename2, wvcdm::File::kReadOnly | wvcdm::File::kBinary)) { + file = file_system->Open(filename2, wvcdm::FileSystem::kReadOnly); + if (!file) { LOGE("UsageTable: File open failed: %s (clearing table)", path.c_str()); generation_ = 0; table_.clear(); return; } - file.Read(reinterpret_cast(&generation_), sizeof(int64_t)); - file.Close(); + file->Read(reinterpret_cast(&generation_), sizeof(int64_t)); + file->Close(); if (stored_table->generation == generation_ + 1) { if (LogCategoryEnabled(kLoggingTraceUsageTable)) { LOGW("UsageTable: File is one generation old. Acceptable rollback."); @@ -297,7 +299,8 @@ bool UsageTable::SaveToFile() { return false; } - wvcdm::File file; + wvcdm::FileSystem *file_system = ce_->file_system(); + wvcdm::File *file; std::string path; // Note: this path is OK for a real implementation, but using security level 1 // would be better. @@ -306,38 +309,34 @@ bool UsageTable::SaveToFile() { LOGE("UsageTable: Unable to get base path"); return false; } - if (!file.IsDirectory(path)) { - if (!file.CreateDirectory(path)) { - LOGE("UsageTable: could not create directory: %s", path.c_str()); - return false; - } - } std::string filename = path + "UsageTable.dat"; - if (!file.Exists(filename)) { + if (!file_system->Exists(filename)) { if (LogCategoryEnabled(kLoggingTraceUsageTable)) { LOGI("UsageTable: No saved usage table. Creating new table."); } } - if (!file.Open(filename, wvcdm::File::kCreate | wvcdm::File::kTruncate | - wvcdm::File::kBinary)) { + file = file_system->Open( + filename, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate); + if (!file) { LOGE("UsageTable: Could not save usage table: %s", path.c_str()); return false; } - file.Write(reinterpret_cast(&encrypted_buffer[0]), file_size); - file.Close(); + file->Write(reinterpret_cast(&encrypted_buffer[0]), file_size); + file->Close(); // On a real implementation, you should NOT put the generation number in // a file in user space. It should be stored in secure memory. std::string filename2 = path + "GenerationNumber.dat"; - if (!file.Open(filename2, wvcdm::File::kCreate | wvcdm::File::kTruncate | - wvcdm::File::kBinary)) { + file = file_system->Open( + filename2, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate); + if (!file) { LOGE("UsageTable: File open failed: %s", path.c_str()); return false; } - file.Write(reinterpret_cast(&generation_), sizeof(int64_t)); - file.Close(); + file->Write(reinterpret_cast(&generation_), sizeof(int64_t)); + file->Close(); return true; } diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 65c10939..99057160 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -1293,7 +1293,7 @@ class OEMCryptoSessionTestsDecryptTests } void TestDecryptCENC(const vector& key, - const vector& encryptionIv, + const vector& /* encryptionIv */, const vector& encryptedData, const vector& unencryptedData) { OEMCryptoResult sts; diff --git a/libwvdrmengine/run_all_unit_tests.sh b/libwvdrmengine/run_all_unit_tests.sh index 5f0edf5d..340217c6 100755 --- a/libwvdrmengine/run_all_unit_tests.sh +++ b/libwvdrmengine/run_all_unit_tests.sh @@ -68,14 +68,16 @@ adb_shell_run oemcrypto_test adb_shell_run request_license_test # cdm_extended_duration_test takes >30 minutes to run. # adb_shell_run cdm_extended_duration_test -adb_shell_run max_res_engine_unittest adb_shell_run policy_engine_unittest +adb_shell_run policy_engine_constraints_unittest adb_shell_run libwvdrmmediacrypto_test adb_shell_run libwvdrmdrmplugin_test adb_shell_run cdm_engine_test adb_shell_run cdm_session_unittest adb_shell_run file_store_unittest +adb_shell_run file_utils_unittest adb_shell_run license_unittest +adb_shell_run license_keys_unittest adb_shell_run initialization_data_unittest adb_shell_run device_files_unittest adb_shell_run timer_unittest