diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index aa4900f7..66db1f5f 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -5,7 +5,6 @@ #include "certificate_provisioning.h" #include "oemcrypto_adapter.h" -#include "timer.h" #include "wv_cdm_types.h" namespace wvcdm { @@ -18,121 +17,114 @@ class WvCdmEventListener; typedef std::map CdmSessionMap; typedef std::map CdmReleaseKeySetMap; -class CdmEngine : public TimerHandler { +class CdmEngine { public: CdmEngine(); virtual ~CdmEngine(); // Session related methods - virtual CdmResponseType OpenSession( - const CdmKeySystem& key_system, - const CdmClientPropertySet* property_set, - CdmSessionId* session_id); - virtual CdmResponseType CloseSession(const CdmSessionId& session_id); + CdmResponseType OpenSession(const CdmKeySystem& key_system, + const CdmClientPropertySet* property_set, + CdmSessionId* session_id); + CdmResponseType CloseSession(const CdmSessionId& session_id); - virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); - virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); + CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); + CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); // License related methods // Construct a valid license request - virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, - const CdmKeySetId& key_set_id, - const CdmInitData& init_data, - const CdmLicenseType license_type, - CdmAppParameterMap& app_parameters, - CdmKeyMessage* key_request, - std::string* server_url); + CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, + const CdmKeySetId& key_set_id, + const CdmInitData& init_data, + const CdmLicenseType license_type, + CdmAppParameterMap& app_parameters, + CdmKeyMessage* key_request, + std::string* server_url); // Accept license response and extract key info. - virtual CdmResponseType AddKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data, - CdmKeySetId* key_set_id); + CdmResponseType AddKey(const CdmSessionId& session_id, + const CdmKeyResponse& key_data, + CdmKeySetId* key_set_id); - virtual CdmResponseType RestoreKey(const CdmSessionId& session_id, - const CdmKeySetId& key_set_id); + CdmResponseType RestoreKey(const CdmSessionId& session_id, + const CdmKeySetId& key_set_id); CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); // Construct valid renewal request for the current session keys. - virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, - CdmKeyMessage* key_request, - std::string* server_url); + CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, + CdmKeyMessage* key_request, + std::string* server_url); // Accept renewal response and update key info. - virtual CdmResponseType RenewKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data); + CdmResponseType RenewKey(const CdmSessionId& session_id, + const CdmKeyResponse& key_data); // Query system information - virtual CdmResponseType QueryStatus(CdmQueryMap* info); + CdmResponseType QueryStatus(CdmQueryMap* info); // Query session information virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id, CdmQueryMap* key_info); // Query license information - virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, - CdmQueryMap* key_info); + CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, + CdmQueryMap* key_info); // Query seesion control information - virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, - CdmQueryMap* key_info); + CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, + CdmQueryMap* key_info); // Provisioning related methods - virtual CdmResponseType GetProvisioningRequest( - CdmProvisioningRequest* request, - std::string* default_url); + CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request, + std::string* default_url); - virtual CdmResponseType HandleProvisioningResponse( - CdmProvisioningResponse& response); + CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response); // Secure stop related methods - virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops); - virtual CdmResponseType ReleaseSecureStops( + CdmResponseType GetSecureStops(CdmSecureStops* secure_stops); + CdmResponseType ReleaseSecureStops( const CdmSecureStopReleaseMessage& message); // Decryption and key related methods // Accept encrypted buffer and return decrypted data. - virtual CdmResponseType Decrypt(const CdmSessionId& session_id, - const CdmDecryptionParameters& parameters); + CdmResponseType Decrypt(const CdmSessionId& session_id, + const CdmDecryptionParameters& parameters); + + size_t SessionSize() const { return sessions_.size(); } // Is the key known to any session? - virtual bool IsKeyValid(const KeyId& key_id); - virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId); + bool IsKeyLoaded(const KeyId& key_id); + bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId); // Event listener related methods - virtual bool AttachEventListener(const CdmSessionId& session_id, - WvCdmEventListener* listener); - virtual bool DetachEventListener(const CdmSessionId& session_id, - WvCdmEventListener* listener); + bool AttachEventListener(const CdmSessionId& session_id, + WvCdmEventListener* listener); + bool DetachEventListener(const CdmSessionId& session_id, + WvCdmEventListener* listener); // Parse a blob of multiple concatenated PSSH atoms to extract the first // widevine pssh static bool ExtractWidevinePssh(const CdmInitData& init_data, CdmInitData* output); + // Timer expiration method + void OnTimerEvent(); + private: // private methods // Cancel all sessions - virtual bool CancelSessions(); - virtual bool ValidateKeySystem(const CdmKeySystem& key_system); + bool CancelSessions(); + bool ValidateKeySystem(const CdmKeySystem& key_system); - // timer related methods to drive policy decisions - virtual void EnablePolicyTimer(); - virtual void DisablePolicyTimer(bool force); - virtual void OnTimerEvent(); - - virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); + void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); // instance variables CdmSessionMap sessions_; CdmReleaseKeySetMap release_key_sets_; - CertificateProvisioning cert_provisioning_; SecurityLevel cert_provisioning_requested_security_level_; - // policy timer - Timer policy_timer_; - CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine); }; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index 6cc5920d..5a132b88 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -79,7 +79,7 @@ class CdmSession { // ReleaseKey() - Accept response and release key. CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); - bool IsKeyValid(const KeyId& key_id); + bool IsKeyLoaded(const KeyId& key_id); bool AttachEventListener(WvCdmEventListener* listener); bool DetachEventListener(WvCdmEventListener* listener); @@ -119,8 +119,6 @@ class CdmSession { // license type release and offline related information CdmKeySetId key_set_id_; - KeyId key_id_; - // Used for certificate based licensing std::string wrapped_key_; bool is_certificate_loaded_; diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index f435829a..1a5cbe13 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -63,7 +63,6 @@ class CryptoSession { std::string* wrapped_rsa_key); // Media data path - bool SelectKey(const std::string& key_id); CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); bool GetRandom(size_t data_length, uint8_t* random_data); @@ -80,6 +79,8 @@ class CryptoSession { size_t GetOffset(std::string message, std::string field); bool SetDestinationBufferType(); + bool SelectKey(const std::string& key_id); + static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature static Lock crypto_lock_; static bool initialized_; diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 929675ce..32791858 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -59,6 +59,9 @@ class DeviceFiles { bool RetrieveFile(const char* name, std::string* data); private: + // Certificate and offline licenses are now stored in security + // level specific directories. In an earlier version they were + // stored in a common directory and need to be copied over. virtual void SecurityLevelPathBackwardCompatibility(); File* file_; diff --git a/libwvdrmengine/cdm/core/include/file_store.h b/libwvdrmengine/cdm/core/include/file_store.h index a41f1131..3d319e5e 100644 --- a/libwvdrmengine/cdm/core/include/file_store.h +++ b/libwvdrmengine/cdm/core/include/file_store.h @@ -12,6 +12,8 @@ namespace wvcdm { // File class. The implementation is platform dependent. class File { public: + class Impl; + // defines as bit flag enum OpenFlags { kNoFlags = 0, @@ -39,7 +41,6 @@ class File { virtual ssize_t FileSize(const std::string& file_path); private: - class Impl; Impl *impl_; CORE_DISALLOW_COPY_AND_ASSIGN(File); diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index 650b5553..efe7576e 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -27,7 +27,12 @@ class PolicyEngine { // status is not calculated to avoid overhead in the decryption path. inline bool can_decrypt() { return can_decrypt_; } - void OnTimerEvent(bool& event_occurred, CdmEventType& event); + // OnTimerEvent is called when a timer fires. It notifies the Policy Engine + // that the timer has fired and that it should check whether any events have + // occurred since the last timer event. If so, it sets event_occurred to true + // and sets event to point to the event that occurred. If not, it sets + // event_occurred to false. + void OnTimerEvent(bool* event_occurred, CdmEventType* event); // SetLicense is used in handling the initial license response. It stores // an exact copy of the policy information stored in the license. diff --git a/libwvdrmengine/cdm/core/include/properties.h b/libwvdrmengine/cdm/core/include/properties.h index 726491d6..8e6192f6 100644 --- a/libwvdrmengine/cdm/core/include/properties.h +++ b/libwvdrmengine/cdm/core/include/properties.h @@ -14,7 +14,7 @@ namespace wvcdm { typedef std::map - CdmClientPropertySetMap; + CdmClientPropertySetMap; // This class saves information about features and properties enabled // for a given platform. At initialization it initializes properties from @@ -42,9 +42,7 @@ class Properties { static inline bool use_certificates_as_identification() { return use_certificates_as_identification_; } - static inline bool extract_pssh_data() { - return extract_pssh_data_; - } + static inline bool extract_pssh_data() { return extract_pssh_data_; } static inline bool decrypt_with_empty_session_support() { return decrypt_with_empty_session_support_; } @@ -64,13 +62,12 @@ class Properties { static bool GetSecurityLevelDirectories(std::vector* dirs); static const std::string GetSecurityLevel(const CdmSessionId& session_id); static const std::vector GetServiceCertificate( - const CdmSessionId& session_id); + const CdmSessionId& session_id); static bool UsePrivacyMode(const CdmSessionId& session_id); static uint32_t GetSessionSharingId(const CdmSessionId& session_id); - static bool AddSessionPropertySet( - const CdmSessionId& session_id, - const CdmClientPropertySet* property_set); + static bool AddSessionPropertySet(const CdmSessionId& session_id, + const CdmClientPropertySet* property_set); static bool RemoveSessionPropertySet(const CdmSessionId& session_id); private: @@ -94,16 +91,17 @@ class Properties { static void set_use_certificates_as_identification(bool flag) { use_certificates_as_identification_ = flag; } - static void set_extract_pssh_data(bool flag) { - extract_pssh_data_ = flag; - } + static void set_extract_pssh_data(bool flag) { extract_pssh_data_ = flag; } + static void set_decrypt_with_empty_session_support(bool flag) { decrypt_with_empty_session_support_ = flag; } - static void set_security_level_path_backward_compatibility_support(bool flag) { + static void set_security_level_path_backward_compatibility_support( + bool flag) { security_level_path_backward_compatibility_support_ = flag; } + private: static bool begin_license_usage_when_received_; static bool require_explicit_renew_request_; static bool oem_crypto_use_secure_buffers_; diff --git a/libwvdrmengine/cdm/core/include/string_conversions.h b/libwvdrmengine/cdm/core/include/string_conversions.h index 4c0ac32a..1d0d63de 100644 --- a/libwvdrmengine/cdm/core/include/string_conversions.h +++ b/libwvdrmengine/cdm/core/include/string_conversions.h @@ -11,6 +11,7 @@ namespace wvcdm { std::vector a2b_hex(const std::string& b); +std::vector a2b_hex(const std::string& label, const std::string& b); std::string a2bs_hex(const std::string& b); std::string b2a_hex(const std::vector& b); std::string b2a_hex(const std::string& b); diff --git a/libwvdrmengine/cdm/core/include/timer.h b/libwvdrmengine/cdm/core/include/timer.h index 8458b1d0..4cfe1616 100644 --- a/libwvdrmengine/cdm/core/include/timer.h +++ b/libwvdrmengine/cdm/core/include/timer.h @@ -31,6 +31,8 @@ class TimerHandler { class Timer { public: + class Impl; + Timer(); ~Timer(); @@ -39,7 +41,6 @@ class Timer { bool IsRunning(); private: - class Impl; Impl *impl_; CORE_DISALLOW_COPY_AND_ASSIGN(Timer); diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 1f5291d9..a70d7944 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -54,7 +54,7 @@ static const std::string QUERY_VALUE_OFFLINE = "Offline"; static const std::string QUERY_VALUE_SECURITY_LEVEL_L1 = "L1"; static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2"; static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3"; -static const std::string QUERY_VALUE_SECURITY_LEVEL_Unknown = "Unknown"; +static const std::string QUERY_VALUE_SECURITY_LEVEL_UNKNOWN = "Unknown"; } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h b/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h index b9bfb476..e2fdfa8b 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h @@ -8,7 +8,7 @@ namespace wvcdm { // Listener for events from the Content Decryption Module. -// The caller of the CDM API must provide an implementation for onEvent +// The caller of the CDM API must provide an implementation for OnEvent // and signal its intent by using the Attach/DetachEventListener methods // in the WvContentDecryptionModule class. class WvCdmEventListener { @@ -16,7 +16,7 @@ class WvCdmEventListener { WvCdmEventListener() {} virtual ~WvCdmEventListener() {} - virtual void onEvent(const CdmSessionId& session_id, + virtual void OnEvent(const CdmSessionId& session_id, CdmEventType cdm_event) = 0; private: diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index ad418081..9a4a3114 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -28,6 +28,11 @@ typedef std::vector CdmSecureStopReleaseMessage; typedef std::string CdmProvisioningRequest; typedef std::string CdmProvisioningResponse; +// Types for shared host/cdm interface pairs used to shared vendor data. +typedef std::pair kStringPairs; +typedef std::vector kVectorBytes; +typedef std::pair kVectorPairs; + enum CdmResponseType { NO_ERROR, UNKNOWN_ERROR, diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index ff1efe1f..0c8cd4fd 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -15,10 +15,6 @@ #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" -namespace { -const int kCdmPolicyTimerDurationSeconds = 1; -} - namespace wvcdm { CdmEngine::CdmEngine() @@ -29,7 +25,6 @@ CdmEngine::CdmEngine() CdmEngine::~CdmEngine() { CancelSessions(); - DisablePolicyTimer(true); CdmSessionMap::iterator i(sessions_.begin()); for (; i != sessions_.end(); ++i) delete i->second; @@ -101,7 +96,6 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) { CdmSession* session = iter->second; sessions_.erase(session_id); - DisablePolicyTimer(false); delete session; return NO_ERROR; } @@ -248,10 +242,6 @@ CdmResponseType CdmEngine::AddKey( return sts; } - if (!license_type_release) { - EnablePolicyTimer(); - } - return KEY_ADDED; } @@ -286,9 +276,6 @@ CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) { //TODO(gmorgan): Issue: what is semantics of canceling a key request. Should //this call cancel all keys for the session? - // TODO(jfore): We should disable the policy timer here if there are no - // active sessions. Sessions are currently not being destroyed here. We can - // add this logic once the semantics of canceling the key is worked out. CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { @@ -296,8 +283,8 @@ CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) { return KEY_ERROR; } - // TODO(edwinwong, rfrias): unload keys here - DisablePolicyTimer(false); + // Re-initialize to release crypto session/keys without closing session + iter->second->Init(); return NO_ERROR; } @@ -372,7 +359,8 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) { break; case kSecurityLevelUninitialized: case kSecurityLevelUnknown: - (*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_Unknown; + (*key_info)[QUERY_KEY_SECURITY_LEVEL] = + QUERY_VALUE_SECURITY_LEVEL_UNKNOWN; break; default: return KEY_ERROR; @@ -504,8 +492,13 @@ CdmResponseType CdmEngine::Decrypt( } if (parameters.decrypt_buffer == NULL) { - LOGE("CdmEngine::Decrypt: no dest decrypt buffer"); - return KEY_ERROR; + if (!parameters.is_secure && + !Properties::Properties::oem_crypto_use_fifo()) { + LOGE("CdmEngine::Decrypt: no dest decrypt buffer"); + return KEY_ERROR; + } // else we must be level 1 direct and we don't need to return a buffer. + // TODO:(eschacker) look at renaming Properties::oem_crypto_use_fifo() + // to something like Properties::oem_crypto_use_direct_rendering(). } CdmSessionMap::iterator iter; @@ -514,7 +507,7 @@ CdmResponseType CdmEngine::Decrypt( // Loop through the sessions to find the session containing the key_id. for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) { - if (iter->second->IsKeyValid(*parameters.key_id)) break; + if (iter->second->IsKeyLoaded(*parameters.key_id)) break; } } else { iter = sessions_.find(session_id); @@ -527,10 +520,10 @@ CdmResponseType CdmEngine::Decrypt( return iter->second->Decrypt(parameters); } -bool CdmEngine::IsKeyValid(const KeyId& key_id) { +bool CdmEngine::IsKeyLoaded(const KeyId& key_id) { for (CdmSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { - if (iter->second->IsKeyValid(key_id)) { + if (iter->second->IsKeyLoaded(key_id)) { return true; } } @@ -545,14 +538,21 @@ bool CdmEngine::FindSessionForKey( return false; } + CdmSessionMap::iterator iter = sessions_.find(*session_id); + if (iter != sessions_.end()) { + if (iter->second->IsKeyLoaded(key_id)) { + return true; + } + } + uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id); - for (CdmSessionMap::iterator iter = sessions_.begin(); - iter != sessions_.end(); ++iter) { - CdmSessionId id = iter->second->session_id(); - if (Properties::GetSessionSharingId(id) == session_sharing_id) { - if (iter->second->IsKeyValid(key_id)) { - *session_id = id; + for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) { + CdmSessionId local_session_id = iter->second->session_id(); + if (Properties::GetSessionSharingId(local_session_id) == + session_sharing_id) { + if (iter->second->IsKeyLoaded(key_id)) { + *session_id = local_session_id; return true; } } @@ -684,16 +684,6 @@ bool CdmEngine::ExtractWidevinePssh( return false; } -void CdmEngine::EnablePolicyTimer() { - if (!policy_timer_.IsRunning()) - policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds); -} - -void CdmEngine::DisablePolicyTimer(bool force) { - if ((sessions_.size() == 0 || force) && policy_timer_.IsRunning()) - policy_timer_.Stop(); -} - void CdmEngine::OnTimerEvent() { for (CdmSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index c3982a91..66b5b187 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -95,7 +95,10 @@ CdmResponseType CdmSession::RestoreOfflineSession( } if (Properties::use_certificates_as_identification()) { - if (!crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { + if (is_certificate_loaded_ || + crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { + is_certificate_loaded_ = true; + } else { return NEED_PROVISIONING; } } @@ -165,8 +168,7 @@ CdmResponseType CdmSession::GenerateKeyRequest( if (is_certificate_loaded_ || crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { is_certificate_loaded_ = true; - } - else { + } else { reinitialize_session_ = true; return NEED_PROVISIONING; } @@ -241,12 +243,12 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response, CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) { if (crypto_session_.get() == NULL) { - LOGW("CdmSession::QueryStatus: Invalid crypto session"); + LOGE("CdmSession::QueryStatus: Invalid crypto session"); return UNKNOWN_ERROR; } if (!crypto_session_->IsOpen()) { - LOGW("CdmSession::QueryStatus: Crypto session not open"); + LOGE("CdmSession::QueryStatus: Crypto session not open"); return UNKNOWN_ERROR; } @@ -262,7 +264,8 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) { break; case kSecurityLevelUninitialized: case kSecurityLevelUnknown: - (*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_Unknown; + (*key_info)[QUERY_KEY_SECURITY_LEVEL] = + QUERY_VALUE_SECURITY_LEVEL_UNKNOWN; break; default: return KEY_ERROR; @@ -321,8 +324,10 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { // session keys. CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request, std::string* server_url) { - if (!license_parser_.PrepareKeyUpdateRequest(true, key_request, server_url)) + if (!license_parser_.PrepareKeyUpdateRequest(true, key_request, server_url)) { + LOGE("CdmSession::GenerateRenewalRequest: ERROR on prepare"); return KEY_ERROR; + } if (license_type_ == kLicenseTypeOffline) { offline_key_renewal_request_ = *key_request; @@ -364,7 +369,7 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { return sts; } -bool CdmSession::IsKeyValid(const KeyId& key_id) { +bool CdmSession::IsKeyLoaded(const KeyId& key_id) { return license_parser_.IsKeyLoaded(key_id); } @@ -428,12 +433,12 @@ void CdmSession::OnTimerEvent() { bool event_occurred = false; CdmEventType event; - policy_engine_.OnTimerEvent(event_occurred, event); + policy_engine_.OnTimerEvent(&event_occurred, &event); if (event_occurred) { for (CdmEventListenerIter iter = listeners_.begin(); iter != listeners_.end(); ++iter) { - (*iter)->onEvent(session_id_, event); + (*iter)->OnEvent(session_id_, event); } } } @@ -442,7 +447,7 @@ void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { if (key_set_id_ == key_set_id) { for (CdmEventListenerIter iter = listeners_.begin(); iter != listeners_.end(); ++iter) { - (*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT); + (*iter)->OnEvent(session_id_, LICENSE_EXPIRED_EVENT); } } } diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index 36fee47b..29e3ff88 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -176,11 +176,20 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse( return UNKNOWN_ERROR; } - if (!signed_response.has_signature() || !signed_response.has_message()) { - LOGE("HandleProvisioningResponse: signature or message not found"); - return UNKNOWN_ERROR; + bool error = false; + if (!signed_response.has_signature()) { + LOGE("HandleProvisioningResponse: signature not found"); + error = true; } + if (!signed_response.has_message()) { + LOGE("HandleProvisioningResponse: message not found"); + error = true; + } + + if (error) + return UNKNOWN_ERROR; + const std::string& signed_message = signed_response.message(); ProvisioningResponse provisioning_response; diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index a866a43f..a48eb62a 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -155,10 +155,7 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) { return false; } - id.resize(id_length + 1); - id[id_length] = '\0'; - - *device_id = reinterpret_cast(&id[0]); + device_id->assign(reinterpret_cast(&id[0]), id_length); return true; } @@ -358,6 +355,8 @@ CdmResponseType CryptoSession::LoadKeys(const std::string& message, if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { enc_mac_key = msg + GetOffset(message, mac_key); enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); + } else { + LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); } std::vector load_key_array(num_keys); for (int i = 0; i < num_keys; ++i) { @@ -394,7 +393,7 @@ CdmResponseType CryptoSession::LoadKeys(const std::string& message, } bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { - LOGV("CryptoSession::LoadKeys: Lock"); + LOGV("CryptoSession::LoadCertificatePrivateKey: Lock"); AutoLock auto_lock(crypto_lock_); LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_); @@ -566,7 +565,7 @@ 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_.compare(*params.key_id) != 0) { + if (key_id_ != *params.key_id) { if (SelectKey(*params.key_id)) { key_id_ = *params.key_id; } else { @@ -604,7 +603,7 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { switch (sts) { case OEMCrypto_SUCCESS: - break; + return NO_ERROR; case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES: return INSUFFICIENT_CRYPTO_RESOURCES; case OEMCrypto_ERROR_KEY_EXPIRED: @@ -612,7 +611,6 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { default: return UNKNOWN_ERROR; } - return NO_ERROR; } bool CryptoSession::GenerateNonce(uint32_t* nonce) { diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index 04234ef0..f79cc5e5 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -22,8 +22,11 @@ namespace { const char kCertificateFileName[] = "cert.bin"; const char kLicenseFileNameExt[] = ".lic"; const char kWildcard[] = "*"; -const char kPathDelimiter[] = "/"; -const char *kSecurityLevelPathCompatibilityExclusionList[] = { "ay64.dat" }; +const char kDirectoryDelimiter = '/'; +const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"}; +size_t kSecurityLevelPathCompatibilityExclusionListSize = + sizeof(kSecurityLevelPathCompatibilityExclusionList) / + sizeof(*kSecurityLevelPathCompatibilityExclusionList); } // namespace namespace wvcdm { @@ -436,7 +439,6 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) { } if (!file_->Open(path, File::kReadOnly | File::kBinary)) { - LOGW("DeviceFiles::RetrieveFile: File open failed: %s", path.c_str()); return false; } @@ -457,27 +459,33 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) { void DeviceFiles::SecurityLevelPathBackwardCompatibility() { std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { - LOGW("DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to " + LOGW( + "DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to " "get base path"); return; } std::vector security_dirs; if (!Properties::GetSecurityLevelDirectories(&security_dirs)) { - LOGW("DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to " + 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.rfind(security_dirs[i]); - if (std::string::npos != pos) + 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 " + LOGV( + "DeviceFiles::SecurityLevelPathBackwardCompatibility: Security level " "specific path not found. Check properties?"); return; } @@ -485,16 +493,16 @@ void DeviceFiles::SecurityLevelPathBackwardCompatibility() { std::string from_dir(path, 0, pos); std::vector files; - file_->List(from_dir, &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 < sizeof(kSecurityLevelPathCompatibilityExclusionList) / - sizeof(const char*); - j++) { - if (files[i].compare(kSecurityLevelPathCompatibilityExclusionList[j]) == 0) { + for (size_t j = 0; j < kSecurityLevelPathCompatibilityExclusionListSize; + ++j) { + if (files[i] == kSecurityLevelPathCompatibilityExclusionList[j]) { exclude = true; break; } @@ -504,8 +512,7 @@ void DeviceFiles::SecurityLevelPathBackwardCompatibility() { 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); + if (!file_->Exists(to_dir)) file_->CreateDirectory(to_dir); std::string to = to_dir + files[i]; file_->Copy(from, to); } diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index d2e805e2..59a841c2 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -90,7 +90,7 @@ typedef OEMCryptoResult (*L1_GenerateRSASignature_t)(OEMCrypto_SESSION session, size_t message_length, uint8_t* signature, size_t* signature_length, - RSA_Padding_Scheme algorithm); + RSA_Padding_Scheme padding_scheme); typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)( OEMCrypto_SESSION session, const uint8_t* enc_session_key, size_t enc_session_key_length, const uint8_t* mac_key_context, @@ -318,7 +318,7 @@ class Adapter { } LevelSession get(OEMCrypto_SESSION session) { - AutoLock auto_lock(lookup_lock_); + AutoLock auto_lock(session_map_lock_); map_iterator pair = session_map_.find(session); if (pair == session_map_.end()) { return LevelSession(); @@ -327,7 +327,6 @@ class Adapter { } OEMCryptoResult OpenSession(OEMCrypto_SESSION* session, SecurityLevel level) { - AutoLock auto_lock(lookup_lock_); LevelSession new_session; OEMCryptoResult result; if (level == kLevelDefault && level1_valid_) { @@ -340,6 +339,7 @@ class Adapter { *session = new_session.session + kLevel3Offset; } if (result == OEMCrypto_SUCCESS) { + AutoLock auto_lock(session_map_lock_); // Make sure session is not already in my list of sessions. while (session_map_.find(*session) != session_map_.end()) { (*session)++; @@ -350,7 +350,7 @@ class Adapter { } OEMCryptoResult CloseSession(OEMCrypto_SESSION session) { - AutoLock auto_lock(lookup_lock_); + AutoLock auto_lock(session_map_lock_); map_iterator pair = session_map_.find(session); if (pair == session_map_.end()) { return OEMCrypto_ERROR_INVALID_SESSION; @@ -367,7 +367,7 @@ class Adapter { struct FunctionPointers level1_; struct FunctionPointers level3_; std::map session_map_; - Lock lookup_lock_; + Lock session_map_lock_; // This is just for debugging the map between session ids. // If we add this to the level 3 session id, then the external session // id will match the internal session id in the last two digits. @@ -583,12 +583,13 @@ extern "C" OEMCryptoResult OEMCrypto_LoadDeviceRSAKey( extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, - uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme algorithm) { + uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme padding_scheme) { if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; LevelSession pair = kAdapter->get(session); if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; return pair.fcn->GenerateRSASignature(pair.session, message, message_length, - signature, signature_length, algorithm); + signature, signature_length, + padding_scheme); } extern "C" OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index 8b1a9e40..6fee78fa 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -40,8 +40,8 @@ void PolicyEngine::Init(Clock* clock) { clock_ = clock; } -void PolicyEngine::OnTimerEvent(bool& event_occured, CdmEventType& event) { - event_occured = false; +void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) { + *event_occurred = false; int64_t current_time = clock_->GetCurrentTime(); // License expiration trumps all. @@ -50,8 +50,8 @@ void PolicyEngine::OnTimerEvent(bool& event_occured, CdmEventType& event) { license_state_ != kLicenseStateExpired) { license_state_ = kLicenseStateExpired; can_decrypt_ = false; - event = LICENSE_EXPIRED_EVENT; - event_occured = true; + *event = LICENSE_EXPIRED_EVENT; + *event_occurred = true; return; } @@ -91,8 +91,8 @@ void PolicyEngine::OnTimerEvent(bool& event_occured, CdmEventType& event) { if (renewal_needed) { UpdateRenewalRequest(current_time); - event = LICENSE_RENEWAL_NEEDED_EVENT; - event_occured = true; + *event = LICENSE_RENEWAL_NEEDED_EVENT; + *event_occurred = true; } } @@ -109,6 +109,10 @@ void PolicyEngine::UpdateLicense( if (!license.has_policy()) return; + if (kLicenseStateExpired == license_state_) { + LOGD("PolicyEngine::UpdateLicense: updating an expired license"); + } + policy_.MergeFrom(license.policy()); if (!policy_.can_play()) { diff --git a/libwvdrmengine/cdm/core/src/properties.cpp b/libwvdrmengine/cdm/core/src/properties.cpp index 14cfc8b2..60ffe4c3 100644 --- a/libwvdrmengine/cdm/core/src/properties.cpp +++ b/libwvdrmengine/cdm/core/src/properties.cpp @@ -5,7 +5,7 @@ #include "wv_cdm_constants.h" namespace { -const char *kSecurityLevelDirs[] = { "L1/", "L3/" }; +const char* kSecurityLevelDirs[] = {"L1/", "L3/"}; } // namespace namespace wvcdm { @@ -30,20 +30,20 @@ void Properties::Init() { kPropertyUseCertificatesAsIdentification; extract_pssh_data_ = kExtractPsshData; decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport; - security_level_path_backward_compatibility_support_ = kSecurityLevelPathBackwardCompatibilitySupport; + security_level_path_backward_compatibility_support_ = + kSecurityLevelPathBackwardCompatibilitySupport; session_property_set_.reset(new CdmClientPropertySetMap()); } bool Properties::AddSessionPropertySet( - const CdmSessionId& session_id, - const CdmClientPropertySet* property_set) { + const CdmSessionId& session_id, const CdmClientPropertySet* property_set) { if (NULL == session_property_set_.get()) { return false; } std::pair result = - session_property_set_->insert( - std::pair(session_id, property_set)); + session_property_set_->insert( + std::pair( + session_id, property_set)); return result.second; } @@ -55,7 +55,7 @@ bool Properties::RemoveSessionPropertySet(const CdmSessionId& session_id) { } const CdmClientPropertySet* Properties::GetCdmClientPropertySet( - const CdmSessionId& session_id) { + const CdmSessionId& session_id) { if (NULL != session_property_set_.get()) { CdmClientPropertySetMap::const_iterator it = session_property_set_->find(session_id); @@ -112,7 +112,7 @@ uint32_t Properties::GetSessionSharingId(const CdmSessionId& session_id) { } bool Properties::GetSecurityLevelDirectories(std::vector* dirs) { - dirs->resize(sizeof(kSecurityLevelDirs)/sizeof(const char*)); + dirs->resize(sizeof(kSecurityLevelDirs) / sizeof(const char*)); for (size_t i = 0; i < dirs->size(); ++i) { (*dirs)[i] = kSecurityLevelDirs[i]; } diff --git a/libwvdrmengine/cdm/core/src/string_conversions.cpp b/libwvdrmengine/cdm/core/src/string_conversions.cpp index 5a8a6d09..c43ed0c5 100644 --- a/libwvdrmengine/cdm/core/src/string_conversions.cpp +++ b/libwvdrmengine/cdm/core/src/string_conversions.cpp @@ -50,6 +50,16 @@ std::vector a2b_hex(const std::string& byte) { return array; } +// converts an ascii hex string(2 bytes per digit) into a decimal byte string +// dump the string with the label. +std::vector a2b_hex(const std::string& label, const std::string& byte) { + + std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]" + << std::endl << std::endl; + + return a2b_hex(byte); +} + std::string a2bs_hex(const std::string& byte) { std::vector array = a2b_hex(byte); return std::string(array.begin(), array.end()); @@ -141,7 +151,7 @@ std::string IntToString(int value) { memset(buffer, 0, kOutputBufSize); snprintf(buffer, kOutputBufSize, "%d", value); - std::string out_string(buffer, sizeof(buffer)); + std::string out_string(buffer); return out_string; } @@ -153,7 +163,7 @@ std::string UintToString(unsigned int value) { memset(buffer, 0, kOutputBufSize); snprintf(buffer, kOutputBufSize, "%u", value); - std::string out_string(buffer, sizeof(buffer)); + std::string out_string(buffer); return out_string; } diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index 8343363c..6d04e18d 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -5,7 +5,7 @@ #if defined(CHROMIUM_BUILD) #include "base/at_exit.h" -#include "base/message_loop.h" +#include "base/message_loop/message_loop.h" #endif #include "cdm_engine.h" #include "config_test_env.h" @@ -19,7 +19,9 @@ #include "wv_cdm_types.h" namespace { +// Http OK response code. const int kHttpOk = 200; + // Default license server, can be configured using --server command line option // Default key id (pssh), can be configured using --keyid command line option std::string g_client_auth; @@ -88,12 +90,11 @@ namespace wvcdm { class WvCdmEngineTest : public testing::Test { public: virtual void SetUp() { - cdm_engine_.reset(new CdmEngine()); - cdm_engine_->OpenSession(g_key_system, NULL, &session_id_); + cdm_engine_.OpenSession(g_key_system, NULL, &session_id_); } virtual void TearDown() { - cdm_engine_->CloseSession(session_id_); + cdm_engine_.CloseSession(session_id_); } protected: @@ -110,21 +111,21 @@ class WvCdmEngineTest : public testing::Test { } EXPECT_EQ(KEY_MESSAGE, - cdm_engine_->GenerateKeyRequest(session_id_, - key_set_id, - init_data, - kLicenseTypeStreaming, - app_parameters, - &key_msg_, - &server_url)); + cdm_engine_.GenerateKeyRequest(session_id_, + key_set_id, + init_data, + kLicenseTypeStreaming, + app_parameters, + &key_msg_, + &server_url)); } void GenerateRenewalRequest(const std::string& key_system, const std::string& init_data) { EXPECT_EQ(KEY_MESSAGE, - cdm_engine_->GenerateRenewalRequest(session_id_, - &key_msg_, - &server_url_)); + cdm_engine_.GenerateRenewalRequest(session_id_, + &key_msg_, + &server_url_)); } // posts a request and extracts the drm message from the response @@ -164,18 +165,18 @@ class WvCdmEngineTest : public testing::Test { std::string resp = GetKeyRequestResponse(server_url, client_auth); CdmKeySetId key_set_id; - EXPECT_EQ(cdm_engine_->AddKey(session_id_, resp, &key_set_id), KEY_ADDED); + EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp, &key_set_id), KEY_ADDED); } void VerifyRenewalKeyResponse(const std::string& server_url, const std::string& client_auth, - std::string& init_data){ + std::string& init_data) { std::string resp = GetKeyRequestResponse(server_url, client_auth); - EXPECT_EQ(cdm_engine_->RenewKey(session_id_, resp), wvcdm::KEY_ADDED); + EXPECT_EQ(cdm_engine_.RenewKey(session_id_, resp), wvcdm::KEY_ADDED); } - scoped_ptr cdm_engine_; + CdmEngine cdm_engine_; std::string key_msg_; std::string session_id_; std::string server_url_; @@ -198,7 +199,10 @@ TEST_F(WvCdmEngineTest, BaseMessageTest) { TEST_F(WvCdmEngineTest, WrongMessageTest) { std::string wrong_message = a2bs_hex(g_wrong_key_id); GenerateKeyRequest(g_key_system, wrong_message); - GetKeyRequestResponse(g_license_server, g_client_auth); + + // We should receive a response with no license, i.e. the extracted license + // response message should be empty. + ASSERT_EQ("", GetKeyRequestResponse(g_license_server, g_client_auth)); } TEST_F(WvCdmEngineTest, NormalDecryption) { @@ -313,7 +317,7 @@ int main(int argc, char **argv) { #if defined(CHROMIUM_BUILD) base::AtExitManager exit; - MessageLoop ttr(MessageLoop::TYPE_IO); + base::MessageLoop ttr(base::MessageLoop::TYPE_IO); #endif return RUN_ALL_TESTS(); } diff --git a/libwvdrmengine/cdm/core/test/config_test_env.cpp b/libwvdrmengine/cdm/core/test/config_test_env.cpp index 25c7e8b6..7886977d 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.cpp +++ b/libwvdrmengine/cdm/core/test/config_test_env.cpp @@ -5,7 +5,7 @@ namespace { // Youtube Content Protection license server data const std::string kYtCpLicenseServer = - "http://kir03wwwg185.widevine.net/drm"; + "http://wv-ref-eme-player.appspot.com/proxy"; const std::string kYtCpClientAuth = ""; const std::string kYtCpKeyId = "000000347073736800000000" // blob size and pssh diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 4c979230..099c5224 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -26,6 +26,7 @@ using ::testing::StrEq; namespace { const uint32_t kCertificateLen = 700; const uint32_t kWrappedKeyLen = 500; +const uint32_t kProtobufEstimatedLen = 75; const std::string kTestCertificate = "124B035F3D256A656F0E505A085E7A6C482B61035E0C4A540F7803137F4C3B45206B7F33" "347F4D7A005E56400F0955011F4E07072D0D46781817460974326A516E3944385760280E" @@ -209,7 +210,7 @@ LicenseInfo license_test_data[] = { "0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF" "A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0" "BEA6CABACA1C2C"), - "https://jmt17.google.com/video/license/GetCencLicense", + "https://jmt17.google.com/video-dev/license/GetCencLicense", wvcdm::a2bs_hex( "0AAF150802100122A8150801121408011210303132333435363738394142" "434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E" @@ -1038,7 +1039,7 @@ MATCHER_P(IsStrEq, str, "") { MATCHER_P2(Contains, str1, str2, "") { // 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 - std::string data(arg, str1.size() + str2.size() + 75); + std::string data(arg, str1.size() + str2.size() + kProtobufEstimatedLen); return (data.find(str1) != std::string::npos && data.find(str2) != std::string::npos); } @@ -1046,7 +1047,7 @@ MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") { // 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 std::string data(arg, str1.size() + str2.size() + str3.size() + str4.size() + - str5.size() + str6.size() + 75); + str5.size() + str6.size() + kProtobufEstimatedLen); return (data.find(str1) != std::string::npos && data.find(str2) != std::string::npos && data.find(str3) != std::string::npos && diff --git a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp index 3331dad0..e26f9b72 100644 --- a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp @@ -7,7 +7,7 @@ #include "test_vectors.h" namespace { -const std::string kTestDirName = "test"; +const std::string kTestDirName = "test_dir"; const std::string kTestFileName = "test.txt"; const std::string kTestFileName2 = "test2.txt"; const std::string kTestFileNameExt = ".txt"; @@ -210,7 +210,7 @@ TEST_F(FileTest, CopyFile) { 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)); + ASSERT_TRUE(file.Exists(path)); std::string path_copy = test_vectors::kTestDir + kTestFileName2; EXPECT_FALSE(file.Exists(path_copy)); @@ -257,9 +257,9 @@ TEST_F(FileTest, ListFiles) { EXPECT_EQ(3u, files.size()); for (size_t i = 0; i < files.size(); ++i) { - EXPECT_TRUE(files[i].compare(kTestDirName) == 0 || - files[i].compare(kTestFileName) == 0 || - files[i].compare(kTestFileName2) == 0); + EXPECT_TRUE(files[i] == kTestDirName || + files[i] == kTestFileName || + files[i] == kTestFileName2); } } diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index 7171b193..6d3d3571 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -57,7 +57,7 @@ class PolicyEngineTest : public ::testing::Test { policy->set_renewal_recovery_duration_seconds(license_duration_ - license_renewal_delay_); // 10 minutes policy->set_renewal_server_url( - "https://jmt17.google.com/video/license/GetCencLicense"); + "https://jmt17.google.com/video-dev/license/GetCencLicense"); policy->set_renewal_delay_seconds(license_renewal_delay_); policy->set_renewal_retry_interval_seconds( license_renewal_retry_interval_); @@ -97,7 +97,7 @@ TEST_F(PolicyEngineTest, PlaybackSuccess) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); policy_engine_->BeginDecryption(); @@ -115,7 +115,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); policy_engine_->BeginDecryption(); @@ -141,10 +141,10 @@ TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -171,10 +171,10 @@ TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -198,10 +198,10 @@ TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -226,10 +226,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -254,10 +254,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -282,10 +282,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -312,10 +312,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); EXPECT_TRUE(policy_engine_->can_decrypt()); @@ -343,13 +343,13 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -374,10 +374,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); @@ -389,7 +389,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { id->set_version(2); policy_engine_->UpdateLicense(license_); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); EXPECT_TRUE(policy_engine_->can_decrypt()); @@ -412,10 +412,10 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); @@ -425,13 +425,13 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { license_renewal_delay_ + 15); policy_engine_->UpdateLicense(license_); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -459,37 +459,37 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -519,37 +519,37 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -566,7 +566,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) { policy_engine_->UpdateLicense(license_); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); EXPECT_TRUE(policy_engine_->can_decrypt()); @@ -593,25 +593,25 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); license_.set_license_start_time(license_start_time_ + @@ -620,12 +620,12 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) { id->set_version(2); policy_engine_->UpdateLicense(license_); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); EXPECT_TRUE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); EXPECT_TRUE(policy_engine_->can_decrypt()); @@ -647,13 +647,13 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); policy_engine_->BeginDecryption(); EXPECT_FALSE(policy_engine_->can_decrypt()); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); @@ -663,7 +663,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) { id->set_version(2); policy_engine_->UpdateLicense(license_); - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); EXPECT_TRUE(policy_engine_->can_decrypt()); @@ -723,7 +723,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_Offline) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); policy_engine_->BeginDecryption(); @@ -765,7 +765,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) { bool event_occurred; CdmEventType event; - policy_engine_->OnTimerEvent(event_occurred, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); policy_engine_->BeginDecryption(); diff --git a/libwvdrmengine/cdm/core/test/url_request.cpp b/libwvdrmengine/cdm/core/test/url_request.cpp index 7c8641ae..1da99c1b 100644 --- a/libwvdrmengine/cdm/core/test/url_request.cpp +++ b/libwvdrmengine/cdm/core/test/url_request.cpp @@ -114,12 +114,14 @@ int UrlRequest::GetResponse(std::string* message) { bytes = socket_.Read(buffer_, kHttpBufferSize, kTimeoutInMs); if (bytes > 0) { response.append(buffer_, bytes); - attempts = kSingleReadAttempt; + if (bytes < static_cast(kHttpBufferSize)) { + attempts = kSingleReadAttempt; + } } else { if (bytes < 0) LOGE("read error = ", errno); // bytes == 0 indicates nothing to read } - }; + } ConcatenateChunkedResponse(response, message); LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str()); diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index c4f435a1..8284b92b 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -3,17 +3,19 @@ #ifndef CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ #define CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ -#include "wv_cdm_types.h" - #include +#include "lock.h" +#include "timer.h" +#include "wv_cdm_types.h" + namespace wvcdm { class CdmClientPropertySet; class CdmEngine; class WvCdmEventListener; -class WvContentDecryptionModule { +class WvContentDecryptionModule : public TimerHandler { public: WvContentDecryptionModule(); virtual ~WvContentDecryptionModule(); @@ -91,6 +93,14 @@ class WvContentDecryptionModule { private: uint32_t GenerateSessionSharingId(); + // timer related methods to drive policy decisions + void EnablePolicyTimer(); + void DisablePolicyTimer(bool force); + void OnTimerEvent(); + + static Lock session_sharing_id_generation_lock_; + Timer policy_timer_; + // instance variables UniquePtr cdm_engine_; diff --git a/libwvdrmengine/cdm/src/file_store.cpp b/libwvdrmengine/cdm/src/file_store.cpp index 9a59632b..83f28f48 100644 --- a/libwvdrmengine/cdm/src/file_store.cpp +++ b/libwvdrmengine/cdm/src/file_store.cpp @@ -18,8 +18,12 @@ namespace { const char kCurrentDirectory[] = "."; const char kParentDirectory[] = ".."; -const char kPathDelimiter[] = "/"; +const char kDirectoryDelimiter = '/'; const char kWildcard[] = "*"; +bool IsCurrentOrParentDirectory(char* dir) { + return strcmp(dir, kCurrentDirectory) == 0 || + strcmp(dir, kParentDirectory) == 0; +} } // namespace namespace wvcdm { @@ -114,9 +118,8 @@ bool File::Remove(const std::string& path) { // first remove files and dir within it struct dirent* entry; while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, kCurrentDirectory) && - (strcmp(entry->d_name, kParentDirectory))) { - std::string path_to_remove = path + kPathDelimiter; + 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); @@ -141,7 +144,7 @@ bool File::Remove(const std::string& path) { } } else { // Handle wildcard specified file deletion - size_t delimiter_pos = path.rfind(kPathDelimiter, wildcard_pos); + 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; @@ -163,7 +166,7 @@ bool File::Remove(const std::string& path) { if (strcmp(entry->d_name + filename_len - ext.size(), ext.c_str()) == 0) { std::string file_path_to_remove = - dir_path + kPathDelimiter + entry->d_name; + dir_path + kDirectoryDelimiter + entry->d_name; if (!Remove(file_path_to_remove)) { closedir(dir); return false; @@ -180,34 +183,34 @@ bool File::Remove(const std::string& path) { 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 does not exist: %d", src.c_str(), errno); + 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) { - LOGV("File::Copy: unable to open file %s: %d", src.c_str(), errno); + 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); + int fd_dest = open(dest.c_str(), O_WRONLY | O_CREAT, stat_buf.st_mode); if (fd_dest < 0) { - LOGV("File::Copy: unable to open file %s: %d", dest.c_str(), errno); + LOGW("File::Copy: unable to open file %s: %d", dest.c_str(), errno); close(fd_src); return false; } off_t offset = 0; - bool sts = true; + 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); - sts = false; + status = false; } close(fd_src); close(fd_dest); - return sts; + return status; } bool File::List(const std::string& path, std::vector* files) { @@ -221,16 +224,16 @@ bool File::List(const std::string& path, std::vector* files) { return false; } - DIR* dir; - if ((dir = opendir(path.c_str())) == NULL) { + 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 (strcmp(entry->d_name, kCurrentDirectory) && - (strcmp(entry->d_name, kParentDirectory))) { + if (!IsCurrentOrParentDirectory(entry->d_name)) { files->push_back(entry->d_name); } } @@ -241,31 +244,29 @@ bool File::List(const std::string& path, std::vector* files) { bool File::CreateDirectory(std::string path) { size_t size = path.size(); - if ((size == 1) && (path[0] == kPathDelimiter[0])) return true; + if ((size == 1) && (path[0] == kDirectoryDelimiter)) return true; if (size <= 1) return false; - if (path.at(size - 1) == kPathDelimiter[0]) { - --size; - path.resize(size); - } - - size_t pos = path.find(kPathDelimiter[0], 1); + size_t pos = path.find(kDirectoryDelimiter, 1); while (pos < size) { - path.at(pos) = '\0'; + path[pos] = '\0'; if (mkdir(path.c_str(), 0775) != 0) { if (errno != EEXIST) { LOGW("File::CreateDirectory: mkdir failed: %d\n", errno); return false; } } - path.at(pos) = kPathDelimiter[0]; - pos = path.find(kPathDelimiter[0], pos + 1); + path[pos] = kDirectoryDelimiter; + pos = path.find(kDirectoryDelimiter, pos + 1); } - if (mkdir(path.c_str(), 0775) != 0) { - if (errno != EEXIST) { - LOGW("File::CreateDirectory: mkdir failed: %d\n", errno); - return false; + + if (path[size - 1] != kDirectoryDelimiter) { + if (mkdir(path.c_str(), 0775) != 0) { + if (errno != EEXIST) { + LOGW("File::CreateDirectory: mkdir failed: %d\n", errno); + return false; + } } } return true; diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 27d68608..68cdcde7 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -11,18 +11,27 @@ #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" +namespace { +const int kCdmPolicyTimerDurationSeconds = 1; +} + namespace wvcdm { +Lock WvContentDecryptionModule::session_sharing_id_generation_lock_; + WvContentDecryptionModule::WvContentDecryptionModule() : cdm_engine_(new CdmEngine()) {} -WvContentDecryptionModule::~WvContentDecryptionModule() {} +WvContentDecryptionModule::~WvContentDecryptionModule() { + DisablePolicyTimer(true); +} CdmResponseType WvContentDecryptionModule::OpenSession( const CdmKeySystem& key_system, CdmClientPropertySet* property_set, CdmSessionId* session_id) { if (property_set && property_set->is_session_sharing_enabled()) { + AutoLock auto_lock(session_sharing_id_generation_lock_); if (property_set->session_sharing_id() == 0) property_set->set_session_sharing_id(GenerateSessionSharingId()); } @@ -32,7 +41,9 @@ CdmResponseType WvContentDecryptionModule::OpenSession( CdmResponseType WvContentDecryptionModule::CloseSession( const CdmSessionId& session_id) { - return cdm_engine_->CloseSession(session_id); + CdmResponseType sts = cdm_engine_->CloseSession(session_id); + DisablePolicyTimer(false); + return sts; } CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( @@ -49,13 +60,19 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( if (sts != NO_ERROR) return sts; } - sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, - init_data, license_type, - app_parameters, key_request, - server_url); + sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, init_data, + license_type, app_parameters, + key_request, server_url); - if (license_type == kLicenseTypeRelease && sts != KEY_MESSAGE) { - cdm_engine_->CloseKeySetSession(key_set_id); + switch(license_type) { + case kLicenseTypeRelease: + if (sts != KEY_MESSAGE) + cdm_engine_->CloseKeySetSession(key_set_id); + break; + default: + if (sts == KEY_MESSAGE) + EnablePolicyTimer(); + break; } return sts; } @@ -124,16 +141,17 @@ CdmResponseType WvContentDecryptionModule::Decrypt( const CdmSessionId& session_id, bool validate_key_id, const CdmDecryptionParameters& parameters) { - CdmSessionId id = session_id; + CdmSessionId local_session_id = session_id; if (validate_key_id && Properties::GetSessionSharingId(session_id) != 0) { - bool status = cdm_engine_->FindSessionForKey(*parameters.key_id, &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 cdm_engine_->Decrypt(id, parameters); + return cdm_engine_->Decrypt(local_session_id, parameters); } bool WvContentDecryptionModule::AttachEventListener( @@ -146,9 +164,25 @@ bool WvContentDecryptionModule::DetachEventListener( return cdm_engine_->DetachEventListener(session_id, listener); } +void WvContentDecryptionModule::EnablePolicyTimer() { + if (!policy_timer_.IsRunning()) + policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds); +} + +void WvContentDecryptionModule::DisablePolicyTimer(bool force) { + if ((cdm_engine_->SessionSize() == 0 || force) && policy_timer_.IsRunning()) + policy_timer_.Stop(); +} + +void WvContentDecryptionModule::OnTimerEvent() { + cdm_engine_->OnTimerEvent(); +} + uint32_t WvContentDecryptionModule::GenerateSessionSharingId() { static int next_session_sharing_id = 0; return ++next_session_sharing_id; } + + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index d0ff5099..fdc437c3 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -21,6 +21,9 @@ namespace { const char kPathDelimiter = '/'; +// Http OK response code. +const int kHttpOk = 200; + // Default license server, can be configured using --server command line option // Default key id (pssh), can be configured using --keyid command line option std::string g_client_auth; @@ -239,10 +242,10 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { return service_certificate_; } virtual bool use_privacy_mode() const { return use_privacy_mode_; } - bool is_session_sharing_enabled() const { + virtual bool is_session_sharing_enabled() const { return is_session_sharing_enabled_; } - uint32_t session_sharing_id() const { return session_sharing_id_; } + virtual uint32_t session_sharing_id() const { return session_sharing_id_; } void set_security_level(const std::string& security_level) { if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) || @@ -273,7 +276,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { class TestWvCdmEventListener : public WvCdmEventListener { public: TestWvCdmEventListener() : WvCdmEventListener() {} - virtual void onEvent(const CdmSessionId& id, CdmEventType event) { + virtual void OnEvent(const CdmSessionId& id, CdmEventType event) { session_id_ = id; event_type_ = event; } @@ -333,8 +336,7 @@ class WvCdmRequestLicenseTest : public testing::Test { // Post a request and extract the drm message from the response std::string GetKeyRequestResponse(const std::string& server_url, - const std::string& client_auth, - int expected_response) { + const std::string& client_auth) { // Use secure connection and chunk transfer coding. UrlRequest url_request(server_url + client_auth, g_port, g_use_secure_transfer, g_use_chunked_transfer); @@ -345,15 +347,11 @@ class WvCdmRequestLicenseTest : public testing::Test { std::string message; int resp_bytes = url_request.GetResponse(&message); - // Youtube server returns 400 for invalid message while play server returns - // 500, so just test inequity here for invalid message int status_code = url_request.GetStatusCode(message); - if (expected_response == 200) { - EXPECT_EQ(200, status_code); - } + EXPECT_EQ(kHttpOk, status_code); std::string drm_msg; - if (200 == status_code) { + if (kHttpOk == status_code) { LicenseRequest lic_request; lic_request.GetDrmMessage(message, drm_msg); LOGV("HTTP response body: (%u bytes)", drm_msg.size()); @@ -363,8 +361,7 @@ class WvCdmRequestLicenseTest : public testing::Test { // Post a request and extract the signed provisioning message from // the HTTP response. - std::string GetCertRequestResponse(const std::string& server_url, - int expected_response) { + std::string GetCertRequestResponse(const std::string& server_url) { // Use secure connection and chunk transfer coding. UrlRequest url_request(server_url, kDefaultHttpsPort, true, true); if (!url_request.is_connected()) { @@ -376,21 +373,15 @@ class WvCdmRequestLicenseTest : public testing::Test { int resp_bytes = url_request.GetResponse(&message); LOGD("end %d bytes response dump", resp_bytes); - // Youtube server returns 400 for invalid message while play server returns - // 500, so just test inequity here for invalid message int status_code = url_request.GetStatusCode(message); - if (expected_response == 200) { - EXPECT_EQ(200, status_code); - } else { - EXPECT_NE(200, status_code); - } + EXPECT_EQ(kHttpOk, status_code); return message; } void VerifyKeyRequestResponse(const std::string& server_url, const std::string& client_auth, std::string& init_data, bool is_renewal) { - std::string resp = GetKeyRequestResponse(server_url, client_auth, 200); + std::string resp = GetKeyRequestResponse(server_url, client_auth); if (is_renewal) { // TODO application makes a license request, CDM will renew the license @@ -403,20 +394,24 @@ class WvCdmRequestLicenseTest : public testing::Test { } } + std::string GetSecurityLevel(TestWvCdmClientPropertySet* property_set) { + decryptor_.OpenSession(g_key_system, property_set, &session_id_); + CdmQueryMap query_info; + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_.QuerySessionStatus(session_id_, &query_info)); + CdmQueryMap::iterator itr = + query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL); + EXPECT_TRUE(itr != query_info.end()); + decryptor_.CloseSession(session_id_); + return itr->second; + } + wvcdm::WvContentDecryptionModule decryptor_; CdmKeyMessage key_msg_; CdmSessionId session_id_; CdmKeySetId key_set_id_; }; -class WvCdmDecryptionTest - : public WvCdmRequestLicenseTest, - public ::testing::WithParamInterface {}; - -class WvCdmSessionSharingTest - : public WvCdmRequestLicenseTest, - public ::testing::WithParamInterface {}; - TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { decryptor_.OpenSession(g_key_system, NULL, &session_id_); std::string provisioning_server_url; @@ -426,7 +421,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); std::string response = - GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); + GetCertRequestResponse(g_config->provisioning_test_server_url()); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); decryptor_.CloseSession(session_id_); @@ -445,12 +440,12 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) { EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); std::string response = - GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); + GetCertRequestResponse(g_config->provisioning_test_server_url()); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); response = - GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); + GetCertRequestResponse(g_config->provisioning_test_server_url()); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(wvcdm::UNKNOWN_ERROR, decryptor_.HandleProvisioningResponse(response)); @@ -471,17 +466,16 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) { property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); property_set_L3.set_use_privacy_mode(false); - CdmResponseType sts = decryptor_.OpenSession(g_key_system, &property_set_L3, - &session_id_L3); + CdmResponseType sts = + decryptor_.OpenSession(g_key_system, &property_set_L3, &session_id_L3); if (NEED_PROVISIONING == sts) { std::string provisioning_server_url; - EXPECT_EQ( - NO_ERROR, - decryptor_.GetProvisioningRequest(&key_msg_, &provisioning_server_url)); + EXPECT_EQ(NO_ERROR, decryptor_.GetProvisioningRequest( + &key_msg_, &provisioning_server_url)); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); std::string response = - GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); + GetCertRequestResponse(g_config->provisioning_test_server_url()); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set_L3, @@ -498,7 +492,7 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) { !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)); EXPECT_TRUE(Properties::UsePrivacyMode(session_id_L1)); EXPECT_EQ(Properties::GetSecurityLevel(session_id_L3), - QUERY_VALUE_SECURITY_LEVEL_L3); + QUERY_VALUE_SECURITY_LEVEL_L3); EXPECT_FALSE(Properties::UsePrivacyMode(session_id_L3)); security_level = Properties::GetSecurityLevel(session_id_Ln); EXPECT_TRUE(security_level.empty() || @@ -520,17 +514,16 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) { EXPECT_EQ(NEED_PROVISIONING, decryptor_.OpenSession(g_key_system, &property_set, &session_id_)); std::string provisioning_server_url; - EXPECT_EQ(NO_ERROR, - decryptor_.GetProvisioningRequest(&key_msg_, - &provisioning_server_url)); + EXPECT_EQ(NO_ERROR, decryptor_.GetProvisioningRequest( + &key_msg_, &provisioning_server_url)); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); std::string response = - GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); + GetCertRequestResponse(g_config->provisioning_test_server_url()); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response)); - EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set, - &session_id_)); + EXPECT_EQ(NO_ERROR, + decryptor_.OpenSession(g_key_system, &property_set, &session_id_)); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); decryptor_.CloseSession(session_id_); @@ -543,8 +536,8 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeTest) { decryptor_.OpenSession(g_key_system, &property_set, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); - std::string resp = GetKeyRequestResponse(g_license_server, - g_client_auth, 200); + std::string resp = + GetKeyRequestResponse(g_license_server, g_client_auth); EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_), wvcdm::NEED_KEY); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); @@ -552,7 +545,8 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeTest) { decryptor_.CloseSession(session_id_); } -TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeWithServiceCertificateTest) { +TEST_F(WvCdmRequestLicenseTest, + DISABLED_PrivacyModeWithServiceCertificateTest) { TestWvCdmClientPropertySet property_set; property_set.set_use_privacy_mode(true); @@ -563,68 +557,10 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeWithServiceCertificateTest) decryptor_.CloseSession(session_id_); } -TEST_P(WvCdmSessionSharingTest, SessionSharingTest) { - SessionSharingSubSampleInfo* session_sharing_info = GetParam(); - - TestWvCdmClientPropertySet property_set; - property_set.set_session_sharing_mode( - session_sharing_info->session_sharing_enabled); - - decryptor_.OpenSession(g_key_system, &property_set, &session_id_); - CdmSessionId gp_session_id_1 = session_id_; - GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); - VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); - - // TODO(rfrias): Move content information to ConfigTestEnv - std::string gp_client_auth2 = - "?source=YOUTUBE&video_id=z3S_NhwueaM&oauth=ya.gtsqawidevine"; - std::string gp_key_id2 = - wvcdm::a2bs_hex( - "000000347073736800000000" // blob size and pssh - "edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id - "08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data - - decryptor_.OpenSession(g_key_system, &property_set, &session_id_); - CdmSessionId gp_session_id_2 = session_id_; - GenerateKeyRequest(g_key_system, gp_key_id2, kLicenseTypeStreaming); - VerifyKeyRequestResponse(g_license_server, gp_client_auth2, gp_key_id2, false); - - SubSampleInfo* data = session_sharing_info->sub_sample; - std::vector decrypt_buffer(data->encrypt_data.size()); - CdmDecryptionParameters decryption_parameters(&data->key_id, - &data->encrypt_data.front(), - data->encrypt_data.size(), - &data->iv, - data->block_offset, - &decrypt_buffer[0]); - decryption_parameters.is_encrypted = data->is_encrypted; - decryption_parameters.is_secure = data->is_secure; - - if (session_sharing_info->session_sharing_enabled || !data->is_encrypted) { - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(gp_session_id_2, - data->validate_key_id, - decryption_parameters)); - EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), - decrypt_buffer.begin())); - } else { - EXPECT_EQ(NEED_KEY, decryptor_.Decrypt(gp_session_id_2, - data->validate_key_id, - decryption_parameters)); - } - - decryptor_.CloseSession(gp_session_id_1); - decryptor_.CloseSession(gp_session_id_2); -} - -INSTANTIATE_TEST_CASE_P( - Cdm, WvCdmSessionSharingTest, - ::testing::Range(&session_sharing_sub_samples[0], - &session_sharing_sub_samples[6])); - TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) { decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); - GetKeyRequestResponse(g_license_server, g_client_auth, 200); + GetKeyRequestResponse(g_license_server, g_client_auth); decryptor_.CloseSession(session_id_); } @@ -633,7 +569,9 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) { std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id); GenerateKeyRequest(g_key_system, wrong_message, kLicenseTypeStreaming); - GetKeyRequestResponse(g_license_server, g_client_auth, 500); + // We should receive a response with no license, i.e. the extracted license + // response message should be empty. + EXPECT_EQ("", GetKeyRequestResponse(g_license_server, g_client_auth)); decryptor_.CloseSession(session_id_); } @@ -666,7 +604,7 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { decryptor_.CloseSession(session_id_); } -TEST_F(WvCdmRequestLicenseTest, DISABLED_ReleaseOfflineKeyTest) { +TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -688,7 +626,7 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_ReleaseOfflineKeyTest) { VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); } -TEST_F(WvCdmRequestLicenseTest, DISABLED_ExpiryOnReleaseOfflineKeyTest) { +TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -744,7 +682,7 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) { decryptor_.CloseSession(session_id_); } -TEST_F(WvCdmRequestLicenseTest, QuerySessionStatus) { +TEST_F(WvCdmRequestLicenseTest, QueryUnmodifiedSessionStatus) { // Test that the global value is returned when no properties are modifying it. CdmQueryMap system_query_info; CdmQueryMap::iterator system_itr; @@ -752,30 +690,15 @@ TEST_F(WvCdmRequestLicenseTest, QuerySessionStatus) { system_itr = system_query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL); ASSERT_TRUE(system_itr != system_query_info.end()); - decryptor_.OpenSession(g_key_system, NULL, &session_id_); - CdmQueryMap unmodified_query_info; - CdmQueryMap::iterator unmodified_itr; - ASSERT_EQ(wvcdm::NO_ERROR, - decryptor_.QuerySessionStatus(session_id_, &unmodified_query_info)); - unmodified_itr = unmodified_query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL); - ASSERT_TRUE(unmodified_itr != unmodified_query_info.end()); - EXPECT_EQ(system_itr->second, unmodified_itr->second); - decryptor_.CloseSession(session_id_); + EXPECT_EQ(system_itr->second, GetSecurityLevel(NULL)); +} +TEST_F(WvCdmRequestLicenseTest, QueryModifiedSessionStatus) { // Test that L3 is returned when properties downgrade security. TestWvCdmClientPropertySet property_set_L3; property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); - decryptor_.OpenSession(g_key_system, &property_set_L3, &session_id_); - CdmQueryMap modified_query_info; - CdmQueryMap::iterator modified_itr; - ASSERT_EQ(wvcdm::NO_ERROR, - decryptor_.QuerySessionStatus(session_id_, &modified_query_info)); - modified_itr = modified_query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL); - ASSERT_TRUE(modified_itr != modified_query_info.end()); - EXPECT_EQ(QUERY_VALUE_SECURITY_LEVEL_L3, modified_itr->second); - decryptor_.CloseSession(session_id_); - + EXPECT_EQ(QUERY_VALUE_SECURITY_LEVEL_L3, GetSecurityLevel(&property_set_L3)); } TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { @@ -891,20 +814,15 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) { size_t pos = std::string::npos; for (size_t i = 0; i < security_dirs.size(); i++) { pos = base_path.rfind(security_dirs[i]); - if (std::string::npos != pos) - break; + if (std::string::npos != pos) break; } EXPECT_NE(std::string::npos, pos); std::string old_base_path(base_path, 0, pos); - std::string path(old_base_path); - path += kPathDelimiter; - size_t path_len = path.size(); File file; for (size_t i = 0; i < security_dirs.size(); i++) { - path.append(security_dirs[i]); + std::string path = old_base_path + kPathDelimiter + security_dirs[i]; file.Remove(path); - path.resize(path_len); } decryptor_.OpenSession(g_key_system, NULL, &session_id_); @@ -913,7 +831,7 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) { &key_msg_, &provisioning_server_url)); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); std::string response = - GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); + GetCertRequestResponse(g_config->provisioning_test_server_url()); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); decryptor_.CloseSession(session_id_); @@ -964,56 +882,21 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) { decryptor_.GenerateKeyRequest(session_id_, key_set_id, g_key_id, kLicenseTypeStreaming, app_parameters, &key_msg_, &server_url)); - EXPECT_EQ(NO_ERROR, - decryptor_.GetProvisioningRequest(&key_msg_, - &provisioning_server_url)); + EXPECT_EQ(NO_ERROR, decryptor_.GetProvisioningRequest( + &key_msg_, &provisioning_server_url)); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); response = - GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); + GetCertRequestResponse(g_config->provisioning_test_server_url()); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response)); - EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set, - &session_id_)); + EXPECT_EQ(NO_ERROR, + decryptor_.OpenSession(g_key_system, &property_set, &session_id_)); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); decryptor_.CloseSession(session_id_); } -TEST_P(WvCdmDecryptionTest, DecryptionTest) { - SubSampleInfo* data = GetParam(); - decryptor_.OpenSession(g_key_system, NULL, &session_id_); - if (data->retrieve_key) { - GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); - VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); - } - - for (size_t i = 0; i < data->num_of_subsamples; i++) { - std::vector decrypt_buffer((data + i)->encrypt_data.size()); - CdmDecryptionParameters decryption_parameters( - &(data + i)->key_id, &(data + i)->encrypt_data.front(), - (data + i)->encrypt_data.size(), &(data + i)->iv, - (data + i)->block_offset, &decrypt_buffer[0]); - decryption_parameters.is_encrypted = (data + i)->is_encrypted; - decryption_parameters.is_secure = (data + i)->is_secure; - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, - (data+i)->validate_key_id, - decryption_parameters)); - - EXPECT_TRUE(std::equal((data + i)->decrypt_data.begin(), - (data + i)->decrypt_data.end(), - decrypt_buffer.begin())); - } - decryptor_.CloseSession(session_id_); -} - -INSTANTIATE_TEST_CASE_P( - Cdm, WvCdmDecryptionTest, - ::testing::Values(&clear_sub_sample, &clear_sub_sample_no_key, - &single_encrypted_sub_sample, - &switch_key_encrypted_sub_sample[0], - &partial_single_encrypted_sub_sample)); - TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) { decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); @@ -1181,6 +1064,107 @@ TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) { decryptor_.CloseSession(session_id_); } */ + +class WvCdmSessionSharingTest + : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCdmSessionSharingTest, SessionSharingTest) { + SessionSharingSubSampleInfo* session_sharing_info = GetParam(); + + TestWvCdmClientPropertySet property_set; + property_set.set_session_sharing_mode( + session_sharing_info->session_sharing_enabled); + + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + CdmSessionId gp_session_id_1 = session_id_; + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + + // TODO(rfrias): Move content information to ConfigTestEnv + std::string gp_client_auth2 = + "?source=YOUTUBE&video_id=z3S_NhwueaM&oauth=ya.gtsqawidevine"; + std::string gp_key_id2 = + wvcdm::a2bs_hex( + "000000347073736800000000" // blob size and pssh + "edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id + "08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data + + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + CdmSessionId gp_session_id_2 = session_id_; + GenerateKeyRequest(g_key_system, gp_key_id2, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, gp_client_auth2, gp_key_id2, false); + + SubSampleInfo* data = session_sharing_info->sub_sample; + std::vector decrypt_buffer(data->encrypt_data.size()); + CdmDecryptionParameters decryption_parameters(&data->key_id, + &data->encrypt_data.front(), + data->encrypt_data.size(), + &data->iv, + data->block_offset, + &decrypt_buffer[0]); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + + if (session_sharing_info->session_sharing_enabled || !data->is_encrypted) { + EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(gp_session_id_2, + data->validate_key_id, + decryption_parameters)); + EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin())); + } else { + EXPECT_EQ(NEED_KEY, decryptor_.Decrypt(gp_session_id_2, + data->validate_key_id, + decryption_parameters)); + } + + decryptor_.CloseSession(gp_session_id_1); + decryptor_.CloseSession(gp_session_id_2); +} + +INSTANTIATE_TEST_CASE_P( + Cdm, WvCdmSessionSharingTest, + ::testing::Range(&session_sharing_sub_samples[0], + &session_sharing_sub_samples[6])); + +class WvCdmDecryptionTest + : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCdmDecryptionTest, DecryptionTest) { + SubSampleInfo* data = GetParam(); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + if (data->retrieve_key) { + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + } + + for (size_t i = 0; i < data->num_of_subsamples; i++) { + std::vector decrypt_buffer((data + i)->encrypt_data.size()); + CdmDecryptionParameters decryption_parameters( + &(data + i)->key_id, &(data + i)->encrypt_data.front(), + (data + i)->encrypt_data.size(), &(data + i)->iv, + (data + i)->block_offset, &decrypt_buffer[0]); + decryption_parameters.is_encrypted = (data + i)->is_encrypted; + decryption_parameters.is_secure = (data + i)->is_secure; + EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, + (data+i)->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal((data + i)->decrypt_data.begin(), + (data + i)->decrypt_data.end(), + decrypt_buffer.begin())); + } + decryptor_.CloseSession(session_id_); +} + +INSTANTIATE_TEST_CASE_P( + Cdm, WvCdmDecryptionTest, + ::testing::Values(&clear_sub_sample, &clear_sub_sample_no_key, + &single_encrypted_sub_sample, + &switch_key_encrypted_sub_sample[0], + &partial_single_encrypted_sub_sample)); + } // namespace wvcdm void show_menu(char* prog_name) { @@ -1190,40 +1174,36 @@ void show_menu(char* prog_name) { << std::endl; std::cout << " e.g. adb shell '" << prog_name << " --server=\"url\"'" << std::endl; - std::cout << " or adb shell '" << prog_name << " -u\"url\"'" - << std::endl << std::endl; + std::cout << " or adb shell '" << prog_name << " -u\"url\"'" << std::endl + << std::endl; std::cout << std::setw(35) << std::left << " -c/--chunked_transfer"; - std::cout << "specifies chunked transfer encoding in request" - << std::endl << std::endl; + std::cout << "specifies chunked transfer encoding in request" << std::endl + << std::endl; std::cout << std::setw(35) << std::left << " -f/--use_full_path"; std::cout << "specify server url is not a proxy server" << std::endl; std::cout << std::endl; - std::cout << std::setw(35) << std::left - << " -i/--license_server_id="; + std::cout << std::setw(35) << std::left << " -i/--license_server_id="; std::cout << "specifies which default server settings to use: " << std::endl; std::cout << std::setw(35) << std::left << " "; std::cout << "gp (case sensitive) for GooglePlay server" << std::endl; std::cout << std::setw(35) << std::left << " "; std::cout << "cp (case sensitive) for Youtube Content Protection server" - << std::endl << std::endl; - - std::cout << std::setw(35) << std::left << " -k/--keyid="; - std::cout << "configure the key id or pssh, in hex format" << std::endl << std::endl; - std::cout << std::setw(35) << std::left - << " -p/--port="; + std::cout << std::setw(35) << std::left << " -k/--keyid="; + std::cout << "configure the key id or pssh, in hex format" << std::endl + << std::endl; + + std::cout << std::setw(35) << std::left << " -p/--port="; std::cout << "specifies the connection port" << std::endl << std::endl; - std::cout << std::setw(35) << std::left - << " -s/--secure_transfer"; + std::cout << std::setw(35) << std::left << " -s/--secure_transfer"; std::cout << "use https transfer protocol" << std::endl << std::endl; - std::cout << std::setw(35) << std::left - << " -u/--server="; + std::cout << std::setw(35) << std::left << " -u/--server="; std::cout << "configure the license server url, please include http[s] in the url" << std::endl << std::endl; @@ -1234,15 +1214,14 @@ int main(int argc, char** argv) { bool show_usage = false; static const struct option long_options[] = { - { "chunked_transfer", no_argument, NULL, 'c' }, - { "keyid", required_argument, NULL, 'k' }, - { "license_server_id", required_argument, NULL, 'i' }, - { "license_server_url", required_argument, NULL, 'u' }, - { "port", required_argument, NULL, 'p' }, - { "secure_transfer", no_argument, NULL, 's' }, - { "use_full_path", no_argument, NULL, 'f' }, - { NULL, 0, NULL, '\0' } - }; + {"chunked_transfer", no_argument, NULL, 'c'}, + {"keyid", required_argument, NULL, 'k'}, + {"license_server_id", required_argument, NULL, 'i'}, + {"license_server_url", required_argument, NULL, 'u'}, + {"port", required_argument, NULL, 'p'}, + {"secure_transfer", no_argument, NULL, 's'}, + {"use_full_path", no_argument, NULL, 'f'}, + {NULL, 0, NULL, '\0'}}; int option_index = 0; int opt = 0; diff --git a/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf b/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf index 47512aee..69a7b5bb 100644 Binary files a/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf and b/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf differ diff --git a/libwvdrmengine/level3/arm/libwvlevel3.a b/libwvdrmengine/level3/arm/libwvlevel3.a index f67161f6..6916ec2b 100644 Binary files a/libwvdrmengine/level3/arm/libwvlevel3.a and b/libwvdrmengine/level3/arm/libwvlevel3.a differ diff --git a/libwvdrmengine/level3/mips/libwvlevel3.a b/libwvdrmengine/level3/mips/libwvlevel3.a index b12e546b..cb9ac8c9 100644 Binary files a/libwvdrmengine/level3/mips/libwvlevel3.a and b/libwvdrmengine/level3/mips/libwvlevel3.a differ diff --git a/libwvdrmengine/level3/x86/libwvlevel3.a b/libwvdrmengine/level3/x86/libwvlevel3.a index 85c4983a..4a26ee70 100644 Binary files a/libwvdrmengine/level3/x86/libwvlevel3.a and b/libwvdrmengine/level3/x86/libwvlevel3.a differ diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index e33350fc..eed21d43 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -120,7 +120,7 @@ class WVDrmPlugin : public android::DrmPlugin, const Vector& signature, bool& match); - virtual void onEvent(const CdmSessionId& cdmSessionId, + virtual void OnEvent(const CdmSessionId& cdmSessionId, CdmEventType cdmEventType); private: diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index 83ea7855..47424451 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -727,7 +727,7 @@ status_t WVDrmPlugin::verify(const Vector& sessionId, } } -void WVDrmPlugin::onEvent(const CdmSessionId& cdmSessionId, +void WVDrmPlugin::OnEvent(const CdmSessionId& cdmSessionId, CdmEventType cdmEventType) { Vector sessionId; EventType eventType = kDrmPluginEventVendorDefined; diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index ac2efb23..7575c38c 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -1141,8 +1141,8 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) { status_t res = plugin.setListener(listener); ASSERT_EQ(OK, res); - plugin.onEvent(cdmSessionId, LICENSE_EXPIRED_EVENT); - plugin.onEvent(cdmSessionId, LICENSE_RENEWAL_NEEDED_EVENT); + plugin.OnEvent(cdmSessionId, LICENSE_EXPIRED_EVENT); + plugin.OnEvent(cdmSessionId, LICENSE_RENEWAL_NEEDED_EVENT); } TEST_F(WVDrmPluginTest, GeneratesProvisioningNeededEvent) { diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index a979daa6..f8c62abe 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -192,7 +192,8 @@ typedef enum OEMCrypto_Algorithm { #define OEMCrypto_FirstSubsample 1 #define OEMCrypto_LastSubsample 2 -/* OEMCrypto_Usage_Entry_Status. +/* + * OEMCrypto_Usage_Entry_Status. * Valid values for status in the usage table. */ typedef enum OEMCrypto_Usage_Entry_Status { @@ -215,7 +216,8 @@ typedef struct { uint8_t pst[]; } OEMCrypto_PST_Report; -/* OEMCrypto_Clock_Security_Level. +/* + * OEMCrypto_Clock_Security_Level. * Valid values for clock_security_level in OEMCrypto_PST_Report. */ typedef enum OEMCrypto_Clock_Security_Level { @@ -231,7 +233,9 @@ typedef enum RSA_Padding_Scheme { } RSA_Padding_Scheme; -/* Obfuscation Renames. */ +/* + * Obfuscation Renames. + */ #define OEMCrypto_Initialize _oecc01 #define OEMCrypto_Terminate _oecc02 #define OEMCrypto_InstallKeybox _oecc03 @@ -270,23 +274,22 @@ typedef enum RSA_Padding_Scheme { /* * OEMCrypto_Initialize * - - Initializes the crypto hardware. - - Parameters: - None - - Returns: - OEMCrypto_SUCCESS success - OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware - - Threading: - No other function calls will be made while this function is running. This - function will not be called again before OEMCrypto_Terminate(). - - Version: - This method is supported by all API versions. - + * Description: + * Initialize the crypto firmware/hardware. + * + * Parameters: + * N/A + * + * Threading: + * No other function calls will be made while this function is running. This + * function will not be called again before OEMCrypto_Terminate. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_Initialize(void); @@ -428,8 +431,12 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( * * Because the nonce will be used to prevent replay attacks, it is desirable * that a rogue application cannot rapidly call this function until a - * repeated nonce is created randomly. With this in mind, we require that - * creation of more than 20 nonces will take at least one full second. + * repeated nonce is created randomly. With this in mind, if more than 20 + * nonces are requested within one second, OEMCrypto will return an error + * after the 20th and not generate any more nonces for the rest of the + * second. After an error, if the application waits at least one second + * before requesting more nonces, then OEMCrypto will reset the error + * condition and generate valid nonces again. * * Parameters: * session (in) - crypto session identifier. @@ -1028,52 +1035,52 @@ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox, /* * OEMCrypto_IsKeyboxValid * - Validates the Widevine Keybox loaded into the security processor device. This - method verifies two fields in the keybox: - - Verify the MAGIC field contains a valid signature (such as, ‘k’’b’’o’’x’). - - Compute the CRC using CRC-32-POSIX-1003.2 standard and compare the checksum - to the CRC stored in the Keybox. The CRC is computed over the entire Keybox - excluding the 4 bytes of the CRC (for example, Keybox[0..123]). For a - description of the fields stored in the keybox, see Keybox Definition. - - Parameters: - none - - Returns: - OEMCrypto_SUCCESS - OEMCrypto_ERROR_BAD_MAGIC - OEMCrypto_ERROR_BAD_CRC - - Threading: - This function may be called simultaneously with any session functions. - - Version: - This method is supported in all API versions. + * Description: + * Validate the Widevine Keybox stored on the device. + * + * The API performs two verification steps on the Keybox. It first verifies + * the MAGIC field contains a valid signature (must be 'kbox'). The API then + * computes the CRC using CRC-32 (Posix 1003.2 standard) and compares the + * checksum to the CRC stored in the Keybox. The CRC is computed over the + * entire Keybox excluding the 4 CRC bytes (i.e. Keybox[0..123]). + * + * Parameters: + * none + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_BAD_MAGIC + * OEMCrypto_ERROR_BAD_CRC + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_IsKeyboxValid(void); /* * OEMCrypto_GetDeviceID * - Retrieve DeviceID from the Keybox. - - Parameters: - [out] deviceId - pointer to the buffer that receives the Device ID - [in/out] idLength – on input, size of the caller’s device ID buffer. On - output, the number of bytes written into the buffer. - - Returns: - OEMCrypto_SUCCESS success - OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return device ID - OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id - - Threading: - This function may be called simultaneously with any session functions. - - Version: - This method is supported in all API versions. + * Description: + * Retrieve the device's unique identifier from the Keybox. + * + * Parameters: + * deviceId (out) - pointer to the buffer that receives the Device ID + * idLength (in/out) - on input, size of the caller's device ID buffer. + * On output, the number of bytes written into the buffer. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER buffer is too small to return the device ID + * OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t *idLength); @@ -1081,24 +1088,31 @@ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, /* * OEMCrypto_GetKeyData * - Return the Key Data field from the Keybox. - - Parameters: - [out] keyData - pointer to the buffer to hold the Key Data field from the - Keybox - [in/out] keyDataLength – on input, the allocated buffer size. On output, the - number of bytes in Key Data - - Returns: - OEMCrypto_SUCCESS success - OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return KeyData - OEMCrypto_ERROR_NO_KEYDATA - - Threading: - This function may be called simultaneously with any session functions. - - Version: - This method is supported in all API versions. + * Description: + * Returns the Key Data field from the Keybox. The Key Data field does not + * need to be encrypted by an OEM root key, but may be if desired. + * + * If the Key Data field was encrypted with an OEM root key when the Keybox + * was stored on the device, then this function should decrypt it and return + * the clear Key Data. If the Key Data was not encrypted, then this function + * should just access and return the clear Key data. + * + * Parameters: + * keyData (out) - pointer to a caller-managed buffer to hold the Key Data + * field from the Keybox + * dataLength (in/out) - on input, the allocated buffer size. On output, + * the number of bytes in KeyData. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER the buffer is too small to return the KeyData + * OEMCrypto_ERROR_NO_KEYDATA failed to return KeyData + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t *keyDataLength); @@ -1106,23 +1120,25 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, /* * OEMCrypto_GetRandom * - Returns a buffer filled with hardware-generated random bytes, if supported by - the hardware. - - Parameters: - [out] randomData - pointer to the buffer that receives random data - [in] dataLength - length of the random data buffer in bytes - - Returns: - OEMCrypto_SUCCESS success - OEMCrypto_ERROR_RNG_FAILED failed to generate random number - OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported - - Threading: - This function may be called simultaneously with any session functions. - - Version: - This method is supported in all API versions. + * Description: + * Return a buffer filled with hardware-generated random bytes. If the + * hardware feature does not exist, return OEMCrypto_ERROR_RNG_NOT_SUPPORTED. + * + * Parameters: + * randomData (out) - Pointer to caller-manager buffer that will receive the + * random data. + * dataLength (in) - Length of the random data buffer in bytes. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_RNG_FAILED failed to generate random number + * OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength); @@ -1263,8 +1279,6 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, * - padding. This is the wrapped key generated * - by OEMCrypto_RewrapDeviceRSAKey. * wrapped_rsa_key_length (in) - length of the wrapped key buffer, in bytes. - * wrapped_rsa_key_iv (in) - The initialization vector used to encrypt - * - wrapped_rsa_key. * * Threading: * This function may be called simultaneously with functions on other sessions, @@ -1546,41 +1560,25 @@ bool OEMCrypto_SupportsUsageTable(); * that entry is “inactive”, then return OEMCrypto_ERROR_INVALID_SESSION. * * Parameters: - * current (out) - this is the current HDCP version, based on the device itself, - * and the display to which it is connected. - * maximum (out) - this is the maximum supported HDCP version for the device, - * ignoring any attached device. - * - * Threading: - * This function may be called simultaneously with any other functions. + * session (in) - crypto session identifier. + * in_buffer (in) - pointer to memory containing data to be encrypted. + * buffer_length (in) - length of the buffer, in bytes. + * iv (in) - IV for encrypting data. Size is specified by the algorithm. + * algorithm (in) - Specifies which encryption algorithm to use. See + * OEMCrypto_Algorithm for valid values. + * out_buffer (out) - pointer to buffer in which encrypted data should be stored. * * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * - * Version: - * This method changed in API version 9. - */ -typedef uint8_t OEMCrypto_HDCP_Capability; -OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current, - OEMCrypto_HDCP_Capability *maximum); - -/* - * OEMCrypto_SupportsUsageTable() - * - * Description: - * This is used to determine if the device can support a usage table. Since this - * function is spoofable, it is not relied on for security purposes. It is for - * information only. The usage table is described in the section above. - * - * Parameters: - * none + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Threading: - * This function may be called simultaneously with any other functions. - * - * Returns: - * Returns true if the device can maintain a usage table. Returns false otherwise. + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. * * Version: * This method changed in API version 9. diff --git a/libwvdrmengine/oemcrypto/include/level3.h b/libwvdrmengine/oemcrypto/include/level3.h index 616ec01f..19a5d367 100644 --- a/libwvdrmengine/oemcrypto/include/level3.h +++ b/libwvdrmengine/oemcrypto/include/level3.h @@ -125,7 +125,7 @@ OEMCryptoResult Level3_GenerateRSASignature(OEMCrypto_SESSION session, size_t message_length, uint8_t* signature, size_t *signature_length, - RSA_Padding_Scheme algorithm); + RSA_Padding_Scheme padding_scheme); OEMCryptoResult Level3_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, const uint8_t* enc_session_key, size_t enc_session_key_length, diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index a993f8b3..d7b0c429 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -31,8 +31,9 @@ static const int kPssSaltLength = 20; namespace { -// Increment counter for AES-CTR -void ctr128_inc(uint8_t* counter) { +// Increment counter for AES-CTR. The CENC spec specifies we increment only +// the low 64 bits of the IV counter, and leave the high 64 bits alone. +void ctr128_inc64(uint8_t* counter) { uint32_t n = 16; do { if (++counter[--n] != 0) return; @@ -899,11 +900,11 @@ bool CryptoEngine::DecryptCTR(SessionContext* session, const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted, - void* clear_data, + uint8_t* clear_data, BufferType buffer_type) { // If the data is clear, we do not need a current key selected. - if (!is_encrypted) { + if (!is_encrypted && buffer_type != kBufferTypeDirect) { memcpy(reinterpret_cast(clear_data), cipher_data, cipher_data_length); return true; @@ -961,20 +962,20 @@ bool CryptoEngine::DecryptCTR(SessionContext* session, // Encrypt the IV. uint8_t ecount_buf[AES_BLOCK_SIZE]; - if (block_offset != 0) { - // The context is needed only when not starting a new block. - AES_encrypt(aes_iv, ecount_buf, &aes_key); - ctr128_inc(aes_iv); - } - // Decryption. - unsigned int block_offset_cur = block_offset; - AES_ctr128_encrypt( - cipher_data, reinterpret_cast(clear_data), cipher_data_length, - &aes_key, aes_iv, ecount_buf, &block_offset_cur); - if (block_offset_cur != ((block_offset + cipher_data_length) % AES_BLOCK_SIZE)) { - LOGE("[DecryptCTR(): FAILURE: byte offset wrong.]"); - return false; + // The CENC spec specifies we increment only the low 64 bits of the IV + // counter, and leave the high 64 bits alone. This is different from the + // OpenSSL implementation, which increments the entire 128 bit iv. That is + // why we implement the CTR loop ourselves. + size_t l = 0; + while (l < cipher_data_length) { + AES_encrypt(aes_iv, ecount_buf, &aes_key); + for (int n = block_offset; n < AES_BLOCK_SIZE && l < cipher_data_length; + ++n, ++l) { + clear_data[l] = cipher_data[l] ^ ecount_buf[n]; + } + ctr128_inc64(aes_iv); + block_offset = 0; } return true; } diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index 15783020..46d2fb3f 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -251,7 +251,7 @@ class CryptoEngine { const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted, - void* clear_data, + uint8_t* clear_data, BufferType buffer_type); private: diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp index 37bac013..25327583 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp @@ -95,8 +95,10 @@ bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) { const WidevineKeybox* keybox = reinterpret_cast(buffer); + size_t device_id_length + = strnlen(reinterpret_cast(keybox->device_id_), 32); device_id_.assign(keybox->device_id_, - keybox->device_id_ + sizeof(keybox->device_id_)); + keybox->device_id_ + device_id_length); device_key_.setValue(reinterpret_cast(keybox->device_key_), sizeof(keybox->device_key_)); device_key_.setType(KEYTYPE_DEVICE); diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index b6464733..1e233514 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -895,7 +895,7 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, size_t message_length, uint8_t* signature, size_t* signature_length, - RSA_Padding_Scheme algorithm) { + RSA_Padding_Scheme padding_scheme) { if (trace_all_calls) { printf("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n"); dump_hex("message", message, message_length); @@ -928,7 +928,7 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, return OEMCrypto_ERROR_INVALID_CONTEXT; } - if (algorithm != kSign_RSASSA_PSS) { + if (padding_scheme != kSign_RSASSA_PSS) { LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_NOT_IMPLEMENTED]"); return OEMCrypto_ERROR_NOT_IMPLEMENTED; } diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index de64eda9..135bf7f0 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -34,7 +34,7 @@ const size_t kNumKeys = 4; const size_t kBufferMaxLength = 256; #if defined(TEST_SPEED_MULTIPLIER) // Can slow test time limits when // debugging is slowing everything. -const int kSpeedMultiplier = TEST_SPEED_MULTIPLIER1; +const int kSpeedMultiplier = TEST_SPEED_MULTIPLIER; #else const int kSpeedMultiplier = 1; #endif @@ -910,29 +910,39 @@ class Session { OEMCrypto_GenerateNonce(session_id(), nonce)); } - void GenerateDerivedKeys() { - GenerateNonce(&nonce_); - vector mac_context = wvcdm::a2b_hex( + void FillDefaultContext(vector* mac_context, + vector* enc_context) { + /* These context strings are normally created by the CDM layer above from + a license request message. */ + *mac_context = wvcdm::a2b_hex( "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" - "34333231180120002a0c31383836373837343035000000000100"); - vector enc_context = wvcdm::a2b_hex( + "34333231180120002a0c31383836373837343035000000000200"); + *enc_context = wvcdm::a2b_hex( "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" "180120002a0c31383836373837343035000000000080"); + + } + + void GenerateDerivedKeys() { + GenerateNonce(&nonce_); + vector mac_context; + vector enc_context; + FillDefaultContext(&mac_context, &enc_context); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GenerateDerivedKeys( session_id(), &mac_context[0], mac_context.size(), &enc_context[0], enc_context.size())); mac_key_server_ = wvcdm::a2b_hex( - "9D41F0A77A76E071841C33B06104D106641421E651FBE55F0AED453CDA7713AC"); + "3CFD60254786AF350B353B4FBB700AB382558400356866BA16C256BCD8C502BF"); mac_key_client_ = wvcdm::a2b_hex( - "125283F299AF42C191E1A989846B388BB16A6E50B2F67D4F876A3C1F662CD5C8"); + "A9DE7B3E4E199ED8D1FBC29CD6B4C772CC4538C8B0D3E208B3E76F2EC0FD6F47"); enc_key_ = wvcdm::a2b_hex("D0BFC35DA9E33436E81C4229E78CB9F4"); } @@ -1309,28 +1319,13 @@ class Session { return true; } - bool GenerateRSASessionKey(vector* enc_session_key, - vector* mac_context, - vector* enc_context) { + bool GenerateRSASessionKey(vector* enc_session_key) { if (!public_rsa_) { cout << "No public RSA key loaded in test code.\n"; return false; } vector session_key = wvcdm::a2b_hex( "6fa479c731d2770b6a61a5d1420bb9d1"); - *mac_context = wvcdm::a2b_hex( - "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" - "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" - "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" - "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" - "34333231180120002a0c31383836373837343035000000000100"); - *enc_context = wvcdm::a2b_hex( - "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" - "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" - "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" - "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" - "180120002a0c31383836373837343035000000000080"); - enc_session_key->assign(RSA_size(public_rsa_), 0); int status = RSA_public_encrypt(session_key.size(), &session_key[0], @@ -1350,13 +1345,12 @@ class Session { wrapped_rsa_key.size())); GenerateNonce(&nonce_); vector enc_session_key; - vector mac_context; - vector enc_context; ASSERT_TRUE(PreparePublicKey(kTestRSAPublicKey2_2048, sizeof(kTestRSAPublicKey2_2048))); - ASSERT_TRUE(GenerateRSASessionKey(&enc_session_key, &mac_context, - &enc_context)); - + ASSERT_TRUE(GenerateRSASessionKey(&enc_session_key)); + vector mac_context; + vector enc_context; + FillDefaultContext(&mac_context, &enc_context); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_DeriveKeysFromSessionKey( session_id(), &enc_session_key[0], enc_session_key.size(), @@ -1364,9 +1358,9 @@ class Session { &enc_context[0], enc_context.size())); mac_key_server_ = wvcdm::a2b_hex( - "B09CB4482675123B66F7A8303D803F6042F43404ED3DE020811CFC13BCDF4C65"); + "1E451E59CB663DA1646194DD28880788ED8ED2EFF913CBD6A0D535D1D5A90381"); mac_key_client_ = wvcdm::a2b_hex( - "B09CB4482675123B66F7A8303D803F6042F43404ED3DE020811CFC13BCDF4C65"); + "F9AAE74690909F2207B53B13307FCA096CA8C49CC6DFE3659873CB952889A74B"); enc_key_ = wvcdm::a2b_hex("CB477D09014D72C9B8DCE76C33EA43B3"); } @@ -1553,24 +1547,22 @@ TEST_F(OEMCryptoClientTest, GetDeviceIdShortBuffer) { OEMCryptoResult sts; uint8_t dev_id[128]; - uint32_t req_len = 11; + uint32_t req_len = 0; for (int i = 0; i < 128; ++i) { dev_id[i] = 0x55; } dev_id[127] = '\0'; size_t dev_id_len = req_len; sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len); - // cout << "GetDeviceIdShortBuffer: sts = " << (int)sts << " request = " - // << req_len << " required = " << dev_id_len << endl; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - // On short buffer error, function should return minimum buffer length ASSERT_TRUE(dev_id_len > req_len); - - // cout << "NormalGetDeviceId: dev_id = " << dev_id - // << " len = " << dev_id_len << endl; - + // Should also return short buffer if passed a zero length and a null buffer. + dev_id_len = req_len; + sts = OEMCrypto_GetDeviceID(NULL, &dev_id_len); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + // On short buffer error, function should return minimum buffer length + ASSERT_TRUE(dev_id_len > req_len); testTearDown(); } @@ -2578,6 +2570,98 @@ TEST_F(DISABLED_TestKeybox, DecryptWithOffset) { testTearDown(); } +// Increment counter for AES-CTR. The CENC spec specifies we increment only +// the low 64 bits of the IV counter, and leave the high 64 bits alone. This is +// different from the OpenSSL implementation, so we implement the CTR loop +// ourselves. +void ctr128_inc64(uint8_t* counter) { + uint32_t n = 16; + do { + if (++counter[--n] != 0) return; + } while (n>8); +} +vector EncryptCTR(const vector &key, + const vector &iv, + const vector &in, size_t block_offset) { + AES_KEY aes_key; + AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); + + uint8_t aes_iv[AES_BLOCK_SIZE]; + memcpy(aes_iv, &iv[0], AES_BLOCK_SIZE); + + // Encrypt the IV. + uint8_t ecount_buf[AES_BLOCK_SIZE]; + + vector out( in.size()); + + size_t cipher_data_length = in.size(); + size_t l = 0; + while (l < cipher_data_length) { + AES_encrypt(aes_iv, ecount_buf, &aes_key); + for (int n = block_offset; n < AES_BLOCK_SIZE && l < cipher_data_length; + ++n, ++l) { + out[l] = in[l] ^ ecount_buf[n]; + } + ctr128_inc64(aes_iv); + block_offset = 0; + } + return out; +} + +TEST_F(DISABLED_TestKeybox, DecryptWithNearWrap) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox, true); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + s.LoadTestKeys(kDuration, 0, 0); + + // Select the key (from FillSimpleMessage) + vector keyId = wvcdm::a2b_hex("000000000000000000000000"); + sts = OEMCrypto_SelectKey(s.session_id(), + &keyId[0], + keyId.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Set up our expected input and output + vector key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D"); + vector encryptionIv = wvcdm::a2b_hex( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); + vector unencryptedData = wvcdm::a2b_hex( + "f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8" + "a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f" + "7085b70bf9ad2c7f37025dd45f93e90304739b1ce098a52e7b99a90f92544a9b" + "dca6f49e0006c80a0cfa018600523ad30e483141fe720d045394815d5c875ad4" + "b4387b8d09b6119bd0943e51b0b9103034496b3a83ba593f79baa188aeb6e08f" + "f6475933e9ce1bb95fbb526424e7966e25830c20da73c65c6fbff110b08e4def" + "eae94f98296770275b0d738207a8217cd6118f6ebc6e393428f2268cfedf800e" + "a7ebc606471b9a9dfccd1589e86d88fde508261eaf190efd20554ce9e14ff3c9"); + size_t block_offset = 5; + vector encryptedData = EncryptCTR(key, encryptionIv, unencryptedData, + block_offset) ; + + // Describe the output + uint8_t outputBuffer[256]; + OEMCrypto_DestBufferDesc destBuffer; + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = outputBuffer; + destBuffer.buffer.clear.max_length = sizeof(outputBuffer); + + // Decrypt the data + sts = OEMCrypto_DecryptCTR(s.session_id(), &encryptedData[0], + encryptedData.size(), true, &encryptionIv[0], + block_offset, &destBuffer, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer, + unencryptedData.size())); + + s.close(); + testTearDown(); +} + TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) { OEMCryptoResult sts; testSetUp(); @@ -3378,7 +3462,7 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox { uint8_t signature[SHA256_DIGEST_LENGTH]; SignBuffer(key_index, clear_buffer_, signature); if( alter_data ) { - signature[0] = 43; + signature[0] ^= 42; } sts = OEMCrypto_SelectKey(s.session_id(),