From 0190f99fb3563deea4e6d82d0d70a32ef6f0d19f Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Mon, 29 Jul 2013 17:29:07 -0700 Subject: [PATCH] Migration from jb-mr2 to master for Widevine CDM Android development of the widevine CDM has been done on the jb-mr2 branch of the cdm code base. This CL contains a merge of that jb-mr2 work to CDM master, and also reflects the evolution of the common Modular DRM code base since jb-mr2 branched. Change-Id: I1d7e1a12d092c00044a4298261146cb97808d4ef --- libwvdrmengine/Android.mk | 22 +- libwvdrmengine/cdm/Android.mk | 8 +- libwvdrmengine/cdm/core/include/cdm_engine.h | 126 +- libwvdrmengine/cdm/core/include/cdm_session.h | 44 +- .../core/include/certificate_provisioning.h | 36 + .../cdm/core/include/crypto_engine.h | 77 - .../cdm/core/include/crypto_session.h | 67 +- .../cdm/core/include/device_files.h | 72 +- libwvdrmengine/cdm/core/include/file_store.h | 26 +- libwvdrmengine/cdm/core/include/license.h | 15 +- libwvdrmengine/cdm/core/include/log.h | 21 +- libwvdrmengine/cdm/core/include/properties.h | 24 +- libwvdrmengine/cdm/core/include/scoped_ptr.h | 64 + .../cdm/core/include/string_conversions.h | 1 + .../cdm/core/include/wv_cdm_constants.h | 9 +- .../cdm/core/include/wv_cdm_event_listener.h | 7 - libwvdrmengine/cdm/core/src/cdm_engine.cpp | 427 +--- libwvdrmengine/cdm/core/src/cdm_session.cpp | 294 ++- .../cdm/core/src/certificate_provisioning.cpp | 229 +++ libwvdrmengine/cdm/core/src/crypto_engine.cpp | 286 --- .../cdm/core/src/crypto_session.cpp | 485 +++-- libwvdrmengine/cdm/core/src/device_files.cpp | 150 +- libwvdrmengine/cdm/core/src/license.cpp | 162 +- libwvdrmengine/cdm/core/src/properties.cpp | 2 + .../cdm/core/src/string_conversions.cpp | 215 +- libwvdrmengine/cdm/core/test/base64_test.cpp | 234 +-- .../cdm/core/test/cdm_engine_test.cpp | 269 +-- .../cdm/core/test/config_test_env.cpp | 48 +- .../cdm/core/test/config_test_env.h | 16 + .../cdm/core/test/device_files_unittest.cpp | 1778 ++++++++++++----- .../cdm/core/test/file_store_unittest.cpp | 292 ++- libwvdrmengine/cdm/core/test/http_socket.cpp | 115 +- libwvdrmengine/cdm/core/test/http_socket.h | 7 +- .../cdm/core/test/http_socket_test.cpp | 111 +- .../cdm/core/test/license_unittest.cpp | 18 +- .../cdm/core/test/policy_engine_unittest.cpp | 29 +- .../cdm/core/test/timer_unittest.cpp | 12 +- libwvdrmengine/cdm/core/test/url_request.cpp | 111 +- libwvdrmengine/cdm/core/test/url_request.h | 11 +- libwvdrmengine/cdm/core/test/wvcdm_test.cpp | 201 -- .../cdm/include/properties_configuration.h | 5 + .../include/wv_content_decryption_module.h | 2 +- libwvdrmengine/cdm/src/file_store.cpp | 72 +- libwvdrmengine/cdm/src/log.cpp | 4 +- libwvdrmengine/cdm/src/properties.cpp | 46 - libwvdrmengine/cdm/src/properties_android.cpp | 95 + libwvdrmengine/cdm/src/timer.cpp | 5 +- .../cdm/src/wv_content_decryption_module.cpp | 8 +- .../cdm/test/request_license_test.cpp | 658 +++--- libwvdrmengine/cdm/test/test_vectors.h | 18 + libwvdrmengine/cdm/test/unit-test.mk | 6 +- libwvdrmengine/include/WVErrors.h | 4 + libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 10 +- .../mediadrm/test/WVDrmPlugin_test.cpp | 39 +- libwvdrmengine/oemcrypto/mock/src/log.cpp | 4 +- libwvdrmengine/oemcrypto/mock/src/log.h | 21 +- .../oemcrypto/mock/src/oemcrypto_key_mock.cpp | 2 - .../oemcrypto/mock/src/oemcrypto_mock.cpp | 73 +- libwvdrmengine/oemcrypto/oemcrypto.gyp | 67 +- libwvdrmengine/oemcrypto/test/Android.mk | 3 +- .../oemcrypto/test/oemcrypto_test.cpp | 25 +- .../third_party/stringencoders/BUILD | 8 + .../third_party/stringencoders/LICENSE | 35 + .../third_party/stringencoders/OWNERS | 4 + .../third_party/stringencoders/README.google | 30 + .../stringencoders/src/modp_b64w.cpp | 269 +++ .../stringencoders/src/modp_b64w.h | 241 +++ .../stringencoders/src/modp_b64w_data.h | 480 +++++ 68 files changed, 4754 insertions(+), 3601 deletions(-) create mode 100644 libwvdrmengine/cdm/core/include/certificate_provisioning.h delete mode 100644 libwvdrmengine/cdm/core/include/crypto_engine.h create mode 100644 libwvdrmengine/cdm/core/include/scoped_ptr.h create mode 100644 libwvdrmengine/cdm/core/src/certificate_provisioning.cpp delete mode 100644 libwvdrmengine/cdm/core/src/crypto_engine.cpp delete mode 100644 libwvdrmengine/cdm/core/test/wvcdm_test.cpp delete mode 100644 libwvdrmengine/cdm/src/properties.cpp create mode 100644 libwvdrmengine/cdm/src/properties_android.cpp create mode 100644 libwvdrmengine/cdm/test/test_vectors.h create mode 100644 libwvdrmengine/third_party/stringencoders/BUILD create mode 100644 libwvdrmengine/third_party/stringencoders/LICENSE create mode 100644 libwvdrmengine/third_party/stringencoders/OWNERS create mode 100644 libwvdrmengine/third_party/stringencoders/README.google create mode 100644 libwvdrmengine/third_party/stringencoders/src/modp_b64w.cpp create mode 100644 libwvdrmengine/third_party/stringencoders/src/modp_b64w.h create mode 100644 libwvdrmengine/third_party/stringencoders/src/modp_b64w_data.h diff --git a/libwvdrmengine/Android.mk b/libwvdrmengine/Android.mk index d5762a12..02a2df54 100644 --- a/libwvdrmengine/Android.mk +++ b/libwvdrmengine/Android.mk @@ -3,6 +3,22 @@ # LOCAL_PATH := $(call my-dir) +# ----------------------------------------------------------------------------- +# Builds libmodp_b64.a +# +include $(CLEAR_VARS) + +LOCAL_MODULE := libmodp_b64 +LOCAL_MODULE_CLASS := STATIC_LIBRARIES + +LOCAL_C_INCLUDES := \ + bionic \ + external/stlport/stlport + +LOCAL_SRC_FILES := third_party/stringencoders/src/modp_b64w.cpp + +include $(BUILD_STATIC_LIBRARY) + # ----------------------------------------------------------------------------- # Builds libcdm_protos.a # Generates *.a, *.pb.h and *.pb.cc for *.proto files. @@ -18,10 +34,12 @@ LOCAL_C_INCLUDES := \ LOCAL_SRC_FILES := $(call all-proto-files-under, cdm/core/src) -# $(call local-intermediates-dir)/proto/$(LOCAL_PATH)/cdm/core/src is used 21 +# $(call local-intermediates-dir)/proto/$(LOCAL_PATH)/cdm/core/src is used # to locate *.pb.h by cdm source -# $(call local-intermediates-dir)/proto is used to locate *.pb.h included 23 +# $(call local-intermediates-dir)/proto is used to locate *.pb.h included # by *.pb.cc +# The module that depends on this prebuilt will have LOCAL_C_INCLUDES prepended +# with this path. LOCAL_EXPORT_C_INCLUDE_DIRS := \ $(call local-intermediates-dir)/proto \ $(call local-intermediates-dir)/proto/$(LOCAL_PATH)/cdm/core/src diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index 1344f62b..3199d37f 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -9,7 +9,8 @@ LOCAL_C_INCLUDES := \ external/stlport/stlport \ vendor/widevine/libwvdrmengine/cdm/core/include \ vendor/widevine/libwvdrmengine/cdm/include \ - vendor/widevine/libwvdrmengine/oemcrypto/include + vendor/widevine/libwvdrmengine/oemcrypto/include \ + vendor/widevine/libwvdrmengine/third_party/stringencoders/src LOCAL_C_INCLUDES += \ external/openssl/include \ @@ -25,7 +26,7 @@ LOCAL_SRC_FILES := \ $(CORE_SRC_DIR)/buffer_reader.cpp \ $(CORE_SRC_DIR)/cdm_engine.cpp \ $(CORE_SRC_DIR)/cdm_session.cpp \ - $(CORE_SRC_DIR)/crypto_engine.cpp \ + $(CORE_SRC_DIR)/certificate_provisioning.cpp \ $(CORE_SRC_DIR)/crypto_session.cpp \ $(CORE_SRC_DIR)/device_files.cpp \ $(CORE_SRC_DIR)/license.cpp \ @@ -36,10 +37,11 @@ LOCAL_SRC_FILES := \ $(SRC_DIR)/file_store.cpp \ $(SRC_DIR)/lock.cpp \ $(SRC_DIR)/log.cpp \ - $(SRC_DIR)/properties.cpp \ + $(SRC_DIR)/properties_android.cpp \ $(SRC_DIR)/timer.cpp \ $(SRC_DIR)/wv_content_decryption_module.cpp +LOCAL_WHOLE_STATIC_LIBRARIES := libmodp_b64 LOCAL_MODULE := libcdm LOCAL_MODULE_TAGS := optional diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 7e698d1a..c4a65757 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -3,7 +3,7 @@ #ifndef CDM_BASE_CDM_ENGINE_H_ #define CDM_BASE_CDM_ENGINE_H_ -#include "crypto_session.h" +#include "certificate_provisioning.h" #include "timer.h" #include "wv_cdm_types.h" @@ -19,117 +19,113 @@ typedef std::map CdmReleaseKeySetMap; class CdmEngine : public TimerHandler { public: CdmEngine(); - ~CdmEngine(); + virtual ~CdmEngine(); // Session related methods - CdmResponseType OpenSession(const CdmKeySystem& key_system, - CdmSessionId* session_id); - CdmResponseType CloseSession(const CdmSessionId& session_id); - CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); - CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); + virtual CdmResponseType OpenSession(const CdmKeySystem& key_system, + CdmSessionId* session_id); + virtual CdmResponseType CloseSession(const CdmSessionId& session_id); + + virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); + virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); // License related methods // Construct a valid license request - CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, - const CdmKeySetId& key_set_id, - const CdmInitData& init_data, - const CdmLicenseType license_type, - CdmAppParameterMap& app_parameters, - CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, + const CdmKeySetId& key_set_id, + const CdmInitData& init_data, + const CdmLicenseType license_type, + CdmAppParameterMap& app_parameters, + CdmKeyMessage* key_request, + std::string* server_url); // Accept license response and extract key info. - CdmResponseType AddKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data, - CdmKeySetId& key_set_id); + virtual CdmResponseType AddKey(const CdmSessionId& session_id, + const CdmKeyResponse& key_data, + CdmKeySetId* key_set_id); - CdmResponseType RestoreKey(const CdmSessionId& session_id, - const CdmKeySetId& key_set_id); + virtual CdmResponseType RestoreKey(const CdmSessionId& session_id, + const CdmKeySetId& key_set_id); - // Cancel session and unload keys. CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); // Construct valid renewal request for the current session keys. - CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, - CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, + CdmKeyMessage* key_request, + std::string* server_url); // Accept renewal response and update key info. - CdmResponseType RenewKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data); + virtual CdmResponseType RenewKey(const CdmSessionId& session_id, + const CdmKeyResponse& key_data); // Query system information - CdmResponseType QueryStatus(CdmQueryMap* info); + virtual CdmResponseType QueryStatus(CdmQueryMap* info); // Query license information - CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, - CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, + CdmQueryMap* key_info); // Query seesion control information - CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, - CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, + CdmQueryMap* key_info); // Provisioning related methods - CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request, - std::string* default_url); + virtual CdmResponseType GetProvisioningRequest( + CdmProvisioningRequest* request, + std::string* default_url); - CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response); + virtual CdmResponseType HandleProvisioningResponse( + CdmProvisioningResponse& response); // Secure stop related methods - CdmResponseType GetSecureStops(CdmSecureStops* secure_stops); - CdmResponseType ReleaseSecureStops(const CdmSecureStopReleaseMessage& message); + virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops); + virtual CdmResponseType ReleaseSecureStops( + const CdmSecureStopReleaseMessage& message); // Decryption and key related methods // Accept encrypted buffer and return decrypted data. - CdmResponseType Decrypt(const CdmSessionId& session_id, - bool is_encrypted, - bool is_secure, - const KeyId& key_id, - const uint8_t* encrypt_buffer, - size_t encrypt_length, - const std::vector& iv, - size_t block_offset, - void* decrypt_buffer, - size_t decrypt_buffer_offset, - bool is_video); + virtual CdmResponseType Decrypt(const CdmSessionId& session_id, + bool is_encrypted, + bool is_secure, + const KeyId& key_id, + const uint8_t* encrypt_buffer, + size_t encrypt_length, + const std::vector& iv, + size_t block_offset, + void* decrypt_buffer, + size_t decrypt_buffer_offset, + bool is_video); // Is the key known to any session? - bool IsKeyValid(const KeyId& key_id); + virtual bool IsKeyValid(const KeyId& key_id); // Event listener related methods - bool AttachEventListener(const CdmSessionId& session_id, - WvCdmEventListener* listener); - bool DetachEventListener(const CdmSessionId& session_id, - WvCdmEventListener* listener); + virtual bool AttachEventListener(const CdmSessionId& session_id, + WvCdmEventListener* listener); + virtual bool DetachEventListener(const CdmSessionId& session_id, + WvCdmEventListener* listener); // Parse a blob of multiple concatenated PSSH atoms to extract the first // widevine pssh static bool ExtractWidevinePssh(const CdmInitData& init_data, CdmInitData* output); + private: // private methods // Cancel all sessions - bool CancelSessions(); - void CleanupProvisioningSession(); - void ComposeJsonRequestAsQueryString(const std::string& message, - CdmProvisioningRequest* request); - - bool ParseJsonResponse(const CdmProvisioningResponse& json_str, - const std::string& start_substr, - const std::string& end_substr, - std::string* result); - bool ValidateKeySystem(const CdmKeySystem& key_system); + virtual bool CancelSessions(); + virtual bool ValidateKeySystem(const CdmKeySystem& key_system); // timer related methods to drive policy decisions - void EnablePolicyTimer(); - void DisablePolicyTimer(); + virtual void EnablePolicyTimer(); + virtual void DisablePolicyTimer(); virtual void OnTimerEvent(); - virtual void OnKeyReleaseEvent(CdmKeySetId key_set_id); + virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); // instance variables - CdmSession* provisioning_session_; CdmSessionMap sessions_; + CertificateProvisioning cert_provisioning_; CdmReleaseKeySetMap release_key_sets_; // policy timer diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index e50cbf5a..fd740833 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -6,25 +6,27 @@ #include #include "crypto_session.h" +#include "device_files.h" #include "license.h" #include "policy_engine.h" -#include "wv_cdm_event_listener.h" +#include "scoped_ptr.h" #include "wv_cdm_types.h" namespace wvcdm { -// TODO(kqyang): Do we need it? CdmKey not defined yet -// typedef std::map CdmSessionKeys; +class WvCdmEventListener; class CdmSession { public: - CdmSession() : session_id_(GenerateSessionId()), license_received_(false), - reinitialize_session_(false), license_type_(kLicenseTypeStreaming) {} + CdmSession() + : session_id_(GenerateSessionId()), + crypto_session_(NULL), + license_received_(false), + reinitialize_session_(false), + license_type_(kLicenseTypeStreaming) {} ~CdmSession() {} CdmResponseType Init(); - CdmResponseType ReInit(); - bool DestroySession(); CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id, const CdmLicenseType license_type); @@ -57,16 +59,11 @@ class CdmSession { CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info); // Decrypt() - Accept encrypted buffer and return decrypted data. - CdmResponseType Decrypt(bool is_encrypted, - bool is_secure, - const KeyId& key_id, - const uint8_t* encrypt_buffer, - size_t encrypt_length, - const std::vector& iv, - size_t block_offset, - void* decrypt_buffer, - size_t decrypt_buffer_offset, - bool is_video); + CdmResponseType Decrypt(bool is_encrypted, bool is_secure, + const KeyId& key_id, const uint8_t* encrypt_buffer, + size_t encrypt_length, const std::vector& iv, + size_t block_offset, void* decrypt_buffer, + size_t decrypt_buffer_offset, bool is_video); // License renewal // GenerateRenewalRequest() - Construct valid renewal request for the current @@ -83,7 +80,7 @@ class CdmSession { CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request, std::string* server_url); - // RenewKey() - Accept renewal response and update key info. + // ReleaseKey() - Accept response and release key. CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); bool IsKeyValid(const KeyId& key_id); @@ -92,22 +89,22 @@ class CdmSession { bool DetachEventListener(WvCdmEventListener* listener); void OnTimerEvent(); - void OnKeyReleaseEvent(CdmKeySetId key_set_id); + void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); private: // Generate unique ID for each new session. CdmSessionId GenerateSessionId(); - CdmKeySetId GenerateKeySetId(CdmInitData& pssh_data); + bool GenerateKeySetId(CdmKeySetId* key_set_id); bool LoadDeviceCertificate(std::string* cert, std::string* wrapped_key); - bool StoreLicense(bool active); + bool StoreLicense(DeviceFiles::LicenseState state); // instance variables const CdmSessionId session_id_; CdmKeySystem key_system_; CdmLicense license_parser_; - CryptoSession* crypto_session_; + scoped_ptr crypto_session_; PolicyEngine policy_engine_; bool license_received_; bool reinitialize_session_; @@ -132,9 +129,6 @@ class CdmSession { std::set listeners_; - // TODO(kqyang): CdmKey not defined yet - // CdmSessionKeys session_keys_; - CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession); }; diff --git a/libwvdrmengine/cdm/core/include/certificate_provisioning.h b/libwvdrmengine/cdm/core/include/certificate_provisioning.h new file mode 100644 index 00000000..33984e6a --- /dev/null +++ b/libwvdrmengine/cdm/core/include/certificate_provisioning.h @@ -0,0 +1,36 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_CERTIFICATE_PROVISIONING_H_ +#define CDM_BASE_CERTIFICATE_PROVISIONING_H_ + +#include "crypto_session.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +class CdmSession; + +class CertificateProvisioning { + public: + CertificateProvisioning() {}; + ~CertificateProvisioning() {}; + + // Provisioning related methods + CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request, + std::string* default_url); + CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response); + + private: + void ComposeJsonRequestAsQueryString(const std::string& message, + CdmProvisioningRequest* request); + bool ParseJsonResponse(const CdmProvisioningResponse& json_str, + const std::string& start_substr, + const std::string& end_substr, + std::string* result); + CryptoSession crypto_session_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning); +}; +} // namespace wvcdm + +#endif // CDM_BASE_CERTIFICATE_PROVISIONING_H_ diff --git a/libwvdrmengine/cdm/core/include/crypto_engine.h b/libwvdrmengine/cdm/core/include/crypto_engine.h deleted file mode 100644 index f6f99b34..00000000 --- a/libwvdrmengine/cdm/core/include/crypto_engine.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// OEMCrypto Client - wrapper class for C-style OEMCrypto interface -// -#ifndef CDM_BASE_CRYPTO_ENGINE_H_ -#define CDM_BASE_CRYPTO_ENGINE_H_ - -#include -#include -#include - -#include "crypto_session.h" -#include "lock.h" -#include "wv_cdm_types.h" - -namespace wvcdm { - -typedef std::map CryptoSessionMap; - -class CryptoEngine { - - friend class CryptoSession; - - private: - - CryptoEngine(); - ~CryptoEngine(); - - public: - - // get an instance of Crypto engine - static CryptoEngine* GetInstance(); - - bool Init(); - bool Terminate(); - bool ValidateKeybox(); - - CryptoSession* CreateSession(const CdmSessionId& session_id); - CryptoSession* FindSession(const CdmSessionId& session_id); - bool DestroySession(const CdmSessionId& session_id); - bool DestroySessions(); - - bool GetToken(std::string* token); - - typedef enum { - kSecurityLevelL1, - kSecurityLevelL2, - kSecurityLevelL3, - kSecurityLevelUnknown - } SecurityLevel; - - SecurityLevel GetSecurityLevel(); - bool GetDeviceUniqueId(std::string* deviceId); - bool GetSystemId(uint32_t* systemId); - bool GetProvisioningId(std::string* provisioningId); - -private: - - void DeleteInstance(); - static CryptoEngine* CreateSingleton(); - - CryptoSession* FindSessionInternal(const CdmSessionId& session_id); - - static CryptoEngine* crypto_engine_; - static Lock crypto_engine_lock_; - - bool initialized_; - mutable Lock crypto_lock_; - mutable Lock sessions_lock_; - CryptoSessionMap sessions_; - - CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine); -}; - -}; // namespace wvcdm - -#endif // CDM_BASE_CRYPTO_ENGINE_H_ diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index e0d6d985..588f855f 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -8,48 +8,53 @@ #include #include -#include "crypto_key.h" +#include "lock.h" #include "OEMCryptoCENC.h" #include "wv_cdm_types.h" namespace wvcdm { -typedef std::map CryptoKeyMap; +class CryptoKey; +typedef std::map CryptoKeyMap; class CryptoSession { public: CryptoSession(); - explicit CryptoSession(const std::string& sname); ~CryptoSession(); + typedef enum { + kSecurityLevelUninitialized, + kSecurityLevelL1, + kSecurityLevelL2, + kSecurityLevelL3, + kSecurityLevelUnknown + } SecurityLevel; + + bool ValidateKeybox(); + bool GetToken(std::string* token); + SecurityLevel GetSecurityLevel(); + bool GetDeviceUniqueId(std::string* device_id); + bool GetSystemId(uint32_t* system_id); + bool GetProvisioningId(std::string* provisioning_id); + bool Open(); void Close(); - bool IsValid() { return valid_; } bool IsOpen() { return open_; } - bool SuccessStatus(); - CryptoResult session_status() { return session_status_; } CryptoSessionId oec_session_id() { return oec_session_id_; } - CdmSessionId cdm_session_id() { return cdm_session_id_; } // Key request/response void GenerateRequestId(std::string& req_id_str); bool PrepareRequest(const std::string& key_deriv_message, - std::string* signature, - bool is_provisioning); + bool is_provisioning, std::string* signature); bool PrepareRenewalRequest(const std::string& message, std::string* signature); - bool LoadKeys(const std::string& message, - const std::string& signature, - const std::string& mac_key_iv, - const std::string& mac_key, - int num_keys, - const CryptoKey* key_array); + bool LoadKeys(const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + int num_keys, const CryptoKey* key_array); bool LoadCertificatePrivateKey(std::string& wrapped_key); - bool RefreshKeys(const std::string& message, - const std::string& signature, - int num_keys, - const CryptoKey* key_array); + bool RefreshKeys(const std::string& message, const std::string& signature, + int num_keys, const CryptoKey* key_array); bool GenerateNonce(uint32_t* nonce); bool GenerateDerivedKeys(const std::string& message); bool GenerateDerivedKeys(const std::string& message, @@ -58,7 +63,6 @@ class CryptoSession { const std::string& signature, const std::string& nonce, const std::string& enc_rsa_key, - size_t enc_rsa_key_length, const std::string& rsa_key_iv, std::string* wrapped_rsa_key); @@ -74,33 +78,34 @@ class CryptoSession { size_t decrypt_buffer_offset, bool is_video); - private: - static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature + bool GetRandom(uint8_t* random_data, size_t data_length); + private: + void Init(); + void Terminate(); void GenerateMacContext(const std::string& input_context, std::string* deriv_context); void GenerateEncryptContext(const std::string& input_context, std::string* deriv_context); - bool GenerateSignature(const std::string& message, - std::string* signature, - bool use_rsa); + bool GenerateSignature(const std::string& message, bool use_rsa, + std::string* signature); size_t GetOffset(std::string message, std::string field); bool SetDestinationBufferType(); - bool valid_; + static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature + static Lock crypto_lock_; + static bool initialized_; + static int session_count_; + bool open_; - CdmSessionId cdm_session_id_; CryptoSessionId oec_session_id_; - CryptoResult session_status_; OEMCryptoBufferType destination_buffer_type_; bool is_destination_buffer_type_valid_; - CryptoKeyMap keys_; - CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession); }; -}; // namespace wvcdm +}; // namespace wvcdm #endif // CDM_BASE_CRYPTO_SESSSION_H_ diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index fa67e144..0ac90043 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -7,6 +7,8 @@ namespace wvcdm { +class File; + class DeviceFiles { public: typedef enum { @@ -15,44 +17,50 @@ class DeviceFiles { kLicenseStateUnknown, } LicenseState; - static bool StoreCertificate(const std::string& certificate, - const std::string& wrapped_private_key); - static bool RetrieveCertificate(std::string* certificate, - std::string* wrapped_private_key); + DeviceFiles() {} + virtual ~DeviceFiles() {} - static bool StoreLicense(const std::string& key_set_id, - const LicenseState state, - const CdmInitData& pssh_data, - const CdmKeyMessage& key_request, - const CdmKeyResponse& key_response, - const CdmKeyMessage& key_renewal_request, - const CdmKeyResponse& key_renewal_response, - const std::string& release_server_url); - static bool RetrieveLicense(const std::string& key_set_id, - LicenseState* state, - CdmInitData* pssh_data, - CdmKeyMessage* key_request, - CdmKeyResponse* key_response, - CdmKeyMessage* key_renewal_request, - CdmKeyResponse* key_renewal_response, - std::string* release_server_url); - static bool DeleteLicense(const std::string& key_set_id); - static bool LicenseExists(const std::string& key_set_id); + virtual bool Init(File* handle); - static std::string GetBasePath(const char* dir); - static const char* kBasePath; - static const char* kPathDelimiter; - static const char* kDeviceCertificateFileName; - static const char* kLicenseFileNameExt; + virtual bool StoreCertificate(const std::string& certificate, + const std::string& wrapped_private_key); + virtual bool RetrieveCertificate(std::string* certificate, + std::string* wrapped_private_key); + + virtual bool StoreLicense(const std::string& key_set_id, + const LicenseState state, + const CdmInitData& pssh_data, + const CdmKeyMessage& key_request, + const CdmKeyResponse& key_response, + const CdmKeyMessage& key_renewal_request, + const CdmKeyResponse& key_renewal_response, + const std::string& release_server_url); + virtual bool RetrieveLicense(const std::string& key_set_id, + LicenseState* state, + CdmInitData* pssh_data, + CdmKeyMessage* key_request, + CdmKeyResponse* key_response, + CdmKeyMessage* key_renewal_request, + CdmKeyResponse* key_renewal_response, + std::string* release_server_url); + virtual bool DeleteLicense(const std::string& key_set_id); + virtual bool LicenseExists(const std::string& key_set_id); + + // For testing only + static std::string GetCertificateFileName(); + static std::string GetLicenseFileNameExtension(); + + protected: + bool Hash(const std::string& data, std::string* hash); + bool StoreFile(const char* name, const std::string& data); + bool RetrieveFile(const char* name, std::string* data); private: - static bool Hash(const std::string& data, std::string* hash); - static bool StoreFile(const char* name, const std::string& data); - static bool RetrieveFile(const char* name, std::string* data); + File* file_; CORE_DISALLOW_COPY_AND_ASSIGN(DeviceFiles); -}; // namespace wvcdm +}; -} +} // namespace wvcdm #endif // CDM_BASE_DEVICE_FILES_H_ diff --git a/libwvdrmengine/cdm/core/include/file_store.h b/libwvdrmengine/cdm/core/include/file_store.h index 0b10c2e1..143a4768 100644 --- a/libwvdrmengine/cdm/core/include/file_store.h +++ b/libwvdrmengine/cdm/core/include/file_store.h @@ -17,28 +17,24 @@ class File { kNoFlags = 0, kBinary = 1, kCreate = 2, - kReadOnly = 4, // defauts to read and write access + kReadOnly = 4, // defaults to read and write access kTruncate = 8 }; File(); - File(const std::string& file_path, int flags); virtual ~File(); - bool Open(const std::string& file_path, int flags); - void Close(); - bool IsOpen(); - bool IsBad(); + virtual bool Open(const std::string& file_path, int flags); + virtual ssize_t Read(char* buffer, size_t bytes); + virtual ssize_t Write(const char* buffer, size_t bytes); + virtual void Close(); - ssize_t Read(void *buf, size_t bytes); - ssize_t Write(const void* buf, size_t bytes); - - static bool Exists(const std::string& file_path); - static bool Remove(const std::string& file_path); - static bool CreateDirectory(const std::string dir_path); - static bool IsDirectory(const std::string& dir_path); - static bool IsRegularFile(const std::string& file_path); - static ssize_t FileSize(const std::string& file_path); + virtual bool Exists(const std::string& file_path); + virtual bool Remove(const std::string& file_path); + virtual bool CreateDirectory(const std::string dir_path); + virtual bool IsDirectory(const std::string& dir_path); + virtual bool IsRegularFile(const std::string& file_path); + virtual ssize_t FileSize(const std::string& file_path); private: class Impl; diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index 40713731..cee5219b 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -3,19 +3,20 @@ #ifndef CDM_BASE_LICENSE_H_ #define CDM_BASE_LICENSE_H_ -#include "license_protocol.pb.h" #include "wv_cdm_types.h" -namespace wvcdm { +namespace video_widevine_server { +namespace sdk { +class SignedMessage; +} +} // namespace video_widevine_server -using video_widevine_server::sdk::LicenseIdentification; -using video_widevine_server::sdk::SignedMessage; +namespace wvcdm { class CryptoSession; class PolicyEngine; class CdmLicense { - public: CdmLicense(); @@ -42,9 +43,9 @@ class CdmLicense { CdmKeyResponse& license_renewal_response); private: - CdmResponseType HandleKeyErrorResponse(const SignedMessage& signed_message); + CdmResponseType HandleKeyErrorResponse( + const video_widevine_server::sdk::SignedMessage& signed_message); - LicenseIdentification license_id_; CryptoSession* session_; PolicyEngine* policy_engine_; std::string server_url_; diff --git a/libwvdrmengine/cdm/core/include/log.h b/libwvdrmengine/cdm/core/include/log.h index db66d2ab..c267c93d 100644 --- a/libwvdrmengine/cdm/core/include/log.h +++ b/libwvdrmengine/cdm/core/include/log.h @@ -17,14 +17,23 @@ typedef enum { LOG_VERBOSE } LogPriority; -void log_write(LogPriority priority, const char *fmt, ...); +// Required to enable/disable verbose logging (LOGV) in Chromium. In Chromium, +// verbose logging level is controlled using command line switches --v (global) +// or --vmodule (per module). This function calls logging::InitLogging to +// initialize logging, which should have already been included in most Chromium +// based binaries. However, it is typically not included by default in +// unittests, in particular, the unittests in CDM core need to call InitLogging +// to be able to control verbose logging in command line. +void InitLogging(int argc, const char* const* argv); + +void Log(const char* file, int line, LogPriority level, const char* fmt, ...); // Log APIs -#define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__)) -#define LOGW(...) ((void)log_write(wvcdm::LOG_WARN, __VA_ARGS__)) -#define LOGI(...) ((void)log_write(wvcdm::LOG_INFO, __VA_ARGS__)) -#define LOGD(...) ((void)log_write(wvcdm::LOG_DEBUG, __VA_ARGS__)) -#define LOGV(...) ((void)log_write(wvcdm::LOG_VERBOSE, __VA_ARGS__)) +#define LOGE(...) Log(__FILE__, __LINE__, wvcdm::LOG_ERROR, __VA_ARGS__) +#define LOGW(...) Log(__FILE__, __LINE__, wvcdm::LOG_WARN, __VA_ARGS__) +#define LOGI(...) Log(__FILE__, __LINE__, wvcdm::LOG_INFO, __VA_ARGS__) +#define LOGD(...) Log(__FILE__, __LINE__, wvcdm::LOG_DEBUG, __VA_ARGS__) +#define LOGV(...) Log(__FILE__, __LINE__, wvcdm::LOG_VERBOSE, __VA_ARGS__) }; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/include/properties.h b/libwvdrmengine/cdm/core/include/properties.h index b7af2f70..a2302242 100644 --- a/libwvdrmengine/cdm/core/include/properties.h +++ b/libwvdrmengine/cdm/core/include/properties.h @@ -30,21 +30,23 @@ class Properties { static inline bool oem_crypto_use_secure_buffers() { return oem_crypto_use_secure_buffers_; } - static inline bool oem_crypto_use_fifo() { - return oem_crypto_use_fifo_; - } + static inline bool oem_crypto_use_fifo() { return oem_crypto_use_fifo_; } static inline bool oem_crypto_use_userspace_buffers() { return oem_crypto_use_userspace_buffers_; } static inline bool use_certificates_as_identification() { return use_certificates_as_identification_; } - static bool GetCompanyName(std::string& company_name); - static bool GetModelName(std::string& model_name); - static bool GetArchitectureName(std::string& arch_name); - static bool GetDeviceName(std::string& device_name); - static bool GetProductName(std::string& product_name); - static bool GetBuildInfo(std::string& build_info); + static inline bool extract_pssh_data() { + return extract_pssh_data_; + } + static bool GetCompanyName(std::string* company_name); + static bool GetModelName(std::string* model_name); + static bool GetArchitectureName(std::string* arch_name); + static bool GetDeviceName(std::string* device_name); + static bool GetProductName(std::string* product_name); + static bool GetBuildInfo(std::string* build_info); + static bool GetDeviceFilesBasePath(std::string* base_path); private: static void set_begin_license_usage_when_received(bool flag) { @@ -65,6 +67,9 @@ 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 bool begin_license_usage_when_received_; static bool require_explicit_renew_request_; @@ -72,6 +77,7 @@ class Properties { static bool oem_crypto_use_fifo_; static bool oem_crypto_use_userspace_buffers_; static bool use_certificates_as_identification_; + static bool extract_pssh_data_; CORE_DISALLOW_COPY_AND_ASSIGN(Properties); }; diff --git a/libwvdrmengine/cdm/core/include/scoped_ptr.h b/libwvdrmengine/cdm/core/include/scoped_ptr.h new file mode 100644 index 00000000..0f32163b --- /dev/null +++ b/libwvdrmengine/cdm/core/include/scoped_ptr.h @@ -0,0 +1,64 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// A simple and partial implementation of scoped_ptr class. +// The implementation is copied from gtest/include/gtest/internal/gtest-port.h. +// +#ifndef CDM_BASE_SCOPED_PTR_H_ +#define CDM_BASE_SCOPED_PTR_H_ + +#include "wv_cdm_types.h" + +namespace wvcdm { + +// A scoped_ptr is like a T*, except that the destructor of scoped_ptr +// automatically deletes the pointer it holds (if any). +// That is, scoped_ptr owns the T object that it points to. +// Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object. +// Also like T*, scoped_ptr is thread-compatible, and once you +// dereference it, you get the thread safety guarantees of T. +// +// The size of scoped_ptr is small. On most compilers, sizeof(scoped_ptr) +// == sizeof(T*). +// +// Current implementation targets having a strict subset of C++11's +// unique_ptr<> features. Known deficiencies include not supporting move-only +// deleteres, function pointers as deleters, and deleters with reference +// types. + +// This implementation of scoped_ptr is PARTIAL, e.g. it does not support move, +// custom deleter etc. +template +class scoped_ptr { + public: + typedef T element_type; + + explicit scoped_ptr(T* p = NULL) : ptr_(p) {} + ~scoped_ptr() { reset(); } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + T* release() { + T* const ptr = ptr_; + ptr_ = NULL; + return ptr; + } + + void reset(T* p = NULL) { + if (p != ptr_) { + if (sizeof(T) > 0) { // Makes sure T is a complete type. + delete ptr_; + } + ptr_ = p; + } + } + private: + T* ptr_; + + CORE_DISALLOW_COPY_AND_ASSIGN(scoped_ptr); +}; + +}; // namespace wvcdm + +#endif // CDM_BASE_SCOPED_PTR_H_ diff --git a/libwvdrmengine/cdm/core/include/string_conversions.h b/libwvdrmengine/cdm/core/include/string_conversions.h index 65579519..4c0ac32a 100644 --- a/libwvdrmengine/cdm/core/include/string_conversions.h +++ b/libwvdrmengine/cdm/core/include/string_conversions.h @@ -15,6 +15,7 @@ std::string a2bs_hex(const std::string& b); std::string b2a_hex(const std::vector& b); std::string b2a_hex(const std::string& b); std::string Base64SafeEncode(const std::vector& bin_input); +std::string Base64SafeEncodeNoPad(const std::vector& bin_input); std::vector Base64SafeDecode(const std::string& bin_input); std::string HexEncode(const uint8_t* bytes, unsigned size); std::string IntToString(int value); diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 06cfafed..1f5291d9 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -5,8 +5,6 @@ #include -#include "wv_cdm_types.h" - namespace wvcdm { static const size_t KEY_CONTROL_SIZE = 16; // TODO(kqyang): Key ID size is not fixed in spec, but conventionally we @@ -19,10 +17,9 @@ static const size_t KEY_SIZE = 16; static const size_t MAC_KEY_SIZE = 32; static const size_t KEYBOX_KEY_DATA_SIZE = 72; -static const std::string SESSION_ID_PREFIX = "sid"; -static const std::string KEY_SET_ID_PREFIX = "ksid"; - -static const CdmKeySystem KEY_SYSTEM = "com.widevine"; +static const char SESSION_ID_PREFIX[] = "sid"; +static const char KEY_SET_ID_PREFIX[] = "ksid"; +static const char KEY_SYSTEM[] = "com.widevine"; // define query keys, values here static const std::string QUERY_KEY_LICENSE_TYPE = "LicenseType"; diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h b/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h index a8136709..b9bfb476 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h @@ -11,22 +11,15 @@ namespace wvcdm { // 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. -// The listener may also specify, when the instance is created, whether to be -// notified about events for a particular session or all sessions. class WvCdmEventListener { public: WvCdmEventListener() {} - WvCdmEventListener(CdmSessionId& session_id) : session_id_(session_id) {} virtual ~WvCdmEventListener() {} virtual void onEvent(const CdmSessionId& session_id, CdmEventType cdm_event) = 0; - virtual CdmSessionId session_id() { return session_id_; } - private: - CdmSessionId session_id_; - CORE_DISALLOW_COPY_AND_ASSIGN(WvCdmEventListener); }; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index fcaed864..9601c53c 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -7,42 +7,22 @@ #include "buffer_reader.h" #include "cdm_session.h" -#include "clock.h" -#include "crypto_engine.h" -#include "device_files.h" #include "license_protocol.pb.h" #include "log.h" #include "properties.h" +#include "scoped_ptr.h" #include "string_conversions.h" #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" -#ifndef CDM_POLICY_TIMER_DURATION_SECONDS -#define CDM_POLICY_TIMER_DURATION_SECONDS 1 -#endif - namespace { -const std::string kDefaultProvisioningServerUrl = - "https://www.googleapis.com/" - "certificateprovisioning/v1/devicecertificates/create" - "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; +const int kCdmPolicyTimerDurationSeconds = 1; } namespace wvcdm { -// Protobuf generated classes. -using video_widevine_server::sdk::ClientIdentification; -using video_widevine_server::sdk::ProvisioningRequest; -using video_widevine_server::sdk::ProvisioningResponse; -using video_widevine_server::sdk::SignedProvisioningMessage; - -typedef std::map::const_iterator CdmSessionIter; -typedef std::map::iterator CdmReleaseKeySetIter; - -CdmEngine::CdmEngine() : provisioning_session_(NULL) { +CdmEngine::CdmEngine() { Properties::Init(); - Clock clock; - srand(static_cast(clock.GetCurrentTime() & 0xFFFFFFFF)); } CdmEngine::~CdmEngine() { @@ -69,28 +49,21 @@ CdmResponseType CdmEngine::OpenSession( return KEY_ERROR; } - CdmSession* new_session = new CdmSession(); - if (!new_session) { - LOGE("CdmEngine::OpenSession: session creation failed"); - return KEY_ERROR; - } + scoped_ptr new_session(new CdmSession()); - CdmSessionId new_session_id = new_session->session_id(); - - if (new_session_id.empty()) { + if (new_session->session_id().empty()) { LOGE("CdmEngine::OpenSession: failure to generate session ID"); - delete(new_session); return UNKNOWN_ERROR; } CdmResponseType sts = new_session->Init(); if (sts != NO_ERROR) { - delete(new_session); + LOGE("CdmEngine::OpenSession: bad session init"); return sts; } - sessions_[new_session_id] = new_session; - *session_id = new_session_id; + *session_id = new_session->session_id(); + sessions_[*session_id] = new_session.release(); return NO_ERROR; } @@ -98,7 +71,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) { LOGI("CdmEngine::OpenKeySetSession"); if (key_set_id.empty()) { - LOGI("CdmEngine::OpenKeySetSession: invalid key set id"); + LOGE("CdmEngine::OpenKeySetSession: invalid key set id"); return KEY_ERROR; } @@ -115,24 +88,22 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) { CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) { LOGI("CdmEngine::CloseSession"); - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str()); return KEY_ERROR; } - sessions_.erase(session_id); - if (sessions_.size() == 0) - DisablePolicyTimer(); - iter->second->DestroySession(); delete iter->second; + sessions_.erase(session_id); + DisablePolicyTimer(); return NO_ERROR; } CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) { LOGI("CdmEngine::CloseKeySetSession"); - CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id); + CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id); if (iter == release_key_sets_.end()) { LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s", key_set_id.c_str()); @@ -169,7 +140,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest( return UNKNOWN_ERROR; } - CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id); + CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id); if (iter == release_key_sets_.end()) { LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s", key_set_id.c_str()); @@ -179,7 +150,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest( id = iter->second; } - CdmSessionIter iter = sessions_.find(id); + CdmSessionMap::iterator iter = sessions_.find(id); if (iter == sessions_.end()) { LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", id.c_str()); @@ -222,28 +193,33 @@ CdmResponseType CdmEngine::GenerateKeyRequest( CdmResponseType CdmEngine::AddKey( const CdmSessionId& session_id, const CdmKeyResponse& key_data, - CdmKeySetId& key_set_id) { + CdmKeySetId* key_set_id) { LOGI("CdmEngine::AddKey"); CdmSessionId id = session_id; bool license_type_release = session_id.empty(); if (license_type_release) { - if (key_set_id.empty()) { - LOGI("CdmEngine::AddKey: invalid key set id"); + if (!key_set_id) { + LOGE("CdmEngine::AddKey: no key set id provided"); return KEY_ERROR; } - CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id); + if (key_set_id->empty()) { + LOGE("CdmEngine::AddKey: invalid key set id"); + return KEY_ERROR; + } + + CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id); if (iter == release_key_sets_.end()) { - LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id.c_str()); + LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str()); return KEY_ERROR; } id = iter->second; } - CdmSessionIter iter = sessions_.find(id); + CdmSessionMap::iterator iter = sessions_.find(id); if (iter == sessions_.end()) { LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str()); @@ -255,7 +231,7 @@ CdmResponseType CdmEngine::AddKey( return KEY_ERROR; } - CdmResponseType sts = iter->second->AddKey(key_data, &key_set_id); + CdmResponseType sts = iter->second->AddKey(key_data, key_set_id); if (KEY_ADDED != sts) { LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts); @@ -279,7 +255,7 @@ CdmResponseType CdmEngine::RestoreKey( return KEY_ERROR; } - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { LOGE("CdmEngine::RestoreKey: session_id not found = %s ", session_id.c_str()); @@ -298,13 +274,15 @@ CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) { // active sessions. Sessions are currently not being destroyed here. We can // add this logic once the semantics of canceling the key is worked out. - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { LOGE("CdmEngine::CancelKeyRequest: session_id not found = %s", session_id.c_str()); return KEY_ERROR; } // TODO(edwinwong, rfrias): unload keys here + delete iter->second; + sessions_.erase(session_id); DisablePolicyTimer(); return NO_ERROR; } @@ -315,7 +293,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest( std::string* server_url) { LOGI("CdmEngine::GenerateRenewalRequest"); - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s", session_id.c_str()); return KEY_ERROR; @@ -345,7 +323,7 @@ CdmResponseType CdmEngine::RenewKey( const CdmKeyResponse& key_data) { LOGI("CdmEngine::RenewKey"); - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str()); return KEY_ERROR; @@ -367,22 +345,19 @@ CdmResponseType CdmEngine::RenewKey( CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) { LOGI("CdmEngine::QueryStatus"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - if (!crypto_engine) { - return KEY_ERROR; - } - - switch (crypto_engine->GetSecurityLevel()) { - case CryptoEngine::kSecurityLevelL1: + CryptoSession crypto_session; + switch (crypto_session.GetSecurityLevel()) { + case CryptoSession::kSecurityLevelL1: (*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1; break; - case CryptoEngine::kSecurityLevelL2: + case CryptoSession::kSecurityLevelL2: (*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L2; break; - case CryptoEngine::kSecurityLevelL3: + case CryptoSession::kSecurityLevelL3: (*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3; break; - case CryptoEngine::kSecurityLevelUnknown: + case CryptoSession::kSecurityLevelUninitialized: + case CryptoSession::kSecurityLevelUnknown: (*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_Unknown; break; default: @@ -390,13 +365,13 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) { } std::string deviceId; - bool success = crypto_engine->GetDeviceUniqueId(&deviceId); + bool success = crypto_session.GetDeviceUniqueId(&deviceId); if (success) { (*key_info)[QUERY_KEY_DEVICE_ID] = deviceId; } uint32_t system_id; - success = crypto_engine->GetSystemId(&system_id); + success = crypto_session.GetSystemId(&system_id); if (success) { std::ostringstream system_id_stream; system_id_stream << system_id; @@ -404,7 +379,7 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) { } std::string provisioning_id; - success = crypto_engine->GetProvisioningId(&provisioning_id); + success = crypto_session.GetProvisioningId(&provisioning_id); if (success) { (*key_info)[QUERY_KEY_PROVISIONING_ID] = provisioning_id; } @@ -416,7 +391,7 @@ CdmResponseType CdmEngine::QueryKeyStatus( const CdmSessionId& session_id, CdmQueryMap* key_info) { LOGI("CdmEngine::QueryKeyStatus"); - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s", session_id.c_str()); return KEY_ERROR; @@ -428,7 +403,7 @@ CdmResponseType CdmEngine::QueryKeyControlInfo( const CdmSessionId& session_id, CdmQueryMap* key_info) { LOGI("CdmEngine::QueryKeyControlInfo"); - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s", session_id.c_str()); return KEY_ERROR; @@ -436,63 +411,6 @@ CdmResponseType CdmEngine::QueryKeyControlInfo( return iter->second->QueryKeyControlInfo(key_info); } -/* - * The certificate provisioning process creates a cdm and a crypto session. - * The lives of these sessions are short and therefore, not added to the - * CdmSessionMap. We need to explicitly delete these objects when error occurs - * or when we are done with provisioning. - */ -void CdmEngine::CleanupProvisioningSession() { - if (provisioning_session_) { - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - if (crypto_engine) { - CdmSessionId cdm_session_id = provisioning_session_->session_id(); - CryptoSession* crypto_session = - crypto_engine->FindSession(cdm_session_id); - if (crypto_session) { - LOGV("delete crypto session for id=%s", cdm_session_id.c_str()); - delete crypto_session; - } else { - LOGE("CleanupProvisioningSession: cannot find crypto_session"); - } - } - delete provisioning_session_; - provisioning_session_ = NULL; - } -} - -/* - * This function converts SignedProvisioningRequest into base64 string. - * It then wraps it in JSON format expected by the Apiary frontend. - * Apiary requires the base64 encoding to replace '+' with minus '-', - * and '/' with underscore '_'; opposite to stubby's. - * - * Returns the JSON formated string in *request. The JSON string will be - * appended as a query parameter, i.e. signedRequest=. All base64 '=' padding chars must be removed. - * - * The JSON formated request takes the following format: - * - * base64 encoded message - */ -void CdmEngine::ComposeJsonRequestAsQueryString( - const std::string& message, - CdmProvisioningRequest* request) { - - // performs base64 encoding for message - std::vector message_vector(message.begin(), message.end()); - std::string message_b64 = Base64SafeEncode(message_vector); - - // removes trailing '=' padding characters; - // the encoded string can have at most 2 '=' padding chars, so start - // searching at the end minus four characters - size_t found_pos = message_b64.find("=", message_b64.size() - 4); - if (std::string::npos != found_pos) { - message_b64.resize(found_pos); - } - request->assign(message_b64); -} - /* * Composes a device provisioning request and output the request in JSON format * in *request. It also returns the default url for the provisioning server @@ -504,143 +422,10 @@ CdmResponseType CdmEngine::GetProvisioningRequest( CdmProvisioningRequest* request, std::string* default_url) { if (!request || !default_url) { - LOGE("GetProvisioningRequest: invalid input parameters"); + LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters"); return UNKNOWN_ERROR; } - - default_url->assign(kDefaultProvisioningServerUrl); - - if (provisioning_session_) { - CleanupProvisioningSession(); - } - - // - //--------------------------------------------------------------------------- - // This function can be called before a cdm session is created. - // First creates a cdm session, then creates a crypto session. - // - CdmSession* cdm_session = new CdmSession(); - if (!cdm_session) { - LOGE("GetProvisioningRequest: fails to create a cdm session"); - return UNKNOWN_ERROR; - } - - if (cdm_session->session_id().empty()) { - LOGE("GetProvisioningRequest: fails to generate session ID"); - delete cdm_session; - return UNKNOWN_ERROR; - } - - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - if (!crypto_engine) { - LOGE("GetProvisioningRequest: fails to create a crypto engine"); - delete cdm_session; - return UNKNOWN_ERROR; - } - - CdmSessionId cdm_session_id = cdm_session->session_id(); - CryptoSession* crypto_session = crypto_engine->CreateSession(cdm_session_id); - if (!crypto_session) { - LOGE("GetProvisioningRequest: fails to create a crypto session"); - delete cdm_session; - return UNKNOWN_ERROR; - } - - // TODO(edwinwong): replace this cdm session pointer with crypto session - // pointer if feasible - provisioning_session_ = cdm_session; - LOGV("provisioning session id=%s", cdm_session_id.c_str()); - - // - //--------------------------------------------------------------------------- - // Prepares device provisioning request. - // - ProvisioningRequest provisioning_request; - ClientIdentification* client_id = provisioning_request.mutable_client_id(); - client_id->set_type(ClientIdentification::KEYBOX); - std::string token; - if (!crypto_engine->GetToken(&token)) { - LOGE("GetProvisioningRequest: fails to get token"); - CleanupProvisioningSession(); - return UNKNOWN_ERROR; - } - client_id->set_token(token); - - uint32_t nonce; - if (!crypto_session->GenerateNonce(&nonce)) { - LOGE("GetProvisioningRequest: fails to generate a nonce"); - CleanupProvisioningSession(); - return UNKNOWN_ERROR; - } - - // The provisioning server does not convert the nonce to uint32_t, it just - // passes the binary data to the response message. - std::string the_nonce(reinterpret_cast(&nonce), sizeof(nonce)); - provisioning_request.set_nonce(the_nonce); - - std::string serialized_message; - provisioning_request.SerializeToString(&serialized_message); - - // Derives signing and encryption keys and constructs signature. - std::string request_signature; - if (!crypto_session->PrepareRequest(serialized_message, - &request_signature, true)) { - request->clear(); - CleanupProvisioningSession(); - return UNKNOWN_ERROR; - } - if (request_signature.empty()) { - request->clear(); - CleanupProvisioningSession(); - return UNKNOWN_ERROR; - } - - SignedProvisioningMessage signed_provisioning_msg; - signed_provisioning_msg.set_message(serialized_message); - signed_provisioning_msg.set_signature(request_signature); - - std::string serialized_request; - signed_provisioning_msg.SerializeToString(&serialized_request); - - // converts request into JSON string - ComposeJsonRequestAsQueryString(serialized_request, request); - - return NO_ERROR; -} - -/* - * Parses the input json_str and locates substring using start_substr and - * end_stubstr. The found base64 substring is then decoded and returns - * in *result. - * - * Returns true for success and false if fails. - */ -bool CdmEngine::ParseJsonResponse( - const CdmProvisioningResponse& json_str, - const std::string& start_substr, - const std::string& end_substr, - std::string* result) { - std::string b64_string; - size_t start = json_str.find(start_substr); - if (start == json_str.npos) { - LOGE("ParseJsonResponse: cannot find start substring"); - return false; - } else { - size_t end = json_str.find(end_substr, start + start_substr.length()); - if (end == json_str.npos) { - LOGE("ParseJsonResponse cannot locate end substring"); - return false; - } - - size_t b64_string_size = end - start - start_substr.length(); - b64_string.assign(json_str, start + start_substr.length(), b64_string_size); - } - - // Decodes base64 substring and returns it in *result - std::vector result_vector = Base64SafeDecode(b64_string); - result->assign(result_vector.begin(), result_vector.end()); - - return true; + return cert_provisioning_.GetProvisioningRequest(request, default_url); } /* @@ -653,103 +438,10 @@ bool CdmEngine::ParseJsonResponse( CdmResponseType CdmEngine::HandleProvisioningResponse( CdmProvisioningResponse& response) { if (response.empty()) { - LOGE("Empty provisioning response."); + LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response."); return UNKNOWN_ERROR; } - - //--------------------------------------------------------------------------- - // Extracts signed response from JSON string, decodes base64 signed response - const std::string kMessageStart = "\"signedResponse\": \""; - const std::string kMessageEnd = "\""; - std::string serialized_signed_response; - if (!ParseJsonResponse(response, kMessageStart, kMessageEnd, - &serialized_signed_response)) { - LOGE("Fails to extract signed serialized response from JSON response"); - return UNKNOWN_ERROR; - } - - // - //--------------------------------------------------------------------------- - // Creates a crypto session using provisioning_session_. - // - if (!provisioning_session_) { - LOGE("HandleProvisioningResponse: invalid provisioning session"); - return UNKNOWN_ERROR; - } - - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - if (!crypto_engine) { - LOGE("HandleProvisioningResponse: fails to create a crypto engine"); - return UNKNOWN_ERROR; - } - - CdmSessionId cdm_session_id = provisioning_session_->session_id(); - CryptoSession* crypto_session = crypto_engine->FindSession(cdm_session_id); - if (!crypto_session) { - LOGE("HandleProvisioningResponse: fails to find %s", - cdm_session_id.c_str()); - return UNKNOWN_ERROR; - } - - //--------------------------------------------------------------------------- - // Authenticates provisioning response using D1s (server key derived from - // the provisioing request's input). Validate provisioning response and - // stores private device RSA key and certificate. - SignedProvisioningMessage signed_response; - if (!signed_response.ParseFromString(serialized_signed_response)) { - LOGE("Fails to parse signed serialized response"); - CleanupProvisioningSession(); - return UNKNOWN_ERROR; - } - - if (!signed_response.has_signature() || !signed_response.has_message()) { - LOGE("Invalid response - signature or message not found"); - CleanupProvisioningSession(); - return UNKNOWN_ERROR; - } - - const std::string& signed_message = signed_response.message(); - ProvisioningResponse provisioning_response; - - if (!provisioning_response.ParseFromString(signed_message)) { - LOGE("Fails to parse signed message"); - CleanupProvisioningSession(); - return UNKNOWN_ERROR; - } - - if (!provisioning_response.has_device_rsa_key()) { - LOGE("Invalid response - key not found"); - CleanupProvisioningSession(); - return UNKNOWN_ERROR; - } - - const std::string& enc_rsa_key = provisioning_response.device_rsa_key(); - const std::string& nonce = provisioning_response.nonce(); - const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv(); - const std::string& signature = signed_response.signature(); - - std::string wrapped_rsa_key; - if (!crypto_session->RewrapDeviceRSAKey(signed_message, - signature, - nonce, - enc_rsa_key, - enc_rsa_key.size(), - rsa_key_iv, - &wrapped_rsa_key)) { - LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails"); - CleanupProvisioningSession(); - return UNKNOWN_ERROR; - } - - const std::string& device_certificate = provisioning_response.device_certificate(); - DeviceFiles::StoreCertificate(device_certificate, wrapped_rsa_key); - - // - //--------------------------------------------------------------------------- - // Deletes cdm and crypto sessions created for provisioning. - // - CleanupProvisioningSession(); - return NO_ERROR; + return cert_provisioning_.HandleProvisioningResponse(response); } CdmResponseType CdmEngine::GetSecureStops( @@ -776,7 +468,7 @@ CdmResponseType CdmEngine::Decrypt( void* decrypt_buffer, size_t decrypt_buffer_offset, bool is_video) { - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { LOGW("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str()); return KEY_ERROR; @@ -789,7 +481,7 @@ CdmResponseType CdmEngine::Decrypt( } bool CdmEngine::IsKeyValid(const KeyId& key_id) { - for (CdmSessionIter iter = sessions_.begin(); + for (CdmSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { if (iter->second->IsKeyValid(key_id)) { return true; @@ -802,7 +494,7 @@ bool CdmEngine::AttachEventListener( const CdmSessionId& session_id, WvCdmEventListener* listener) { - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { return false; } @@ -814,7 +506,7 @@ bool CdmEngine::DetachEventListener( const CdmSessionId& session_id, WvCdmEventListener* listener) { - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { return false; } @@ -923,28 +615,25 @@ bool CdmEngine::ExtractWidevinePssh( } void CdmEngine::EnablePolicyTimer() { - if (!policy_timer_.IsRunning()) - policy_timer_.Start(this, CDM_POLICY_TIMER_DURATION_SECONDS); + policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds); } void CdmEngine::DisablePolicyTimer() { - - if (policy_timer_.IsRunning()) + if (sessions_.size() == 0 && policy_timer_.IsRunning()) policy_timer_.Stop(); } void CdmEngine::OnTimerEvent() { - - for (CdmSessionIter iter = sessions_.begin(); + for (CdmSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { iter->second->OnTimerEvent(); } } -void CdmEngine::OnKeyReleaseEvent(CdmKeySetId key_set_id) { +void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { - for (CdmSessionIter iter = sessions_.begin(); + for (CdmSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { iter->second->OnKeyReleaseEvent(key_set_id); } diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 452d1f7b..9f0edf48 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -7,87 +7,67 @@ #include #include -#include "clock.h" #include "cdm_engine.h" -#include "crypto_engine.h" +#include "clock.h" +#include "crypto_session.h" #include "device_files.h" +#include "file_store.h" #include "log.h" -#include "openssl/sha.h" #include "properties.h" #include "string_conversions.h" #include "wv_cdm_constants.h" +#include "wv_cdm_event_listener.h" + +namespace { +const size_t kKeySetIdLength = 14; +} // namespace namespace wvcdm { typedef std::set::iterator CdmEventListenerIter; CdmResponseType CdmSession::Init() { - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - if (!crypto_engine) { - LOGE("CdmSession::Init failed to get CryptoEngine instance."); - return UNKNOWN_ERROR; - } - - crypto_session_ = crypto_engine->CreateSession(session_id_); - if (!crypto_session_) { - LOGE("CdmSession::Init crypto session creation failure"); - return UNKNOWN_ERROR; - } + scoped_ptr session(new CryptoSession()); + if (!session->Open()) return UNKNOWN_ERROR; std::string token; if (Properties::use_certificates_as_identification()) { - if (!LoadDeviceCertificate(&token, &wrapped_key_)) { - LOGE("CdmSession::Init provisioning needed"); - return NEED_PROVISIONING; - } - } - else { - if (!crypto_engine->GetToken(&token)) { - LOGE("CdmSession::Init token retrieval failure"); - return UNKNOWN_ERROR; - } + if (!LoadDeviceCertificate(&token, &wrapped_key_)) return NEED_PROVISIONING; + } else { + if (!session->GetToken(&token)) return UNKNOWN_ERROR; } - if (license_parser_.Init(token, crypto_session_, &policy_engine_)) - return NO_ERROR; - else + if (!license_parser_.Init(token, session.get(), &policy_engine_)) return UNKNOWN_ERROR; -} -CdmResponseType CdmSession::ReInit() { - DestroySession(); - return Init(); -} - -bool CdmSession::DestroySession() { - if (crypto_session_) { - delete crypto_session_; - crypto_session_ = NULL; - } - return true; + crypto_session_.reset(session.release()); + license_received_ = false; + reinitialize_session_ = false; + return NO_ERROR; } CdmResponseType CdmSession::RestoreOfflineSession( - const CdmKeySetId& key_set_id, - const CdmLicenseType license_type) { + const CdmKeySetId& key_set_id, const CdmLicenseType license_type) { key_set_id_ = key_set_id; // Retrieve license information from persistent store + File file; + DeviceFiles handle; + if (!handle.Init(&file)) return UNKNOWN_ERROR; + DeviceFiles::LicenseState license_state; - if (!DeviceFiles::RetrieveLicense(key_set_id, &license_state, - &offline_pssh_data_, - &offline_key_request_, - &offline_key_response_, - &offline_key_renewal_request_, - &offline_key_renewal_response_, - &offline_release_server_url_)) { + if (!handle.RetrieveLicense(key_set_id, &license_state, &offline_pssh_data_, + &offline_key_request_, &offline_key_response_, + &offline_key_renewal_request_, + &offline_key_renewal_response_, + &offline_release_server_url_)) { LOGE("CdmSession::Init failed to retrieve license. key set id = %s", - key_set_id.c_str()); + key_set_id.c_str()); return UNKNOWN_ERROR; } - if (license_state != DeviceFiles::kLicenseStateActive) { + if (license_state != DeviceFiles::kLicenseStateActive) { LOGE("CdmSession::Init invalid offline license state = %s", license_state); return UNKNOWN_ERROR; } @@ -117,21 +97,18 @@ bool CdmSession::VerifySession(const CdmKeySystem& key_system, } CdmResponseType CdmSession::GenerateKeyRequest( - const CdmInitData& init_data, - const CdmLicenseType license_type, - const CdmAppParameterMap& app_parameters, - CdmKeyMessage* key_request, + const CdmInitData& init_data, const CdmLicenseType license_type, + const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, std::string* server_url) { - if (reinitialize_session_) { - CdmResponseType sts = ReInit(); + CdmResponseType sts = Init(); if (sts != NO_ERROR) { + LOGW("CdmSession::GenerateKeyRequest: Reinitialization failed"); return sts; } - reinitialize_session_ = false; } - if (!crypto_session_) { + if (crypto_session_.get() == NULL) { LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session"); return UNKNOWN_ERROR; } @@ -145,17 +122,23 @@ CdmResponseType CdmSession::GenerateKeyRequest( if (license_type_ == kLicenseTypeRelease) { return GenerateReleaseRequest(key_request, server_url); - } - else if (license_received_) { // renewal - return Properties::require_explicit_renew_request() ? - UNKNOWN_ERROR : GenerateRenewalRequest(key_request, server_url); - } - else { - CdmInitData pssh_data; - if (!CdmEngine::ExtractWidevinePssh(init_data, &pssh_data)) { + } else if (license_received_) { // renewal + return Properties::require_explicit_renew_request() + ? UNKNOWN_ERROR + : GenerateRenewalRequest(key_request, server_url); + } else { + if (init_data.empty()) { + LOGW("CdmSession::GenerateKeyRequest: init data absent"); return KEY_ERROR; } + CdmInitData pssh_data = init_data; + if (Properties::extract_pssh_data()) { + if (!CdmEngine::ExtractWidevinePssh(init_data, &pssh_data)) { + return KEY_ERROR; + } + } + if (Properties::use_certificates_as_identification()) { if (!crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { reinitialize_session_ = true; @@ -163,11 +146,8 @@ CdmResponseType CdmSession::GenerateKeyRequest( } } - if (!license_parser_.PrepareKeyRequest(pssh_data, - license_type, - app_parameters, - key_request, - server_url)) { + if (!license_parser_.PrepareKeyRequest( + pssh_data, license_type, app_parameters, key_request, server_url)) { return KEY_ERROR; } @@ -182,10 +162,9 @@ CdmResponseType CdmSession::GenerateKeyRequest( } // AddKey() - Accept license response and extract key info. -CdmResponseType CdmSession::AddKey( - const CdmKeyResponse& key_response, - CdmKeySetId* key_set_id) { - if (!crypto_session_) { +CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response, + CdmKeySetId* key_set_id) { + if (crypto_session_.get() == NULL) { LOGW("CdmSession::AddKey: Invalid crypto session"); return UNKNOWN_ERROR; } @@ -197,25 +176,32 @@ CdmResponseType CdmSession::AddKey( if (license_type_ == kLicenseTypeRelease) { return ReleaseKey(key_response); - } - else if (license_received_) { // renewal - return Properties::require_explicit_renew_request() ? - UNKNOWN_ERROR : RenewKey(key_response); - } - else { + } else if (license_received_) { // renewal + return Properties::require_explicit_renew_request() + ? UNKNOWN_ERROR + : RenewKey(key_response); + } else { CdmResponseType sts = license_parser_.HandleKeyResponse(key_response); - if (sts != KEY_ADDED) - return sts; + if (sts != KEY_ADDED) return sts; license_received_ = true; if (license_type_ == kLicenseTypeOffline) { offline_key_response_ = key_response; - key_set_id_ = GenerateKeySetId(offline_pssh_data_); - if (!StoreLicense(true)) { + if (!GenerateKeySetId(&key_set_id_)) { + LOGE("CdmSession::AddKey: Unable to generate key set Id"); + return UNKNOWN_ERROR; + } + + if (!StoreLicense(DeviceFiles::kLicenseStateActive)) { LOGE("CdmSession::AddKey: Unable to store license"); - ReInit(); + CdmResponseType sts = Init(); + if (sts != NO_ERROR) { + LOGW("CdmSession::AddKey: Reinitialization failed"); + return sts; + } + key_set_id_.clear(); return UNKNOWN_ERROR; } @@ -231,7 +217,7 @@ CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) { } CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) { - if ((!crypto_session_) || (!crypto_session_->IsOpen())) + if (crypto_session_.get() == NULL || !crypto_session_->IsOpen()) return UNKNOWN_ERROR; std::stringstream ss; @@ -248,17 +234,12 @@ CdmResponseType CdmSession::CancelKeyRequest() { } // Decrypt() - Accept encrypted buffer and return decrypted data. -CdmResponseType CdmSession::Decrypt(bool is_encrypted, - bool is_secure, - const KeyId& key_id, - const uint8_t* encrypt_buffer, - size_t encrypt_length, - const std::vector& iv, - size_t block_offset, - void* decrypt_buffer, - size_t decrypt_buffer_offset, - bool is_video) { - if (!crypto_session_ || !crypto_session_->IsOpen()) +CdmResponseType CdmSession::Decrypt( + bool is_encrypted, bool is_secure, const KeyId& key_id, + const uint8_t* encrypt_buffer, size_t encrypt_length, + const std::vector& iv, size_t block_offset, void* decrypt_buffer, + size_t decrypt_buffer_offset, bool is_video) { + if (crypto_session_.get() == NULL || !crypto_session_->IsOpen()) return UNKNOWN_ERROR; // Check if key needs to be selected @@ -266,17 +247,15 @@ CdmResponseType CdmSession::Decrypt(bool is_encrypted, if (key_id_.compare(key_id) != 0) { if (crypto_session_->SelectKey(key_id)) { key_id_ = key_id; - } - else { + } else { return NEED_KEY; } } } - return crypto_session_->Decrypt(is_encrypted, is_secure, encrypt_buffer, - encrypt_length, iv, block_offset, - decrypt_buffer, decrypt_buffer_offset, - is_video); + return crypto_session_->Decrypt( + is_encrypted, is_secure, encrypt_buffer, encrypt_length, iv, block_offset, + decrypt_buffer, decrypt_buffer_offset, is_video); } // License renewal @@ -295,37 +274,34 @@ CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request, // RenewKey() - Accept renewal response and update key info. CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) { - CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(true, - key_response); - if (sts != KEY_ADDED) - return sts; + CdmResponseType sts = + license_parser_.HandleKeyUpdateResponse(true, key_response); + if (sts != KEY_ADDED) return sts; if (license_type_ == kLicenseTypeOffline) { offline_key_renewal_response_ = key_response; - if (!StoreLicense(true)) - return UNKNOWN_ERROR; + if (!StoreLicense(DeviceFiles::kLicenseStateActive)) return UNKNOWN_ERROR; } return KEY_ADDED; } CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request, std::string* server_url) { - if (license_parser_.PrepareKeyUpdateRequest(false, key_request, - server_url)) { + if (license_parser_.PrepareKeyUpdateRequest(false, key_request, server_url)) { // Mark license as being released - if (!StoreLicense(false)) - return UNKNOWN_ERROR; - - return KEY_MESSAGE; + if (StoreLicense(DeviceFiles::kLicenseStateReleasing)) return KEY_MESSAGE; } return UNKNOWN_ERROR; } // ReleaseKey() - Accept release response and release license. CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { - CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false, - key_response); - DeviceFiles::DeleteLicense(key_set_id_); + CdmResponseType sts = + license_parser_.HandleKeyUpdateResponse(false, key_response); + File file; + DeviceFiles handle; + if (handle.Init(&file)) handle.DeleteLicense(key_set_id_); + return sts; } @@ -342,51 +318,51 @@ CdmSessionId CdmSession::GenerateSessionId() { return SESSION_ID_PREFIX + IntToString(++session_num); } +bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) { + if (!key_set_id) { + LOGW("CdmSession::GenerateKeySetId: key set id destination not provided"); + return false; + } -CdmSessionId CdmSession::GenerateKeySetId(CdmInitData& pssh_data) { - Clock clock; - int64_t current_time = clock.GetCurrentTime(); - std::string key_set_id; + std::vector random_data( + (kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0); - while (key_set_id.empty()) { - int random = rand(); + File file; + DeviceFiles handle; + if (!handle.Init(&file)) return false; - std::vector hash(SHA256_DIGEST_LENGTH, 0); - SHA256_CTX sha256; - SHA256_Init(&sha256); - SHA256_Update(&sha256, pssh_data.data(), pssh_data.size()); - SHA256_Update(&sha256, ¤t_time, sizeof(int64_t)); - SHA256_Update(&sha256, &random, sizeof(random)); - SHA256_Final(&hash[0], &sha256); - for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) { - hash[i%(SHA256_DIGEST_LENGTH/4)] ^= hash[i]; - } - hash.resize(SHA256_DIGEST_LENGTH/4); - key_set_id = KEY_SET_ID_PREFIX + b2a_hex(hash); + while (key_set_id->empty()) { + if (!crypto_session_->GetRandom(&random_data[0], random_data.size())) + return false; - if (DeviceFiles::LicenseExists(key_set_id)) { // key set collision - key_set_id.clear(); + *key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data); + + // key set collision + if (handle.LicenseExists(*key_set_id)) { + key_set_id->clear(); } } - return key_set_id; + return true; } bool CdmSession::LoadDeviceCertificate(std::string* certificate, std::string* wrapped_key) { - return DeviceFiles::RetrieveCertificate(certificate, - wrapped_key); + File file; + DeviceFiles handle; + if (!handle.Init(&file)) return false; + + return handle.RetrieveCertificate(certificate, wrapped_key); } -bool CdmSession::StoreLicense(bool active) { - DeviceFiles::LicenseState state = DeviceFiles::kLicenseStateReleasing; - if (active) - state = DeviceFiles::kLicenseStateActive; +bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) { + File file; + DeviceFiles handle; + if (!handle.Init(&file)) return false; - return DeviceFiles::StoreLicense(key_set_id_, state, offline_pssh_data_, - offline_key_request_, offline_key_response_, - offline_key_renewal_request_, - offline_key_renewal_response_, - offline_release_server_url_); + return handle.StoreLicense( + key_set_id_, state, offline_pssh_data_, offline_key_request_, + offline_key_response_, offline_key_renewal_request_, + offline_key_renewal_response_, offline_release_server_url_); } bool CdmSession::AttachEventListener(WvCdmEventListener* listener) { @@ -407,22 +383,16 @@ void CdmSession::OnTimerEvent() { if (event_occurred) { for (CdmEventListenerIter iter = listeners_.begin(); iter != listeners_.end(); ++iter) { - CdmSessionId id = (*iter)->session_id(); - if (id.empty() || (id.compare(session_id_) == 0)) { - (*iter)->onEvent(session_id_, event); - } + (*iter)->onEvent(session_id_, event); } } } -void CdmSession::OnKeyReleaseEvent(CdmKeySetId key_set_id) { - if (key_set_id_.compare(key_set_id) == 0) { +void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { + if (key_set_id_ == key_set_id) { for (CdmEventListenerIter iter = listeners_.begin(); - iter != listeners_.end(); ++iter) { - CdmSessionId id = (*iter)->session_id(); - if (id.empty() || (id.compare(session_id_) == 0)) { - (*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT); - } + iter != listeners_.end(); ++iter) { + (*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 new file mode 100644 index 00000000..e7332266 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -0,0 +1,229 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "certificate_provisioning.h" +#include "device_files.h" +#include "file_store.h" +#include "license_protocol.pb.h" +#include "log.h" +#include "string_conversions.h" + +namespace { +const std::string kDefaultProvisioningServerUrl = + "https://www.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; +} + +namespace wvcdm { +// Protobuf generated classes. +using video_widevine_server::sdk::ClientIdentification; +using video_widevine_server::sdk::ProvisioningRequest; +using video_widevine_server::sdk::ProvisioningResponse; +using video_widevine_server::sdk::SignedProvisioningMessage; + +/* + * This function converts SignedProvisioningRequest into base64 string. + * It then wraps it in JSON format expected by the Apiary frontend. + * Apiary requires the base64 encoding to replace '+' with minus '-', + * and '/' with underscore '_'; opposite to stubby's. + * + * Returns the JSON formated string in *request. The JSON string will be + * appended as a query parameter, i.e. signedRequest=. All base64 '=' padding chars must be removed. + * + * The JSON formated request takes the following format: + * + * base64 encoded message + */ +void CertificateProvisioning::ComposeJsonRequestAsQueryString( + const std::string& message, + CdmProvisioningRequest* request) { + + // Performs base64 encoding for message + std::vector message_vector(message.begin(), message.end()); + std::string message_b64 = Base64SafeEncodeNoPad(message_vector); + request->assign(message_b64); +} + +/* + * Composes a device provisioning request and output the request in JSON format + * in *request. It also returns the default url for the provisioning server + * in *default_url. + * + * Returns NO_ERROR for success and UNKNOWN_ERROR if fails. + */ +CdmResponseType CertificateProvisioning::GetProvisioningRequest( + CdmProvisioningRequest* request, + std::string* default_url) { + default_url->assign(kDefaultProvisioningServerUrl); + + if (!crypto_session_.Open()) { + LOGE("GetProvisioningRequest: fails to create a crypto session"); + return UNKNOWN_ERROR; + } + + // Prepares device provisioning request. + ProvisioningRequest provisioning_request; + ClientIdentification* client_id = provisioning_request.mutable_client_id(); + client_id->set_type(ClientIdentification::KEYBOX); + std::string token; + if (!crypto_session_.GetToken(&token)) { + LOGE("GetProvisioningRequest: fails to get token"); + return UNKNOWN_ERROR; + } + client_id->set_token(token); + + uint32_t nonce; + if (!crypto_session_.GenerateNonce(&nonce)) { + LOGE("GetProvisioningRequest: fails to generate a nonce"); + return UNKNOWN_ERROR; + } + + // The provisioning server does not convert the nonce to uint32_t, it just + // passes the binary data to the response message. + std::string the_nonce(reinterpret_cast(&nonce), sizeof(nonce)); + provisioning_request.set_nonce(the_nonce); + + std::string serialized_message; + provisioning_request.SerializeToString(&serialized_message); + + // Derives signing and encryption keys and constructs signature. + std::string request_signature; + if (!crypto_session_.PrepareRequest(serialized_message, true, + &request_signature)) { + LOGE("GetProvisioningRequest: fails to prepare request"); + return UNKNOWN_ERROR; + } + if (request_signature.empty()) { + LOGE("GetProvisioningRequest: request signature is empty"); + return UNKNOWN_ERROR; + } + + SignedProvisioningMessage signed_provisioning_msg; + signed_provisioning_msg.set_message(serialized_message); + signed_provisioning_msg.set_signature(request_signature); + + std::string serialized_request; + signed_provisioning_msg.SerializeToString(&serialized_request); + + // Converts request into JSON string + ComposeJsonRequestAsQueryString(serialized_request, request); + return NO_ERROR; +} + +/* + * Parses the input json_str and locates substring using start_substr and + * end_stubstr. The found base64 substring is then decoded and returns + * in *result. + * + * Returns true for success and false if fails. + */ +bool CertificateProvisioning::ParseJsonResponse( + const CdmProvisioningResponse& json_str, + const std::string& start_substr, + const std::string& end_substr, + std::string* result) { + std::string b64_string; + size_t start = json_str.find(start_substr); + if (start == json_str.npos) { + LOGE("ParseJsonResponse: cannot find start substring"); + return false; + } + size_t end = json_str.find(end_substr, start + start_substr.length()); + if (end == json_str.npos) { + LOGE("ParseJsonResponse cannot locate end substring"); + return false; + } + + size_t b64_string_size = end - start - start_substr.length(); + b64_string.assign(json_str, start + start_substr.length(), b64_string_size); + + // Decodes base64 substring and returns it in *result + std::vector result_vector = Base64SafeDecode(b64_string); + result->assign(result_vector.begin(), result_vector.end()); + + return true; +} + +/* + * The response message consists of a device certificate and the device RSA key. + * The device RSA key is stored in the T.E.E. The device certificate is stored + * in the device. + * + * Returns NO_ERROR for success and UNKNOWN_ERROR if fails. + */ +CdmResponseType CertificateProvisioning::HandleProvisioningResponse( + CdmProvisioningResponse& response) { + + // Extracts signed response from JSON string, decodes base64 signed response + const std::string kMessageStart = "\"signedResponse\": \""; + const std::string kMessageEnd = "\""; + std::string serialized_signed_response; + if (!ParseJsonResponse(response, kMessageStart, kMessageEnd, + &serialized_signed_response)) { + LOGE("Fails to extract signed serialized response from JSON response"); + return UNKNOWN_ERROR; + } + + // Authenticates provisioning response using D1s (server key derived from + // the provisioing request's input). Validate provisioning response and + // stores private device RSA key and certificate. + SignedProvisioningMessage signed_response; + if (!signed_response.ParseFromString(serialized_signed_response)) { + LOGE("HandleProvisioningResponse: fails to parse signed response"); + return UNKNOWN_ERROR; + } + + if (!signed_response.has_signature() || !signed_response.has_message()) { + LOGE("HandleProvisioningResponse: signature or message not found"); + return UNKNOWN_ERROR; + } + + const std::string& signed_message = signed_response.message(); + ProvisioningResponse provisioning_response; + + if (!provisioning_response.ParseFromString(signed_message)) { + LOGE("HandleProvisioningResponse: Fails to parse signed message"); + return UNKNOWN_ERROR; + } + + if (!provisioning_response.has_device_rsa_key()) { + LOGE("HandleProvisioningResponse: key not found"); + return UNKNOWN_ERROR; + } + + const std::string& enc_rsa_key = provisioning_response.device_rsa_key(); + const std::string& nonce = provisioning_response.nonce(); + const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv(); + const std::string& signature = signed_response.signature(); + std::string wrapped_rsa_key; + if (!crypto_session_.RewrapDeviceRSAKey(signed_message, + signature, + nonce, + enc_rsa_key, + rsa_key_iv, + &wrapped_rsa_key)){ + LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails"); + return UNKNOWN_ERROR; + } + + crypto_session_.Close(); + + const std::string& device_certificate = + provisioning_response.device_certificate(); + + File file; + DeviceFiles handle; + if (!handle.Init(&file)) { + LOGE("HandleProvisioningResponse: failed to init DeviceFiles"); + return UNKNOWN_ERROR; + } + if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) { + LOGE("HandleProvisioningResponse: failed to save provisioning certificate"); + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/crypto_engine.cpp b/libwvdrmengine/cdm/core/src/crypto_engine.cpp deleted file mode 100644 index 76425d94..00000000 --- a/libwvdrmengine/cdm/core/src/crypto_engine.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Crypto - wrapper classes for OEMCrypto interface -// - -#include "crypto_engine.h" - -#include // TODO(fredgc): Add ntoh to wv_cdm_utilities.h -#include -#include - -#include "log.h" -#include "OEMCryptoCENC.h" -#include "properties.h" -#include "wv_cdm_constants.h" - -namespace wvcdm { - -CryptoEngine* CryptoEngine::crypto_engine_ = NULL; -Lock CryptoEngine::crypto_engine_lock_; - -// wrapper classes for OEMCrypto interface -// CryptoEngine -- top-level interface -// CryptoSession -- session-specific interface -// CryptoKey -- key interface - -// CryptoEngine methods - -CryptoEngine::CryptoEngine() : initialized_(false) {} - -CryptoEngine::~CryptoEngine() { - if (initialized_) { - Terminate(); - } - - CryptoSessionMap::iterator i(sessions_.begin()); - for (; i != sessions_.end(); ++i) - delete i->second; - sessions_.clear(); -} - -// get the instance of OEMCrypto Client -CryptoEngine* CryptoEngine::GetInstance() { - if (NULL == crypto_engine_) { - crypto_engine_ = CreateSingleton(); - } - return crypto_engine_; -} - -CryptoEngine* CryptoEngine::CreateSingleton() { - AutoLock auto_lock(crypto_engine_lock_); - if (NULL == crypto_engine_) { - crypto_engine_ = new CryptoEngine; - } - return crypto_engine_; -} - -void CryptoEngine::DeleteInstance() { - if (NULL != crypto_engine_) { - delete crypto_engine_; - LOGV("CryptoEngine::DeleteInstance"); - crypto_engine_ = NULL; - } -} - -bool CryptoEngine::Init() { - LOGV("CryptoEngine::Init: Lock"); - AutoLock auto_lock(crypto_lock_); - if (!initialized_) { - OEMCryptoResult result = OEMCrypto_Initialize(); - initialized_ = (OEMCrypto_SUCCESS == result); - } - return initialized_; -} - -bool CryptoEngine::Terminate() { - DestroySessions(); - LOGV("CryptoEngine::Terminate: Lock"); - AutoLock auto_lock(crypto_lock_); - OEMCryptoResult result = OEMCrypto_Terminate(); - if (OEMCrypto_SUCCESS == result) { - initialized_ = false; - } - return !initialized_; -} - -bool CryptoEngine::ValidateKeybox() { - LOGV("CryptoEngine::ValidateKeybox: Lock"); - AutoLock auto_lock(crypto_lock_); - OEMCryptoResult result = OEMCrypto_IsKeyboxValid(); - return (OEMCrypto_SUCCESS == result); -} - -CryptoSession* CryptoEngine::CreateSession(const CdmSessionId& session_id) { - LOGV("CryptoEngine::CreateSession: SLock"); - AutoLock auto_lock(sessions_lock_); - if (0 == sessions_.size()) { - if (!Init()) { - return NULL; - } - } - - CryptoSessionMap::iterator it = sessions_.find(session_id); - if (it != sessions_.end()) { - LOGE("CryptoEngine::CreateSession : Duplicate session ID."); - return NULL; - } - - CryptoSession* new_session = new CryptoSession(session_id); - if (!new_session) { - return NULL; - } - - if (!new_session->Open()) { - delete new_session; - return NULL; - } - - sessions_[session_id] = new_session; - return new_session; -} - -CryptoSession* CryptoEngine::FindSessionInternal( - const CdmSessionId& session_id) { - // must hold sessions_lock_ - CryptoSessionMap::iterator it = sessions_.find(session_id); - if (it != sessions_.end()) { - return it->second; - } - return NULL; -} - -CryptoSession* CryptoEngine::FindSession(const CdmSessionId& session_id) { - LOGV("CryptoEngine::FindSession: SLock"); - AutoLock auto_lock(sessions_lock_); - return FindSessionInternal(session_id); -} - -bool CryptoEngine::DestroySession(const CdmSessionId& session_id) { - LOGV("CryptoEngine::DestroySession: SLock"); - AutoLock auto_lock(sessions_lock_); - if (0 == sessions_.size()) { - return false; - } - CryptoSession* session = FindSessionInternal(session_id); - if (session) { - delete session; - sessions_.erase(session_id); - return true; - } else { - return false; - } -} - -bool CryptoEngine::DestroySessions() { - for (CryptoSessionMap::iterator it = sessions_.begin(); - it != sessions_.end(); ++it) { - delete it->second; - } - sessions_.clear(); - return true; -} - -bool CryptoEngine::GetToken(std::string* token) { - LOGV("CryptoEngine::GetToken: Lock"); - AutoLock auto_lock(crypto_lock_); - if (!token) { - LOGE("CryptoEngine::GetToken : No token passed to method."); - return false; - } - uint8_t buf[KEYBOX_KEY_DATA_SIZE]; - size_t bufSize = sizeof(buf); - OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize); - if (OEMCrypto_SUCCESS != sts) { - return false; - } - token->assign((const char*)buf, bufSize); - return true; -} - -CryptoEngine::SecurityLevel CryptoEngine::GetSecurityLevel() { - if (!Init()) - return kSecurityLevelUnknown; - - LOGV("CryptoEngine::GetSecurityLevel: Lock"); - AutoLock auto_lock(crypto_lock_); - - std::string security_level = OEMCrypto_SecurityLevel(); - - if ((security_level.size() != 2) || - (security_level.at(0) != 'L')) { - return kSecurityLevelUnknown; - } - - switch (security_level.at(1)) { - case '1': return kSecurityLevelL1; - case '2': return kSecurityLevelL2; - case '3': return kSecurityLevelL3; - default : return kSecurityLevelUnknown; - } - - return kSecurityLevelUnknown; -} - -bool CryptoEngine::GetDeviceUniqueId(std::string* deviceId) { - if (!Init()) - return false; - - if (!deviceId) { - LOGE("CryptoEngine::GetDeviceUniqueId : No buffer passed to method."); - return false; - } - - LOGV("CryptoEngine::GetDeviceUniqueId: Lock"); - AutoLock auto_lock(crypto_lock_); - - std::vector id; - size_t idLength = 32; - - id.resize(idLength); - - OEMCryptoResult sts = OEMCrypto_GetDeviceID(&id[0], &idLength); - - if (OEMCrypto_SUCCESS != sts) { - return false; - } - - *deviceId = reinterpret_cast(&id[0]); - return true; -} - -bool CryptoEngine::GetSystemId(uint32_t* systemId) { - if (!Init()) - return false; - - if (!systemId) { - LOGE("CryptoEngine::GetSystemId : No buffer passed to method."); - return false; - } - - LOGV("CryptoEngine::GetSystemId: Lock"); - AutoLock auto_lock(crypto_lock_); - - uint8_t buf[KEYBOX_KEY_DATA_SIZE]; - size_t bufSize = sizeof(buf); - - OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize); - - if (OEMCrypto_SUCCESS != sts) { - return false; - } - - // Decode 32-bit int encoded as network-byte-order byte array starting at - // index 4. - uint32_t* id = reinterpret_cast(&buf[4]); - - *systemId = ntohl(*id); - return true; -} - -bool CryptoEngine::GetProvisioningId(std::string* provisioningId) { - if (!Init()) - return false; - - if (!provisioningId) { - LOGE("CryptoEngine::GetProvisioningId : No buffer passed to method."); - return false; - } - - LOGV("CryptoEngine::GetProvisioningId: Lock"); - AutoLock auto_lock(crypto_lock_); - - uint8_t buf[KEYBOX_KEY_DATA_SIZE]; - size_t bufSize = sizeof(buf); - - OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize); - - if (OEMCrypto_SUCCESS != sts) { - return false; - } - - provisioningId->assign(reinterpret_cast(&buf[8]), 16); - return true; -} - -}; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index d0323cb8..83be8476 100755 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -5,9 +5,10 @@ #include "crypto_session.h" +#include // TODO(fredgc): Add ntoh to wv_cdm_utilities.h #include -#include "crypto_engine.h" +#include "crypto_key.h" #include "log.h" // TODO(gmorgan,jtinker): decide if OEMCryptoCENC is needed here. #include "OEMCryptoCENC.h" @@ -21,95 +22,226 @@ std::string EncodeUint32(unsigned int u) { std::string s; s.append(1, (u >> 24) & 0xFF); s.append(1, (u >> 16) & 0xFF); - s.append(1, (u >> 8) & 0xFF); - s.append(1, (u >> 0) & 0xFF); + s.append(1, (u >> 8) & 0xFF); + s.append(1, (u >> 0) & 0xFF); return s; } } namespace wvcdm { -// wrapper classes for OEMCrypto interface -// CryptoEngine -- top-level interface -// CryptoSession -- session-specific interface -// CryptoKey -- key interface +Lock CryptoSession::crypto_lock_; +bool CryptoSession::initialized_ = false; +int CryptoSession::session_count_ = 0; -// CryptoSession methods - -CryptoSession::CryptoSession() : - valid_(false), - open_(false), - is_destination_buffer_type_valid_(false) {} - -CryptoSession::CryptoSession(const std::string& sname) : - valid_(true), - open_(false), - cdm_session_id_(sname), - is_destination_buffer_type_valid_(false) {} +CryptoSession::CryptoSession() + : open_(false), is_destination_buffer_type_valid_(false) { + Init(); +} CryptoSession::~CryptoSession() { if (open_) { Close(); } - LOGV("CryptoSession::dtor: SLock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - crypto_engine->sessions_.erase(cdm_session_id_); - if (0 == crypto_engine->sessions_.size()) { - crypto_engine->DeleteInstance(); + Terminate(); +} + +void CryptoSession::Init() { + LOGV("CryptoSession::Init"); + AutoLock auto_lock(crypto_lock_); + session_count_ += 1; + if (initialized_) return; + OEMCryptoResult sts = OEMCrypto_Initialize(); + if (OEMCrypto_SUCCESS != sts) { + LOGE("OEMCrypto_Initialize failed: %d", sts); + return; + } + initialized_ = true; +} + +void CryptoSession::Terminate() { + LOGV("CryptoSession::Terminate"); + AutoLock auto_lock(crypto_lock_); + session_count_ -= 1; + if (session_count_ > 0 || !initialized_) return; + OEMCryptoResult sts = OEMCrypto_Terminate(); + if (OEMCrypto_SUCCESS != sts) { + LOGE("OEMCrypto_Terminate failed: %d", sts); + } + initialized_ = false; +} + +bool CryptoSession::ValidateKeybox() { + LOGV("CryptoSession::ValidateKeybox: Lock"); + AutoLock auto_lock(crypto_lock_); + if (!initialized_) { + return false; + } + OEMCryptoResult result = OEMCrypto_IsKeyboxValid(); + return (OEMCrypto_SUCCESS == result); +} + +bool CryptoSession::GetToken(std::string* token) { + if (!token) { + LOGE("CryptoSession::GetToken : No token passed to method."); + return false; + } + uint8_t buf[KEYBOX_KEY_DATA_SIZE]; + size_t bufSize = sizeof(buf); + LOGV("CryptoSession::GetToken: Lock"); + AutoLock auto_lock(crypto_lock_); + if (!initialized_) { + return false; + } + OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize); + if (OEMCrypto_SUCCESS != sts) { + return false; + } + token->assign((const char*)buf, (size_t)bufSize); + return true; +} + +CryptoSession::SecurityLevel CryptoSession::GetSecurityLevel() { + LOGV("CryptoSession::GetSecurityLevel: Lock"); + AutoLock auto_lock(crypto_lock_); + if (!initialized_) { + return kSecurityLevelUninitialized; + } + std::string security_level = OEMCrypto_SecurityLevel(); + + if ((security_level.size() != 2) || (security_level.at(0) != 'L')) { + return kSecurityLevelUnknown; } - CryptoKeyMap::iterator i(keys_.begin()); - for (; i != keys_.end(); ++i) - delete i->second; - keys_.clear(); + switch (security_level.at(1)) { + case '1': + return kSecurityLevelL1; + case '2': + return kSecurityLevelL2; + case '3': + return kSecurityLevelL3; + default: + return kSecurityLevelUnknown; + } + + return kSecurityLevelUnknown; +} + +bool CryptoSession::GetDeviceUniqueId(std::string* device_id) { + if (!device_id) { + LOGE("CryptoSession::GetDeviceUniqueId : No buffer passed to method."); + return false; + } + + std::vector id; + size_t id_length = 32; + + id.resize(id_length); + + LOGV("CryptoSession::GetDeviceUniqueId: Lock"); + AutoLock auto_lock(crypto_lock_); + if (!initialized_) { + return false; + } + OEMCryptoResult sts = OEMCrypto_GetDeviceID(&id[0], &id_length); + + if (OEMCrypto_SUCCESS != sts) { + return false; + } + + *device_id = reinterpret_cast(&id[0]); + return true; +} + +bool CryptoSession::GetSystemId(uint32_t* system_id) { + if (!system_id) { + LOGE("CryptoSession::GetSystemId : No buffer passed to method."); + return false; + } + + uint8_t buf[KEYBOX_KEY_DATA_SIZE]; + size_t buf_size = sizeof(buf); + + LOGV("CryptoSession::GetSystemId: Lock"); + AutoLock auto_lock(crypto_lock_); + if (!initialized_) { + return false; + } + OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size); + + if (OEMCrypto_SUCCESS != sts) { + return false; + } + + // Decode 32-bit int encoded as network-byte-order byte array starting at + // index 4. + uint32_t* id = reinterpret_cast(&buf[4]); + + *system_id = ntohl(*id); + return true; +} + +bool CryptoSession::GetProvisioningId(std::string* provisioning_id) { + if (!provisioning_id) { + LOGE("CryptoSession::GetProvisioningId : No buffer passed to method."); + return false; + } + + uint8_t buf[KEYBOX_KEY_DATA_SIZE]; + size_t buf_size = sizeof(buf); + + LOGV("CryptoSession::GetProvisioningId: Lock"); + AutoLock auto_lock(crypto_lock_); + if (!initialized_) { + return false; + } + OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size); + + if (OEMCrypto_SUCCESS != sts) { + return false; + } + + provisioning_id->assign(reinterpret_cast(&buf[8]), 16); + return true; } bool CryptoSession::Open() { LOGV("CryptoSession::Open: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); + AutoLock auto_lock(crypto_lock_); + if (!initialized_) return false; + if (open_) return true; + OEMCrypto_SESSION sid; - OEMCryptoResult sts; - if (open_) - return false; - sts = OEMCrypto_OpenSession(&sid); - if (OEMCrypto_SUCCESS != sts) { - open_ = false; - } else { + if (OEMCrypto_SUCCESS == OEMCrypto_OpenSession(&sid)) { oec_session_id_ = static_cast(sid); - LOGV("OpenSession: id= %ld", (uint32_t) oec_session_id_); + LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_); open_ = true; } return open_; } void CryptoSession::Close() { - LOGV("CryptoSession::Close: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); - LOGV("CloseSession: id=%ld open=%s", (uint32_t) oec_session_id_, open_? "true" : "false") ; - if (open_) { - OEMCryptoResult sts = OEMCrypto_CloseSession(oec_session_id_); - if (OEMCrypto_SUCCESS == sts) { - open_ = false; - } + LOGV("CloseSession: id=%ld open=%s", (uint32_t)oec_session_id_, + open_ ? "true" : "false"); + AutoLock auto_lock(crypto_lock_); + if (!open_) return; + if (OEMCrypto_SUCCESS == OEMCrypto_CloseSession(oec_session_id_)) { + open_ = false; } } void CryptoSession::GenerateRequestId(std::string& req_id_str) { LOGV("CryptoSession::GenerateRequestId: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); + AutoLock auto_lock(crypto_lock_); // TODO(gmorgan): Get unique ID from OEMCrypto req_id_str.assign("987654321"); } bool CryptoSession::PrepareRequest(const std::string& message, - std::string* signature, - bool is_provisioning) { + bool is_provisioning, + std::string* signature) { LOGV("CryptoSession::PrepareRequest: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); + AutoLock auto_lock(crypto_lock_); if (!signature) { LOGE("CryptoSession::PrepareRequest : No output destination provided."); @@ -117,15 +249,11 @@ bool CryptoSession::PrepareRequest(const std::string& message, } if (!Properties::use_certificates_as_identification() || is_provisioning) { - if (!GenerateDerivedKeys(message)) - return false; + if (!GenerateDerivedKeys(message)) return false; - if (!GenerateSignature(message, signature, false)) - return false; - } - else { - if (!GenerateSignature(message, signature, true)) - return false; + if (!GenerateSignature(message, false, signature)) return false; + } else { + if (!GenerateSignature(message, true, signature)) return false; } return true; @@ -134,15 +262,15 @@ bool CryptoSession::PrepareRequest(const std::string& message, bool CryptoSession::PrepareRenewalRequest(const std::string& message, std::string* signature) { LOGV("CryptoSession::PrepareRenewalRequest: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); + AutoLock auto_lock(crypto_lock_); if (!signature) { - LOGE("CryptoSession::PrepareRenewalRequest : No output destination provided."); + LOGE("CryptoSession::PrepareRenewalRequest : No output destination " + "provided."); return false; } - if (!GenerateSignature(message, signature, false)) { + if (!GenerateSignature(message, false, signature)) { return false; } @@ -162,13 +290,14 @@ void CryptoSession::GenerateMacContext(const std::string& input_context, deriv_context->assign(kSigningKeyLabel); deriv_context->append(1, '\0'); deriv_context->append(input_context); - deriv_context->append(EncodeUint32(kSigningKeySizeBits*2)); + deriv_context->append(EncodeUint32(kSigningKeySizeBits * 2)); } void CryptoSession::GenerateEncryptContext(const std::string& input_context, std::string* deriv_context) { if (!deriv_context) { - LOGE("CryptoSession::GenerateEncryptContext : No output destination provided."); + LOGE("CryptoSession::GenerateEncryptContext : No output destination " + "provided."); return; } @@ -193,12 +322,10 @@ size_t CryptoSession::GetOffset(std::string message, std::string field) { bool CryptoSession::LoadKeys(const std::string& message, const std::string& signature, const std::string& mac_key_iv, - const std::string& mac_key, - int num_keys, + const std::string& mac_key, int num_keys, const CryptoKey* key_array) { LOGV("CryptoSession::LoadKeys: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); + AutoLock auto_lock(crypto_lock_); const uint8_t* msg = reinterpret_cast(message.data()); const uint8_t* enc_mac_key = NULL; @@ -208,7 +335,7 @@ bool CryptoSession::LoadKeys(const std::string& message, enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); } std::vector load_key_array(num_keys); - for (int i=0; ikey_id = msg + GetOffset(message, ki->key_id()); @@ -219,30 +346,28 @@ bool CryptoSession::LoadKeys(const std::string& message, if (ki->HasKeyControl()) { ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv()); ko->key_control = msg + GetOffset(message, ki->key_control()); - } - else { - LOGE("For key %d: XXX key has no control block. size=%d", i, ki->key_control().size()); + } else { + LOGE("For key %d: XXX key has no control block. size=%d", i, + ki->key_control().size()); ko->key_control_iv = NULL; ko->key_control = NULL; } } - LOGV("LoadKeys: id=%ld", (uint32_t) oec_session_id_); - return (OEMCrypto_SUCCESS == OEMCrypto_LoadKeys( - oec_session_id_, msg, message.size(), - reinterpret_cast(signature.data()), - signature.size(), enc_mac_key_iv, enc_mac_key, - num_keys, &load_key_array[0])); + LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_); + return (OEMCrypto_SUCCESS == + OEMCrypto_LoadKeys(oec_session_id_, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), enc_mac_key_iv, enc_mac_key, + num_keys, &load_key_array[0])); } bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { LOGV("CryptoSession::LoadKeys: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); + AutoLock auto_lock(crypto_lock_); - LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t) oec_session_id_); + LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_); OEMCryptoResult sts = OEMCrypto_LoadDeviceRSAKey( - oec_session_id_, - reinterpret_cast(wrapped_key.data()), + oec_session_id_, reinterpret_cast(wrapped_key.data()), wrapped_key.size()); if (OEMCrypto_SUCCESS != sts) { @@ -254,16 +379,14 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { } bool CryptoSession::RefreshKeys(const std::string& message, - const std::string& signature, - int num_keys, + const std::string& signature, int num_keys, const CryptoKey* key_array) { LOGV("CryptoSession::RefreshKeys: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); + AutoLock auto_lock(crypto_lock_); const uint8_t* msg = reinterpret_cast(message.data()); std::vector load_key_array(num_keys); - for (int i=0; ikey_id().empty()) { @@ -278,30 +401,28 @@ bool CryptoSession::RefreshKeys(const std::string& message, ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv()); } ko->key_control = msg + GetOffset(message, ki->key_control()); - } - else { + } else { ko->key_control_iv = NULL; ko->key_control = NULL; } } LOGV("RefreshKeys: id=%ld", static_cast(oec_session_id_)); - return (OEMCrypto_SUCCESS == OEMCrypto_RefreshKeys( - oec_session_id_, msg, message.size(), - reinterpret_cast(signature.data()), - signature.size(), - num_keys, &load_key_array[0])); + return ( + OEMCrypto_SUCCESS == + OEMCrypto_RefreshKeys(oec_session_id_, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), num_keys, &load_key_array[0])); } bool CryptoSession::SelectKey(const std::string& key_id) { LOGV("CryptoSession::SelectKey: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); + AutoLock auto_lock(crypto_lock_); const uint8_t* key_id_string = reinterpret_cast(key_id.data()); LOGV("SelectKey: id=%ld", static_cast(oec_session_id_)); - OEMCryptoResult sts = OEMCrypto_SelectKey(oec_session_id_, key_id_string, - key_id.size()); + OEMCryptoResult sts = + OEMCrypto_SelectKey(oec_session_id_, key_id_string, key_id.size()); if (OEMCrypto_SUCCESS != sts) { return false; } @@ -314,7 +435,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message) { GenerateMacContext(message, &mac_deriv_message); GenerateEncryptContext(message, &enc_deriv_message); - LOGV("GenerateDerivedKeys: id=%ld", (uint32_t) oec_session_id_); + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); OEMCryptoResult sts = OEMCrypto_GenerateDerivedKeys( oec_session_id_, reinterpret_cast(mac_deriv_message.data()), @@ -337,10 +458,9 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message, GenerateMacContext(message, &mac_deriv_message); GenerateEncryptContext(message, &enc_deriv_message); - LOGV("GenerateDerivedKeys: id=%ld", (uint32_t) oec_session_id_); + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); OEMCryptoResult sts = OEMCrypto_DeriveKeysFromSessionKey( - oec_session_id_, - reinterpret_cast(session_key.data()), + oec_session_id_, reinterpret_cast(session_key.data()), session_key.size(), reinterpret_cast(mac_deriv_message.data()), mac_deriv_message.size(), @@ -355,51 +475,43 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message, return true; } -bool CryptoSession::GenerateSignature(const std::string& message, - std::string* signature, - bool use_rsa) { - LOGV("GenerateSignature: id=%ld", (uint32_t) oec_session_id_); - if (!signature) - return false; +bool CryptoSession::GenerateSignature(const std::string& message, bool use_rsa, + std::string* signature) { + LOGV("GenerateSignature: id=%ld", (uint32_t)oec_session_id_); + if (!signature) return false; size_t length = 0; - OEMCryptoResult sts; + OEMCryptoResult sts = OEMCrypto_SUCCESS; if (use_rsa) { sts = OEMCrypto_GenerateRSASignature( - oec_session_id_, - reinterpret_cast(message.data()), - message.size(), - NULL, - &length); - } - else { + oec_session_id_, reinterpret_cast(message.data()), + message.size(), NULL, &length); + if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { + LOGD("GenerateSignature: OEMCrypto_GenerateRSASignature err=%d", sts); + return false; + } + } else { + length = kSignatureSize; + // TODO(gmorgan,kqyang): Use OEMCrypto_GenerateSignature to determine + // length after marvell fixes their implementation. + /* sts = OEMCrypto_GenerateSignature( - oec_session_id_, - reinterpret_cast(message.data()), - message.size(), - NULL, - &length); - } - - if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { - LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); - return false; + oec_session_id_, reinterpret_cast(message.data()), + message.size(), NULL, &length); + */ } signature->resize(length); if (use_rsa) { sts = OEMCrypto_GenerateRSASignature( - oec_session_id_, - reinterpret_cast(message.data()), + oec_session_id_, reinterpret_cast(message.data()), message.size(), reinterpret_cast(const_cast(signature->data())), &length); - } - else { + } else { sts = OEMCrypto_GenerateSignature( - oec_session_id_, - reinterpret_cast(message.data()), + oec_session_id_, reinterpret_cast(message.data()), message.size(), reinterpret_cast(const_cast(signature->data())), &length); @@ -410,29 +522,24 @@ bool CryptoSession::GenerateSignature(const std::string& message, return false; } - // TODO(fredgc): remove in K, when L1 library reports correct length. + // TODO(fredgc): b/8878371 + // remove in K, when L1 library reports correct length. signature->resize(length); return true; } -CdmResponseType CryptoSession::Decrypt(bool is_encrypted, - bool is_secure, - const uint8_t* encrypt_buffer, - size_t encrypt_length, - const std::vector& iv, - size_t block_offset, - void* decrypt_buffer, - size_t decrypt_buffer_offset, - bool is_video) { +CdmResponseType CryptoSession::Decrypt( + bool is_encrypted, bool is_secure, const uint8_t* encrypt_buffer, + size_t encrypt_length, const std::vector& iv, size_t block_offset, + void* decrypt_buffer, size_t decrypt_buffer_offset, bool is_video) { if (!is_destination_buffer_type_valid_) { - if (!SetDestinationBufferType()) - return UNKNOWN_ERROR; + if (!SetDestinationBufferType()) return UNKNOWN_ERROR; } OEMCrypto_DestBufferDesc buffer_descriptor; buffer_descriptor.type = - is_secure ? destination_buffer_type_: OEMCrypto_BufferType_Clear; + is_secure ? destination_buffer_type_ : OEMCrypto_BufferType_Clear; switch (buffer_descriptor.type) { case OEMCrypto_BufferType_Clear: @@ -452,13 +559,8 @@ CdmResponseType CryptoSession::Decrypt(bool is_encrypted, } OEMCryptoResult sts = OEMCrypto_DecryptCTR( - oec_session_id_, - encrypt_buffer, - encrypt_length, - is_encrypted, - &iv[0], - block_offset, - &buffer_descriptor, + oec_session_id_, encrypt_buffer, encrypt_length, is_encrypted, &iv[0], + block_offset, &buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); if (OEMCrypto_SUCCESS != sts) { @@ -474,29 +576,23 @@ bool CryptoSession::GenerateNonce(uint32_t* nonce) { } LOGV("CryptoSession::GenerateNonce: Lock"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); - return(OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(oec_session_id_, nonce)); + AutoLock auto_lock(crypto_lock_); + + return (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(oec_session_id_, nonce)); } bool CryptoSession::SetDestinationBufferType() { - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - if (Properties::oem_crypto_use_secure_buffers()) { - if (crypto_engine->GetSecurityLevel() == CryptoEngine::kSecurityLevelL1) { + if (GetSecurityLevel() == CryptoSession::kSecurityLevelL1) { destination_buffer_type_ = OEMCrypto_BufferType_Secure; - } - else { + } else { destination_buffer_type_ = OEMCrypto_BufferType_Clear; } - } - else if (Properties::oem_crypto_use_fifo()) { + } else if (Properties::oem_crypto_use_fifo()) { destination_buffer_type_ = OEMCrypto_BufferType_Direct; - } - else if (Properties::oem_crypto_use_userspace_buffers()) { + } else if (Properties::oem_crypto_use_userspace_buffers()) { destination_buffer_type_ = OEMCrypto_BufferType_Clear; - } - else { + } else { return false; } @@ -508,14 +604,10 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, const std::string& signature, const std::string& nonce, const std::string& enc_rsa_key, - size_t enc_rsa_key_length, const std::string& rsa_key_iv, std::string* wrapped_rsa_key) { - LOGV("CryptoSession::RewrapDeviceRSAKey: Lock+++"); - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - AutoLock auto_lock(crypto_engine->crypto_lock_); - - LOGV("crypto session id=%ld", static_cast(oec_session_id_)); + LOGD("CryptoSession::RewrapDeviceRSAKey, session id=%ld", + static_cast(oec_session_id_)); const uint8_t* signed_msg = reinterpret_cast(message.data()); const uint8_t* msg_rsa_key = NULL; @@ -524,21 +616,17 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, if (enc_rsa_key.size() >= MAC_KEY_SIZE && rsa_key_iv.size() >= KEY_IV_SIZE) { msg_rsa_key = signed_msg + GetOffset(message, enc_rsa_key); msg_rsa_key_iv = signed_msg + GetOffset(message, rsa_key_iv); - msg_nonce = reinterpret_cast( - signed_msg + GetOffset(message, nonce)); + msg_nonce = reinterpret_cast(signed_msg + + GetOffset(message, nonce)); } // Gets wrapped_rsa_key_length by passing NULL as uint8_t* wrapped_rsa_key // and 0 as wrapped_rsa_key_length. size_t wrapped_rsa_key_length = 0; OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey( - oec_session_id_, - signed_msg, message.size(), + oec_session_id_, signed_msg, message.size(), reinterpret_cast(signature.data()), signature.size(), - msg_nonce, - msg_rsa_key, enc_rsa_key_length, - msg_rsa_key_iv, - NULL, + msg_nonce, msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv, NULL, &wrapped_rsa_key_length); if (status != OEMCrypto_ERROR_SHORT_BUFFER) { LOGE("OEMCrypto_RewrapDeviceRSAKey fails to get wrapped_rsa_key_length"); @@ -547,20 +635,16 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, wrapped_rsa_key->resize(wrapped_rsa_key_length); status = OEMCrypto_RewrapDeviceRSAKey( - oec_session_id_, - signed_msg, - message.size(), - reinterpret_cast(signature.data()), - signature.size(), - msg_nonce, - msg_rsa_key, - enc_rsa_key_length, - msg_rsa_key_iv, - reinterpret_cast(const_cast(wrapped_rsa_key->data())), + oec_session_id_, signed_msg, message.size(), + reinterpret_cast(signature.data()), signature.size(), + msg_nonce, msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv, + reinterpret_cast(&(*wrapped_rsa_key)[0]), &wrapped_rsa_key_length); - // TODO(fredgc): remove in K, when L1 library reports correct length. + // TODO(fredgc): b/8878371 + // remove in K, when L1 library reports correct length. wrapped_rsa_key->resize(wrapped_rsa_key_length); + if (OEMCrypto_SUCCESS != status) { LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status); return false; @@ -569,4 +653,15 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, return true; } +bool CryptoSession::GetRandom(uint8_t* random_data, size_t data_length) { + OEMCryptoResult sts = OEMCrypto_GetRandom(random_data, data_length); + + if (sts != OEMCrypto_SUCCESS) { + LOGE("OEMCrypto_GetRandom fails with %d", sts); + return false; + } + + return true; +} + }; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index 9d4ca854..e76ec528 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -4,21 +4,12 @@ #include #include -#include -#include #include "device_files.pb.h" #include "file_store.h" #include "log.h" #include "openssl/sha.h" - -namespace wvcdm { - -// TODO(rfrias): Make this work for non-unix paths -const char* DeviceFiles::kBasePath = "/data/mediadrm/IDM"; -const char* DeviceFiles::kPathDelimiter = "/"; -const char* DeviceFiles::kDeviceCertificateFileName = "cert.bin"; -const char* DeviceFiles::kLicenseFileNameExt = ".lic"; +#include "properties.h" // Protobuf generated classes. using video_widevine_client::sdk::DeviceCertificate; @@ -27,6 +18,23 @@ using video_widevine_client::sdk::License; using video_widevine_client::sdk::License_LicenseState_ACTIVE; using video_widevine_client::sdk::License_LicenseState_RELEASING; +namespace { +const char kCertificateFileName[] = "cert.bin"; +const char kLicenseFileNameExt[] = ".lic"; +} // namespace + +namespace wvcdm { + + +bool DeviceFiles::Init(File* handle) { + file_ = handle; + if (handle == NULL) { + LOGW("DeviceFiles::Init: Invalid file handle parameter"); + return false; + } + return true; +} + bool DeviceFiles::StoreCertificate(const std::string& certificate, const std::string& wrapped_private_key) { // Fill in file information @@ -35,7 +43,7 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate, file.set_type(video_widevine_client::sdk::File::DEVICE_CERTIFICATE); file.set_version(video_widevine_client::sdk::File::VERSION_1); - DeviceCertificate *device_certificate = file.mutable_device_certificate(); + DeviceCertificate* device_certificate = file.mutable_device_certificate(); device_certificate->set_certificate(certificate); device_certificate->set_wrapped_private_key(wrapped_private_key); @@ -49,21 +57,20 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate, return false; } - // File in hashed file data + // Fill in hashed file data HashedFile hashed_file; hashed_file.set_file(serialized_string); hashed_file.set_hash(hash); hashed_file.SerializeToString(&serialized_string); - return StoreFile(kDeviceCertificateFileName, serialized_string); + return StoreFile(kCertificateFileName, serialized_string); } bool DeviceFiles::RetrieveCertificate(std::string* certificate, std::string* wrapped_private_key) { - std::string serialized_hashed_file; - if (!RetrieveFile(kDeviceCertificateFileName, &serialized_hashed_file)) + if (!RetrieveFile(kCertificateFileName, &serialized_hashed_file)) return false; HashedFile hashed_file; @@ -244,49 +251,80 @@ bool DeviceFiles::RetrieveLicense( } bool DeviceFiles::DeleteLicense(const std::string& key_set_id) { - std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt; - return File::Remove(path); + if (!file_) { + LOGW("DeviceFiles::DeleteLicense: Invalid file handle"); + return false; + } + + std::string path; + if (!Properties::GetDeviceFilesBasePath(&path)) { + LOGW("DeviceFiles::StoreFile: Unable to get base path"); + return false; + } + path.append(key_set_id); + path.append(kLicenseFileNameExt); + + return file_->Remove(path); } bool DeviceFiles::LicenseExists(const std::string& key_set_id) { - std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt; - return File::Exists(path); + if (!file_) { + LOGW("DeviceFiles::LicenseExists: Invalid file handle"); + return false; + } + + std::string path; + if (!Properties::GetDeviceFilesBasePath(&path)) { + LOGW("DeviceFiles::StoreFile: Unable to get base path"); + return false; + } + path.append(key_set_id); + path.append(kLicenseFileNameExt); + + return file_->Exists(path); } bool DeviceFiles::Hash(const std::string& data, std::string* hash) { - if (!hash) - return false; + if (!hash) return false; hash->resize(SHA256_DIGEST_LENGTH); SHA256_CTX sha256; SHA256_Init(&sha256); SHA256_Update(&sha256, data.data(), data.size()); - SHA256_Final(reinterpret_cast(const_cast(hash->data())), &sha256); + SHA256_Final(reinterpret_cast(&(*hash)[0]), &sha256); return true; } bool DeviceFiles::StoreFile(const char* name, const std::string& data) { - if (!name) + if (!file_) { + LOGW("DeviceFiles::StoreFile: Invalid file handle"); return false; + } - std::string path = GetBasePath(kBasePath); + if (!name) { + LOGW("DeviceFiles::StoreFile: Unspecified file name parameter"); + return false; + } - if (!File::IsDirectory(path)) { - if (!File::CreateDirectory(path)) - return false; + std::string path; + if (!Properties::GetDeviceFilesBasePath(&path)) { + LOGW("DeviceFiles::StoreFile: Unable to get base path"); + return false; + } + + if (!file_->IsDirectory(path)) { + if (!file_->CreateDirectory(path)) return false; } path += name; - File file(path, File::kCreate | File::kTruncate | File::kBinary); - if (file.IsBad()) { + if (!file_->Open(path, File::kCreate | File::kTruncate | File::kBinary)) { LOGW("DeviceFiles::StoreFile: File open failed: %s", path.c_str()); return false; } - ssize_t bytes = file.Write(data.data(), data.size()); - - file.Close(); + ssize_t bytes = file_->Write(data.data(), data.size()); + file_->Close(); if (bytes != static_cast(data.size())) { LOGW("DeviceFiles::StoreFile: write failed: %d %d", data.size(), bytes); @@ -298,36 +336,51 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) { } bool DeviceFiles::RetrieveFile(const char* name, std::string* data) { - if (!data) + if (!file_) { + LOGW("DeviceFiles::RetrieveFile: Invalid file handle"); return false; + } - std::string path = GetBasePath(kBasePath) + name; + if (!name) { + LOGW("DeviceFiles::RetrieveFile: Unspecified file name parameter"); + return false; + } - if (!File::Exists(path)) { + if (!data) { + LOGW("DeviceFiles::RetrieveFile: Unspecified data parameter"); + return false; + } + + std::string path; + if (!Properties::GetDeviceFilesBasePath(&path)) { + LOGW("DeviceFiles::StoreFile: Unable to get base path"); + return false; + } + + path += name; + + if (!file_->Exists(path)) { LOGW("DeviceFiles::RetrieveFile: %s does not exist", path.c_str()); return false; } - ssize_t bytes = File::FileSize(path); - + ssize_t bytes = file_->FileSize(path); if (bytes <= 0) { LOGW("DeviceFiles::RetrieveFile: File size invalid: %d", path.c_str()); return false; } - File file(path, File::kReadOnly | File::kBinary); - if (file.IsBad()) { + if (!file_->Open(path, File::kReadOnly | File::kBinary)) { LOGW("DeviceFiles::RetrieveFile: File open failed: %s", path.c_str()); return false; } data->resize(bytes); - - bytes = file.Read(reinterpret_cast(const_cast(data->data())), - data->size()); + bytes = file_->Read(&(*data)[0], data->size()); + file_->Close(); if (bytes != static_cast(data->size())) { - LOGW("DeviceFiles::StoreFile: write failed: %d %d", data->size(), bytes); + LOGW("DeviceFiles::RetrieveFile: read failed"); return false; } @@ -336,11 +389,12 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) { return true; } -std::string DeviceFiles::GetBasePath(const char* dir) { - // TODO(rfrias): Make this work for non-unix paths - std::stringstream ss; - ss << dir << getuid() << kPathDelimiter; - return ss.str(); +std::string DeviceFiles::GetCertificateFileName() { + return kCertificateFileName; } +std::string DeviceFiles::GetLicenseFileNameExtension() { + return kLicenseFileNameExt; } + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 893d916f..982cb024 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -4,7 +4,7 @@ #include -#include "crypto_engine.h" +#include "crypto_key.h" #include "crypto_session.h" #include "log.h" #include "policy_engine.h" @@ -13,13 +13,13 @@ #include "wv_cdm_constants.h" namespace { - std::string kCompanyNameKey = "company_name"; - std::string kModelNameKey = "model_name"; - std::string kArchitectureNameKey = "architecture_name"; - std::string kDeviceNameKey = "device_name"; - std::string kProductNameKey = "product_name"; - std::string kBuildInfoKey = "build_info"; - std::string kDeviceIdKey = "device_id"; +std::string kCompanyNameKey = "company_name"; +std::string kModelNameKey = "model_name"; +std::string kArchitectureNameKey = "architecture_name"; +std::string kDeviceNameKey = "device_name"; +std::string kProductNameKey = "product_name"; +std::string kBuildInfoKey = "build_info"; +std::string kDeviceIdKey = "device_id"; } namespace wvcdm { @@ -30,13 +30,13 @@ using video_widevine_server::sdk::ClientIdentification_NameValue; using video_widevine_server::sdk::LicenseRequest; using video_widevine_server::sdk::LicenseRequest_ContentIdentification; using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC; -using video_widevine_server::sdk::LicenseRequest_ContentIdentification_ExistingLicense; +using video_widevine_server::sdk:: + LicenseRequest_ContentIdentification_ExistingLicense; using video_widevine_server::sdk::License; using video_widevine_server::sdk::License_KeyContainer; using video_widevine_server::sdk::LicenseError; using video_widevine_server::sdk::SignedMessage; - static std::vector ExtractContentKeys(const License& license) { std::vector key_array; @@ -50,7 +50,11 @@ static std::vector ExtractContentKeys(const License& license) { key.set_key_id(license.key(i).id()); // Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes, // the padding will always be 16 bytes. - length = license.key(i).key().size() - 16; + if (license.key(i).key().size() > 16) { + length = license.key(i).key().size() - 16; + } else { + length = 0; + } key.set_key_data(license.key(i).key().substr(0, length)); key.set_key_data_iv(license.key(i).iv()); if (license.key(i).has_key_control()) { @@ -62,7 +66,9 @@ static std::vector ExtractContentKeys(const License& license) { case License_KeyContainer::KEY_CONTROL: if (license.key(i).has_key_control()) { key.set_key_control(license.key(i).key_control().key_control_block()); - key.set_key_control_iv(license.key(i).key_control().iv()); + if (license.key(i).key_control().has_iv()) { + key.set_key_control_iv(license.key(i).key_control().iv()); + } key_array.push_back(key); } break; @@ -75,17 +81,14 @@ static std::vector ExtractContentKeys(const License& license) { return key_array; } -CdmLicense::CdmLicense(): session_(NULL) {} +CdmLicense::CdmLicense() : session_(NULL) {} CdmLicense::~CdmLicense() {} -bool CdmLicense::Init(const std::string& token, - CryptoSession* session, +bool CdmLicense::Init(const std::string& token, CryptoSession* session, PolicyEngine* policy_engine) { - if (token.size() == 0) - return false; - if (session == NULL || !session->IsValid() || !session->IsOpen()) - return false; + if (token.size() == 0) return false; + if (session == NULL || !session->IsOpen()) return false; token_ = token; session_ = session; policy_engine_ = policy_engine; @@ -97,8 +100,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request, std::string* server_url) { - if (!session_ || - token_.empty()) { + if (!session_ || token_.empty()) { return false; } if (init_data.empty()) { @@ -109,10 +111,6 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, LOGE("CdmLicense::PrepareKeyRequest : No signed request provided."); return false; } - if (!server_url) { - LOGE("CdmLicense::PrepareKeyRequest : No server url provided."); - return false; - } // TODO(gmorgan): Request ID owned by session? std::string request_id; @@ -135,38 +133,38 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, client_info->set_value(iter->second); } std::string value; - if (Properties::GetCompanyName(value)) { + if (Properties::GetCompanyName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kCompanyNameKey); client_info->set_value(value); } - if (Properties::GetModelName(value)) { + if (Properties::GetModelName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kModelNameKey); client_info->set_value(value); } - if (Properties::GetArchitectureName(value)) { + if (Properties::GetArchitectureName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kArchitectureNameKey); client_info->set_value(value); } - if (Properties::GetDeviceName(value)) { + if (Properties::GetDeviceName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kDeviceNameKey); client_info->set_value(value); } - if (Properties::GetProductName(value)) { + if (Properties::GetProductName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kProductNameKey); client_info->set_value(value); } - if (Properties::GetBuildInfo(value)) { + if (Properties::GetBuildInfo(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kBuildInfoKey); client_info->set_value(value); } - if (CryptoEngine::GetInstance()->GetDeviceUniqueId(&value)) { + if (session_->GetDeviceUniqueId(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kDeviceIdKey); client_info->set_value(value); @@ -188,8 +186,8 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, cenc_content_id->set_license_type(video_widevine_server::sdk::STREAMING); break; default: - LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %u", - (int)license_type); + LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d", + license_type); return false; break; } @@ -220,8 +218,8 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, // Derive signing and encryption keys and construct signature. std::string license_request_signature; - if (!session_->PrepareRequest(serialized_license_req, - &license_request_signature, false)) { + if (!session_->PrepareRequest(serialized_license_req, false, + &license_request_signature)) { signed_request->clear(); return false; } @@ -268,7 +266,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal, LicenseRequest_ContentIdentification_ExistingLicense* current_license = license_request.mutable_content_id()->mutable_license(); - current_license->mutable_license_id()->CopyFrom(license_id_); + current_license->mutable_license_id()->CopyFrom(policy_engine_->license_id()); // Get/set the nonce. This value will be reflected in the Key Control Block // of the license response. @@ -318,23 +316,19 @@ CdmResponseType CdmLicense::HandleKeyResponse( } SignedMessage signed_response; - if (!signed_response.ParseFromString(license_response)) - return KEY_ERROR; + if (!signed_response.ParseFromString(license_response)) return KEY_ERROR; if (signed_response.type() == SignedMessage::ERROR) { return HandleKeyErrorResponse(signed_response); } - if (!signed_response.has_signature()) - return KEY_ERROR; + if (!signed_response.has_signature()) return KEY_ERROR; License license; - if (!license.ParseFromString(signed_response.msg())) - return KEY_ERROR; + if (!license.ParseFromString(signed_response.msg())) return KEY_ERROR; if (Properties::use_certificates_as_identification()) { - if (!signed_response.has_session_key()) - return KEY_ERROR; + if (!signed_response.has_session_key()) return KEY_ERROR; if (!session_->GenerateDerivedKeys(key_request_, signed_response.session_key())) @@ -344,8 +338,7 @@ CdmResponseType CdmLicense::HandleKeyResponse( // Extract mac key std::string mac_key_iv; std::string mac_key; - if (license.policy().can_renew()) - { + if (license.policy().can_renew()) { for (int i = 0; i < license.key_size(); ++i) { if (license.key(i).type() == License_KeyContainer::SIGNING) { mac_key_iv.assign(license.key(i).iv()); @@ -355,15 +348,9 @@ CdmResponseType CdmLicense::HandleKeyResponse( } } - if (mac_key_iv.size() != KEY_IV_SIZE || - mac_key.size() != MAC_KEY_SIZE) { + if (mac_key_iv.size() != KEY_IV_SIZE || mac_key.size() != MAC_KEY_SIZE) { return KEY_ERROR; } - - // License Id should not be empty for renewable license - if (!license.has_id()) return KEY_ERROR; - - license_id_.CopyFrom(license.id()); } std::vector key_array = ExtractContentKeys(license); @@ -376,17 +363,16 @@ CdmResponseType CdmLicense::HandleKeyResponse( server_url_ = license.policy().renewal_server_url(); } + // TODO(kqyang, jfore, gmorgan): change SetLicense function signature to + // be able to return true/false to accept/reject the license. (Pending code + // merge from Eureka) policy_engine_->SetLicense(license); - if (session_->LoadKeys(signed_response.msg(), - signed_response.signature(), - mac_key_iv, - mac_key, - key_array.size(), + if (session_->LoadKeys(signed_response.msg(), signed_response.signature(), + mac_key_iv, mac_key, key_array.size(), &key_array[0])) { return KEY_ADDED; - } - else { + } else { return KEY_ERROR; } } @@ -429,41 +415,31 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse( return KEY_ERROR; } - if (license.id().version() > license_id_.version()) { - // This is the normal case. - license_id_.CopyFrom(license.id()); - - if (is_renewal) { - if (license.policy().has_renewal_server_url() && - license.policy().renewal_server_url().size() > 0) { - server_url_ = license.policy().renewal_server_url(); - } - } - - policy_engine_->UpdateLicense(license); - - if (!is_renewal) - return KEY_ADDED; - - std::vector key_array = ExtractContentKeys(license); - - if (session_->RefreshKeys(signed_response.msg(), - signed_response.signature(), - key_array.size(), - &key_array[0])) { - return KEY_ADDED; - } - else { - return KEY_ERROR; + if (is_renewal) { + if (license.policy().has_renewal_server_url() && + license.policy().renewal_server_url().size() > 0) { + server_url_ = license.policy().renewal_server_url(); } } - // This isn't supposed to happen. - // TODO(jfore): Handle wrap? We can miss responses and that should be - // considered normal until retries are exhausted. - LOGE("CdmLicense::HandleKeyUpdateResponse: license version: expected > %u," - " actual = %u", license_id_.version(), license.id().version()); - return KEY_ERROR; + // TODO(kqyang, jfore, gmorgan): change UpdateLicense function signature to + // be able to return true/false to accept/reject the license. (Pending code + // merge from Eureka) + policy_engine_->UpdateLicense(license); + + if (!is_renewal) + return KEY_ADDED; + + std::vector key_array = ExtractContentKeys(license); + + if (session_->RefreshKeys(signed_response.msg(), + signed_response.signature(), + key_array.size(), + &key_array[0])) { + return KEY_ADDED; + } else { + return KEY_ERROR; + } } bool CdmLicense::RestoreOfflineLicense( diff --git a/libwvdrmengine/cdm/core/src/properties.cpp b/libwvdrmengine/cdm/core/src/properties.cpp index 1f466f9f..de641453 100644 --- a/libwvdrmengine/cdm/core/src/properties.cpp +++ b/libwvdrmengine/cdm/core/src/properties.cpp @@ -12,6 +12,7 @@ bool Properties::oem_crypto_use_secure_buffers_; bool Properties::oem_crypto_use_fifo_; bool Properties::oem_crypto_use_userspace_buffers_; bool Properties::use_certificates_as_identification_; +bool Properties::extract_pssh_data_; void Properties::Init() { begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived; @@ -21,6 +22,7 @@ void Properties::Init() { oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers; use_certificates_as_identification_ = kPropertyUseCertificatesAsIdentification; + extract_pssh_data_ = kExtractPsshData; } } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/string_conversions.cpp b/libwvdrmengine/cdm/core/src/string_conversions.cpp index 8afeda3f..5a8a6d09 100644 --- a/libwvdrmengine/cdm/core/src/string_conversions.cpp +++ b/libwvdrmengine/cdm/core/src/string_conversions.cpp @@ -10,45 +10,7 @@ #include #include "log.h" - -namespace { -/* - * Returns a 8-bit char that is mapped to the 6-bit base64 in_ch. - * - * Extracted from http://www.ietf.org/rfc/rfc3548.txt. - * - The "URL and Filename safe" Base 64 Alphabet - - Value Encoding Value Encoding Value Encoding Value Encoding - 0 A 17 R 34 i 51 z - 1 B 18 S 35 j 52 0 - 2 C 19 T 36 k 53 1 - 3 D 20 U 37 l 54 2 - 4 E 21 V 38 m 55 3 - 5 F 22 W 39 n 56 4 - 6 G 23 X 40 o 57 5 - 7 H 24 Y 41 p 58 6 - 8 I 25 Z 42 q 59 7 - 9 J 26 a 43 r 60 8 - 10 K 27 b 44 s 61 9 - 11 L 28 c 45 t 62 - (minus) - 12 M 29 d 46 u 63 _ - 13 N 30 e 47 v (underline) - 14 O 31 f 48 w - 15 P 32 g 49 x - 16 Q 33 h 50 y (pad) = - */ -char B64ToBin(char in_ch) { - if (in_ch >= 'A' && in_ch <= 'Z') return in_ch - 'A'; - if (in_ch >= 'a' && in_ch <= 'z') return in_ch - 'a' + 26; - if (in_ch >= '0' && in_ch <= '9') return in_ch - '0' + 52; - if (in_ch == '-') return 62; - if (in_ch == '_') return 63; - - // arbitrary delimiter not in Base64 encoded alphabet, do not pick 0 - return '?'; -} -} +#include "modp_b64w.h" namespace wvcdm { @@ -104,181 +66,56 @@ std::string b2a_hex(const std::string& byte) { // Filename-friendly base64 encoding (RFC4648), commonly referred as // Base64WebSafeEncode. -// This is the encoding required by GooglePlay for certain -// license server transactions. It is also used for logging -// certain strings. +// This is the encoding required by GooglePlay to interface with the +// provisioning server's Apiary interface as well as for certain license server +// transactions. It is also used for logging certain strings. // The difference between web safe encoding vs regular encoding is that // the web safe version replaces '+' with '-' and '/' with '_'. std::string Base64SafeEncode(const std::vector& bin_input) { - static const char kBase64Chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789-_"; if (bin_input.empty()) { return std::string(); } int in_size = bin_input.size(); - int final_quantum_in_bytes = in_size % 3; - int full_in_chunks = in_size / 3; - int out_size = full_in_chunks * 4; - if (final_quantum_in_bytes) out_size += 4; + std::string b64_output(modp_b64w_encode_len(in_size), 0); - std::string b64_output(out_size, '\0'); - int in_index = 0; - int out_index = 0; - unsigned long buffer; - unsigned char out_cc; - static const unsigned long kInMask = 0xff; - static const unsigned long kOutMask = 0x3f; - - for (int i = 0; i < full_in_chunks; ++i) { - // up to 3 bytes (0..255) in - buffer = (bin_input.at(in_index) & kInMask); - buffer <<= 8; - buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask); - buffer <<= 8; - buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask); - ++in_index; - - // up to 4 bytes (0..63) out - out_cc = (buffer >> 18) & kOutMask; - b64_output.at(out_index) = kBase64Chars[out_cc]; - if (++out_index >= out_size) - break; - out_cc = (buffer >> 12) & kOutMask; - b64_output.at(out_index) = kBase64Chars[out_cc]; - if (++out_index >= out_size) - break; - out_cc = (buffer >> 6) & kOutMask; - b64_output.at(out_index) = kBase64Chars[out_cc]; - if (++out_index >= out_size) - break; - out_cc = buffer & kOutMask; - b64_output.at(out_index) = kBase64Chars[out_cc]; - ++out_index; + int out_size = modp_b64w_encode(&b64_output[0], + reinterpret_cast(&bin_input[0]), + in_size); + if (out_size == -1) { + LOGE("Base64SafeEncode failed"); + return std::string(); } - if (final_quantum_in_bytes) { - switch(final_quantum_in_bytes) { - case 1: { - // reads 24-bits data, which is made up of one 8-bits char - buffer = (bin_input.at(in_index++) & kInMask); - buffer <<= 16; + b64_output.resize(out_size); + return b64_output; +} - // writes two 6-bits chars followed by two '=' padding char - out_cc = (buffer >> 18) & kOutMask; - b64_output.at(out_index++) = kBase64Chars[out_cc]; - out_cc = (buffer >> 12) & kOutMask; - b64_output.at(out_index++) = kBase64Chars[out_cc]; - b64_output.at(out_index++) = '='; - b64_output.at(out_index) = '='; - break; - } - case 2: { - // reads 24-bits data, which is made up of two 8-bits chars - buffer = (bin_input.at(in_index++) & kInMask); - buffer <<= 8; - buffer |= (bin_input.at(in_index++) & kInMask); - buffer <<= 8; - - // writes three 6-bits chars followed by one '=' padding char - out_cc = (buffer >> 18) & kOutMask; - b64_output.at(out_index++) = kBase64Chars[out_cc]; - out_cc = (buffer >> 12) & kOutMask; - b64_output.at(out_index++) = kBase64Chars[out_cc]; - out_cc = (buffer >> 6) & kOutMask; - b64_output.at(out_index++) = kBase64Chars[out_cc]; - b64_output.at(out_index) = '='; - break; - } - default: - break; - } - } +std::string Base64SafeEncodeNoPad(const std::vector& bin_input) { + std::string b64_output = Base64SafeEncode(bin_input); + // Output size: ceiling [ bin_input.size() * 4 / 3 ]. + b64_output.resize((bin_input.size() * 4 + 2) / 3); return b64_output; } // Decode for Filename-friendly base64 encoding (RFC4648), commonly referred // as Base64WebSafeDecode. -// This is the encoding required by GooglePlay for certain -// license server transactions. It is also used for logging -// certain strings. std::vector Base64SafeDecode(const std::string& b64_input) { if (b64_input.empty()) { return std::vector(); } int in_size = b64_input.size(); - int out_size = in_size; - std::vector bin_output(out_size, '\0'); - int in_index = 0; - int out_index = 0; - unsigned long buffer; - unsigned char out_cc; - static const unsigned long kOutMask = 0xff; - - int counter = 0; - size_t delimiter_pos = b64_input.rfind('='); - if (delimiter_pos != std::string::npos) { - // Special case for partial last quantum indicated by '=' - // at the end of encoded input. - counter = 1; - } - for (; counter < (in_size / 4); ++counter) { - // up to 4 bytes (0..63) in - buffer = B64ToBin(b64_input.at(in_index)); - buffer <<= 6; - buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index)); - buffer <<= 6; - buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index)); - buffer <<= 6; - buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index)); - ++in_index; - // up to 3 bytes (0..255) out - out_cc = (buffer >> 16) & kOutMask; - bin_output.at(out_index) = out_cc; - if (++out_index >= out_size) - break; - out_cc = (buffer >> 8) & kOutMask; - bin_output.at(out_index) = out_cc; - if (++out_index >= out_size) - break; - out_cc = buffer & kOutMask; - bin_output.at(out_index) = out_cc; - ++out_index; + std::vector bin_output(modp_b64w_decode_len(in_size), 0); + int out_size = modp_b64w_decode(reinterpret_cast(&bin_output[0]), + b64_input.data(), + in_size); + if (out_size == -1) { + LOGE("Base64SafeDecode failed"); + return std::vector(0); } - if (delimiter_pos != std::string::npos) { - // it is either 2 chars plus 2 '=' or 3 chars plus one '=' - buffer = B64ToBin(b64_input.at(in_index++)); - buffer <<= 6; - buffer |= B64ToBin(b64_input.at(in_index++)); - buffer <<= 6; - char special_char = b64_input.at(in_index++); - if ('=' == special_char) { - // we have 2 chars and 2 '=' - buffer <<= 6; - out_cc = (buffer >> 16) & kOutMask; - bin_output.at(out_index++) = out_cc; - out_cc = (buffer >> 8) & kOutMask; - bin_output.at(out_index) = out_cc; - } else { - // we have 3 chars and 1 '=' - buffer |= B64ToBin(special_char); - buffer <<= 6; - buffer |= B64ToBin(b64_input.at(in_index)); - out_cc = (buffer >> 16) & kOutMask; - bin_output.at(out_index++) = out_cc; - out_cc = (buffer >> 8) & kOutMask; - bin_output.at(out_index++) = out_cc; - out_cc = buffer & kOutMask; - bin_output.at(out_index) = out_cc; - } - } - - // adjust vector to reflect true size - bin_output.resize(out_index); + bin_output.resize(out_size); return bin_output; } diff --git a/libwvdrmengine/cdm/core/test/base64_test.cpp b/libwvdrmengine/cdm/core/test/base64_test.cpp index 37e5dd02..4c4ab7ca 100644 --- a/libwvdrmengine/cdm/core/test/base64_test.cpp +++ b/libwvdrmengine/cdm/core/test/base64_test.cpp @@ -1,193 +1,73 @@ // Copyright 2013 Google Inc. All Rights Reserved. +#include + #include "gtest/gtest.h" #include "log.h" #include "string_conversions.h" namespace { - std::string kMultipleOf24BitsData("Good day!"); - std::string kOneByteOverData("Hello Googler"); - std::string kTwoBytesOverData("Hello Googlers"); - std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh"); - std::string kOneByteOverB64Data("SGVsbG8gR29vZ2xlcg=="); - std::string kTwoBytesOverB64Data("SGVsbG8gR29vZ2xlcnM="); - std::string kTestData = - "\030\361\\\366\267> \331\210\360\\-\311:\324\256\376" - "\261\234\241\326d\326\177\346\346\223\333Y\305\214\330"; - std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg="; - std::string kB64ShortString("r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A="); - std::string kB64LongString = - "CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo" - "TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg" - "yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp" - "NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe" - "WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R" - "lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP" - "9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T" - "jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s" - "OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c" - "4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm" - "1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB" - "efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA" - "jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0" - "LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn" - "bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E" - "L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb" - "jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b" - "rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH" - "yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F" - "sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb" - "MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S" - "dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4" - "CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF" - "E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH" - "JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI" - "O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu" - "vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g" - "k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug" - "_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9" - "G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp" - "-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc" - "4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G" - "rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU" - "WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF" - "7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem" - "et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr" - "gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe" - "G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT" - "UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN" - "1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT" - "sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw" - "qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO" - "t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF" - "gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl" - "F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA="; -} + +// Test vectors taken from http://tools.ietf.org/html/rfc4648#section-10 +const std::string kNullString(""); +const std::string kf("f"); +const std::string kfo("fo"); +const std::string kfoo("foo"); +const std::string kfoob("foob"); +const std::string kfooba("fooba"); +const std::string kfoobar("foobar"); +const std::string kfB64("Zg=="); +const std::string kfoB64("Zm8="); +const std::string kfooB64("Zm9v"); +const std::string kfoobB64("Zm9vYg=="); +const std::string kfoobaB64("Zm9vYmE="); +const std::string kfoobarB64("Zm9vYmFy"); + +// Arbitrary clear test vectors +const std::string kMultipleOf24BitsData("Good day!"); +const std::string kOneByteOverData("Hello Googler"); +const std::string kTwoBytesOverData("Hello Googlers"); +const std::string kTestData = + "\030\361\\\366\267> \331\210\360\\-\311:\324\256\376" + "\261\234\241\326d\326\177\346\346\223\333Y\305\214\330"; + +// Arbitrary encoded test vectors +const std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh"); +const std::string kOneByteOverB64Data("SGVsbG8gR29vZ2xlcg=="); +const std::string kTwoBytesOverB64Data("SGVsbG8gR29vZ2xlcnM="); +const std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg="; + +const std::pair kBase64TestVectors[] = { + make_pair(&kNullString, &kNullString), + make_pair(&kf, &kfB64), + make_pair(&kfo, &kfoB64), + make_pair(&kfoo, &kfooB64), + make_pair(&kfoob, &kfoobB64), + make_pair(&kfooba, &kfoobaB64), + make_pair(&kfoobar, &kfoobarB64), + make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data), + make_pair(&kOneByteOverData, &kOneByteOverB64Data), + make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data), + make_pair(&kTestData, &kB64TestData), +}; + +} // unnamed namespace namespace wvcdm { -class Base64Test : public testing::Test { - public: - Base64Test() {} - ~Base64Test() {} +class Base64EncodeDecodeTest : public ::testing::TestWithParam< + std::pair > {}; -}; - -TEST_F(Base64Test, Base64MultipleOf24BitsTest) -{ - // encodes string - std::vector message_vector(kMultipleOf24BitsData.begin(), - kMultipleOf24BitsData.end()); - std::string message_b64 = Base64SafeEncode(message_vector); - - // decodes string - std::vector result_vector = Base64SafeDecode(message_b64); - std::string result; - result.assign(result_vector.begin(), result_vector.end()); - EXPECT_STREQ(kMultipleOf24BitsData.data(), result.data()); -} - -TEST_F(Base64Test, Base64OneByteOverTest) -{ - // encodes string - std::vector message_vector(kOneByteOverData.begin(), - kOneByteOverData.end()); - std::string message_b64 = Base64SafeEncode(message_vector); - - // decodes string - std::vector result_vector = Base64SafeDecode(message_b64); - std::string result; - result.assign(result_vector.begin(), result_vector.end()); - EXPECT_STREQ(kOneByteOverData.data(), result.data()); -} - -TEST_F(Base64Test, Base64TwoBytesOverTest) -{ - // encodes string - std::vector message_vector(kTwoBytesOverData.begin(), - kTwoBytesOverData.end()); - std::string message_b64 = Base64SafeEncode(message_vector); - - // decodes string - std::vector result_vector = Base64SafeDecode(message_b64); - std::string result; - result.assign(result_vector.begin(), result_vector.end()); - EXPECT_STREQ(kTwoBytesOverData.data(), result.data()); -} - -TEST_F(Base64Test, Base64EncodeTest) -{ - // encodes string - std::vector message_vector(kTestData.begin(), kTestData.end()); - std::string message_b64 = Base64SafeEncode(message_vector); - std::string result; - result.assign(message_b64.begin(), message_b64.end()); - EXPECT_STREQ(kB64TestData.data(), result.data()); - - // decodes string - std::vector result_vector = Base64SafeDecode(message_b64); - result.clear(); - result.assign(result_vector.begin(), result_vector.end()); - EXPECT_STREQ(kTestData.data(), result.data()); -} - -TEST_F(Base64Test, Base64MultipleOf24BitsDecodeTest) -{ - // decodes string - std::vector decoded_vector = Base64SafeDecode(kMultipleOf24BitsB64Data); - std::string result; - result.assign(decoded_vector.begin(), decoded_vector.end()); - EXPECT_STREQ(kMultipleOf24BitsData.data(), result.data()); - - // encodes string +TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) { + std::pair values = GetParam(); + std::vector decoded_vector = Base64SafeDecode(values.second->data()); + std::string decoded_string(decoded_vector.begin(), decoded_vector.end()); + EXPECT_STREQ(values.first->data(), decoded_string.data()); std::string b64_string = Base64SafeEncode(decoded_vector); - EXPECT_STREQ(kMultipleOf24BitsB64Data.data(), b64_string.data()); + EXPECT_STREQ(values.second->data(), b64_string.data()); } -TEST_F(Base64Test, Base64OneByteOverDecodeTest) -{ - // decodes string - std::vector decoded_vector = Base64SafeDecode(kOneByteOverB64Data); - std::string result; - result.assign(decoded_vector.begin(), decoded_vector.end()); - EXPECT_STREQ(kOneByteOverData.data(), result.data()); - - // encodes string - std::string b64_string = Base64SafeEncode(decoded_vector); - EXPECT_STREQ(kOneByteOverB64Data.data(), b64_string.data()); -} - -TEST_F(Base64Test, Base64TwoBytesOverDecodeTest) -{ - // decodes string - std::vector decoded_vector = Base64SafeDecode(kTwoBytesOverB64Data); - std::string result; - result.assign(decoded_vector.begin(), decoded_vector.end()); - EXPECT_STREQ(kTwoBytesOverData.data(), result.data()); - - // encodes string - std::string b64_string = Base64SafeEncode(decoded_vector); - EXPECT_STREQ(kTwoBytesOverB64Data.data(), b64_string.data()); -} - -TEST_F(Base64Test, Base64ShortDecodeTest) -{ - // decodes string - std::vector decoded_vector = Base64SafeDecode(kB64ShortString); - - // encodes string - std::string b64_string = Base64SafeEncode(decoded_vector); - EXPECT_STREQ(kB64ShortString.data(), b64_string.data()); -} - -TEST_F(Base64Test, Base64LongDecodeTest) -{ - // decodes string - std::vector decoded_vector = Base64SafeDecode(kB64LongString); - - // encodes string - std::string b64_string = Base64SafeEncode(decoded_vector); - EXPECT_STREQ(kB64LongString.data(), b64_string.data()); -} +INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64EncodeDecodeTest, + ::testing::ValuesIn(kBase64TestVectors)); } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index bed53f37..1ae66e11 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -3,11 +3,17 @@ #include #include +#if defined(CHROMIUM_BUILD) +#include "base/at_exit.h" +#include "base/message_loop.h" +#endif #include "cdm_engine.h" #include "config_test_env.h" #include "gtest/gtest.h" #include "license_request.h" #include "log.h" +#include "properties.h" +#include "scoped_ptr.h" #include "string_conversions.h" #include "url_request.h" #include "wv_cdm_types.h" @@ -22,197 +28,208 @@ std::string g_license_server; std::string g_port; wvcdm::KeyId g_wrong_key_id; int g_use_full_path = 0; // cannot use boolean in getopt_long + +// This is the RSA certificate from the provisioning server. The client +// sends this certificate to a license server as verification in the +// provisioning test case. +static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse = + "{\"signedResponse\": {" + "\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo" + "TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg" + "yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp" + "NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe" + "WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R" + "lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP" + "9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T" + "jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s" + "OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c" + "4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm" + "1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB" + "efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA" + "jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0" + "LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn" + "bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E" + "L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb" + "jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b" + "rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH" + "yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F" + "sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb" + "MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S" + "dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4" + "CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF" + "E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH" + "JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI" + "O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu" + "vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g" + "k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug" + "_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9" + "G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp" + "-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc" + "4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G" + "rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU" + "WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF" + "7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem" + "et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr" + "gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe" + "G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT" + "UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN" + "1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT" + "sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw" + "qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO" + "t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF" + "gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl" + "F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=\"," + "\"signature\": \"r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=\"}}"; } // namespace namespace wvcdm { class WvCdmEngineTest : public testing::Test { public: - WvCdmEngineTest() {} - ~WvCdmEngineTest() {} + virtual void SetUp() { + cdm_engine_.reset(new CdmEngine()); + cdm_engine_->OpenSession(g_key_system, &session_id_); + } + + virtual void TearDown() { + cdm_engine_->CloseSession(session_id_); + } protected: void GenerateKeyRequest(const std::string& key_system, - const std::string& init_data) { - wvcdm::CdmAppParameterMap app_parameters; + const std::string& key_id) { + CdmAppParameterMap app_parameters; std::string server_url; - std::string key_set_id; - EXPECT_EQ(cdm_engine_.GenerateKeyRequest(session_id_, - key_set_id, - init_data, - kLicenseTypeStreaming, - app_parameters, - &key_msg_, - &server_url), wvcdm::KEY_MESSAGE); + std::string init_data = key_id; + CdmKeySetId key_set_id; + + // TODO(rfrias): Temporary change till b/9465346 is addressed + if (!Properties::extract_pssh_data()) { + EXPECT_TRUE(CdmEngine::ExtractWidevinePssh(key_id, &init_data)); + } + + EXPECT_EQ(KEY_MESSAGE, + 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) { - std::string server_url; - EXPECT_EQ(cdm_engine_.GenerateRenewalRequest(session_id_, - &key_msg_, - &server_url), - wvcdm::KEY_MESSAGE); - } - - // concatinates all chunks into one blob - // TODO (edwinwong) move this function to url_request class as GetMessageBody - void ConcatenateChunkedResponse(const std::string http_response, - std::string* message_body) { - if (http_response.empty()) - return; - - message_body->clear(); - const std::string kChunkedTag = "Transfer-Encoding: chunked\r\n\r\n"; - size_t chunked_tag_pos = http_response.find(kChunkedTag); - if (std::string::npos != chunked_tag_pos) { - // processes chunked encoding - size_t chunk_size = 0; - size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size(); - sscanf(&http_response[chunk_size_pos], "%x", &chunk_size); - if (chunk_size > http_response.size()) { - // precaution, in case we misread chunk size - LOGE("invalid chunk size %u", chunk_size); - return; - } - - /* - * searches for chunks - * - * header - * chunk size\r\n <-- chunk_size_pos @ beginning of chunk size - * chunk data\r\n <-- chunk_pos @ beginning of chunk data - * chunk size\r\n - * chunk data\r\n - * 0\r\n - */ - const std::string kCrLf = "\r\n"; - size_t chunk_pos = http_response.find(kCrLf, chunk_size_pos) + - kCrLf.size(); - message_body->assign(&http_response[0], chunk_size_pos); - while ((chunk_size > 0) && (std::string::npos != chunk_pos)) { - message_body->append(&http_response[chunk_pos], chunk_size); - - // searches for next chunk - chunk_size_pos = chunk_pos + chunk_size + kCrLf.size(); - sscanf(&http_response[chunk_size_pos], "%x", &chunk_size); - if (chunk_size > http_response.size()) { - // precaution, in case we misread chunk size - LOGE("invalid chunk size %u", chunk_size); - break; - } - chunk_pos = http_response.find(kCrLf, chunk_size_pos) + kCrLf.size(); - } - } else { - // response is not chunked encoded - message_body->assign(http_response); - } + EXPECT_EQ(KEY_MESSAGE, + cdm_engine_->GenerateRenewalRequest(session_id_, + &key_msg_, + &server_url_)); } // posts a request and extracts the drm message from the response std::string GetKeyRequestResponse(const std::string& server_url, const std::string& client_auth, int expected_response) { - std::string port; - if (server_url.find("https") != std::string::npos) { - port.assign("443"); - } else { - port.assign(g_port); - } - UrlRequest url_request(server_url + client_auth, port); - + // Use secure connection and chunk transfer coding. + UrlRequest url_request(server_url + client_auth, g_port, true, true); if (!url_request.is_connected()) { return ""; } - url_request.PostRequestChunk(key_msg_); - std::string http_response; - std::string message_body; - int resp_bytes = url_request.GetResponse(http_response); - if (resp_bytes) { - ConcatenateChunkedResponse(http_response, &message_body); - } - LOGD("response:\r\n%s", message_body.c_str()); + url_request.PostRequest(key_msg_); + std::string response; + int resp_bytes = url_request.GetResponse(&response); + LOGD("response:\r\n%s", response.c_str()); LOGD("end %d bytes response dump", resp_bytes); // 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_body); - if (expected_response == 200) { - EXPECT_EQ(200, status_code); + int status_code = url_request.GetStatusCode(response); + int kHttpOk = 200; + if (expected_response == kHttpOk) { + EXPECT_EQ(kHttpOk, status_code); } else { - EXPECT_NE(200, status_code); + EXPECT_NE(kHttpOk, status_code); } - std::string drm_msg; - if (200 == status_code) { + if (status_code != kHttpOk) { + return ""; + } else { + std::string drm_msg; LicenseRequest lic_request; - lic_request.GetDrmMessage(message_body, drm_msg); + lic_request.GetDrmMessage(response, drm_msg); LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(), - HexEncode(reinterpret_cast(drm_msg.data()), - drm_msg.size()).c_str()); + HexEncode(reinterpret_cast(drm_msg.data()), + drm_msg.size()).c_str()); + return drm_msg; } - return drm_msg; } - void VerifyKeyRequestResponse(const std::string& server_url, - const std::string& client_auth, - std::string& init_data, - bool is_renewal) { + void VerifyNewKeyResponse(const std::string& server_url, + const std::string& client_auth, + std::string& init_data){ std::string resp = GetKeyRequestResponse(server_url, client_auth, 200); - if (is_renewal) { - EXPECT_EQ(cdm_engine_.RenewKey(session_id_, resp), wvcdm::KEY_ADDED); - } - else { - std::string key_set_id; - EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp, key_set_id), - wvcdm::KEY_ADDED); - } + CdmKeySetId key_set_id; + EXPECT_EQ(cdm_engine_->AddKey(session_id_, resp, &key_set_id), KEY_ADDED); } - wvcdm::CdmEngine cdm_engine_; + void VerifyRenewalKeyResponse(const std::string& server_url, + const std::string& client_auth, + std::string& init_data){ + std::string resp = GetKeyRequestResponse(server_url, + client_auth, + 200); + EXPECT_EQ(cdm_engine_->RenewKey(session_id_, resp), wvcdm::KEY_ADDED); + } + + scoped_ptr cdm_engine_; std::string key_msg_; std::string session_id_; + std::string server_url_; }; +TEST(WvCdmProvisioningTest, ProvisioningTest) { + CdmEngine cdm_engine; + CdmProvisioningRequest prov_request; + std::string provisioning_server_url; + + cdm_engine.GetProvisioningRequest(&prov_request, &provisioning_server_url); + cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse); +} + TEST_F(WvCdmEngineTest, BaseMessageTest) { - cdm_engine_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id); GetKeyRequestResponse(g_license_server, g_client_auth, 200); - cdm_engine_.CloseSession(session_id_); } TEST_F(WvCdmEngineTest, WrongMessageTest) { - cdm_engine_.OpenSession(g_key_system, &session_id_); - - std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id); + std::string wrong_message = a2bs_hex(g_wrong_key_id); GenerateKeyRequest(g_key_system, wrong_message); GetKeyRequestResponse(g_license_server, g_client_auth, 500); - cdm_engine_.CloseSession(session_id_); } TEST_F(WvCdmEngineTest, NormalDecryption) { - cdm_engine_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id); - VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); - cdm_engine_.CloseSession(session_id_); + VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id); } TEST_F(WvCdmEngineTest, LicenseRenewal) { - cdm_engine_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id); - VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id); GenerateRenewalRequest(g_key_system, g_key_id); - VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, true); - cdm_engine_.CloseSession(session_id_); + VerifyRenewalKeyResponse(server_url_.empty() ? g_license_server : server_url_, + g_client_auth, + g_key_id); } + } // namespace wvcdm int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); + wvcdm::InitLogging(argc, argv); wvcdm::ConfigTestEnv config; g_client_auth.assign(config.client_auth()); @@ -231,6 +248,8 @@ int main(int argc, char **argv) { { "keyid", required_argument, NULL, 'k' }, { "port", required_argument, NULL, 'p' }, { "server", required_argument, NULL, 's' }, + { "vmodule", required_argument, NULL, 0 }, + { "v", required_argument, NULL, 0 }, { NULL, 0, NULL, '\0' } }; @@ -301,5 +320,9 @@ int main(int argc, char **argv) { config.set_port(g_port); config.set_key_id(g_key_id); +#if defined(CHROMIUM_BUILD) + base::AtExitManager exit; + MessageLoop ttr(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 84559557..f421c4e6 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.cpp +++ b/libwvdrmengine/cdm/core/test/config_test_env.cpp @@ -18,18 +18,19 @@ static const std::string kLicenseServer = "http://hamid.kir.corp.google.com:8888/drm"; static const std::string kClientAuth = ""; static const std::string kKeyId = - "000000347073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id + "000000347073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id "0801121030313233343536373839616263646566"; // key - for gHali #elif (USE_SERVER == USE_SERVER_YT) static const std::string kLicenseServer = - "https://www.youtube.com/api/drm/widevine?video_id=03681262dc412c06&source=YOUTUBE"; + "https://www.youtube.com/api/drm/" + "widevine?video_id=03681262dc412c06&source=YOUTUBE"; static const std::string kClientAuth = ""; static const std::string kKeyId = - "000000347073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id + "000000347073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id "0801121093789920E8D6520098577DF8F2DD5546"; // pssh data #elif (USE_SERVER == USE_SERVER_GP) @@ -45,23 +46,36 @@ static const std::string kClientAuth = "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine"; static const std::string kKeyId = - "000000347073736800000000" // blob size and pssh - "edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id + "000000347073736800000000" // blob size and pssh + "edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id "08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data #else #error "Must define USE_SERVER" #endif -//static const char kWidevineKeySystem[] = "com.widevine.alpha"; - // An invalid key id, expected to fail static const std::string kWrongKeyId = - "000000347073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id + "000000347073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id "0901121094889920E8D6520098577DF8F2DD5546"; // pssh data -} // namespace +// Url returned by GetProvisioningRequest() +const std::string kProductionProvisioningServerUrl = + "https://www.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; + +// Return production-rooted certificates that have test bit set, +// request_license_test uses this url. +const std::string kProductionTestProvisioningServerUrl = + "https://www.googleapis.com/" + "certificateprovisioning/v1exttest/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; + +const std::string kServerSdkLicenseServer = + "http://kir03fcpg174.widevine.net/widevine/cgi-bin/drm.cgi"; +} // namespace namespace wvcdm { @@ -70,8 +84,10 @@ ConfigTestEnv::ConfigTestEnv() key_id_(kKeyId), key_system_("com.widevine.alpha"), license_server_(kLicenseServer), - port_("80"), - wrong_key_id_(kWrongKeyId) { -} + port_(kDefaultHttpsPort), + provisioning_server_url_(kProductionProvisioningServerUrl), + provisioning_test_server_url_(kProductionTestProvisioningServerUrl), + server_sdk_license_server_(kServerSdkLicenseServer), + wrong_key_id_(kWrongKeyId) {} -} // namespace wvcdm +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/config_test_env.h b/libwvdrmengine/cdm/core/test/config_test_env.h index 1c54f316..c97c3164 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.h +++ b/libwvdrmengine/cdm/core/test/config_test_env.h @@ -6,6 +6,10 @@ #include #include "wv_cdm_types.h" +namespace { +const std::string kDefaultHttpsPort = "443"; +} + namespace wvcdm { // Configures default test environment. @@ -19,6 +23,15 @@ class ConfigTestEnv { const CdmKeySystem& key_system() const { return key_system_; } const std::string& license_server() const { return license_server_; } const std::string& port() const { return port_; } + const std::string& provisioning_server_url() const { + return provisioning_server_url_; + } + const std::string& provisioning_test_server_url() const { + return provisioning_test_server_url_; + } + const std::string& server_sdk_license_server() const { + return server_sdk_license_server_; + } const KeyId& wrong_key_id() const { return wrong_key_id_; } void set_key_id(KeyId& key_id) { key_id_.assign(key_id); } @@ -36,6 +49,9 @@ class ConfigTestEnv { CdmKeySystem key_system_; std::string license_server_; std::string port_; + std::string provisioning_server_url_; + std::string provisioning_test_server_url_; + std::string server_sdk_license_server_; KeyId wrong_key_id_; CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv); diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 7088a629..096ea3e1 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -2,562 +2,1334 @@ #include "device_files.h" #include "file_store.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" +#include "properties.h" +#include "string_conversions.h" namespace wvcdm { +// gmock methods +using ::testing::_; +using ::testing::AllOf; +using ::testing::Eq; +using ::testing::Gt; +using ::testing::HasSubstr; +using ::testing::NotNull; +using ::testing::Return; +using ::testing::ReturnArg; +using ::testing::SetArrayArgument; +using ::testing::StrEq; + namespace { - std::string kKeySetId1 = "ksid05e79dab2750370195d1"; - std::string kKeySetId2 = "ksid17fe1e9005934171bbd1"; - std::string kKeySetId3 = "ksid2eadb793100566012934"; +const uint32_t kCertificateLen = 700; +const uint32_t kWrappedKeyLen = 500; +const std::string kTestCertificate = + "124B035F3D256A656F0E505A085E7A6C482B61035E0C4A540F7803137F4C3B45206B7F33" + "347F4D7A005E56400F0955011F4E07072D0D46781817460974326A516E3944385760280E" + "4F166B380F033D045231201E6146041C3A6F01345C59300D32592732192C0F2310586306" + "7B31467B1477010D6F1D1944272509572A26217E1E6F7B666F46153E7749106E48760468" + "19467E164A731773155B3236537D5128682014174D125063380E48356A370B5015416A7F" + "672F132E37364E154B41540F440E47092775531508495F1E55576F363C0C190C3A332179" + "415B343905563E37645E68007053315A1A20286E7C3B4320424A5F7F36635558686C3565" + "762122237D344A411C0F00342135776753461D105C21111E5024434E5E0F275D12061658" + "4435410F210E5228532D214F505D0F0B3C34032C7C597F6159665E664C682C5A6C03212E" + "71333C3A642D796A65642E151827086E2D671C130B172C43192C792D294440630163526D" + "0658537A073E0F32231E7426593230692A4468386D3511542F1A6F71440128466E510445" + "294F4465113D1B1A711D4D67691363093B680854322B041C2F72524A513E5F0E407C6233" + "1728520E6C0C09107C26737B78287231661952283619647A6241391940297D2067036D44" + "3C64766918236C51175A636F000A2E5A4C5B725D5500652B1C39283037723F0255092976" + "6F2D204F0E616F1233206B75661B0F755E1E3807491079663A191C0B2D5E363B3768663A" + "4E222A1D32015D3D783E5148313F05713B140347231C59243648313C23770F554E012715" + "3350597775274A580306202E65265957291F490F642A2E7C6700716400617C7E6A303266" + "523B102906195E003C2D111A7D4740122C6941003726602B59263B5C09473D4E025E3541" + "701B122D340A3D145436137002687E4C470D2F6F4C357A3245384D737B734E2274301179" + "402473486311156E5A0C78644C593273"; +const std::string kTestWrappedPrivateKey = + "4F724B065326371A2F5F6F51467C2E26555C453B5C7C1B4F2738454B782E3E7B5340435A" + "66374D0612052C521A233D7A67194871751C78575E5177070130264C4F037633320E667B" + "1A49192924491338693D106E6113014A733A241A1A033E28352178146B4F543D38104A59" + "19120325502C31365506096D59585E08774B5B567A7B5D03451E6B11633E52672C226103" + "104B3E4C031A6403050F3A574D2C501711773802741F7F3A0D364757101D02181C7D4D35" + "207167506A424C094E4A72316F791F162D76657D2B5D3C2D7B273A286927717561316518" + "7E55282430491467086425432347701C3116446D21645C756B2D3D0F797C3220322D622A" + "254D0B7D4F1D5D0C0A36755D1246741A34783C45157247091C78232B7D2E0E1F637A2A37" + "39085D76166747034350613969072F5B5C5B21657E470C7E513B3F091D74455A3A073705" + "7B7E3B5337191D4E7536087C334B6028530F3F5B23380B6A076031294501003D6D1F240F" + "63053D5D0B271B6A0F26185650731308660B0447566041684F584C22216E567D3B775569" + "5F7F3D6B64525E7227165948101540243C19495C4C702F37490F26613353797825624143" + "263043020E1E6760123D51056F2F1E482F2E3D021B27677D3E7E3C0C11757C3448275E08" + "382E111263644C6D224714706D760A054A586E17505C3429575A41043F184209"; +const std::string kTestCertificateFileData = + "0ABD09080110011AB6090ABC05124B035F3D256A656F0E505A085E7A6C482B61035E0C4A" + "540F7803137F4C3B45206B7F33347F4D7A005E56400F0955011F4E07072D0D4678181746" + "0974326A516E3944385760280E4F166B380F033D045231201E6146041C3A6F01345C5930" + "0D32592732192C0F23105863067B31467B1477010D6F1D1944272509572A26217E1E6F7B" + "666F46153E7749106E4876046819467E164A731773155B3236537D5128682014174D1250" + "63380E48356A370B5015416A7F672F132E37364E154B41540F440E47092775531508495F" + "1E55576F363C0C190C3A332179415B343905563E37645E68007053315A1A20286E7C3B43" + "20424A5F7F36635558686C3565762122237D344A411C0F00342135776753461D105C2111" + "1E5024434E5E0F275D120616584435410F210E5228532D214F505D0F0B3C34032C7C597F" + "6159665E664C682C5A6C03212E71333C3A642D796A65642E151827086E2D671C130B172C" + "43192C792D294440630163526D0658537A073E0F32231E7426593230692A4468386D3511" + "542F1A6F71440128466E510445294F4465113D1B1A711D4D67691363093B680854322B04" + "1C2F72524A513E5F0E407C62331728520E6C0C09107C26737B7828723166195228361964" + "7A6241391940297D2067036D443C64766918236C51175A636F000A2E5A4C5B725D550065" + "2B1C39283037723F02550929766F2D204F0E616F1233206B75661B0F755E1E3807491079" + "663A191C0B2D5E363B3768663A4E222A1D32015D3D783E5148313F05713B140347231C59" + "243648313C23770F554E0127153350597775274A580306202E65265957291F490F642A2E" + "7C6700716400617C7E6A303266523B102906195E003C2D111A7D4740122C694100372660" + "2B59263B5C09473D4E025E3541701B122D340A3D145436137002687E4C470D2F6F4C357A" + "3245384D737B734E2274301179402473486311156E5A0C78644C59327312F4034F724B06" + "5326371A2F5F6F51467C2E26555C453B5C7C1B4F2738454B782E3E7B5340435A66374D06" + "12052C521A233D7A67194871751C78575E5177070130264C4F037633320E667B1A491929" + "24491338693D106E6113014A733A241A1A033E28352178146B4F543D38104A5919120325" + "502C31365506096D59585E08774B5B567A7B5D03451E6B11633E52672C226103104B3E4C" + "031A6403050F3A574D2C501711773802741F7F3A0D364757101D02181C7D4D3520716750" + "6A424C094E4A72316F791F162D76657D2B5D3C2D7B273A2869277175613165187E552824" + "30491467086425432347701C3116446D21645C756B2D3D0F797C3220322D622A254D0B7D" + "4F1D5D0C0A36755D1246741A34783C45157247091C78232B7D2E0E1F637A2A3739085D76" + "166747034350613969072F5B5C5B21657E470C7E513B3F091D74455A3A0737057B7E3B53" + "37191D4E7536087C334B6028530F3F5B23380B6A076031294501003D6D1F240F63053D5D" + "0B271B6A0F26185650731308660B0447566041684F584C22216E567D3B7755695F7F3D6B" + "64525E7227165948101540243C19495C4C702F37490F2661335379782562414326304302" + "0E1E6760123D51056F2F1E482F2E3D021B27677D3E7E3C0C11757C3448275E08382E1112" + "63644C6D224714706D760A054A586E17505C3429575A41043F1842091220F8D0A23D4B1B" + "C7B23A38B921BC1EA8938D1FD22FF9A389B58DA856A3E2625F27"; - std::string kPsshData1 = "0801121093789920E8D6520098577DF8F2DD5546"; - std::string kPsshData2 = "0801121030313233343536373839616263646566"; - std::string kPsshData3 = "08011210e02562e04cd55351b14b3d748d36ed8e"; +struct LicenseInfo { + std::string key_set_id; + DeviceFiles::LicenseState license_state; + std::string pssh_data; + std::string key_request; + std::string key_response; + std::string key_renewal_request; + std::string key_renewal_response; + std::string key_release_url; + std::string file_data; +}; - std::string kKeyRequest1 = "0801128D0C0ACF0B080112EF090AB0020802121007D9FFDE" - "13AA95C122678053362136BD18D6DDC78C05228E023082010A0282010100B2873F12C61" - "8883986B67A404733168D34676EA3261231B4EB12CB6F862AAE529D21B449D78E7A8FBB" - "9CC1A8AE119D89EC571EA23EE92517AFB86EA089E81CF3A773AEDEDFC1979AEE115D657" - "FE372CBF4178E57F60BEDD15491EF28891ED8663437019C0EF5F79FD0ADDAAE87281DD9" - "10ED10356DD6B606C0DD2452343329151BD23A42060869252892BD2C0368787E880F716" - "438E5835BC93FED65D0017D8758E20E5E614B44BAFAF7286484624CC5EB109D85F1C048" - "9B25A75528C0CD23961067192EF65766EAB5E97FE7EBE25D63DDE41BB34048B8A378EAE" - "AEBC86C44AB4AA757F29B48C9476660DCC4EE656EB35AC309213399CBEF38394FE32B88" - "3F9B02030100012899203001128002435CAFA39AD7DC12A40F943FE8DE10C5DD911059D" - "7B0990742E11419B2E6E797EAE46B5D017F0EF3F429AA43E4F570BBE46F56BBCEACBDFA" - "8495E4451249D39D5FEFC4F5557498B233C4C9C101E2388E107582D3C3FBF266AB9FE96" - "A40405A2098583B33D7AD083E105458DF7AE92FD517D538B4671614DA0D167F9661EE0C" - "574AB95A5BE87B1BEE27659D875A37A1730F84033ACB6BCA8D3C575A2692E7390F062BC" - "E1E58D9C621A1A11CB215615ED0C5C12"; - std::string kKeyRequest2 = "0801128D0C0ACF0B080112EF090AB0020802121007D9FFDE" - "13AA95C122678053362136BD188BDCC78C05228E023082010A0282010100C53F667AAA8" - "27758D3ABF81DAFA223373473EE13E4C500A09CD35CE2620D5DA0D987A237EBFCE1F793" - "D6F88187749A85B3AF4D737899EF19C3029461E95202E9DF91DD5869FE482FCA17160AF" - "7AC380D03880EBE3EA55B7FB3ED5ED6B9FAD19234AA75BB642A9A86AEF7CDA86F7B2EC7" - "C40C31C380F95C1F3C1564B012D530A7971AA656BBD27DAB715F0D5771FB7608453AF88" - "A331E3E5852F6F9B076A95C807307E541C39216D64B02D180A1009AFB60777EF7C72275" - "033ABD6FDF12145FFD379B6CB25F1127A783A40D65BF95AB74A08E39209EF2C0341A92A" - "217FD223734C7D1F0DC0D5B50FBA521FF1F841D1E5ED18DFC13A6D28E5A0A4FACBE6C78" - "31B5020301000128992030011280022DBC4444671C0C4CF0FBB3BB30C80C78FAFA67223" - "C6FE3F0BB495B1959B7472E8A64BC13838B3FB731FAC0609FEAB325FE8B7358C2F63D5E" - "E6C0380B85DBA3FE207B9D6AE7C31467A36A9E8D3D786CC4C383BC2774131D6C099D06D" - "052ED13A4545A0026A4FC949DE3B79D1C0C6A0D581A3D3598E46D3E4AC827C94045D693" - "37CAF103CC8839EA7FF020C0721AB17B140640C5E31C1727073CA48F445DA5EE55521D8" - "85732BAA17BD672B62C717ADBC13F235"; - std::string kKeyRequest3 = "0801128D0C0ACF0B080112EF090AB0020802121007D9FFDE" - "13AA95C122678053362136BD18D6DDC78C05228E023082010A0282010100B2873F12C61" - "8883986B67A404733168D34676EA3261231B4EB12CB6F862AAE529D21B449D78E7A8FBB" - "9CC1A8AE119D89EC571EA23EE92517AFB86EA089E81CF3A773AEDEDFC1979AEE115D657" - "FE372CBF4178E57F60BEDD15491EF28891ED8663437019C0EF5F79FD0ADDAAE87281DD9" - "10ED10356DD6B606C0DD2452343329151BD23A42060869252892BD2C0368787E880F716" - "438E5835BC93FED65D0017D8758E20E5E614B44BAFAF7286484624CC5EB109D85F1C048" - "9B25A75528C0CD23961067192EF65766EAB5E97FE7EBE25D63DDE41BB34048B8A378EAE" - "AEBC86C44AB4AA757F29B48C9476660DCC4EE656EB35AC309213399CBEF38394FE32B88" - "3F9B02030100012899203001128002435CAFA39AD7DC12A40F943FE8DE10C5DD911059D" - "7B0990742E11419B2E6E797EAE46B5D017F0EF3F429AA43E4F570BBE46F56BBCEACBDFA" - "8495E4451249D39D5FEFC4F5557498B233C4C9C101E2388E107582D3C3FBF266AB9FE96" - "A40405A2098583B33D7AD083E105458DF7AE92FD517D538B4671614DA0D167F9661EE0C" - "574AB95A5BE87B1BEE27659D875A37A1730F84033ACB6BCA8D3C575A2692E7390F062BC" - "E1E58D9C621A1A11CB215615ED0C5C12"; +size_t kNumberOfLicenses = 3; +LicenseInfo license_test_data[] = { + // license 0 + {"ksid54C57C966E23CEF5", DeviceFiles::kLicenseStateActive, + wvcdm::a2bs_hex("0801121030313233343536373839414243444546"), + wvcdm::a2bs_hex( + "080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591B" + "C4D07A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD" + "55FB685FDB3025574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EF" + "FA0E5DFC3DE9A34BA5F08BE349553C319A9FB274905A8770ADC9CA4A2CBC" + "D8E556A1587FA18BFD4D286C644A6904F19EAAFBDFADD3B371B306D0B289" + "F459B491C814B5AD1F747610E990A60248A7DA5152F1CCFC047EF4230013" + "1F9C4758F4D9F30579393B860AAD9AD2EE43D721D6DB9F5800EF188386B9" + "4825AE05A883AC976D6970DF43EA6C83B86CE6D0F540207725B9890FCCEC" + "83A49027872DAFD2740B7748E9C3B1752D6F12859CED07E8882969B433EC" + "66F17FFC29AC6FDBEA79230B0FAED5D94CF6B829A420BBE3270323941776" + "EE60DD6BFD660BDDCA870203010001288001300112800250D1F8B1ECF849" + "B60FF93E37C4DEEF09E6FFB10BCFC996A4A24B7AA96928835ED5A72E1584" + "6D0A14015733239BD8B6E6D5E5D229B08394CE1E0692C159C44337CA7CAF" + "88476449B068D9D2FADED8EB1BC0F4B8F0FCAF293E8690E7403209534180" + "3408A0E8279E545945EE97838FDE7812F7171C3CC4F5ECF9418BBF1D336C" + "E58F4CBB1B44D4ADE6BF3364BAE7EC093281846E569E13E7719014030A60" + "59044FE7BBFF3E8F5723AEDD54DC6E0D041B309D7700B55575690E95CE60" + "85D0914F991C5F45E98CBB9C45BA33F47FD0862EBCC7EEBA8E60643C86E8" + "5476B18AEF8DE871571A75681A75F75028A5B58751C09A5296AAE99CEDCD" + "9785E9E2103240D40A1AB6050AB002080112102CE5CCF42200D6B5BCCF33" + "D7CC2D9C7018EAD1B88D05228E023082010A0282010100BE1B661EEC4700" + "DF4B0C83292D02AE029B8A224DD3048125049F74E30E1257FC2BE8D9CFAF" + "0BFFCACAF7305351771C78FA451F13AF5EEBFB360941A4396A805833730D" + "C6E534C62408B7C5076FC22568021C59ED34F98487196DA32078DAFCA37C" + "7CFB8E79612FA384963DF2167D5E87305D7BC92D621C10160672C27B9A69" + "1B1534F60D78C5893E40C5FF8A3F9DF8898612E9A5CCB56F4A0CC2A61A7A" + "EB04A9DCC015D9BC37DEF2AB9EAA9AAFD838869081D9249755F129BB0DBE" + "CA3B894975A65A36FD005CE77CD407E925D3172E33122A11D327968A08F8" + "E771FAEB2540EB52D17C4906405F47C31F60F0AF6C78AF53291B236E692B" + "506A2AF92AF43E3A81020301000128800130011280033A08A60418E5C81B" + "8D71A5C0A28C26999FF4FA992E14107CA8A9E6A2B36259834000FE35DCD8" + "14426F9C5D332418ED94C9C0C992217B1B6DC01C99085A3C3956C8267B87" + "73BABCF3F2C841C67D830F9DBC780DD68BF4E2FE424C6A54123BE4B2A1F7" + "E1F4DB58AB1164DAE9CF75C3392284A44B8CDB85D837E86C6B908243987E" + "552C8693878C9A1B7BEA3759783036F1595C406D6CBBA7F8642A9B3B244D" + "AA1F00531D0B908ADE4B533FD9FAFA21D0FB0C033D2AD5DDF24C60F4FAC3" + "0820758877F2E1A78EB44E9336DCFAFDF572BB22A84A5DEFDF2EB87B61DE" + "26EE9C4CEAA646A2AFDB2BB953845E6D7FE6F79A9501D1C379C96961316B" + "5D2A66F38C222091AF74141B6CAF93507485A5D8F82808025451824F00C8" + "B6A0CD5803F6564584138C8B18BC679B442D837307B5CC90B1FD1FD32288" + "B4A5D18D2D80E5E6A7A9EFD255B8B363038BCC67AF534EAEE4A5903E304E" + "ED4990BB5BE735DB027A6DE35329D321EC051B956C55A5B11674017517FC" + "C3C7FF7397C13A7B7087A1F6AEC7F6761A130A0C636F6D70616E795F6E61" + "6D6512034C47451A150A0A6D6F64656C5F6E616D6512074E657875732034" + "1A200A116172636869746563747572655F6E616D65120B61726D65616269" + "2D7637611A130A0B6465766963655F6E616D6512046D616B6F1A150A0C70" + "726F647563745F6E616D6512056F6363616D1A440A0A6275696C645F696E" + "666F1236676F6F676C652F6F6363616D2F6D616B6F3A342E332F4A425F4D" + "52322F3731343239313A7573657264656275672F6465762D6B6579731A21" + "0A096465766963655F696412144C474D4332303132313030353030363339" + "32373812250A230A14080112103031323334353637383941424344454610" + "021A09393837363534333231180120002A0C333934303739343733370000" + "30151A8002734FBDED946EB74A1B61811C4C4A491214F6BEA125A80F0141" + "65B28AA97AD0AF60E4D129EB7F424AD659F24E5EED4B702BEE328E38B72C" + "A6F38CD0ECFD2E6D7B98147744C9B8A9610B3BDFE17675FF7D584C5BF680" + "64B0FE513FC322C9148795E4C2F2443C3024F5C1F29E6FEFB6D77005DAB2" + "2CD2B63131908DE4D88795BB931CEA38452CC568BE25032245E372F07A12" + "97F51748C7EA02F2C88360AFE7ABBC71DCDD5366126258E5AFA27C2A20B3" + "39FA1E7AE925B494B361F6F7116F20BE8EE6E446146027F4FD4300F4A0B0" + "A3361EE34925F338D0AACF20AE919B4BAE81C1D57A8D2B8FA38732A57697" + "C316C180717C182A971C94E4AC4C7DF8F161CB8CC1"), + wvcdm::a2bs_hex( + "080212CC020A190A0939383736353433323112084B9F26DAB8B06E112002" + "2800124108011801301E4239687474703A2F2F6B69723033666370673137" + "342E7769646576696E652E6E65742F7769646576696E652F6367692D6269" + "6E2F64726D2E6367691A6612102531DFD6CCEA511D00F8C0172F1189AA1A" + "5057FF9D9DBD5A205B1DEB075E4A90467C1E074CDE6071BFF831AD590BD5" + "D117614F33CE2C3CE1824FC0D54B36ECEAE58DF5C8F9347C2FEED17A3327" + "E8F52B8ECA6313A1FA6A042EB9525DD328113C05F920011A7E0A10303132" + "3334353637383941424344454612106D23622142B58F6D1EDD33AF3ECD2C" + "7E1A20884EE13BEA9DECDDBF68B532131C82B11CEC4D23C7FA9F3EF4C5EE" + "172E7C9736200242340A2050BFE71BB1BA683E35E0B49BB33048E5103FBB" + "B9C3E1CD6EBCDA7DD485DBAF431210D69D6F14C95CB6CFDB998E50D00F4D" + "A020DBDFA68F051A20AE5D6895E70F86F42F5FE3C58A505A865D05AB94B1" + "ABAA6CC59C3322F61C458D228002331F2BE95B5C796E0921CC27A7295501" + "DA10044E5CA36C0E2866FF068EA3515A6786BD5D60D74D80C6BA8BE6AAD0" + "85AF967909A143171E9CDDE36EA528402867CD04FB6F97A150CDE55F9B81" + "9F4104BEF48E4280D76645569E10AEF524D34D865B5B9E3EBC66C45EEBBE" + "16AB04493E7AEC4F99E7A99F3FC08FA431BECCC1978A079FA4801DB75E13" + "29A9921604E6F80CB148AA2DD5C8348057E9F4FC2AEA57EA4D215D0A8D48" + "6294860DFB4F4C42D57D9542B76179E179DD4AA23F9F7B2AE432B39E4CE8" + "F156E84877DDA781AAAAFC797FF75AFE2019ADC3A2E419BF0253C705BD47" + "97A96866AC4C059AD8F2E9C6B617C60C6ADCDB894C25F0C7D29252F52FD5"), + wvcdm::a2bs_hex( + "08011231121D1A1B0A190A0939383736353433323112084B9F26DAB8B06E" + "112002280018022A0C31353532333030360000000030151A20C30375683C" + "4D2033E05DCC95DDFB278CFB5125A021C3C043A16ACC933A768A27"), + wvcdm::a2bs_hex( + "0802123B0A190A0939383736353433323112084B9F26DAB8B06E11200228" + "0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF" + "A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0" + "BEA6CABACA1C2C"), + "https://jmt17.google.com/video-dev/license/GetCencLicense", + wvcdm::a2bs_hex( + "0AAF150802100122A8150801121408011210303132333435363738394142" + "434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E" + "C5335E346F591BC4D07A7D507618A5D3A68F05228E023082010A02820101" + "00A947904B8DBD55FB685FDB3025574517CCCC74EE4FEAF6629D5179A52F" + "F85CE7409528EFFA0E5DFC3DE9A34BA5F08BE349553C319A9FB274905A87" + "70ADC9CA4A2CBCD8E556A1587FA18BFD4D286C644A6904F19EAAFBDFADD3" + "B371B306D0B289F459B491C814B5AD1F747610E990A60248A7DA5152F1CC" + "FC047EF42300131F9C4758F4D9F30579393B860AAD9AD2EE43D721D6DB9F" + "5800EF188386B94825AE05A883AC976D6970DF43EA6C83B86CE6D0F54020" + "7725B9890FCCEC83A49027872DAFD2740B7748E9C3B1752D6F12859CED07" + "E8882969B433EC66F17FFC29AC6FDBEA79230B0FAED5D94CF6B829A420BB" + "E3270323941776EE60DD6BFD660BDDCA8702030100012880013001128002" + "50D1F8B1ECF849B60FF93E37C4DEEF09E6FFB10BCFC996A4A24B7AA96928" + "835ED5A72E15846D0A14015733239BD8B6E6D5E5D229B08394CE1E0692C1" + "59C44337CA7CAF88476449B068D9D2FADED8EB1BC0F4B8F0FCAF293E8690" + "E74032095341803408A0E8279E545945EE97838FDE7812F7171C3CC4F5EC" + "F9418BBF1D336CE58F4CBB1B44D4ADE6BF3364BAE7EC093281846E569E13" + "E7719014030A6059044FE7BBFF3E8F5723AEDD54DC6E0D041B309D7700B5" + "5575690E95CE6085D0914F991C5F45E98CBB9C45BA33F47FD0862EBCC7EE" + "BA8E60643C86E85476B18AEF8DE871571A75681A75F75028A5B58751C09A" + "5296AAE99CEDCD9785E9E2103240D40A1AB6050AB002080112102CE5CCF4" + "2200D6B5BCCF33D7CC2D9C7018EAD1B88D05228E023082010A0282010100" + "BE1B661EEC4700DF4B0C83292D02AE029B8A224DD3048125049F74E30E12" + "57FC2BE8D9CFAF0BFFCACAF7305351771C78FA451F13AF5EEBFB360941A4" + "396A805833730DC6E534C62408B7C5076FC22568021C59ED34F98487196D" + "A32078DAFCA37C7CFB8E79612FA384963DF2167D5E87305D7BC92D621C10" + "160672C27B9A691B1534F60D78C5893E40C5FF8A3F9DF8898612E9A5CCB5" + "6F4A0CC2A61A7AEB04A9DCC015D9BC37DEF2AB9EAA9AAFD838869081D924" + "9755F129BB0DBECA3B894975A65A36FD005CE77CD407E925D3172E33122A" + "11D327968A08F8E771FAEB2540EB52D17C4906405F47C31F60F0AF6C78AF" + "53291B236E692B506A2AF92AF43E3A81020301000128800130011280033A" + "08A60418E5C81B8D71A5C0A28C26999FF4FA992E14107CA8A9E6A2B36259" + "834000FE35DCD814426F9C5D332418ED94C9C0C992217B1B6DC01C99085A" + "3C3956C8267B8773BABCF3F2C841C67D830F9DBC780DD68BF4E2FE424C6A" + "54123BE4B2A1F7E1F4DB58AB1164DAE9CF75C3392284A44B8CDB85D837E8" + "6C6B908243987E552C8693878C9A1B7BEA3759783036F1595C406D6CBBA7" + "F8642A9B3B244DAA1F00531D0B908ADE4B533FD9FAFA21D0FB0C033D2AD5" + "DDF24C60F4FAC30820758877F2E1A78EB44E9336DCFAFDF572BB22A84A5D" + "EFDF2EB87B61DE26EE9C4CEAA646A2AFDB2BB953845E6D7FE6F79A9501D1" + "C379C96961316B5D2A66F38C222091AF74141B6CAF93507485A5D8F82808" + "025451824F00C8B6A0CD5803F6564584138C8B18BC679B442D837307B5CC" + "90B1FD1FD32288B4A5D18D2D80E5E6A7A9EFD255B8B363038BCC67AF534E" + "AEE4A5903E304EED4990BB5BE735DB027A6DE35329D321EC051B956C55A5" + "B11674017517FCC3C7FF7397C13A7B7087A1F6AEC7F6761A130A0C636F6D" + "70616E795F6E616D6512034C47451A150A0A6D6F64656C5F6E616D651207" + "4E6578757320341A200A116172636869746563747572655F6E616D65120B" + "61726D656162692D7637611A130A0B6465766963655F6E616D6512046D61" + "6B6F1A150A0C70726F647563745F6E616D6512056F6363616D1A440A0A62" + "75696C645F696E666F1236676F6F676C652F6F6363616D2F6D616B6F3A34" + "2E332F4A425F4D52322F3731343239313A7573657264656275672F646576" + "2D6B6579731A210A096465766963655F696412144C474D43323031323130" + "3035303036333932373812250A230A140801121030313233343536373839" + "41424344454610021A09393837363534333231180120002A0C3339343037" + "3934373337000030151A8002734FBDED946EB74A1B61811C4C4A491214F6" + "BEA125A80F014165B28AA97AD0AF60E4D129EB7F424AD659F24E5EED4B70" + "2BEE328E38B72CA6F38CD0ECFD2E6D7B98147744C9B8A9610B3BDFE17675" + "FF7D584C5BF68064B0FE513FC322C9148795E4C2F2443C3024F5C1F29E6F" + "EFB6D77005DAB22CD2B63131908DE4D88795BB931CEA38452CC568BE2503" + "2245E372F07A1297F51748C7EA02F2C88360AFE7ABBC71DCDD5366126258" + "E5AFA27C2A20B339FA1E7AE925B494B361F6F7116F20BE8EE6E446146027" + "F4FD4300F4A0B0A3361EE34925F338D0AACF20AE919B4BAE81C1D57A8D2B" + "8FA38732A57697C316C180717C182A971C94E4AC4C7DF8F161CB8CC122F6" + "04080212CC020A190A0939383736353433323112084B9F26DAB8B06E1120" + "022800124108011801301E4239687474703A2F2F6B697230336663706731" + "37342E7769646576696E652E6E65742F7769646576696E652F6367692D62" + "696E2F64726D2E6367691A6612102531DFD6CCEA511D00F8C0172F1189AA" + "1A5057FF9D9DBD5A205B1DEB075E4A90467C1E074CDE6071BFF831AD590B" + "D5D117614F33CE2C3CE1824FC0D54B36ECEAE58DF5C8F9347C2FEED17A33" + "27E8F52B8ECA6313A1FA6A042EB9525DD328113C05F920011A7E0A103031" + "323334353637383941424344454612106D23622142B58F6D1EDD33AF3ECD" + "2C7E1A20884EE13BEA9DECDDBF68B532131C82B11CEC4D23C7FA9F3EF4C5" + "EE172E7C9736200242340A2050BFE71BB1BA683E35E0B49BB33048E5103F" + "BBB9C3E1CD6EBCDA7DD485DBAF431210D69D6F14C95CB6CFDB998E50D00F" + "4DA020DBDFA68F051A20AE5D6895E70F86F42F5FE3C58A505A865D05AB94" + "B1ABAA6CC59C3322F61C458D228002331F2BE95B5C796E0921CC27A72955" + "01DA10044E5CA36C0E2866FF068EA3515A6786BD5D60D74D80C6BA8BE6AA" + "D085AF967909A143171E9CDDE36EA528402867CD04FB6F97A150CDE55F9B" + "819F4104BEF48E4280D76645569E10AEF524D34D865B5B9E3EBC66C45EEB" + "BE16AB04493E7AEC4F99E7A99F3FC08FA431BECCC1978A079FA4801DB75E" + "1329A9921604E6F80CB148AA2DD5C8348057E9F4FC2AEA57EA4D215D0A8D" + "486294860DFB4F4C42D57D9542B76179E179DD4AA23F9F7B2AE432B39E4C" + "E8F156E84877DDA781AAAAFC797FF75AFE2019ADC3A2E419BF0253C705BD" + "4797A96866AC4C059AD8F2E9C6B617C60C6ADCDB894C25F0C7D29252F52F" + "D52A5708011231121D1A1B0A190A0939383736353433323112084B9F26DA" + "B8B06E112002280018022A0C31353532333030360000000030151A20C303" + "75683C4D2033E05DCC95DDFB278CFB5125A021C3C043A16ACC933A768A27" + "32610802123B0A190A0939383736353433323112084B9F26DAB8B06E1120" + "02280112001A16200342120A106B63746C0000000000ECDCBE0000000020" + "DBDFA68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA687" + "8FF0BEA6CABACA1C2C3A3968747470733A2F2F6A6D7431372E676F6F676C" + "652E636F6D2F766964656F2D6465762F6C6963656E73652F47657443656E" + "634C6963656E73651220F6974C1CFFD00E3144488FC092D3DF4F6007A3CA" + "C4756EB046DC74B1C2E512CC")}, + // license 1 + {"ksidC8EAA2579A282EB0", DeviceFiles::kLicenseStateReleasing, + wvcdm::a2bs_hex("0801121030313233343536373839414243444546"), + wvcdm::a2bs_hex( + "080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591B" + "C4D07A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD" + "55FB685FDB3025574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EF" + "FA0E5DFC3DE9A34BA5F08BE349553C319A9FB274905A8770ADC9CA4A2CBC" + "D8E556A1587FA18BFD4D286C644A6904F19EAAFBDFADD3B371B306D0B289" + "F459B491C814B5AD1F747610E990A60248A7DA5152F1CCFC047EF4230013" + "1F9C4758F4D9F30579393B860AAD9AD2EE43D721D6DB9F5800EF188386B9" + "4825AE05A883AC976D6970DF43EA6C83B86CE6D0F540207725B9890FCCEC" + "83A49027872DAFD2740B7748E9C3B1752D6F12859CED07E8882969B433EC" + "66F17FFC29AC6FDBEA79230B0FAED5D94CF6B829A420BBE3270323941776" + "EE60DD6BFD660BDDCA870203010001288001300112800250D1F8B1ECF849" + "B60FF93E37C4DEEF09E6FFB10BCFC996A4A24B7AA96928835ED5A72E1584" + "6D0A14015733239BD8B6E6D5E5D229B08394CE1E0692C159C44337CA7CAF" + "88476449B068D9D2FADED8EB1BC0F4B8F0FCAF293E8690E7403209534180" + "3408A0E8279E545945EE97838FDE7812F7171C3CC4F5ECF9418BBF1D336C" + "E58F4CBB1B44D4ADE6BF3364BAE7EC093281846E569E13E7719014030A60" + "59044FE7BBFF3E8F5723AEDD54DC6E0D041B309D7700B55575690E95CE60" + "85D0914F991C5F45E98CBB9C45BA33F47FD0862EBCC7EEBA8E60643C86E8" + "5476B18AEF8DE871571A75681A75F75028A5B58751C09A5296AAE99CEDCD" + "9785E9E2103240D40A1AB6050AB002080112102CE5CCF42200D6B5BCCF33" + "D7CC2D9C7018EAD1B88D05228E023082010A0282010100BE1B661EEC4700" + "DF4B0C83292D02AE029B8A224DD3048125049F74E30E1257FC2BE8D9CFAF" + "0BFFCACAF7305351771C78FA451F13AF5EEBFB360941A4396A805833730D" + "C6E534C62408B7C5076FC22568021C59ED34F98487196DA32078DAFCA37C" + "7CFB8E79612FA384963DF2167D5E87305D7BC92D621C10160672C27B9A69" + "1B1534F60D78C5893E40C5FF8A3F9DF8898612E9A5CCB56F4A0CC2A61A7A" + "EB04A9DCC015D9BC37DEF2AB9EAA9AAFD838869081D9249755F129BB0DBE" + "CA3B894975A65A36FD005CE77CD407E925D3172E33122A11D327968A08F8" + "E771FAEB2540EB52D17C4906405F47C31F60F0AF6C78AF53291B236E692B" + "506A2AF92AF43E3A81020301000128800130011280033A08A60418E5C81B" + "8D71A5C0A28C26999FF4FA992E14107CA8A9E6A2B36259834000FE35DCD8" + "14426F9C5D332418ED94C9C0C992217B1B6DC01C99085A3C3956C8267B87" + "73BABCF3F2C841C67D830F9DBC780DD68BF4E2FE424C6A54123BE4B2A1F7" + "E1F4DB58AB1164DAE9CF75C3392284A44B8CDB85D837E86C6B908243987E" + "552C8693878C9A1B7BEA3759783036F1595C406D6CBBA7F8642A9B3B244D" + "AA1F00531D0B908ADE4B533FD9FAFA21D0FB0C033D2AD5DDF24C60F4FAC3" + "0820758877F2E1A78EB44E9336DCFAFDF572BB22A84A5DEFDF2EB87B61DE" + "26EE9C4CEAA646A2AFDB2BB953845E6D7FE6F79A9501D1C379C96961316B" + "5D2A66F38C222091AF74141B6CAF93507485A5D8F82808025451824F00C8" + "B6A0CD5803F6564584138C8B18BC679B442D837307B5CC90B1FD1FD32288" + "B4A5D18D2D80E5E6A7A9EFD255B8B363038BCC67AF534EAEE4A5903E304E" + "ED4990BB5BE735DB027A6DE35329D321EC051B956C55A5B11674017517FC" + "C3C7FF7397C13A7B7087A1F6AEC7F6761A130A0C636F6D70616E795F6E61" + "6D6512034C47451A150A0A6D6F64656C5F6E616D6512074E657875732034" + "1A200A116172636869746563747572655F6E616D65120B61726D65616269" + "2D7637611A130A0B6465766963655F6E616D6512046D616B6F1A150A0C70" + "726F647563745F6E616D6512056F6363616D1A440A0A6275696C645F696E" + "666F1236676F6F676C652F6F6363616D2F6D616B6F3A342E332F4A425F4D" + "52322F3731343239313A7573657264656275672F6465762D6B6579731A21" + "0A096465766963655F696412144C474D4332303132313030353030363339" + "32373812250A230A14080112103031323334353637383941424344454610" + "021A09393837363534333231180120002A0C383837303136333500000000" + "30151A80023F7318E29C5A50C8ADAA4B09ADCD97B75588B17002C5C2BC9A" + "FA35C53098AF22DF5CC300407CD2E84EBE01911C785513649E2CCF4E4290" + "20D3B93F3A54748C11ECFF4D62F562A4D3E96812F663D4F761C00C3E88AB" + "D8A1DC10E017A44DD3E040775FED5F07649090D1142C9D21373CD604219E" + "24935E10F287F20B0E080FDF76B6096B24F82A3E37850DE229DE33EBCE7A" + "0FA53F652C33007EA7027F95A44C36D04CBD676EB5C0BF69508F45E0C322" + "0D1706B0B851B3FCAF7AC2370EAD80C5D1620887633A42024862FCEA9F95" + "A719AAB989C1923C6452ECB0B75AF1CAFBFB06C5EC31BBF0EE4D16ACCC9A" + "F05B77D61C4855491B3D4AC150F3BCB7AE536AF333"), + wvcdm::a2bs_hex( + "080212CC020A190A093938373635343332311208F97F2B3856CBB3DD2002" + "2800124108011801301E4239687474703A2F2F6B69723033666370673137" + "342E7769646576696E652E6E65742F7769646576696E652F6367692D6269" + "6E2F64726D2E6367691A661210C5C43FE0178AEE7B85042F749D5A40251A" + "5013A1501E0F90A64E103336944A37BAAAEAC17E46E880DF6EA23A7A890D" + "A082CBBF82710B8C3982E8AB25A208A89EEFB5250D4B2CCC2F362856E05D" + "1941E387801A19886B1F3AAE60D06EDA400087B06920011A7E0A10303132" + "333435363738394142434445461210A34D2B04D596DFE1DC29CFDF116E39" + "211A2031AD1B369D225842A14B5D5F8366F5FF8EB94AA7CD13EB45BA7291" + "68E19D5F5F200242340A20A0D6D65CC677C12B86A7A99F89F446BCFDA185" + "44B15B2FEF8349ED5C247F7BE91210ED8D58320B0F4F948F960C7D49872C" + "DE2083E5A68F051A207481A2B82C83DF3090D57EDC042711A42CF4F87E79" + "CE136DAFE25F48F4A9068322800256113CA771F4250CAD2928161D07B525" + "61019003DBFBD362F20587D51BD999D57D2B035BC115C54C8B4BC37661A6" + "6A101DE5B42D82E582309AFD8E211C947A2D33CAFB58F89EEE2DA9524614" + "0311134429D8A5D15E03A169B0EB2579DA3BD6E4322D6C46EE964F6931CF" + "9DA52FB59B1D3B9BCC5959211CC23D97690FA8E869ADF68BCDA8A1211DDB" + "EBF967617AF0BFDA73E0AE79D8A7CCED208602EDC72CEF44A02901A52EEB" + "87CF9841D186BC95A65956BAD48F3C9E43F027CC03B73DFF5CAFC0B64727" + "E2D7B3A9CF25F97C475207C8A9DF091A585288A71AE64B7B2089871F7272" + "381CCBEF55EBF3DCB21B134FE48BFD5299DCCA6B01B55EEA61F9F990D0AF"), + wvcdm::a2bs_hex( + "08011231121D1A1B0A190A093938373635343332311208F97F2B3856CBB3" + "DD2002280018022A0C33333932383235393733000030151A209ADE9B0A41" + "1583962BDA31BE5BE937E589BB3DCC06F6F4C48FBE4FAE86DC9ABA"), + wvcdm::a2bs_hex( + "0802123B0A190A093938373635343332311208F97F2B3856CBB3DD200228" + "0112001A16200342120A106B63746C00000000CA3A6A75000000002083E5" + "A68F051A20BDA6A56F7CBFD0942198F87C23A34AA5CBD64AFEB134277774" + "CCF8E789D815DD"), + "https://www.youtube.com/api/drm/" + "widevine?video_id=03681262dc412c06&source=YOUTUBE", + wvcdm::a2bs_hex( + "0AC7150802100122C0150802121408011210303132333435363738394142" + "434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E" + "C5335E346F591BC4D07A7D507618A5D3A68F05228E023082010A02820101" + "00A947904B8DBD55FB685FDB3025574517CCCC74EE4FEAF6629D5179A52F" + "F85CE7409528EFFA0E5DFC3DE9A34BA5F08BE349553C319A9FB274905A87" + "70ADC9CA4A2CBCD8E556A1587FA18BFD4D286C644A6904F19EAAFBDFADD3" + "B371B306D0B289F459B491C814B5AD1F747610E990A60248A7DA5152F1CC" + "FC047EF42300131F9C4758F4D9F30579393B860AAD9AD2EE43D721D6DB9F" + "5800EF188386B94825AE05A883AC976D6970DF43EA6C83B86CE6D0F54020" + "7725B9890FCCEC83A49027872DAFD2740B7748E9C3B1752D6F12859CED07" + "E8882969B433EC66F17FFC29AC6FDBEA79230B0FAED5D94CF6B829A420BB" + "E3270323941776EE60DD6BFD660BDDCA8702030100012880013001128002" + "50D1F8B1ECF849B60FF93E37C4DEEF09E6FFB10BCFC996A4A24B7AA96928" + "835ED5A72E15846D0A14015733239BD8B6E6D5E5D229B08394CE1E0692C1" + "59C44337CA7CAF88476449B068D9D2FADED8EB1BC0F4B8F0FCAF293E8690" + "E74032095341803408A0E8279E545945EE97838FDE7812F7171C3CC4F5EC" + "F9418BBF1D336CE58F4CBB1B44D4ADE6BF3364BAE7EC093281846E569E13" + "E7719014030A6059044FE7BBFF3E8F5723AEDD54DC6E0D041B309D7700B5" + "5575690E95CE6085D0914F991C5F45E98CBB9C45BA33F47FD0862EBCC7EE" + "BA8E60643C86E85476B18AEF8DE871571A75681A75F75028A5B58751C09A" + "5296AAE99CEDCD9785E9E2103240D40A1AB6050AB002080112102CE5CCF4" + "2200D6B5BCCF33D7CC2D9C7018EAD1B88D05228E023082010A0282010100" + "BE1B661EEC4700DF4B0C83292D02AE029B8A224DD3048125049F74E30E12" + "57FC2BE8D9CFAF0BFFCACAF7305351771C78FA451F13AF5EEBFB360941A4" + "396A805833730DC6E534C62408B7C5076FC22568021C59ED34F98487196D" + "A32078DAFCA37C7CFB8E79612FA384963DF2167D5E87305D7BC92D621C10" + "160672C27B9A691B1534F60D78C5893E40C5FF8A3F9DF8898612E9A5CCB5" + "6F4A0CC2A61A7AEB04A9DCC015D9BC37DEF2AB9EAA9AAFD838869081D924" + "9755F129BB0DBECA3B894975A65A36FD005CE77CD407E925D3172E33122A" + "11D327968A08F8E771FAEB2540EB52D17C4906405F47C31F60F0AF6C78AF" + "53291B236E692B506A2AF92AF43E3A81020301000128800130011280033A" + "08A60418E5C81B8D71A5C0A28C26999FF4FA992E14107CA8A9E6A2B36259" + "834000FE35DCD814426F9C5D332418ED94C9C0C992217B1B6DC01C99085A" + "3C3956C8267B8773BABCF3F2C841C67D830F9DBC780DD68BF4E2FE424C6A" + "54123BE4B2A1F7E1F4DB58AB1164DAE9CF75C3392284A44B8CDB85D837E8" + "6C6B908243987E552C8693878C9A1B7BEA3759783036F1595C406D6CBBA7" + "F8642A9B3B244DAA1F00531D0B908ADE4B533FD9FAFA21D0FB0C033D2AD5" + "DDF24C60F4FAC30820758877F2E1A78EB44E9336DCFAFDF572BB22A84A5D" + "EFDF2EB87B61DE26EE9C4CEAA646A2AFDB2BB953845E6D7FE6F79A9501D1" + "C379C96961316B5D2A66F38C222091AF74141B6CAF93507485A5D8F82808" + "025451824F00C8B6A0CD5803F6564584138C8B18BC679B442D837307B5CC" + "90B1FD1FD32288B4A5D18D2D80E5E6A7A9EFD255B8B363038BCC67AF534E" + "AEE4A5903E304EED4990BB5BE735DB027A6DE35329D321EC051B956C55A5" + "B11674017517FCC3C7FF7397C13A7B7087A1F6AEC7F6761A130A0C636F6D" + "70616E795F6E616D6512034C47451A150A0A6D6F64656C5F6E616D651207" + "4E6578757320341A200A116172636869746563747572655F6E616D65120B" + "61726D656162692D7637611A130A0B6465766963655F6E616D6512046D61" + "6B6F1A150A0C70726F647563745F6E616D6512056F6363616D1A440A0A62" + "75696C645F696E666F1236676F6F676C652F6F6363616D2F6D616B6F3A34" + "2E332F4A425F4D52322F3731343239313A7573657264656275672F646576" + "2D6B6579731A210A096465766963655F696412144C474D43323031323130" + "3035303036333932373812250A230A140801121030313233343536373839" + "41424344454610021A09393837363534333231180120002A0C3838373031" + "3633350000000030151A80023F7318E29C5A50C8ADAA4B09ADCD97B75588" + "B17002C5C2BC9AFA35C53098AF22DF5CC300407CD2E84EBE01911C785513" + "649E2CCF4E429020D3B93F3A54748C11ECFF4D62F562A4D3E96812F663D4" + "F761C00C3E88ABD8A1DC10E017A44DD3E040775FED5F07649090D1142C9D" + "21373CD604219E24935E10F287F20B0E080FDF76B6096B24F82A3E37850D" + "E229DE33EBCE7A0FA53F652C33007EA7027F95A44C36D04CBD676EB5C0BF" + "69508F45E0C3220D1706B0B851B3FCAF7AC2370EAD80C5D1620887633A42" + "024862FCEA9F95A719AAB989C1923C6452ECB0B75AF1CAFBFB06C5EC31BB" + "F0EE4D16ACCC9AF05B77D61C4855491B3D4AC150F3BCB7AE536AF33322F6" + "04080212CC020A190A093938373635343332311208F97F2B3856CBB3DD20" + "022800124108011801301E4239687474703A2F2F6B697230336663706731" + "37342E7769646576696E652E6E65742F7769646576696E652F6367692D62" + "696E2F64726D2E6367691A661210C5C43FE0178AEE7B85042F749D5A4025" + "1A5013A1501E0F90A64E103336944A37BAAAEAC17E46E880DF6EA23A7A89" + "0DA082CBBF82710B8C3982E8AB25A208A89EEFB5250D4B2CCC2F362856E0" + "5D1941E387801A19886B1F3AAE60D06EDA400087B06920011A7E0A103031" + "32333435363738394142434445461210A34D2B04D596DFE1DC29CFDF116E" + "39211A2031AD1B369D225842A14B5D5F8366F5FF8EB94AA7CD13EB45BA72" + "9168E19D5F5F200242340A20A0D6D65CC677C12B86A7A99F89F446BCFDA1" + "8544B15B2FEF8349ED5C247F7BE91210ED8D58320B0F4F948F960C7D4987" + "2CDE2083E5A68F051A207481A2B82C83DF3090D57EDC042711A42CF4F87E" + "79CE136DAFE25F48F4A9068322800256113CA771F4250CAD2928161D07B5" + "2561019003DBFBD362F20587D51BD999D57D2B035BC115C54C8B4BC37661" + "A66A101DE5B42D82E582309AFD8E211C947A2D33CAFB58F89EEE2DA95246" + "140311134429D8A5D15E03A169B0EB2579DA3BD6E4322D6C46EE964F6931" + "CF9DA52FB59B1D3B9BCC5959211CC23D97690FA8E869ADF68BCDA8A1211D" + "DBEBF967617AF0BFDA73E0AE79D8A7CCED208602EDC72CEF44A02901A52E" + "EB87CF9841D186BC95A65956BAD48F3C9E43F027CC03B73DFF5CAFC0B647" + "27E2D7B3A9CF25F97C475207C8A9DF091A585288A71AE64B7B2089871F72" + "72381CCBEF55EBF3DCB21B134FE48BFD5299DCCA6B01B55EEA61F9F990D0" + "AF2A5708011231121D1A1B0A190A093938373635343332311208F97F2B38" + "56CBB3DD2002280018022A0C33333932383235393733000030151A209ADE" + "9B0A411583962BDA31BE5BE937E589BB3DCC06F6F4C48FBE4FAE86DC9ABA" + "32610802123B0A190A093938373635343332311208F97F2B3856CBB3DD20" + "02280112001A16200342120A106B63746C00000000CA3A6A750000000020" + "83E5A68F051A20BDA6A56F7CBFD0942198F87C23A34AA5CBD64AFEB13427" + "7774CCF8E789D815DD3A5168747470733A2F2F7777772E796F7574756265" + "2E636F6D2F6170692F64726D2F7769646576696E653F766964656F5F6964" + "3D3033363831323632646334313263303626736F757263653D594F555455" + "42451220EC449C6B026C43004743061B3A3DCB7208B2AD11600254841B96" + "1CFA1AD57172")}, + // license 2 + {"ksidE8C37662C88DC673", DeviceFiles::kLicenseStateReleasing, + wvcdm::a2bs_hex("0801121030313233343536373839414243444546"), + wvcdm::a2bs_hex( + "080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591B" + "C4D07A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD" + "55FB685FDB3025574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EF" + "FA0E5DFC3DE9A34BA5F08BE349553C319A9FB274905A8770ADC9CA4A2CBC" + "D8E556A1587FA18BFD4D286C644A6904F19EAAFBDFADD3B371B306D0B289" + "F459B491C814B5AD1F747610E990A60248A7DA5152F1CCFC047EF4230013" + "1F9C4758F4D9F30579393B860AAD9AD2EE43D721D6DB9F5800EF188386B9" + "4825AE05A883AC976D6970DF43EA6C83B86CE6D0F540207725B9890FCCEC" + "83A49027872DAFD2740B7748E9C3B1752D6F12859CED07E8882969B433EC" + "66F17FFC29AC6FDBEA79230B0FAED5D94CF6B829A420BBE3270323941776" + "EE60DD6BFD660BDDCA870203010001288001300112800250D1F8B1ECF849" + "B60FF93E37C4DEEF09E6FFB10BCFC996A4A24B7AA96928835ED5A72E1584" + "6D0A14015733239BD8B6E6D5E5D229B08394CE1E0692C159C44337CA7CAF" + "88476449B068D9D2FADED8EB1BC0F4B8F0FCAF293E8690E7403209534180" + "3408A0E8279E545945EE97838FDE7812F7171C3CC4F5ECF9418BBF1D336C" + "E58F4CBB1B44D4ADE6BF3364BAE7EC093281846E569E13E7719014030A60" + "59044FE7BBFF3E8F5723AEDD54DC6E0D041B309D7700B55575690E95CE60" + "85D0914F991C5F45E98CBB9C45BA33F47FD0862EBCC7EEBA8E60643C86E8" + "5476B18AEF8DE871571A75681A75F75028A5B58751C09A5296AAE99CEDCD" + "9785E9E2103240D40A1AB6050AB002080112102CE5CCF42200D6B5BCCF33" + "D7CC2D9C7018EAD1B88D05228E023082010A0282010100BE1B661EEC4700" + "DF4B0C83292D02AE029B8A224DD3048125049F74E30E1257FC2BE8D9CFAF" + "0BFFCACAF7305351771C78FA451F13AF5EEBFB360941A4396A805833730D" + "C6E534C62408B7C5076FC22568021C59ED34F98487196DA32078DAFCA37C" + "7CFB8E79612FA384963DF2167D5E87305D7BC92D621C10160672C27B9A69" + "1B1534F60D78C5893E40C5FF8A3F9DF8898612E9A5CCB56F4A0CC2A61A7A" + "EB04A9DCC015D9BC37DEF2AB9EAA9AAFD838869081D9249755F129BB0DBE" + "CA3B894975A65A36FD005CE77CD407E925D3172E33122A11D327968A08F8" + "E771FAEB2540EB52D17C4906405F47C31F60F0AF6C78AF53291B236E692B" + "506A2AF92AF43E3A81020301000128800130011280033A08A60418E5C81B" + "8D71A5C0A28C26999FF4FA992E14107CA8A9E6A2B36259834000FE35DCD8" + "14426F9C5D332418ED94C9C0C992217B1B6DC01C99085A3C3956C8267B87" + "73BABCF3F2C841C67D830F9DBC780DD68BF4E2FE424C6A54123BE4B2A1F7" + "E1F4DB58AB1164DAE9CF75C3392284A44B8CDB85D837E86C6B908243987E" + "552C8693878C9A1B7BEA3759783036F1595C406D6CBBA7F8642A9B3B244D" + "AA1F00531D0B908ADE4B533FD9FAFA21D0FB0C033D2AD5DDF24C60F4FAC3" + "0820758877F2E1A78EB44E9336DCFAFDF572BB22A84A5DEFDF2EB87B61DE" + "26EE9C4CEAA646A2AFDB2BB953845E6D7FE6F79A9501D1C379C96961316B" + "5D2A66F38C222091AF74141B6CAF93507485A5D8F82808025451824F00C8" + "B6A0CD5803F6564584138C8B18BC679B442D837307B5CC90B1FD1FD32288" + "B4A5D18D2D80E5E6A7A9EFD255B8B363038BCC67AF534EAEE4A5903E304E" + "ED4990BB5BE735DB027A6DE35329D321EC051B956C55A5B11674017517FC" + "C3C7FF7397C13A7B7087A1F6AEC7F6761A130A0C636F6D70616E795F6E61" + "6D6512034C47451A150A0A6D6F64656C5F6E616D6512074E657875732034" + "1A200A116172636869746563747572655F6E616D65120B61726D65616269" + "2D7637611A130A0B6465766963655F6E616D6512046D616B6F1A150A0C70" + "726F647563745F6E616D6512056F6363616D1A440A0A6275696C645F696E" + "666F1236676F6F676C652F6F6363616D2F6D616B6F3A342E332F4A425F4D" + "52322F3731343239313A7573657264656275672F6465762D6B6579731A21" + "0A096465766963655F696412144C474D4332303132313030353030363339" + "32373812250A230A14080112103031323334353637383941424344454610" + "021A09393837363534333231180120002A0C313038313531363936380000" + "30151A80027EA7ADEF77500FBC6A6081E739E0C50E1BDE6DE4AB39110938" + "6768A95A04A52BE6693A5A98A25AC8EB9CDD6F40DCCF86A3DA6C700E256A" + "676BD3D7E492090DCF732C57333D9370F6D7AB87661701597099CD45C2BC" + "DFF1D47183E510D7A6D3561EFC7D4EB21814CB2CA0777F26DD491B4D0146" + "9BB81A701545E2D3E98E1ADAB3A3BBD1D0433B312B3B5139E88D3A92520B" + "A399B2BE3489A72C3629745E4D8FC6DF6C8925A8FD8D6C809CA80DBC2903" + "0615A55523305BC64DDFF52A87BD0DE9EEAB6445C5A1847E5E6FE8D640C7" + "B07F3B066B911793F06E973A02FA6EDD274570C4CA982D353F1E72A5B776" + "95D554B4FB554B46F5FA5B3B00805C136A9ED21FC2"), + wvcdm::a2bs_hex( + "080212CC020A190A0939383736353433323112087AD49366C8D919132002" + "2800124108011801301E4239687474703A2F2F6B69723033666370673137" + "342E7769646576696E652E6E65742F7769646576696E652F6367692D6269" + "6E2F64726D2E6367691A6612109161841718D5D0A4C4368820F4D030721A" + "500F94F9BC0FF6B730709C6DEFD88D1CA8C7991A149D470493BDAD89E333" + "AFC949F77D995CEA5E3D3DA5F7DF84E90CD4A9B4E138EA5F7EA75A520A25" + "017D69A9460D46548259F82959304CDEFE41936BE420011A7E0A10303132" + "3334353637383941424344454612104F88BFEECE468B962BF09EA1257DA5" + "0B1A200D48C122E022033C3E67A6ED4DA99B8AEA6F4B9E78634A548C060F" + "49D39D9700200242340A209DE408B6F116F428C8E801C63AF34570A6C31D" + "72180AA11F85D8DD4BC1C4D35412104E73935C2CC38C21408C537B3A5F19" + "8B2081E7A68F051A20BC2696A2A1FBDF425675CAD455DEA2B44040D1F8F0" + "B6C675A28384CACFDF2F132280022D09FDA096972AA77FFEB09EA08AE882" + "E89AC8591B398452CFB1383CCA16611571E223FE8DE82CDE9111557B2A87" + "A253B87B822F037FB492DE4B91B8AD4DB2E2F8B2E81BF1DE36CC7520CB4B" + "B3516E18322777287310257F2EC7110332504756DA8BC873448E93BA05FD" + "1AEB7AD1016D7BBB7FF5E7111987005322E342679F3D241429AE930A479D" + "9F338699D3D6969A6479D1363AEB4AF19BDE9A73B33CD0EBFCF272FCEEC6" + "222AC08DCBD36077E0459D940BAE84ABA584700C02E70F3AE034ED7B764C" + "6EE5E85663D657270C9AB40D3109920AB1C1C5DA1358E384EDF673253C04" + "F20AA6B0CC98F421A4CD86C4C88042B0DE9902D5D00B6AD817B1A313ED5B"), + wvcdm::a2bs_hex( + "08011231121D1A1B0A190A0939383736353433323112087AD49366C8D919" + "132002280018022A0C35333631323234343600000030151A208CC3C7D328" + "DFACD43764C9FB582B858C8FF1D9863FF59C4D983478DB858AC32A"), + wvcdm::a2bs_hex( + "0802123B0A190A0939383736353433323112087AD49366C8D91913200228" + "0112001A16200342120A106B63746C000000001FF4944E000000002082E7" + "A68F051A2041EF0A9267D613D17AA90E1D1DA5BE091860E5E296D41D6D0F" + "75E73660C279B3"), + "http://hamid.kir.corp.google.com:8888/drm", + wvcdm::a2bs_hex( + "0A9F15080210012298150802121408011210303132333435363738394142" + "434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E" + "C5335E346F591BC4D07A7D507618A5D3A68F05228E023082010A02820101" + "00A947904B8DBD55FB685FDB3025574517CCCC74EE4FEAF6629D5179A52F" + "F85CE7409528EFFA0E5DFC3DE9A34BA5F08BE349553C319A9FB274905A87" + "70ADC9CA4A2CBCD8E556A1587FA18BFD4D286C644A6904F19EAAFBDFADD3" + "B371B306D0B289F459B491C814B5AD1F747610E990A60248A7DA5152F1CC" + "FC047EF42300131F9C4758F4D9F30579393B860AAD9AD2EE43D721D6DB9F" + "5800EF188386B94825AE05A883AC976D6970DF43EA6C83B86CE6D0F54020" + "7725B9890FCCEC83A49027872DAFD2740B7748E9C3B1752D6F12859CED07" + "E8882969B433EC66F17FFC29AC6FDBEA79230B0FAED5D94CF6B829A420BB" + "E3270323941776EE60DD6BFD660BDDCA8702030100012880013001128002" + "50D1F8B1ECF849B60FF93E37C4DEEF09E6FFB10BCFC996A4A24B7AA96928" + "835ED5A72E15846D0A14015733239BD8B6E6D5E5D229B08394CE1E0692C1" + "59C44337CA7CAF88476449B068D9D2FADED8EB1BC0F4B8F0FCAF293E8690" + "E74032095341803408A0E8279E545945EE97838FDE7812F7171C3CC4F5EC" + "F9418BBF1D336CE58F4CBB1B44D4ADE6BF3364BAE7EC093281846E569E13" + "E7719014030A6059044FE7BBFF3E8F5723AEDD54DC6E0D041B309D7700B5" + "5575690E95CE6085D0914F991C5F45E98CBB9C45BA33F47FD0862EBCC7EE" + "BA8E60643C86E85476B18AEF8DE871571A75681A75F75028A5B58751C09A" + "5296AAE99CEDCD9785E9E2103240D40A1AB6050AB002080112102CE5CCF4" + "2200D6B5BCCF33D7CC2D9C7018EAD1B88D05228E023082010A0282010100" + "BE1B661EEC4700DF4B0C83292D02AE029B8A224DD3048125049F74E30E12" + "57FC2BE8D9CFAF0BFFCACAF7305351771C78FA451F13AF5EEBFB360941A4" + "396A805833730DC6E534C62408B7C5076FC22568021C59ED34F98487196D" + "A32078DAFCA37C7CFB8E79612FA384963DF2167D5E87305D7BC92D621C10" + "160672C27B9A691B1534F60D78C5893E40C5FF8A3F9DF8898612E9A5CCB5" + "6F4A0CC2A61A7AEB04A9DCC015D9BC37DEF2AB9EAA9AAFD838869081D924" + "9755F129BB0DBECA3B894975A65A36FD005CE77CD407E925D3172E33122A" + "11D327968A08F8E771FAEB2540EB52D17C4906405F47C31F60F0AF6C78AF" + "53291B236E692B506A2AF92AF43E3A81020301000128800130011280033A" + "08A60418E5C81B8D71A5C0A28C26999FF4FA992E14107CA8A9E6A2B36259" + "834000FE35DCD814426F9C5D332418ED94C9C0C992217B1B6DC01C99085A" + "3C3956C8267B8773BABCF3F2C841C67D830F9DBC780DD68BF4E2FE424C6A" + "54123BE4B2A1F7E1F4DB58AB1164DAE9CF75C3392284A44B8CDB85D837E8" + "6C6B908243987E552C8693878C9A1B7BEA3759783036F1595C406D6CBBA7" + "F8642A9B3B244DAA1F00531D0B908ADE4B533FD9FAFA21D0FB0C033D2AD5" + "DDF24C60F4FAC30820758877F2E1A78EB44E9336DCFAFDF572BB22A84A5D" + "EFDF2EB87B61DE26EE9C4CEAA646A2AFDB2BB953845E6D7FE6F79A9501D1" + "C379C96961316B5D2A66F38C222091AF74141B6CAF93507485A5D8F82808" + "025451824F00C8B6A0CD5803F6564584138C8B18BC679B442D837307B5CC" + "90B1FD1FD32288B4A5D18D2D80E5E6A7A9EFD255B8B363038BCC67AF534E" + "AEE4A5903E304EED4990BB5BE735DB027A6DE35329D321EC051B956C55A5" + "B11674017517FCC3C7FF7397C13A7B7087A1F6AEC7F6761A130A0C636F6D" + "70616E795F6E616D6512034C47451A150A0A6D6F64656C5F6E616D651207" + "4E6578757320341A200A116172636869746563747572655F6E616D65120B" + "61726D656162692D7637611A130A0B6465766963655F6E616D6512046D61" + "6B6F1A150A0C70726F647563745F6E616D6512056F6363616D1A440A0A62" + "75696C645F696E666F1236676F6F676C652F6F6363616D2F6D616B6F3A34" + "2E332F4A425F4D52322F3731343239313A7573657264656275672F646576" + "2D6B6579731A210A096465766963655F696412144C474D43323031323130" + "3035303036333932373812250A230A140801121030313233343536373839" + "41424344454610021A09393837363534333231180120002A0C3130383135" + "3136393638000030151A80027EA7ADEF77500FBC6A6081E739E0C50E1BDE" + "6DE4AB391109386768A95A04A52BE6693A5A98A25AC8EB9CDD6F40DCCF86" + "A3DA6C700E256A676BD3D7E492090DCF732C57333D9370F6D7AB87661701" + "597099CD45C2BCDFF1D47183E510D7A6D3561EFC7D4EB21814CB2CA0777F" + "26DD491B4D01469BB81A701545E2D3E98E1ADAB3A3BBD1D0433B312B3B51" + "39E88D3A92520BA399B2BE3489A72C3629745E4D8FC6DF6C8925A8FD8D6C" + "809CA80DBC29030615A55523305BC64DDFF52A87BD0DE9EEAB6445C5A184" + "7E5E6FE8D640C7B07F3B066B911793F06E973A02FA6EDD274570C4CA982D" + "353F1E72A5B77695D554B4FB554B46F5FA5B3B00805C136A9ED21FC222F6" + "04080212CC020A190A0939383736353433323112087AD49366C8D9191320" + "022800124108011801301E4239687474703A2F2F6B697230336663706731" + "37342E7769646576696E652E6E65742F7769646576696E652F6367692D62" + "696E2F64726D2E6367691A6612109161841718D5D0A4C4368820F4D03072" + "1A500F94F9BC0FF6B730709C6DEFD88D1CA8C7991A149D470493BDAD89E3" + "33AFC949F77D995CEA5E3D3DA5F7DF84E90CD4A9B4E138EA5F7EA75A520A" + "25017D69A9460D46548259F82959304CDEFE41936BE420011A7E0A103031" + "323334353637383941424344454612104F88BFEECE468B962BF09EA1257D" + "A50B1A200D48C122E022033C3E67A6ED4DA99B8AEA6F4B9E78634A548C06" + "0F49D39D9700200242340A209DE408B6F116F428C8E801C63AF34570A6C3" + "1D72180AA11F85D8DD4BC1C4D35412104E73935C2CC38C21408C537B3A5F" + "198B2081E7A68F051A20BC2696A2A1FBDF425675CAD455DEA2B44040D1F8" + "F0B6C675A28384CACFDF2F132280022D09FDA096972AA77FFEB09EA08AE8" + "82E89AC8591B398452CFB1383CCA16611571E223FE8DE82CDE9111557B2A" + "87A253B87B822F037FB492DE4B91B8AD4DB2E2F8B2E81BF1DE36CC7520CB" + "4BB3516E18322777287310257F2EC7110332504756DA8BC873448E93BA05" + "FD1AEB7AD1016D7BBB7FF5E7111987005322E342679F3D241429AE930A47" + "9D9F338699D3D6969A6479D1363AEB4AF19BDE9A73B33CD0EBFCF272FCEE" + "C6222AC08DCBD36077E0459D940BAE84ABA584700C02E70F3AE034ED7B76" + "4C6EE5E85663D657270C9AB40D3109920AB1C1C5DA1358E384EDF673253C" + "04F20AA6B0CC98F421A4CD86C4C88042B0DE9902D5D00B6AD817B1A313ED" + "5B2A5708011231121D1A1B0A190A0939383736353433323112087AD49366" + "C8D919132002280018022A0C35333631323234343600000030151A208CC3" + "C7D328DFACD43764C9FB582B858C8FF1D9863FF59C4D983478DB858AC32A" + "32610802123B0A190A0939383736353433323112087AD49366C8D9191320" + "02280112001A16200342120A106B63746C000000001FF4944E0000000020" + "82E7A68F051A2041EF0A9267D613D17AA90E1D1DA5BE091860E5E296D41D" + "6D0F75E73660C279B33A29687474703A2F2F68616D69642E6B69722E636F" + "72702E676F6F676C652E636F6D3A383838382F64726D12205CD2C43C618C" + "CA27BBCB2EEBDE32B57CBD51B424FD85DAB715B7F5A87546FD40")}}; - std::string kKeyResponse1 = "080212C2040A460A0939383736353433323112086D03DC5" - "FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E7967396755464B" - "6963455954335A34626738366C77200128001283010801180120809A9E0128809A9E013" - "0AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F766964656F2D" - "6465762F6865617274626561742F63656E633F736F757263653D594F555455424526766" - "964656F5F69643D45474843364F484E624F6F266F617574683D79612E67747371617769" - "646576696E65483C503C1A661210EBA4B8B97E17DC6C45D359222B9327121A50583647C" - "BC8240C2760A79DC72DF7FC0216C0943F12307B9AAED429277A6D0024F93CCA17B7F2BA" - "C19875E041F12E6729A98E0DFF2EA9806D6C20AF801A5A3E19DAF918FC9AF230C8765DF" - "B5BE869DF7320011A80010A10E02562E04CD55351B14B3D748D36ED8E12107C9AC11A9D" - "39304821C47969729A43321A2077E79664AF73B13FC6C55B5B9B2000E174E94F7D9E9BB" - "B58A547A82FAAAF46182002280242340A202031C7ADB3C2F80975499E285CEE0AF8343A" - "F3DF50A49EEBCCEB3747C955FEFB12107E021073F71CF814A9CCAA7008B38CD81A80010" - "A100065901A64A25899A5193664ABF9AF621210874AB758C27A29677D62D2EE092905E2" - "1A207C4AD3CB7A80740215598D5E74883"; - std::string kKeyResponse2 = "080212C2040A460A0939383736353433323112080C1913B" - "45E8893481A2B596E4B58753250316A5F53785073494E6B377459354E7967396755464B" - "6963455954335A34626738366C77200128001283010801180120809A9E0128809A9E013" - "0AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F766964656F2D" - "6465762F6865617274626561742F63656E633F736F757263653D594F555455424526766" - "964656F5F69643D45474843364F484E624F6F266F617574683D79612E67747371617769" - "646576696E65483C503C1A661210A2B662DC43891A32E65E0CC31CD597B21A50046489D" - "7EE8A674A4976800FD0553075498F90FD1997A63AECFF3481BF5CA908502676FA7E7E6B" - "02179FE6C42FAE7351EF85C8494BBE0782CB4C7A5AA6E439590C2D39DD5DB2B1A3DA91E" - "3D43FA5ABDD20011A80010A10E02562E04CD55351B14B3D748D36ED8E1210B0EF7CC3B2" - "0E10F4F704DBCF3142AB611A20F684136083E458C6B2977DB4D1FC559F2AFF2C454FEDF" - "CD68F29D50D351D58DC2002280242340A20223C0E4281615524569AF90EC3BE98349B27" - "0016C5DCC4CFDF11CB55C9DCFB231210888EF5E2311B3B3407549903D8F30D771A80010" - "A100065901A64A25899A5193664ABF9AF621210F38F473DC71703C52CC970FCACFA2111" - "1A20067E176521B6176A97157EDB9D06C"; - std::string kKeyResponse3 = "080212C2040A460A0939383736353433323112086D03DC5" - "FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E7967396755464B" - "6963455954335A34626738366C77200128001283010801180120809A9E0128809A9E013" - "0AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F766964656F2D" - "6465762F6865617274626561742F63656E633F736F757263653D594F555455424526766" - "964656F5F69643D45474843364F484E624F6F266F617574683D79612E67747371617769" - "646576696E65483C503C1A661210EBA4B8B97E17DC6C45D359222B9327121A50583647C" - "BC8240C2760A79DC72DF7FC0216C0943F12307B9AAED429277A6D0024F93CCA17B7F2BA" - "C19875E041F12E6729A98E0DFF2EA9806D6C20AF801A5A3E19DAF918FC9AF230C8765DF" - "B5BE869DF7320011A80010A10E02562E04CD55351B14B3D748D36ED8E12107C9AC11A9D" - "39304821C47969729A43321A2077E79664AF73B13FC6C55B5B9B2000E174E94F7D9E9BB" - "B58A547A82FAAAF46182002280242340A202031C7ADB3C2F80975499E285CEE0AF8343A" - "F3DF50A49EEBCCEB3747C955FEFB12107E021073F71CF814A9CCAA7008B38CD81A80010" - "A100065901A64A25899A5193664ABF9AF621210874AB758C27A29677D62D2EE092905E2" - "1A207C4AD3CB7A80740215598D5E74883"; +LicenseInfo license_update_test_data[] = { + // active license + {"key_set_id_: ksid2A048BC7FAEC885A", DeviceFiles::kLicenseStateActive, + wvcdm::a2bs_hex("0801121030313233343536373839414243444546"), + wvcdm::a2bs_hex( + "080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591B" + "C4D07A7D5076189EDFB68F05228E023082010A0282010100CC1715C81AD3" + "F6F279C686F826E6D7C8961EB13318367D06B4061BBC57E3C616A226A10F" + "042CAD54D44C6484C725CD721A2A97C088E60AFF0E9C8E03477A1B56B4C5" + "55C27CEAF55024375D8D3FB352DA4AAA2E911C876CB1B36162922E9130CC" + "C5FB72F8DD41D05DE6889C4814A7344BA8C605DE399CA3CBBF1E7DE3411E" + "DFC60F9D3802C0BEE2B98FB71A5AB9C1A3D53FB55599183B84FDDC98AC30" + "96B2EF99C62B545C5DCA3371F4D27DEF2052A23F13DE42DE46B462CED2AB" + "ABB96B610A47E0620AA10D862FEB66BB4F00B13DFE61703AE872F0B4850C" + "39138FC5DE4538E27BEAC8A48CC9526401BE3B42C7C6C5D9624662081D7A" + "5A1C581EB09619DD9DD3020301000128800130011280026AB9AC42F1C17C" + "1ECFB710BF2C35383F41CF7EFAB0DFDCC69090C20DE141CB43055FD707C6" + "11CDAEE700076A1EBA32432D5C2B62A73B8B1672AD2C4303598C02D34823" + "A6BE387046937F55BB65F5B3571FDC6A1F0D947031003BA651F8E48BF33D" + "66B7A32A72CAC75EF66EF280B2D4F14FBCA70ECC508091FE83AD886A680F" + "55AB62F306435BC0043825F6A401BB9C341230127D3298B67F82767050C9" + "5769964B0B5C27A36FA76ED161ABE4B6C18556C807706509A5146ADD958A" + "F79B49EDE48CBCD6320C4DEC0BF564C5DD7E7EBA37A4CD1D27F8D80E1B69" + "31C92AC8E5C3BEC0ADAE621A3B78952485EBFC81A194BA75BBD2C821C28A" + "EB5D21CBE0A270E55E1AB6050AB002080112102CE5CCF42200D6B5BCCF33" + "D7CC2D9C7018EAD1B88D05228E023082010A0282010100BE1B661EEC4700" + "DF4B0C83292D02AE029B8A224DD3048125049F74E30E1257FC2BE8D9CFAF" + "0BFFCACAF7305351771C78FA451F13AF5EEBFB360941A4396A805833730D" + "C6E534C62408B7C5076FC22568021C59ED34F98487196DA32078DAFCA37C" + "7CFB8E79612FA384963DF2167D5E87305D7BC92D621C10160672C27B9A69" + "1B1534F60D78C5893E40C5FF8A3F9DF8898612E9A5CCB56F4A0CC2A61A7A" + "EB04A9DCC015D9BC37DEF2AB9EAA9AAFD838869081D9249755F129BB0DBE" + "CA3B894975A65A36FD005CE77CD407E925D3172E33122A11D327968A08F8" + "E771FAEB2540EB52D17C4906405F47C31F60F0AF6C78AF53291B236E692B" + "506A2AF92AF43E3A81020301000128800130011280033A08A60418E5C81B" + "8D71A5C0A28C26999FF4FA992E14107CA8A9E6A2B36259834000FE35DCD8" + "14426F9C5D332418ED94C9C0C992217B1B6DC01C99085A3C3956C8267B87" + "73BABCF3F2C841C67D830F9DBC780DD68BF4E2FE424C6A54123BE4B2A1F7" + "E1F4DB58AB1164DAE9CF75C3392284A44B8CDB85D837E86C6B908243987E" + "552C8693878C9A1B7BEA3759783036F1595C406D6CBBA7F8642A9B3B244D" + "AA1F00531D0B908ADE4B533FD9FAFA21D0FB0C033D2AD5DDF24C60F4FAC3" + "0820758877F2E1A78EB44E9336DCFAFDF572BB22A84A5DEFDF2EB87B61DE" + "26EE9C4CEAA646A2AFDB2BB953845E6D7FE6F79A9501D1C379C96961316B" + "5D2A66F38C222091AF74141B6CAF93507485A5D8F82808025451824F00C8" + "B6A0CD5803F6564584138C8B18BC679B442D837307B5CC90B1FD1FD32288" + "B4A5D18D2D80E5E6A7A9EFD255B8B363038BCC67AF534EAEE4A5903E304E" + "ED4990BB5BE735DB027A6DE35329D321EC051B956C55A5B11674017517FC" + "C3C7FF7397C13A7B7087A1F6AEC7F6761A130A0C636F6D70616E795F6E61" + "6D6512034C47451A150A0A6D6F64656C5F6E616D6512074E657875732034" + "1A200A116172636869746563747572655F6E616D65120B61726D65616269" + "2D7637611A130A0B6465766963655F6E616D6512046D616B6F1A150A0C70" + "726F647563745F6E616D6512056F6363616D1A440A0A6275696C645F696E" + "666F1236676F6F676C652F6F6363616D2F6D616B6F3A342E332F4A425F4D" + "52322F3731343239313A7573657264656275672F6465762D6B6579731A21" + "0A096465766963655F696412144C474D4332303132313030353030363339" + "32373812250A230A14080112103031323334353637383941424344454610" + "021A09393837363534333231180120002A0C333037383036303230340000" + "30151A8002B5CA9C6B097EF2CBE2F8136C761130F3456ED706127260151B" + "4FF044DE233C1828B8618A312C031A2F844BEF0917F9B8C6B8993A5D33E2" + "4B57B672A6C79D93EC98C46C5263EB8195FF7A5EBEBA08A6F1080C19340A" + "068E575568AE5EBADDD638FB435AC3EEC901E5F250BC974C498D6378C8BC" + "1F4BACCED5725B8B77160444923DA3B729DCB681186565B49EEFFE27CF16" + "31F09EC31E543AAFE9F5996FB0BEAA5F80305D67ECF173A8BD4A3B2CC75C" + "EC3AA5881FF433E80838E1E1FA23CE7F22346DDDF7FEC3DB0CE2C3F845CF" + "9471088A022C8D0A63860764AE558BD0B5F66D78881ADBF2D398F9BA349E" + "FB2532C61E243DD45BB11C99422D13A82B7AAE9671"), + wvcdm::a2bs_hex( + "080212CC020A190A09393837363534333231120892BE96420F0D5BF32002" + "2800124108011801301E4239687474703A2F2F6B69723033666370673137" + "342E7769646576696E652E6E65742F7769646576696E652F6367692D6269" + "6E2F64726D2E6367691A661210BB2E2D2B9C39697794897E08262749B41A" + "50C0DED62431B1701F59E076E07EB0D2D43AEC6C589B35790739EB0B0ED4" + "7236D0ECCE9A5408BE5F46F412334A5F4A4E3E493F202A263E185F06AE37" + "BA4351647BB9E6C997189FE1A03DCBF3FC90F46E5120011A7E0A10303132" + "333435363738394142434445461210319D7FB66154DFEC2AEDB164F29AAC" + "301A207448440734605CB29424FD1DA435A405DEE837757EA6A68C633A65" + "228317843D200242340A207F287706380C8085A4E5F85843D1C3B379F9CE" + "19ED5A2DAAF476B8AFE10488BF12100C8CDB1DA4C9FEBE5BBB530FE0D3DA" + "8720F4DFB68F051A20F4BCCEEEA658C5DD18D7B841E6D8991E616B57B592" + "C44ED67050939B136815272280025CD92AB4672778CB865D528A2EAAAD06" + "435AE9186F1C159AFA1689473C4D8C8A5B8C64400CBBD0A02659EA0271A1" + "F40052030CA285B9C7211791BDD72193D5E01CEE43B0482DEAF034C8E9BD" + "88C7331BFA5CD71C2A3062EBD07CE1C80CCF3C5D7EC2D921D1BC5414D797" + "0CB098889D3FB5BF669EE5283E009CDCC880E79C77A21B12C7C0B8062D66" + "CBDEC2DCFD23885144C776B98C8A7A176C4EA183085EF02D2060904ADA3C" + "B161F4D198A0279DA19D7001EB2D20C70ABAA04B3760CB165006F6CBA4BE" + "0A2628C0C8398C122FF0DCF9292590E3C37BC7DB20F3B0921268F41FE76B" + "D3EE764EBA13A22FDABC170860503FB93CC4A08D61102519D56A25EB9E30"), + wvcdm::a2bs_hex( + "08011231121D1A1B0A190A09393837363534333231120892BE96420F0D5B" + "F32002280018022A0C31393132353333373731000030151A20F4FDBECE54" + "7252D12BB9D488DAD50C76577A2FBCCC73F36D3C6B35096B8A3DC6"), + wvcdm::a2bs_hex( + "0802123B0A190A09393837363534333231120892BE96420F0D5BF3200228" + "0112001A16200342120A106B63746C0000000071FEF30B0000000020F4DF" + "B68F051A2000351030900858FCFD6977B67803ADFD1280AA661E6B0BD30B" + "08B2C467355129"), + "http://hamid.kir.corp.google.com:8888/drm", + wvcdm::a2bs_hex( + "0A9F15080210012298150801121408011210303132333435363738394142" + "434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E" + "C5335E346F591BC4D07A7D5076189EDFB68F05228E023082010A02820101" + "00CC1715C81AD3F6F279C686F826E6D7C8961EB13318367D06B4061BBC57" + "E3C616A226A10F042CAD54D44C6484C725CD721A2A97C088E60AFF0E9C8E" + "03477A1B56B4C555C27CEAF55024375D8D3FB352DA4AAA2E911C876CB1B3" + "6162922E9130CCC5FB72F8DD41D05DE6889C4814A7344BA8C605DE399CA3" + "CBBF1E7DE3411EDFC60F9D3802C0BEE2B98FB71A5AB9C1A3D53FB5559918" + "3B84FDDC98AC3096B2EF99C62B545C5DCA3371F4D27DEF2052A23F13DE42" + "DE46B462CED2ABABB96B610A47E0620AA10D862FEB66BB4F00B13DFE6170" + "3AE872F0B4850C39138FC5DE4538E27BEAC8A48CC9526401BE3B42C7C6C5" + "D9624662081D7A5A1C581EB09619DD9DD302030100012880013001128002" + "6AB9AC42F1C17C1ECFB710BF2C35383F41CF7EFAB0DFDCC69090C20DE141" + "CB43055FD707C611CDAEE700076A1EBA32432D5C2B62A73B8B1672AD2C43" + "03598C02D34823A6BE387046937F55BB65F5B3571FDC6A1F0D947031003B" + "A651F8E48BF33D66B7A32A72CAC75EF66EF280B2D4F14FBCA70ECC508091" + "FE83AD886A680F55AB62F306435BC0043825F6A401BB9C341230127D3298" + "B67F82767050C95769964B0B5C27A36FA76ED161ABE4B6C18556C8077065" + "09A5146ADD958AF79B49EDE48CBCD6320C4DEC0BF564C5DD7E7EBA37A4CD" + "1D27F8D80E1B6931C92AC8E5C3BEC0ADAE621A3B78952485EBFC81A194BA" + "75BBD2C821C28AEB5D21CBE0A270E55E1AB6050AB002080112102CE5CCF4" + "2200D6B5BCCF33D7CC2D9C7018EAD1B88D05228E023082010A0282010100" + "BE1B661EEC4700DF4B0C83292D02AE029B8A224DD3048125049F74E30E12" + "57FC2BE8D9CFAF0BFFCACAF7305351771C78FA451F13AF5EEBFB360941A4" + "396A805833730DC6E534C62408B7C5076FC22568021C59ED34F98487196D" + "A32078DAFCA37C7CFB8E79612FA384963DF2167D5E87305D7BC92D621C10" + "160672C27B9A691B1534F60D78C5893E40C5FF8A3F9DF8898612E9A5CCB5" + "6F4A0CC2A61A7AEB04A9DCC015D9BC37DEF2AB9EAA9AAFD838869081D924" + "9755F129BB0DBECA3B894975A65A36FD005CE77CD407E925D3172E33122A" + "11D327968A08F8E771FAEB2540EB52D17C4906405F47C31F60F0AF6C78AF" + "53291B236E692B506A2AF92AF43E3A81020301000128800130011280033A" + "08A60418E5C81B8D71A5C0A28C26999FF4FA992E14107CA8A9E6A2B36259" + "834000FE35DCD814426F9C5D332418ED94C9C0C992217B1B6DC01C99085A" + "3C3956C8267B8773BABCF3F2C841C67D830F9DBC780DD68BF4E2FE424C6A" + "54123BE4B2A1F7E1F4DB58AB1164DAE9CF75C3392284A44B8CDB85D837E8" + "6C6B908243987E552C8693878C9A1B7BEA3759783036F1595C406D6CBBA7" + "F8642A9B3B244DAA1F00531D0B908ADE4B533FD9FAFA21D0FB0C033D2AD5" + "DDF24C60F4FAC30820758877F2E1A78EB44E9336DCFAFDF572BB22A84A5D" + "EFDF2EB87B61DE26EE9C4CEAA646A2AFDB2BB953845E6D7FE6F79A9501D1" + "C379C96961316B5D2A66F38C222091AF74141B6CAF93507485A5D8F82808" + "025451824F00C8B6A0CD5803F6564584138C8B18BC679B442D837307B5CC" + "90B1FD1FD32288B4A5D18D2D80E5E6A7A9EFD255B8B363038BCC67AF534E" + "AEE4A5903E304EED4990BB5BE735DB027A6DE35329D321EC051B956C55A5" + "B11674017517FCC3C7FF7397C13A7B7087A1F6AEC7F6761A130A0C636F6D" + "70616E795F6E616D6512034C47451A150A0A6D6F64656C5F6E616D651207" + "4E6578757320341A200A116172636869746563747572655F6E616D65120B" + "61726D656162692D7637611A130A0B6465766963655F6E616D6512046D61" + "6B6F1A150A0C70726F647563745F6E616D6512056F6363616D1A440A0A62" + "75696C645F696E666F1236676F6F676C652F6F6363616D2F6D616B6F3A34" + "2E332F4A425F4D52322F3731343239313A7573657264656275672F646576" + "2D6B6579731A210A096465766963655F696412144C474D43323031323130" + "3035303036333932373812250A230A140801121030313233343536373839" + "41424344454610021A09393837363534333231180120002A0C3330373830" + "3630323034000030151A8002B5CA9C6B097EF2CBE2F8136C761130F3456E" + "D706127260151B4FF044DE233C1828B8618A312C031A2F844BEF0917F9B8" + "C6B8993A5D33E24B57B672A6C79D93EC98C46C5263EB8195FF7A5EBEBA08" + "A6F1080C19340A068E575568AE5EBADDD638FB435AC3EEC901E5F250BC97" + "4C498D6378C8BC1F4BACCED5725B8B77160444923DA3B729DCB681186565" + "B49EEFFE27CF1631F09EC31E543AAFE9F5996FB0BEAA5F80305D67ECF173" + "A8BD4A3B2CC75CEC3AA5881FF433E80838E1E1FA23CE7F22346DDDF7FEC3" + "DB0CE2C3F845CF9471088A022C8D0A63860764AE558BD0B5F66D78881ADB" + "F2D398F9BA349EFB2532C61E243DD45BB11C99422D13A82B7AAE967122F6" + "04080212CC020A190A09393837363534333231120892BE96420F0D5BF320" + "022800124108011801301E4239687474703A2F2F6B697230336663706731" + "37342E7769646576696E652E6E65742F7769646576696E652F6367692D62" + "696E2F64726D2E6367691A661210BB2E2D2B9C39697794897E08262749B4" + "1A50C0DED62431B1701F59E076E07EB0D2D43AEC6C589B35790739EB0B0E" + "D47236D0ECCE9A5408BE5F46F412334A5F4A4E3E493F202A263E185F06AE" + "37BA4351647BB9E6C997189FE1A03DCBF3FC90F46E5120011A7E0A103031" + "32333435363738394142434445461210319D7FB66154DFEC2AEDB164F29A" + "AC301A207448440734605CB29424FD1DA435A405DEE837757EA6A68C633A" + "65228317843D200242340A207F287706380C8085A4E5F85843D1C3B379F9" + "CE19ED5A2DAAF476B8AFE10488BF12100C8CDB1DA4C9FEBE5BBB530FE0D3" + "DA8720F4DFB68F051A20F4BCCEEEA658C5DD18D7B841E6D8991E616B57B5" + "92C44ED67050939B136815272280025CD92AB4672778CB865D528A2EAAAD" + "06435AE9186F1C159AFA1689473C4D8C8A5B8C64400CBBD0A02659EA0271" + "A1F40052030CA285B9C7211791BDD72193D5E01CEE43B0482DEAF034C8E9" + "BD88C7331BFA5CD71C2A3062EBD07CE1C80CCF3C5D7EC2D921D1BC5414D7" + "970CB098889D3FB5BF669EE5283E009CDCC880E79C77A21B12C7C0B8062D" + "66CBDEC2DCFD23885144C776B98C8A7A176C4EA183085EF02D2060904ADA" + "3CB161F4D198A0279DA19D7001EB2D20C70ABAA04B3760CB165006F6CBA4" + "BE0A2628C0C8398C122FF0DCF9292590E3C37BC7DB20F3B0921268F41FE7" + "6BD3EE764EBA13A22FDABC170860503FB93CC4A08D61102519D56A25EB9E" + "302A5708011231121D1A1B0A190A09393837363534333231120892BE9642" + "0F0D5BF32002280018022A0C31393132353333373731000030151A20F4FD" + "BECE547252D12BB9D488DAD50C76577A2FBCCC73F36D3C6B35096B8A3DC6" + "32610802123B0A190A09393837363534333231120892BE96420F0D5BF320" + "02280112001A16200342120A106B63746C0000000071FEF30B0000000020" + "F4DFB68F051A2000351030900858FCFD6977B67803ADFD1280AA661E6B0B" + "D30B08B2C4673551293A29687474703A2F2F68616D69642E6B69722E636F" + "72702E676F6F676C652E636F6D3A383838382F64726D1220E9BF6AE79B64" + "B788838B5EDDEBEF9E20FD8CFFDEB037DEFEE982DF21A2D32031")}, + // license being released. all fields are identical except for license + // state and hashed file data + {"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "", + wvcdm::a2bs_hex( + "0A9F15080210012298150802121408011210303132333435363738394142" + "434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E" + "C5335E346F591BC4D07A7D5076189EDFB68F05228E023082010A02820101" + "00CC1715C81AD3F6F279C686F826E6D7C8961EB13318367D06B4061BBC57" + "E3C616A226A10F042CAD54D44C6484C725CD721A2A97C088E60AFF0E9C8E" + "03477A1B56B4C555C27CEAF55024375D8D3FB352DA4AAA2E911C876CB1B3" + "6162922E9130CCC5FB72F8DD41D05DE6889C4814A7344BA8C605DE399CA3" + "CBBF1E7DE3411EDFC60F9D3802C0BEE2B98FB71A5AB9C1A3D53FB5559918" + "3B84FDDC98AC3096B2EF99C62B545C5DCA3371F4D27DEF2052A23F13DE42" + "DE46B462CED2ABABB96B610A47E0620AA10D862FEB66BB4F00B13DFE6170" + "3AE872F0B4850C39138FC5DE4538E27BEAC8A48CC9526401BE3B42C7C6C5" + "D9624662081D7A5A1C581EB09619DD9DD302030100012880013001128002" + "6AB9AC42F1C17C1ECFB710BF2C35383F41CF7EFAB0DFDCC69090C20DE141" + "CB43055FD707C611CDAEE700076A1EBA32432D5C2B62A73B8B1672AD2C43" + "03598C02D34823A6BE387046937F55BB65F5B3571FDC6A1F0D947031003B" + "A651F8E48BF33D66B7A32A72CAC75EF66EF280B2D4F14FBCA70ECC508091" + "FE83AD886A680F55AB62F306435BC0043825F6A401BB9C341230127D3298" + "B67F82767050C95769964B0B5C27A36FA76ED161ABE4B6C18556C8077065" + "09A5146ADD958AF79B49EDE48CBCD6320C4DEC0BF564C5DD7E7EBA37A4CD" + "1D27F8D80E1B6931C92AC8E5C3BEC0ADAE621A3B78952485EBFC81A194BA" + "75BBD2C821C28AEB5D21CBE0A270E55E1AB6050AB002080112102CE5CCF4" + "2200D6B5BCCF33D7CC2D9C7018EAD1B88D05228E023082010A0282010100" + "BE1B661EEC4700DF4B0C83292D02AE029B8A224DD3048125049F74E30E12" + "57FC2BE8D9CFAF0BFFCACAF7305351771C78FA451F13AF5EEBFB360941A4" + "396A805833730DC6E534C62408B7C5076FC22568021C59ED34F98487196D" + "A32078DAFCA37C7CFB8E79612FA384963DF2167D5E87305D7BC92D621C10" + "160672C27B9A691B1534F60D78C5893E40C5FF8A3F9DF8898612E9A5CCB5" + "6F4A0CC2A61A7AEB04A9DCC015D9BC37DEF2AB9EAA9AAFD838869081D924" + "9755F129BB0DBECA3B894975A65A36FD005CE77CD407E925D3172E33122A" + "11D327968A08F8E771FAEB2540EB52D17C4906405F47C31F60F0AF6C78AF" + "53291B236E692B506A2AF92AF43E3A81020301000128800130011280033A" + "08A60418E5C81B8D71A5C0A28C26999FF4FA992E14107CA8A9E6A2B36259" + "834000FE35DCD814426F9C5D332418ED94C9C0C992217B1B6DC01C99085A" + "3C3956C8267B8773BABCF3F2C841C67D830F9DBC780DD68BF4E2FE424C6A" + "54123BE4B2A1F7E1F4DB58AB1164DAE9CF75C3392284A44B8CDB85D837E8" + "6C6B908243987E552C8693878C9A1B7BEA3759783036F1595C406D6CBBA7" + "F8642A9B3B244DAA1F00531D0B908ADE4B533FD9FAFA21D0FB0C033D2AD5" + "DDF24C60F4FAC30820758877F2E1A78EB44E9336DCFAFDF572BB22A84A5D" + "EFDF2EB87B61DE26EE9C4CEAA646A2AFDB2BB953845E6D7FE6F79A9501D1" + "C379C96961316B5D2A66F38C222091AF74141B6CAF93507485A5D8F82808" + "025451824F00C8B6A0CD5803F6564584138C8B18BC679B442D837307B5CC" + "90B1FD1FD32288B4A5D18D2D80E5E6A7A9EFD255B8B363038BCC67AF534E" + "AEE4A5903E304EED4990BB5BE735DB027A6DE35329D321EC051B956C55A5" + "B11674017517FCC3C7FF7397C13A7B7087A1F6AEC7F6761A130A0C636F6D" + "70616E795F6E616D6512034C47451A150A0A6D6F64656C5F6E616D651207" + "4E6578757320341A200A116172636869746563747572655F6E616D65120B" + "61726D656162692D7637611A130A0B6465766963655F6E616D6512046D61" + "6B6F1A150A0C70726F647563745F6E616D6512056F6363616D1A440A0A62" + "75696C645F696E666F1236676F6F676C652F6F6363616D2F6D616B6F3A34" + "2E332F4A425F4D52322F3731343239313A7573657264656275672F646576" + "2D6B6579731A210A096465766963655F696412144C474D43323031323130" + "3035303036333932373812250A230A140801121030313233343536373839" + "41424344454610021A09393837363534333231180120002A0C3330373830" + "3630323034000030151A8002B5CA9C6B097EF2CBE2F8136C761130F3456E" + "D706127260151B4FF044DE233C1828B8618A312C031A2F844BEF0917F9B8" + "C6B8993A5D33E24B57B672A6C79D93EC98C46C5263EB8195FF7A5EBEBA08" + "A6F1080C19340A068E575568AE5EBADDD638FB435AC3EEC901E5F250BC97" + "4C498D6378C8BC1F4BACCED5725B8B77160444923DA3B729DCB681186565" + "B49EEFFE27CF1631F09EC31E543AAFE9F5996FB0BEAA5F80305D67ECF173" + "A8BD4A3B2CC75CEC3AA5881FF433E80838E1E1FA23CE7F22346DDDF7FEC3" + "DB0CE2C3F845CF9471088A022C8D0A63860764AE558BD0B5F66D78881ADB" + "F2D398F9BA349EFB2532C61E243DD45BB11C99422D13A82B7AAE967122F6" + "04080212CC020A190A09393837363534333231120892BE96420F0D5BF320" + "022800124108011801301E4239687474703A2F2F6B697230336663706731" + "37342E7769646576696E652E6E65742F7769646576696E652F6367692D62" + "696E2F64726D2E6367691A661210BB2E2D2B9C39697794897E08262749B4" + "1A50C0DED62431B1701F59E076E07EB0D2D43AEC6C589B35790739EB0B0E" + "D47236D0ECCE9A5408BE5F46F412334A5F4A4E3E493F202A263E185F06AE" + "37BA4351647BB9E6C997189FE1A03DCBF3FC90F46E5120011A7E0A103031" + "32333435363738394142434445461210319D7FB66154DFEC2AEDB164F29A" + "AC301A207448440734605CB29424FD1DA435A405DEE837757EA6A68C633A" + "65228317843D200242340A207F287706380C8085A4E5F85843D1C3B379F9" + "CE19ED5A2DAAF476B8AFE10488BF12100C8CDB1DA4C9FEBE5BBB530FE0D3" + "DA8720F4DFB68F051A20F4BCCEEEA658C5DD18D7B841E6D8991E616B57B5" + "92C44ED67050939B136815272280025CD92AB4672778CB865D528A2EAAAD" + "06435AE9186F1C159AFA1689473C4D8C8A5B8C64400CBBD0A02659EA0271" + "A1F40052030CA285B9C7211791BDD72193D5E01CEE43B0482DEAF034C8E9" + "BD88C7331BFA5CD71C2A3062EBD07CE1C80CCF3C5D7EC2D921D1BC5414D7" + "970CB098889D3FB5BF669EE5283E009CDCC880E79C77A21B12C7C0B8062D" + "66CBDEC2DCFD23885144C776B98C8A7A176C4EA183085EF02D2060904ADA" + "3CB161F4D198A0279DA19D7001EB2D20C70ABAA04B3760CB165006F6CBA4" + "BE0A2628C0C8398C122FF0DCF9292590E3C37BC7DB20F3B0921268F41FE7" + "6BD3EE764EBA13A22FDABC170860503FB93CC4A08D61102519D56A25EB9E" + "302A5708011231121D1A1B0A190A09393837363534333231120892BE9642" + "0F0D5BF32002280018022A0C31393132353333373731000030151A20F4FD" + "BECE547252D12BB9D488DAD50C76577A2FBCCC73F36D3C6B35096B8A3DC6" + "32610802123B0A190A09393837363534333231120892BE96420F0D5BF320" + "02280112001A16200342120A106B63746C0000000071FEF30B0000000020" + "F4DFB68F051A2000351030900858FCFD6977B67803ADFD1280AA661E6B0B" + "D30B08B2C4673551293A29687474703A2F2F68616D69642E6B69722E636F" + "72702E676F6F676C652E636F6D3A383838382F64726D1220003650AD34C0" + "CB1D348F83B6694E4983DA8BCF9AEFAA4A4B23022DA08CF3DA44")}}; - std::string kKeyRenewalRequest1 = ""; - std::string kKeyRenewalRequest2 = "0801125E124A1A480A460A0939383736353433323" - "112080C1913B45E8893481A2B596E4B58753250316A5F53785073494E6B377459354E79" - "67396755464B6963455954335A34626738366C772001280018022A0C383231353639373" - "13400000030151A20789770161DD09DD5210E1A399DCA51C7741D2FC83488B731D22F4C" - "B01E74D240"; - std::string kKeyRenewalRequest3 = "0801125E124A1A480A460A0939383736353433323" - "112086D03DC5FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E79" - "67396755464B6963455954335A34626738366C772001280018022A0C313736393630363" - "23739000030151A209ABDAF1E996DFED08BF64B71356DAD8244BFAD335749206C2A1749" - "61CB15B9AD"; +} // namespace - std::string kKeyRenewalResponse1 = ""; - std::string kKeyRenewalResponse2 = ""; - std::string kKeyRenewalResponse3 = "080212EC010A460A093938373635343332311208" - "6D03DC5FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E7967396" - "755464B6963455954335A34626738366C77200128011283010801180120809A9E012880" - "9A9E0130AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F76696" - "4656F2D6465762F6865617274626561742F63656E633F736F757263653D594F55545542" - "4526766964656F5F69643D45474843364F484E624F6F266F617574683D79612E6774737" - "1617769646576696E65483C503C1A16200342120A106B63746C0000012C697A0C870000" - "000820E3DDC78C051A20466E04142A0F410F5BA032BD8B0B77C800E9972C707E034E2ED" - "E83661C6C5386"; +class MockFile : public File { + public: + MOCK_METHOD2(Open, bool(const std::string&, int flags)); + MOCK_METHOD0(Close, void()); - std::string kKeyReleaseServerUrl1 = "http://hamid.kir.corp.google.com:8888/drm"; - std::string kKeyReleaseServerUrl2 = "https://www.youtube.com/api/drm/widevine?video_id=03681262dc412c06&source=YOUTUBE"; - std::string kKeyReleaseServerUrl3 = "https://jmt17.google.com/video-dev/license/GetCencLicense"; -} + MOCK_METHOD2(Read, ssize_t(char*, size_t)); + MOCK_METHOD2(Write, ssize_t(const char*, size_t)); -TEST(DeviceFilesTest, StoreCertificate) { - std::string device_base_path = - DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string device_certificate_path = - device_base_path + DeviceFiles::kDeviceCertificateFileName; + MOCK_METHOD1(Exists, bool(const std::string&)); + MOCK_METHOD1(Remove, bool(const std::string&)); + MOCK_METHOD1(CreateDirectory, bool(const std::string)); + MOCK_METHOD1(IsDirectory, bool(const std::string&)); + MOCK_METHOD1(IsRegularFile, bool(const std::string&)); + MOCK_METHOD1(FileSize, ssize_t(const std::string&)); +}; - if (!File::Exists(device_base_path)) - EXPECT_TRUE(File::CreateDirectory(device_base_path)); - if (File::Exists(device_certificate_path)) - EXPECT_TRUE(File::Remove(device_certificate_path)); - - char test_buf[1200]; - for (size_t i = 0; i < sizeof(test_buf); i++) { - test_buf[i] = i % 128; +class DeviceFilesTest : public ::testing::Test { + protected: + virtual void SetUp() { + ASSERT_TRUE(Properties::GetDeviceFilesBasePath(&device_base_path_)); } - size_t cert_len = 500; - std::string certificate(&test_buf[0], cert_len); - std::string wrapped_private_key(&test_buf[cert_len], - sizeof(test_buf) - cert_len - 1); - - EXPECT_TRUE(DeviceFiles::StoreCertificate(certificate, wrapped_private_key)); - EXPECT_TRUE(File::Exists(device_certificate_path)); - EXPECT_GT(File::FileSize(device_certificate_path), - (ssize_t)sizeof(test_buf)); -} - -TEST(DeviceFilesTest, StoreCertificateInitial) { - std::string device_base_path = - DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string device_certificate_path = - device_base_path + DeviceFiles::kDeviceCertificateFileName; - - if (File::Exists(device_base_path)) - EXPECT_TRUE(File::Remove(device_base_path)); - - char test_buf[1200]; - for (size_t i = 0; i < sizeof(test_buf); i++) { - test_buf[i] = i % 128; + std::string GenerateRandomData(uint32_t len) { + std::string data(len, 0); + for (size_t i = 0; i < len; i++) { + data[i] = rand() % 256; + } + return data; } - size_t cert_len = 500; - std::string certificate(&test_buf[0], cert_len); - std::string wrapped_private_key(&test_buf[cert_len], - sizeof(test_buf) - cert_len - 1); - - EXPECT_TRUE(DeviceFiles::StoreCertificate(certificate, wrapped_private_key)); - EXPECT_TRUE(File::Exists(device_certificate_path)); - EXPECT_GT(File::FileSize(device_certificate_path), - (ssize_t)sizeof(test_buf)); -} - -TEST(DeviceFilesTest, RetrieveCertificate) { - std::string device_base_path = - DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string device_certificate_path = - device_base_path + DeviceFiles::kDeviceCertificateFileName; - - if (File::Exists(device_base_path)) - EXPECT_TRUE(File::Remove(device_base_path)); - - char test_buf[1200]; - for (size_t i = 0; i < sizeof(test_buf); i++) { - test_buf[i] = i % 128; + size_t GetLicenseDataSize(LicenseInfo& data) { + return sizeof(DeviceFiles::LicenseState) + data.pssh_data.size() + + data.key_request.size() + data.key_response.size() + + data.key_renewal_request.size() + data.key_renewal_response.size() + + data.key_release_url.size(); } - size_t cert_len = 500; - std::string certificate(&test_buf[0], cert_len); - std::string wrapped_private_key(&test_buf[cert_len], - sizeof(test_buf) - cert_len - 1); + std::string device_base_path_; +}; - EXPECT_TRUE(DeviceFiles::StoreCertificate(certificate, wrapped_private_key)); - EXPECT_TRUE(File::Exists(device_certificate_path)); - EXPECT_GT(File::FileSize(device_certificate_path), - (ssize_t)sizeof(test_buf)); +class DeviceFilesStoreTest : public DeviceFilesTest, + public ::testing::WithParamInterface {}; - std::string in_certificate; - std::string in_wrapped_private_key; - EXPECT_TRUE(DeviceFiles::RetrieveCertificate(&in_certificate, - &in_wrapped_private_key)); - EXPECT_TRUE(memcmp(certificate.data(), in_certificate.data(), - certificate.size()) == 0); - EXPECT_TRUE(memcmp(wrapped_private_key.data(), in_wrapped_private_key.data(), - wrapped_private_key.size()) == 0); - EXPECT_TRUE(File::Remove(device_base_path)); +MATCHER(IsCreateFileFlagSet, "") { return File::kCreate & arg; } +MATCHER(IsBinaryFileFlagSet, "") { return File::kBinary & arg; } +MATCHER_P(IsStrEq, str, "") { + // Estimating the length of data. We can have gmock provide length + // as well as pointer to data but that will introduce a dependency on tr1 + return memcmp(arg, str.c_str(), str.size()) == 0; +} +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() + 500); + return (data.find(str1) != std::string::npos && + data.find(str2) != std::string::npos); +} +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() + 500); + return (data.find(str1) != std::string::npos && + data.find(str2) != std::string::npos, + data.find(str3) != std::string::npos, + data.find(str4) != std::string::npos, + data.find(str5) != std::string::npos, + data.find(str6) != std::string::npos); } -TEST(DeviceFilesTest, StoreLicense) { - std::string device_base_path = - DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string license_path_1 = device_base_path + kKeySetId1 + - DeviceFiles::kLicenseFileNameExt; +TEST_P(DeviceFilesStoreTest, StoreCertificate) { + MockFile file; + std::string certificate(GenerateRandomData(kCertificateLen)); + std::string wrapped_private_key(GenerateRandomData(kWrappedKeyLen)); + std::string device_certificate_path = + device_base_path_ + DeviceFiles::GetCertificateFileName(); - if (!File::Exists(device_base_path)) - EXPECT_TRUE(File::CreateDirectory(device_base_path)); - if (File::Exists(license_path_1)) - EXPECT_TRUE(File::Remove(license_path_1)); + bool dir_exists = GetParam(); + EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) + .WillOnce(Return(dir_exists)); + if (dir_exists) { + EXPECT_CALL(file, CreateDirectory(_)).Times(0); + } else { + EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path_))) + .WillOnce(Return(true)); + } - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, - DeviceFiles::kLicenseStateActive, - kPsshData1, kKeyRequest1, - kKeyResponse1, kKeyRenewalRequest1, - kKeyRenewalResponse1, - kKeyReleaseServerUrl1)); - size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + - kKeyRequest1.size() + kKeyResponse1.size() + - kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + - kKeyReleaseServerUrl1.size(); - EXPECT_TRUE(File::Exists(license_path_1)); - EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + EXPECT_CALL(file, Open(StrEq(device_certificate_path), + AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet()))) + .WillOnce(Return(true)); + EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key), + Gt(certificate.size() + wrapped_private_key.size()))) + .WillOnce(ReturnArg<1>()); + EXPECT_CALL(file, Close()).Times(1); + EXPECT_CALL(file, Read(_, _)).Times(0); + + DeviceFiles device_files; + EXPECT_TRUE(device_files.Init(&file)); + EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key)); } -TEST(DeviceFilesTest, StoreLicenseInitial) { - std::string device_base_path = - DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string license_path_1 = device_base_path + kKeySetId1 + - DeviceFiles::kLicenseFileNameExt; +INSTANTIATE_TEST_CASE_P(StoreCertificate, DeviceFilesStoreTest, + ::testing::Values(true, false)); - if (!File::Exists(device_base_path)) - EXPECT_TRUE(File::Remove(device_base_path)); +TEST_F(DeviceFilesTest, ReadCertificate) { + MockFile file; + std::string device_certificate_path = + device_base_path_ + DeviceFiles::GetCertificateFileName(); + std::string data = a2bs_hex(kTestCertificateFileData); - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, - DeviceFiles::kLicenseStateActive, - kPsshData1, kKeyRequest1, - kKeyResponse1, kKeyRenewalRequest1, - kKeyRenewalResponse1, - kKeyReleaseServerUrl1)); - size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + - kKeyRequest1.size() + kKeyResponse1.size() + - kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + - kKeyReleaseServerUrl1.size(); - EXPECT_TRUE(File::Exists(license_path_1)); - EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + EXPECT_CALL(file, Exists(StrEq(device_certificate_path))) + .WillOnce(Return(true)); + EXPECT_CALL(file, FileSize(StrEq(device_certificate_path))) + .WillOnce(Return(data.size())); + EXPECT_CALL(file, Open(StrEq(device_certificate_path), IsBinaryFileFlagSet())) + .WillOnce(Return(true)); + EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))).WillOnce(DoAll( + SetArrayArgument<0>(data.begin(), data.end()), Return(data.size()))); + EXPECT_CALL(file, Close()).Times(1); + EXPECT_CALL(file, Write(_, _)).Times(0); + + DeviceFiles device_files; + EXPECT_TRUE(device_files.Init(&file)); + + std::string certificate, wrapped_private_key; + ASSERT_TRUE( + device_files.RetrieveCertificate(&certificate, &wrapped_private_key)); + EXPECT_EQ(kTestCertificate, b2a_hex(certificate)); + EXPECT_EQ(kTestWrappedPrivateKey, b2a_hex(wrapped_private_key)); } -TEST(DeviceFilesTest, StoreLicenses) { - std::string device_base_path = - DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string license_path_1 = device_base_path + kKeySetId1 + - DeviceFiles::kLicenseFileNameExt; - std::string license_path_2 = device_base_path + kKeySetId2 + - DeviceFiles::kLicenseFileNameExt; - std::string license_path_3 = device_base_path + kKeySetId3 + - DeviceFiles::kLicenseFileNameExt; +TEST_P(DeviceFilesStoreTest, StoreLicense) { + MockFile file; + size_t license_num = 0; + std::string license_path = device_base_path_ + + license_test_data[license_num].key_set_id + + DeviceFiles::GetLicenseFileNameExtension(); - if (!File::Exists(device_base_path)) - EXPECT_TRUE(File::Remove(device_base_path)); + bool dir_exists = GetParam(); + EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) + .WillOnce(Return(dir_exists)); + if (dir_exists) { + EXPECT_CALL(file, CreateDirectory(_)).Times(0); + } else { + EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path_))) + .WillOnce(Return(true)); + } - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, - DeviceFiles::kLicenseStateActive, - kPsshData1, kKeyRequest1, - kKeyResponse1, kKeyRenewalRequest1, - kKeyRenewalResponse1, - kKeyReleaseServerUrl1)); - size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + - kKeyRequest1.size() + kKeyResponse1.size() + - kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + - kKeyReleaseServerUrl1.size(); - EXPECT_TRUE(File::Exists(license_path_1)); - EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + EXPECT_CALL(file, Open(StrEq(license_path), + AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet()))) + .WillOnce(Return(true)); + EXPECT_CALL( + file, Write(Contains(license_test_data[license_num].pssh_data, + license_test_data[license_num].key_request, + license_test_data[license_num].key_response, + license_test_data[license_num].key_renewal_request, + license_test_data[license_num].key_renewal_response, + license_test_data[license_num].key_release_url), + Gt(GetLicenseDataSize(license_test_data[license_num])))) + .WillOnce(ReturnArg<1>()); + EXPECT_CALL(file, Close()).Times(1); + EXPECT_CALL(file, Read(_, _)).Times(0); - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId2, - DeviceFiles::kLicenseStateReleasing, - kPsshData2, kKeyRequest2, - kKeyResponse2, kKeyRenewalRequest2, - kKeyRenewalResponse2, - kKeyReleaseServerUrl2)); - size = sizeof(DeviceFiles::LicenseState) + kPsshData2.size() + - kKeyRequest2.size() + kKeyResponse2.size() + - kKeyRenewalRequest2.size() + kKeyRenewalResponse2.size() + - kKeyReleaseServerUrl2.size(); - EXPECT_TRUE(File::Exists(license_path_2)); - EXPECT_GT(File::FileSize(license_path_2), static_cast(size)); - - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId3, - DeviceFiles::kLicenseStateReleasing, - kPsshData3, kKeyRequest3, - kKeyResponse3, kKeyRenewalRequest3, - kKeyRenewalResponse3, - kKeyReleaseServerUrl3)); - size = sizeof(DeviceFiles::LicenseState) + kPsshData3.size() + - kKeyRequest3.size() + kKeyResponse3.size() + - kKeyRenewalRequest3.size() + kKeyRenewalResponse3.size() + - kKeyReleaseServerUrl3.size(); - EXPECT_TRUE(File::Exists(license_path_3)); - EXPECT_GT(File::FileSize(license_path_3), static_cast(size)); + DeviceFiles device_files; + EXPECT_TRUE(device_files.Init(&file)); + EXPECT_TRUE(device_files.StoreLicense( + license_test_data[license_num].key_set_id, + license_test_data[license_num].license_state, + license_test_data[license_num].pssh_data, + license_test_data[license_num].key_request, + license_test_data[license_num].key_response, + license_test_data[license_num].key_renewal_request, + license_test_data[license_num].key_renewal_response, + license_test_data[license_num].key_release_url)); } -TEST(DeviceFilesTest, RetrieveLicenses) { - std::string device_base_path = - DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string license_path_1 = device_base_path + kKeySetId1 + - DeviceFiles::kLicenseFileNameExt; - std::string license_path_2 = device_base_path + kKeySetId2 + - DeviceFiles::kLicenseFileNameExt; - std::string license_path_3 = device_base_path + kKeySetId3 + - DeviceFiles::kLicenseFileNameExt; +INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest, + ::testing::Values(true, false)); - if (!File::Exists(device_base_path)) - EXPECT_TRUE(File::Remove(device_base_path)); +TEST_F(DeviceFilesTest, StoreLicenses) { + MockFile file; + EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))) + .Times(kNumberOfLicenses).WillRepeatedly(Return(true)); + EXPECT_CALL(file, CreateDirectory(_)).Times(0); - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, - DeviceFiles::kLicenseStateActive, - kPsshData1, kKeyRequest1, - kKeyResponse1, kKeyRenewalRequest1, - kKeyRenewalResponse1, - kKeyReleaseServerUrl1)); - size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + - kKeyRequest1.size() + kKeyResponse1.size() + - kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + - kKeyReleaseServerUrl1.size(); - EXPECT_TRUE(File::Exists(license_path_1)); - EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + for (size_t i = 0; i < kNumberOfLicenses; ++i) { + std::string license_path = device_base_path_ + + license_test_data[i].key_set_id + + DeviceFiles::GetLicenseFileNameExtension(); - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId2, - DeviceFiles::kLicenseStateReleasing, - kPsshData2, kKeyRequest2, - kKeyResponse2, kKeyRenewalRequest2, - kKeyRenewalResponse2, - kKeyReleaseServerUrl2)); - size = sizeof(DeviceFiles::LicenseState) + kPsshData2.size() + - kKeyRequest2.size() + kKeyResponse2.size() + - kKeyRenewalRequest2.size() + kKeyRenewalResponse2.size() + - kKeyReleaseServerUrl2.size(); - EXPECT_TRUE(File::Exists(license_path_2)); - EXPECT_GT(File::FileSize(license_path_2), static_cast(size)); + EXPECT_CALL(file, Open(StrEq(license_path), + AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet()))) + .WillOnce(Return(true)); - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId3, - DeviceFiles::kLicenseStateReleasing, - kPsshData3, kKeyRequest3, - kKeyResponse3, kKeyRenewalRequest3, - kKeyRenewalResponse3, - kKeyReleaseServerUrl3)); - size = sizeof(DeviceFiles::LicenseState) + kPsshData3.size() + - kKeyRequest3.size() + kKeyResponse3.size() + - kKeyRenewalRequest3.size() + kKeyRenewalResponse3.size() + - kKeyReleaseServerUrl3.size(); - EXPECT_TRUE(File::Exists(license_path_3)); - EXPECT_GT(File::FileSize(license_path_3), static_cast(size)); - - DeviceFiles::LicenseState state; - std::string pssh_data, key_request, key_response, key_renewal_request, - key_renewal_response, release_server_url; - - EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, - &key_request, &key_response, - &key_renewal_request, - &key_renewal_response, - &release_server_url)); - EXPECT_TRUE(state == DeviceFiles::kLicenseStateActive); - EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size()) - == 0); - EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(), - key_request.size()) == 0); - EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(), - key_response.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(), - key_renewal_request.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(), - key_renewal_response.size()) == 0); - - EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId2, &state, &pssh_data, - &key_request, &key_response, - &key_renewal_request, - &key_renewal_response, - &release_server_url)); - EXPECT_TRUE(state == DeviceFiles::kLicenseStateReleasing); - EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData2.data(), pssh_data.size()) - == 0); - EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest2.data(), - key_request.size()) == 0); - EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse2.data(), - key_response.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest2.data(), - key_renewal_request.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse2.data(), - key_renewal_response.size()) == 0); - - EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId3, &state, &pssh_data, - &key_request, &key_response, - &key_renewal_request, - &key_renewal_response, - &release_server_url)); - EXPECT_TRUE(state == DeviceFiles::kLicenseStateReleasing); - EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData3.data(), pssh_data.size()) - == 0); - EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest3.data(), - key_request.size()) == 0); - EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse3.data(), - key_response.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest3.data(), - key_renewal_request.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse3.data(), - key_renewal_response.size()) == 0); + EXPECT_CALL(file, Write(Contains(license_test_data[i].pssh_data, + license_test_data[i].key_request, + license_test_data[i].key_response, + license_test_data[i].key_renewal_request, + license_test_data[i].key_renewal_response, + license_test_data[i].key_release_url), + Gt(GetLicenseDataSize(license_test_data[i])))) + .WillOnce(ReturnArg<1>()); + } + EXPECT_CALL(file, Close()).Times(kNumberOfLicenses); + EXPECT_CALL(file, Read(_, _)).Times(0); + DeviceFiles device_files; + EXPECT_TRUE(device_files.Init(&file)); + for (size_t i = 0; i < kNumberOfLicenses; i++) { + EXPECT_TRUE(device_files.StoreLicense( + license_test_data[i].key_set_id, license_test_data[i].license_state, + license_test_data[i].pssh_data, license_test_data[i].key_request, + license_test_data[i].key_response, + license_test_data[i].key_renewal_request, + license_test_data[i].key_renewal_response, + license_test_data[i].key_release_url)); + } } -TEST(DeviceFilesTest, UpdateLicenseState) { - std::string device_base_path = - DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string license_path_1 = device_base_path + kKeySetId1 + - DeviceFiles::kLicenseFileNameExt; +TEST_F(DeviceFilesTest, RetrieveLicenses) { + MockFile file; - if (!File::Exists(device_base_path)) - EXPECT_TRUE(File::Remove(device_base_path)); + for (size_t i = 0; i < kNumberOfLicenses; ++i) { + std::string license_path = device_base_path_ + + license_test_data[i].key_set_id + + DeviceFiles::GetLicenseFileNameExtension(); - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, - DeviceFiles::kLicenseStateActive, - kPsshData1, kKeyRequest1, - kKeyResponse1, kKeyRenewalRequest1, - kKeyRenewalResponse1, - kKeyReleaseServerUrl1)); - size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + - kKeyRequest1.size() + kKeyResponse1.size() + - kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + - kKeyReleaseServerUrl1.size(); - EXPECT_TRUE(File::Exists(license_path_1)); - EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + size_t size = license_test_data[i].file_data.size(); - DeviceFiles::LicenseState state; - std::string pssh_data, key_request, key_response, key_renewal_request, - key_renewal_response, release_server_url; + EXPECT_CALL(file, Exists(StrEq(license_path))).WillOnce(Return(true)); + EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size)); + EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet())) + .WillOnce(Return(true)); + EXPECT_CALL(file, Read(NotNull(), Eq(size))).WillOnce( + DoAll(SetArrayArgument<0>(license_test_data[i].file_data.begin(), + license_test_data[i].file_data.end()), + Return(size))); + } + EXPECT_CALL(file, Close()).Times(kNumberOfLicenses); + EXPECT_CALL(file, Write(_, _)).Times(0); - EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, - &key_request, &key_response, - &key_renewal_request, - &key_renewal_response, - &release_server_url)); - EXPECT_TRUE(state == DeviceFiles::kLicenseStateActive); - EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size()) - == 0); - EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(), - key_request.size()) == 0); - EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(), - key_response.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(), - key_renewal_request.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(), - key_renewal_response.size()) == 0); + DeviceFiles device_files; + EXPECT_TRUE(device_files.Init(&file)); + DeviceFiles::LicenseState license_state; + CdmInitData pssh_data; + CdmKeyMessage key_request; + CdmKeyResponse key_response; + CdmKeyMessage key_renewal_request; + CdmKeyResponse key_renewal_response; + std::string release_server_url; - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, - DeviceFiles::kLicenseStateReleasing, - kPsshData1, kKeyRequest1, - kKeyResponse1, kKeyRenewalRequest1, - kKeyRenewalResponse1, - kKeyReleaseServerUrl1)); - size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + - kKeyRequest1.size() + kKeyResponse1.size() + - kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + - kKeyReleaseServerUrl1.size(); - EXPECT_TRUE(File::Exists(license_path_1)); - EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); - - EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, - &key_request, &key_response, - &key_renewal_request, - &key_renewal_response, - &release_server_url)); - EXPECT_TRUE(state == DeviceFiles::kLicenseStateReleasing); - EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size()) - == 0); - EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(), - key_request.size()) == 0); - EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(), - key_response.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(), - key_renewal_request.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(), - key_renewal_response.size()) == 0); + for (size_t i = 0; i < kNumberOfLicenses; i++) { + DeviceFiles::LicenseState license_state; + EXPECT_TRUE(device_files.RetrieveLicense( + license_test_data[i].key_set_id, &license_state, &pssh_data, + &key_request, &key_response, &key_renewal_request, + &key_renewal_response, &release_server_url)); + EXPECT_EQ(license_test_data[i].license_state, license_state); + EXPECT_EQ(license_test_data[i].pssh_data, pssh_data); + EXPECT_EQ(license_test_data[i].key_request, key_request); + EXPECT_EQ(license_test_data[i].key_response, key_response); + EXPECT_EQ(license_test_data[i].key_request, key_request); + EXPECT_EQ(license_test_data[i].key_response, key_response); + } } -TEST(DeviceFilesTest, DeleteLicense) { - std::string device_base_path = - DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string license_path_1 = device_base_path + kKeySetId1 + - DeviceFiles::kLicenseFileNameExt; +TEST_F(DeviceFilesTest, UpdateLicenseState) { + MockFile file; + std::string license_path = device_base_path_ + + license_update_test_data[0].key_set_id + + DeviceFiles::GetLicenseFileNameExtension(); - if (!File::Exists(device_base_path)) - EXPECT_TRUE(File::Remove(device_base_path)); + EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))).Times(2) + .WillRepeatedly(Return(true)); + EXPECT_CALL(file, CreateDirectory(_)).Times(0); + EXPECT_CALL(file, Open(StrEq(license_path), + AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet()))) + .Times(2).WillRepeatedly(Return(true)); + EXPECT_CALL(file, Write(IsStrEq(license_update_test_data[0].file_data), + Eq(license_update_test_data[0].file_data.size()))) + .WillOnce(ReturnArg<1>()); + EXPECT_CALL(file, Write(IsStrEq(license_update_test_data[1].file_data), + Eq(license_update_test_data[1].file_data.size()))) + .WillOnce(ReturnArg<1>()); + EXPECT_CALL(file, Close()).Times(2); + EXPECT_CALL(file, Read(_, _)).Times(0); - EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, - DeviceFiles::kLicenseStateActive, - kPsshData1, kKeyRequest1, - kKeyResponse1, kKeyRenewalRequest1, - kKeyRenewalResponse1, - kKeyReleaseServerUrl1)); - size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + - kKeyRequest1.size() + kKeyResponse1.size() + - kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + - kKeyReleaseServerUrl1.size(); - EXPECT_TRUE(File::Exists(license_path_1)); - EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + DeviceFiles device_files; + EXPECT_TRUE(device_files.Init(&file)); + EXPECT_TRUE(device_files.StoreLicense( + license_update_test_data[0].key_set_id, + license_update_test_data[0].license_state, + license_update_test_data[0].pssh_data, + license_update_test_data[0].key_request, + license_update_test_data[0].key_response, + license_update_test_data[0].key_renewal_request, + license_update_test_data[0].key_renewal_response, + license_update_test_data[0].key_release_url)); - DeviceFiles::LicenseState state; - std::string pssh_data, key_request, key_response, key_renewal_request, - key_renewal_response, release_server_url; - - EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, - &key_request, &key_response, - &key_renewal_request, - &key_renewal_response, - &release_server_url)); - EXPECT_TRUE(state == DeviceFiles::kLicenseStateActive); - EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size()) - == 0); - EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(), - key_request.size()) == 0); - EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(), - key_response.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(), - key_renewal_request.size()) == 0); - EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(), - key_renewal_response.size()) == 0); - - EXPECT_TRUE(DeviceFiles::DeleteLicense(kKeySetId1)); - - EXPECT_FALSE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, - &key_request, &key_response, - &key_renewal_request, - &key_renewal_response, - &release_server_url)); - EXPECT_TRUE(File::Remove(device_base_path)); + EXPECT_TRUE(device_files.StoreLicense( + license_update_test_data[0].key_set_id, + license_update_test_data[1].license_state, + license_update_test_data[0].pssh_data, + license_update_test_data[0].key_request, + license_update_test_data[0].key_response, + license_update_test_data[0].key_renewal_request, + license_update_test_data[0].key_renewal_response, + license_update_test_data[0].key_release_url)); } +TEST_F(DeviceFilesTest, DeleteLicense) { + MockFile file; + std::string license_path = device_base_path_ + + license_test_data[0].key_set_id + + DeviceFiles::GetLicenseFileNameExtension(); + + size_t size = license_test_data[0].file_data.size(); + + EXPECT_CALL(file, Exists(StrEq(license_path))).Times(2).WillOnce(Return(true)) + .WillOnce(Return(false)); + EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size)); + EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet())) + .WillOnce(Return(true)); + EXPECT_CALL(file, Read(NotNull(), Eq(size))).WillOnce( + DoAll(SetArrayArgument<0>(license_test_data[0].file_data.begin(), + license_test_data[0].file_data.end()), + Return(size))); + EXPECT_CALL(file, Remove(StrEq(license_path))).WillOnce(Return(true)); + EXPECT_CALL(file, Close()).Times(1); + EXPECT_CALL(file, Write(_, _)).Times(0); + + DeviceFiles device_files; + EXPECT_TRUE(device_files.Init(&file)); + DeviceFiles::LicenseState license_state; + CdmInitData pssh_data; + CdmKeyMessage key_request; + CdmKeyResponse key_response; + CdmKeyMessage key_renewal_request; + CdmKeyResponse key_renewal_response; + std::string release_server_url; + + EXPECT_TRUE(device_files.RetrieveLicense( + license_test_data[0].key_set_id, &license_state, &pssh_data, &key_request, + &key_response, &key_renewal_request, &key_renewal_response, + &release_server_url)); + EXPECT_EQ(license_test_data[0].license_state, license_state); + EXPECT_EQ(license_test_data[0].pssh_data, pssh_data); + EXPECT_EQ(license_test_data[0].key_request, key_request); + EXPECT_EQ(license_test_data[0].key_response, key_response); + EXPECT_EQ(license_test_data[0].key_request, key_request); + EXPECT_EQ(license_test_data[0].key_response, key_response); + + EXPECT_TRUE(device_files.DeleteLicense(license_test_data[0].key_set_id)); + EXPECT_FALSE(device_files.LicenseExists(license_test_data[0].key_set_id)); } + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp index 7384d8d9..c3b8d859 100644 --- a/libwvdrmengine/cdm/core/test/file_store_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/file_store_unittest.cpp @@ -3,187 +3,179 @@ #include "device_files.h" #include "file_store.h" #include "gtest/gtest.h" +#include "properties.h" +#include "test_vectors.h" namespace { - // TODO(rfrias): Make this work for non-unix paths - const std::string kFileExists = "/system/bin/sh"; - const std::string kDirExists = "/system/bin"; - const std::string kFileDoesNotExist = "/system/bin/shxyxyxy"; - const std::string kDirDoesNotExist = "/system/binxyxyxy"; +const std::string kTestFileName = "test.txt"; } // namespace namespace wvcdm { -TEST(FileTest, FileExists) { - EXPECT_TRUE(File::Exists(kFileExists)); - EXPECT_TRUE(File::Exists(kDirExists)); - EXPECT_FALSE(File::Exists(kFileDoesNotExist)); - EXPECT_FALSE(File::Exists(kDirDoesNotExist)); -} +class FileTest : public testing::Test { + protected: + virtual void SetUp() { CreateTestDir(); } + virtual void TearDown() { RemoveTestDir(); } -TEST(FileTest, CreateDirectory) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string dirWoDelimiter = dir.substr(0, dir.size()-1); - if (File::Exists(dirWoDelimiter)) - EXPECT_TRUE(File::Remove(dirWoDelimiter)); - EXPECT_FALSE(File::Exists(dirWoDelimiter)); - EXPECT_TRUE(File::CreateDirectory(dirWoDelimiter)); - EXPECT_TRUE(File::Exists(dirWoDelimiter)); - EXPECT_TRUE(File::Remove(dirWoDelimiter)); - EXPECT_TRUE(File::CreateDirectory(dir)); - EXPECT_TRUE(File::Exists(dir)); - EXPECT_TRUE(File::Remove(dir)); -} + void CreateTestDir() { + File file; + if (!file.Exists(test_vectors::kTestDir)) { + EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir)); + } + EXPECT_TRUE(file.Exists(test_vectors::kTestDir)); + } -TEST(FileTest, RemoveDir) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - if (!File::Exists(dir)) - EXPECT_TRUE(File::CreateDirectory(dir)); - EXPECT_TRUE(File::Exists(dir)); - EXPECT_TRUE(File::Remove(dir)); - EXPECT_FALSE(File::Exists(dir)); -} + void RemoveTestDir() { + File file; + EXPECT_TRUE(file.Remove(test_vectors::kTestDir)); + } -TEST(FileTest, OpenFileUsingConstructor) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string path = dir + DeviceFiles::kDeviceCertificateFileName; - if (!File::Exists(dir)) - EXPECT_TRUE(File::CreateDirectory(dir)); - EXPECT_TRUE(File::Exists(dir)); - File::Remove(path); - File file(path, File::kCreate); - EXPECT_TRUE(file.IsOpen()); - file.Close(); - EXPECT_TRUE(File::Exists(path)); -} + std::string GenerateRandomData(uint32_t len) { + std::string data(len, 0); + for (size_t i = 0; i < len; i++) { + data[i] = rand() % 256; + } + return data; + } +}; -TEST(FileTest, OpenFile) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string path = dir + DeviceFiles::kDeviceCertificateFileName; - if (!File::Exists(dir)) - EXPECT_TRUE(File::CreateDirectory(dir)); - EXPECT_TRUE(File::Exists(dir)); - File::Remove(path); +TEST_F(FileTest, FileExists) { File file; - file.Open(path, File::kCreate); - EXPECT_TRUE(file.IsOpen()); + EXPECT_TRUE(file.Exists(test_vectors::kFileExists)); + EXPECT_TRUE(file.Exists(test_vectors::kDirExists)); + EXPECT_FALSE(file.Exists(test_vectors::kFileDoesNotExist)); + EXPECT_FALSE(file.Exists(test_vectors::kDirDoesNotExist)); +} + +TEST_F(FileTest, CreateDirectory) { + File file; + std::string dir_wo_delimiter = + test_vectors::kTestDir.substr(0, test_vectors::kTestDir.size() - 1); + if (file.Exists(dir_wo_delimiter)) EXPECT_TRUE(file.Remove(dir_wo_delimiter)); + EXPECT_FALSE(file.Exists(dir_wo_delimiter)); + EXPECT_TRUE(file.CreateDirectory(dir_wo_delimiter)); + EXPECT_TRUE(file.Exists(dir_wo_delimiter)); + EXPECT_TRUE(file.Remove(dir_wo_delimiter)); + EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir)); + EXPECT_TRUE(file.Exists(test_vectors::kTestDir)); + EXPECT_TRUE(file.Remove(test_vectors::kTestDir)); +} + +TEST_F(FileTest, RemoveDir) { + File file; + EXPECT_TRUE(file.Remove(test_vectors::kTestDir)); + EXPECT_FALSE(file.Exists(test_vectors::kTestDir)); +} + +TEST_F(FileTest, OpenFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + File handle; + EXPECT_TRUE(handle.Remove(path)); + + File file; + EXPECT_TRUE(file.Open(path, File::kCreate)); file.Close(); - EXPECT_TRUE(File::Exists(path)); + + EXPECT_TRUE(handle.Exists(path)); } -TEST(FileTest, RemoveDirAndFile) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string path = dir + DeviceFiles::kDeviceCertificateFileName; - if (!File::Exists(dir)) - EXPECT_TRUE(File::CreateDirectory(dir)); - EXPECT_TRUE(File::Exists(dir)); - File file(path, File::kCreate); - EXPECT_TRUE(file.IsOpen()); +TEST_F(FileTest, RemoveDirAndFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + + File file; + EXPECT_TRUE(file.Open(path, File::kCreate)); file.Close(); - EXPECT_TRUE(File::Remove(path)); - EXPECT_TRUE(File::Remove(dir)); - EXPECT_FALSE(File::Exists(path)); - EXPECT_FALSE(File::Exists(dir)); -} + EXPECT_TRUE(file.Exists(path)); + EXPECT_TRUE(file.Remove(path)); + EXPECT_FALSE(file.Exists(path)); -TEST(FileTest, IsDir) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string path = dir + DeviceFiles::kDeviceCertificateFileName; - if (!File::Exists(dir)) - EXPECT_TRUE(File::CreateDirectory(dir)); - EXPECT_TRUE(File::Exists(dir)); - File file(path, File::kCreate); - EXPECT_TRUE(file.IsOpen()); + EXPECT_TRUE(file.Open(path, File::kCreate)); file.Close(); - EXPECT_TRUE(File::Exists(path)); - EXPECT_TRUE(File::Exists(dir)); - EXPECT_FALSE(File::IsDirectory(path)); - EXPECT_TRUE(File::IsDirectory(dir)); + EXPECT_TRUE(file.Exists(path)); + RemoveTestDir(); + EXPECT_FALSE(file.Exists(test_vectors::kTestDir)); + EXPECT_FALSE(file.Exists(path)); } -TEST(FileTest, IsRegularFile) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string path = dir + DeviceFiles::kDeviceCertificateFileName; - if (!File::Exists(dir)) - EXPECT_TRUE(File::CreateDirectory(dir)); - EXPECT_TRUE(File::Exists(dir)); - File file(path, File::kCreate); - EXPECT_TRUE(file.IsOpen()); +TEST_F(FileTest, IsDir) { + std::string path = test_vectors::kTestDir + kTestFileName; + File file; + EXPECT_TRUE(file.Open(path, File::kCreate)); file.Close(); - EXPECT_TRUE(File::Exists(path)); - EXPECT_TRUE(File::Exists(dir)); - EXPECT_TRUE(File::IsRegularFile(path)); - EXPECT_FALSE(File::IsRegularFile(dir)); + + EXPECT_TRUE(file.Exists(path)); + EXPECT_TRUE(file.Exists(test_vectors::kTestDir)); + EXPECT_FALSE(file.IsDirectory(path)); + EXPECT_TRUE(file.IsDirectory(test_vectors::kTestDir)); } -TEST(FileTest, WriteReadTextFile) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string path = dir + DeviceFiles::kDeviceCertificateFileName; - if (!File::Exists(dir)) - EXPECT_TRUE(File::CreateDirectory(dir)); - EXPECT_TRUE(File::Exists(dir)); - File::Remove(path); +TEST_F(FileTest, IsRegularFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + File file; + EXPECT_TRUE(file.Open(path, File::kCreate)); + file.Close(); - const char* test_string = "This is a test"; - File file1(path, File::kCreate); - EXPECT_TRUE(file1.IsOpen()); - EXPECT_TRUE(file1.Write(test_string, strlen(test_string)+1)); - file1.Close(); - EXPECT_TRUE(File::Exists(path)); - - char buf[100]; - File file2(path, File::kReadOnly); - EXPECT_TRUE(file2.IsOpen()); - EXPECT_EQ((ssize_t)strlen(test_string)+1, file2.Read(buf, sizeof(buf))); - file2.Close(); - EXPECT_STREQ(test_string, buf); + EXPECT_TRUE(file.Exists(path)); + EXPECT_TRUE(file.Exists(test_vectors::kTestDir)); + EXPECT_TRUE(file.IsRegularFile(path)); + EXPECT_FALSE(file.IsRegularFile(test_vectors::kTestDir)); } -TEST(FileTest, WriteReadBinaryFile) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string path = dir + DeviceFiles::kDeviceCertificateFileName; - if (!File::Exists(dir)) - EXPECT_TRUE(File::CreateDirectory(dir)); - EXPECT_TRUE(File::Exists(dir)); - File::Remove(path); +TEST_F(FileTest, FileSize) { + std::string path = test_vectors::kTestDir + kTestFileName; + File file; + file.Remove(path); - unsigned char test_buf[600]; - for (size_t i = 0; i < sizeof(test_buf); i++) { - test_buf[i] = i % 128; - } - File file1(path, File::kCreate | File::kBinary); - EXPECT_TRUE(file1.IsOpen()); - EXPECT_TRUE(file1.Write(test_buf, sizeof(test_buf))); - file1.Close(); - EXPECT_TRUE(File::Exists(path)); + std::string write_data = GenerateRandomData(600); + File wr_file; + EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary)); + EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size())); + wr_file.Close(); + EXPECT_TRUE(file.Exists(path)); - char buf[1000]; - File file2(path, File::kReadOnly); - EXPECT_TRUE(file2.IsOpen()); - EXPECT_EQ((ssize_t)sizeof(test_buf), file2.Read(buf, sizeof(buf))); - file2.Close(); - EXPECT_TRUE(memcmp(test_buf, buf, sizeof(test_buf)) == 0); + EXPECT_EQ(static_cast(write_data.size()), file.FileSize(path)); } -TEST(FileTest, FileSize) { - std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); - std::string path = dir + DeviceFiles::kDeviceCertificateFileName; - if (!File::Exists(dir)) - EXPECT_TRUE(File::CreateDirectory(dir)); - File::Remove(path); +TEST_F(FileTest, WriteReadTextFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + File file; + file.Remove(path); - unsigned char test_buf[600]; - for (size_t i = 0; i < sizeof(test_buf); i++) { - test_buf[i] = i % 128; - } - File file1(path, File::kCreate | File::kBinary); - EXPECT_TRUE(file1.IsOpen()); - EXPECT_TRUE(file1.Write(test_buf, sizeof(test_buf))); - file1.Close(); - EXPECT_TRUE(File::Exists(path)); + std::string write_data = "This is a test"; + File wr_file; + EXPECT_TRUE(wr_file.Open(path, File::kCreate)); + EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size())); + wr_file.Close(); + EXPECT_TRUE(file.Exists(path)); - EXPECT_EQ((ssize_t)sizeof(test_buf), File::FileSize(path)); - EXPECT_TRUE(File::Remove(dir)); + std::string read_data; + read_data.resize(file.FileSize(path)); + File rd_file; + EXPECT_TRUE(rd_file.Open(path, File::kReadOnly)); + EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size())); + rd_file.Close(); + EXPECT_EQ(write_data, read_data); } +TEST_F(FileTest, WriteReadBinaryFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + File file; + file.Remove(path); + + std::string write_data = GenerateRandomData(600); + File wr_file; + EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary)); + EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size())); + wr_file.Close(); + EXPECT_TRUE(file.Exists(path)); + + std::string read_data; + read_data.resize(file.FileSize(path)); + File rd_file; + EXPECT_TRUE(rd_file.Open(path, File::kReadOnly)); + EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size())); + rd_file.Close(); + EXPECT_EQ(write_data, read_data); } + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/http_socket.cpp b/libwvdrmengine/cdm/core/test/http_socket.cpp index 2761c271..e704eac3 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.cpp +++ b/libwvdrmengine/cdm/core/test/http_socket.cpp @@ -5,13 +5,14 @@ #include #include #include -#include "openssl/bio.h" -#include "openssl/err.h" -#include "openssl/x509.h" #include #include #include "log.h" +#include "openssl/bio.h" +#include "openssl/err.h" +#include "openssl/x509.h" + namespace wvcdm { SSL_CTX* HttpSocket::InitSslContext(void) { @@ -22,8 +23,7 @@ SSL_CTX* HttpSocket::InitSslContext(void) { SSL_load_error_strings(); method = SSLv3_client_method(); ctx = SSL_CTX_new(method); - if (NULL == ctx) - { + if (NULL == ctx) { LOGE("failed to create SSL context"); } return ctx; @@ -35,8 +35,7 @@ void HttpSocket::ShowServerCertificate(const SSL* ssl) { // gets the server certificate cert = SSL_get_peer_certificate(ssl); - if (cert != NULL) - { + if (cert != NULL) { LOGV("server certificate:"); line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); LOGV("subject: %s", line); @@ -45,31 +44,26 @@ void HttpSocket::ShowServerCertificate(const SSL* ssl) { LOGV("issuer: %s", line); free(line); X509_free(cert); - } - else + } else { LOGE("Failed to get server certificate"); + } } -HttpSocket::HttpSocket() : - secure_connect_(true), - socket_fd_(-1), - ssl_(NULL), - ssl_ctx_(NULL), - timeout_enabled_(false) { - +HttpSocket::HttpSocket() + : secure_connect_(true), + socket_fd_(-1), + ssl_(NULL), + ssl_ctx_(NULL), + timeout_enabled_(false) { SSL_library_init(); } -HttpSocket::~HttpSocket() -{ - CloseSocket(); -} +HttpSocket::~HttpSocket() { CloseSocket(); } -void HttpSocket::CloseSocket() -{ +void HttpSocket::CloseSocket() { if (socket_fd_ != -1) { - close(socket_fd_); - socket_fd_ = -1; + close(socket_fd_); + socket_fd_ = -1; } if (secure_connect_) { if (ssl_) { @@ -120,11 +114,10 @@ void HttpSocket::GetDomainNameAndPathFromUrl(const std::string& url, } } -bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_timeout) -{ - secure_connect_ = (strstr(url, "https") != NULL) ? true : false; - if (secure_connect_) - ssl_ctx_ = InitSslContext(); +bool HttpSocket::Connect(const char* url, const std::string& port, + bool enable_timeout, bool secure_connection) { + secure_connect_ = secure_connection; + if (secure_connect_) ssl_ctx_ = InitSslContext(); GetDomainNameAndPathFromUrl(url, domain_name_, resource_path_); @@ -135,7 +128,8 @@ bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_t } int reuse = 1; - if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) { + if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == + -1) { CloseSocket(); LOGE("setsockopt error %d", errno); return false; @@ -143,7 +137,7 @@ bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_t struct addrinfo hints; memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; + hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; struct addrinfo* addr_info = NULL; bool status = true; @@ -155,7 +149,8 @@ bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_t } else { if (connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen) == -1) { CloseSocket(); - LOGE("cannot connect socket to %s, error=%d", domain_name_.c_str(), errno); + LOGE("cannot connect socket to %s, error=%d", domain_name_.c_str(), + errno); status = false; } } @@ -164,36 +159,38 @@ bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_t freeaddrinfo(addr_info); } + if (!status) return false; + // secures connection if (secure_connect_ && ssl_ctx_) { ssl_ = SSL_new(ssl_ctx_); - if (ssl_) { - BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE); - if (a_bio) { - SSL_set_bio(ssl_, a_bio, a_bio); - int ret = SSL_connect(ssl_); - if (1 != ret) { - char buf[256]; - LOGE("SSL_connect error:%s", ERR_error_string(ERR_get_error(), buf)); - } - } else { - LOGE("BIO_new_socket error"); - } - } else { + if (!ssl_) { LOGE("failed SSL_new"); + return false; + } + + BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE); + if (!a_bio) { + LOGE("BIO_new_socket error"); + return false; + } + + SSL_set_bio(ssl_, a_bio, a_bio); + int ret = SSL_connect(ssl_); + if (1 != ret) { + char buf[256]; + LOGE("SSL_connect error:%s", ERR_error_string(ERR_get_error(), buf)); + return false; } } - return status; + return true; } -int HttpSocket::Read(char* data, int len) { - return(Read(data, len, 0)); -} +int HttpSocket::Read(char* data, int len) { return (Read(data, len, 0)); } // makes non-blocking mode only during read, it supports timeout for read // returns -1 for error, number of bytes read for success -int HttpSocket::Read(char* data, int len, int timeout_in_ms) -{ +int HttpSocket::Read(char* data, int len, int timeout_in_ms) { bool use_timeout = (timeout_enabled_ && (timeout_in_ms > 0)); int original_flags = 0; if (use_timeout) { @@ -216,7 +213,7 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms) fd_set read_fds; struct timeval tv; tv.tv_sec = timeout_in_ms / 1000; - tv.tv_usec = (timeout_in_ms % 1000) * 1000; + tv.tv_usec = (timeout_in_ms % 1000) * 1000; FD_ZERO(&read_fds); FD_SET(socket_fd_, &read_fds); if (select(socket_fd_ + 1, &read_fds, NULL, NULL, &tv) == -1) { @@ -224,7 +221,7 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms) break; } if (!FD_ISSET(socket_fd_, &read_fds)) { - LOGE("socket read timeout"); + LOGD("socket read timeout"); break; } } @@ -240,7 +237,8 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms) total_read += read; } else if (read == 0) { // in blocking mode, zero read mean's peer closed. - // in non-blocking mode, select said that there is data. so it should not happen + // in non-blocking mode, select said that there is data. so it should not + // happen break; } else { LOGE("recv returned %d, error = %d", read, errno); @@ -249,13 +247,12 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms) } if (use_timeout) { - fcntl(socket_fd_, F_SETFL, original_flags); // now blocking again + fcntl(socket_fd_, F_SETFL, original_flags); // now blocking again } return total_read; } -int HttpSocket::Write(const char* data, int len) -{ +int HttpSocket::Write(const char* data, int len) { int total_sent = 0; int sent = 0; int to_send = len; @@ -270,7 +267,7 @@ int HttpSocket::Write(const char* data, int len) data += sent; total_sent += sent; } else if (sent == 0) { - usleep(10); // retry later + usleep(10); // retry later } else { LOGE("send returned error %d", errno); } @@ -278,4 +275,4 @@ int HttpSocket::Write(const char* data, int len) return total_sent; } -} // namespace wvcdm +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/http_socket.h b/libwvdrmengine/cdm/core/test/http_socket.h index bbb012a7..79363b10 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.h +++ b/libwvdrmengine/cdm/core/test/http_socket.h @@ -16,7 +16,8 @@ class HttpSocket { ~HttpSocket(); void CloseSocket(); - bool Connect(const char* url, const std::string& port, bool enable_timeout); + bool Connect(const char* url, const std::string& port, bool enable_timeout, + bool secure_connection); void GetDomainNameAndPathFromUrl(const std::string& url, std::string& domain_name, std::string& resource_path); @@ -27,7 +28,9 @@ class HttpSocket { int Write(const char* data, int len); private: - void CloseSslContext(SSL_CTX* ctx) const { if (ctx) SSL_CTX_free(ctx); } + void CloseSslContext(SSL_CTX* ctx) const { + if (ctx) SSL_CTX_free(ctx); + } SSL_CTX* InitSslContext(void); void ShowServerCertificate(const SSL* ssl); diff --git a/libwvdrmengine/cdm/core/test/http_socket_test.cpp b/libwvdrmengine/cdm/core/test/http_socket_test.cpp index cf7b1d8e..fbdca0e5 100644 --- a/libwvdrmengine/cdm/core/test/http_socket_test.cpp +++ b/libwvdrmengine/cdm/core/test/http_socket_test.cpp @@ -5,12 +5,14 @@ #include "http_socket.h" #include "log.h" #include "string_conversions.h" +#include "url_request.h" namespace { - std::string gTestServer("https://www.google.com"); - std::string gTestData("Hello"); - const int kHttpBufferSize = 4096; - char gBuffer[kHttpBufferSize]; +const std::string kHttpsTestServer("https://www.google.com"); +std::string gTestServer(kHttpsTestServer); +std::string gTestData("Hello"); +const int kHttpBufferSize = 4096; +char gBuffer[kHttpBufferSize]; } namespace wvcdm { @@ -21,9 +23,10 @@ class HttpSocketTest : public testing::Test { ~HttpSocketTest() { socket_.CloseSocket(); } protected: - bool Connect(const std::string& server_url) { + bool Connect(const std::string& server_url, bool secure_connection) { - if (socket_.Connect(server_url.c_str(), "80", true)) { + std::string port = secure_connection ? "443" : "80"; + if (socket_.Connect(server_url.c_str(), port, true, secure_connection)) { LOGD("connected to %s", socket_.domain_name().c_str()); } else { LOGE("failed to connect to %s", socket_.domain_name().c_str()); @@ -44,7 +47,7 @@ class HttpSocketTest : public testing::Test { request.append("\r\nUser-Agent: httpSocketTest/1.0\r\n"); request.append("Content-Length: "); memset(gBuffer, 0, kHttpBufferSize); - snprintf(gBuffer, kHttpBufferSize, "%d\r\n", static_cast(data.size())); + snprintf(gBuffer, kHttpBufferSize, "%d\r\n", static_cast(data.size())); request.append(gBuffer); request.append("Content-Type: multipart/form-data\r\n"); @@ -77,80 +80,89 @@ class HttpSocketTest : public testing::Test { std::string resource_path_; }; -TEST_F(HttpSocketTest, GetDomainNameAndPathFromUrlTest) -{ - socket_.GetDomainNameAndPathFromUrl("http://code.google.com/p/googletest/wiki/Primer", - domain_name_, - resource_path_); +TEST_F(HttpSocketTest, GetDomainNameAndPathFromUrlTest) { + socket_.GetDomainNameAndPathFromUrl( + "https://code.google.com/p/googletest/wiki/Primer", domain_name_, + resource_path_); EXPECT_STREQ("code.google.com", domain_name_.c_str()); EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str()); - socket_.GetDomainNameAndPathFromUrl("http://code.google.com/p/googletest/wiki/Primer/", - domain_name_, - resource_path_); + socket_.GetDomainNameAndPathFromUrl( + "http://code.google.com/p/googletest/wiki/Primer/", domain_name_, + resource_path_); EXPECT_STREQ("code.google.com", domain_name_.c_str()); EXPECT_STREQ("p/googletest/wiki/Primer/", resource_path_.c_str()); - socket_.GetDomainNameAndPathFromUrl("http://code.google.com/", - domain_name_, + socket_.GetDomainNameAndPathFromUrl("http://code.google.com/", domain_name_, resource_path_); EXPECT_STREQ("code.google.com", domain_name_.c_str()); EXPECT_STREQ("", resource_path_.c_str()); - socket_.GetDomainNameAndPathFromUrl("http://code.google.com", - domain_name_, + socket_.GetDomainNameAndPathFromUrl("http://code.google.com", domain_name_, resource_path_); EXPECT_STREQ("code.google.com", domain_name_.c_str()); EXPECT_STREQ("", resource_path_.c_str()); - socket_.GetDomainNameAndPathFromUrl("code.google.com/p/googletest/wiki/Primer", - domain_name_, - resource_path_); + socket_.GetDomainNameAndPathFromUrl( + "code.google.com/p/googletest/wiki/Primer", domain_name_, resource_path_); EXPECT_STREQ("code.google.com", domain_name_.c_str()); EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str()); - socket_.GetDomainNameAndPathFromUrl("code.google.com", - domain_name_, + socket_.GetDomainNameAndPathFromUrl("code.google.com", domain_name_, resource_path_); EXPECT_STREQ("code.google.com", domain_name_.c_str()); EXPECT_STREQ("", resource_path_.c_str()); - socket_.GetDomainNameAndPathFromUrl("code.google.com/", - domain_name_, + socket_.GetDomainNameAndPathFromUrl("code.google.com/", domain_name_, resource_path_); EXPECT_STREQ("code.google.com", domain_name_.c_str()); EXPECT_STREQ("", resource_path_.c_str()); - socket_.GetDomainNameAndPathFromUrl("", - domain_name_, - resource_path_); + socket_.GetDomainNameAndPathFromUrl("", domain_name_, resource_path_); EXPECT_TRUE(domain_name_.empty()); EXPECT_TRUE(resource_path_.empty()); socket_.GetDomainNameAndPathFromUrl("http://10.21.200.68:8888/drm", - domain_name_, - resource_path_); + domain_name_, resource_path_); EXPECT_STREQ("10.21.200.68", domain_name_.c_str()); EXPECT_STREQ("drm", resource_path_.c_str()); - socket_.GetDomainNameAndPathFromUrl("http://10.21.200.68:8888", - domain_name_, + socket_.GetDomainNameAndPathFromUrl("http://10.21.200.68:8888", domain_name_, resource_path_); EXPECT_STREQ("10.21.200.68", domain_name_.c_str()); EXPECT_TRUE(resource_path_.empty()); } -TEST_F(HttpSocketTest, ConnectTest) -{ - EXPECT_TRUE(Connect(gTestServer)); +TEST_F(HttpSocketTest, ConnectTest) { + const bool kUseSecureConnection = true; + + if (gTestServer.find("https") != std::string::npos) { + EXPECT_TRUE(Connect(gTestServer, kUseSecureConnection)); + socket_.CloseSocket(); + + // https connection allows insecure connection through port 80 as well + EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection)); + socket_.CloseSocket(); + } else { + EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection)); + socket_.CloseSocket(); + + // Test for the case that non-https connection must not use port 443 + EXPECT_FALSE(Connect(gTestServer, kUseSecureConnection)); + socket_.CloseSocket(); + } + + EXPECT_FALSE(Connect("ww.g.c", kUseSecureConnection)); socket_.CloseSocket(); - EXPECT_FALSE(Connect("ww.g.c")); + + EXPECT_FALSE(Connect("ww.g.c", !kUseSecureConnection)); socket_.CloseSocket(); } -TEST_F(HttpSocketTest, RoundTripTest) -{ - ASSERT_TRUE(Connect(gTestServer)); +TEST_F(HttpSocketTest, RoundTripTest) { + int secure_connection = + (gTestServer.find("https") != std::string::npos) ? true : false; + ASSERT_TRUE(Connect(gTestServer, secure_connection)); EXPECT_TRUE(PostRequest(gTestData)); GetResponse(); socket_.CloseSocket(); @@ -158,28 +170,31 @@ TEST_F(HttpSocketTest, RoundTripTest) } // namespace wvcdm -int main(int argc, char **argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); std::string temp; - std::string test_server(gTestServer); + std::string test_server(kHttpsTestServer); std::string test_data(gTestData); - for (int i=1; i"; - std::cout << "configure the test server url, please include http[s] in the url" << std::endl; + std::cout + << "configure the test server url, please include http[s] in the url" + << std::endl; std::cout << std::setw(30) << std::left << " "; std::cout << "default: " << test_server << std::endl; std::cout << std::setw(30) << std::left << " --data="; - std::cout << "configure data to send, in ascii string format" << std::endl; + std::cout << "configure data to send, in ascii string format" + << std::endl; std::cout << std::setw(30) << std::left << " "; std::cout << "default: " << test_data << std::endl << std::endl; return 0; diff --git a/libwvdrmengine/cdm/core/test/license_unittest.cpp b/libwvdrmengine/cdm/core/test/license_unittest.cpp index f26995a4..9cc6c862 100644 --- a/libwvdrmengine/cdm/core/test/license_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/license_unittest.cpp @@ -1,6 +1,5 @@ // Copyright 2012 Google Inc. All Rights Reserved. -#include "crypto_engine.h" #include "crypto_session.h" #include "license.h" #include "gtest/gtest.h" @@ -49,15 +48,13 @@ namespace wvcdm { class LicenseTest : public ::testing::Test { protected: virtual void SetUp() { - CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); - EXPECT_TRUE(crypto_engine != NULL); - session_ = crypto_engine->CreateSession("Dummy"); + session_ = new CryptoSession(); EXPECT_TRUE(session_ != NULL); std::string token; - EXPECT_TRUE(crypto_engine->GetToken(&token)); + EXPECT_TRUE(session_->GetToken(&token)); - EXPECT_TRUE(session_->IsOpen()); + EXPECT_TRUE(session_->Open()); EXPECT_TRUE(license_.Init(token, session_, &policy_engine_)); } @@ -76,7 +73,8 @@ TEST(LicenseTestSession, InitNullSession) { EXPECT_FALSE(license.Init("Dummy", NULL, NULL)); } -TEST_F(LicenseTest, PrepareKeyRequest) { +// TODO(rfrias): Fix or remove test. +TEST_F(LicenseTest, DISABLED_PrepareKeyRequest) { std::string signed_request; CdmAppParameterMap app_parameters; std::string server_url; @@ -88,7 +86,8 @@ TEST_F(LicenseTest, PrepareKeyRequest) { EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest)); } -TEST_F(LicenseTest, HandleKeyResponseValid) { +// TODO(rfrias): Fix or remove test. +TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) { std::string signed_request; CdmAppParameterMap app_parameters; std::string server_url; @@ -101,7 +100,8 @@ TEST_F(LicenseTest, HandleKeyResponseValid) { EXPECT_TRUE(license_.HandleKeyResponse(a2bs_hex(kValidResponse))); } -TEST_F(LicenseTest, HandleKeyResponseInvalid) { +// TODO(rfrias): Fix or remove test. +TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) { std::string signed_request; CdmAppParameterMap app_parameters; std::string server_url; diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index 05c1cb71..7d3afc48 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -65,6 +65,10 @@ class PolicyEngineTest : public ::testing::Test { } virtual void TearDown() { + delete policy_engine_; + // Done by policy engine: delete mock_clock_; + policy_engine_ = NULL; + mock_clock_ = NULL; } MockClock* mock_clock_; @@ -85,7 +89,6 @@ TEST_F(PolicyEngineTest, NoLicense) { TEST_F(PolicyEngineTest, PlaybackSuccess) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(3) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 5)) .WillOnce(Return(license_start_time_ + 10)); @@ -103,10 +106,7 @@ TEST_F(PolicyEngineTest, PlaybackSuccess) { TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(3) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 10)); + .WillOnce(Return(license_start_time_ + 5)); License_Policy* policy = license_.mutable_policy(); policy->set_can_play(false); @@ -126,7 +126,6 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) { TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(4) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 5)) .WillOnce(Return(license_start_time_ + 3600)) @@ -157,7 +156,6 @@ TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) { TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(4) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 10000)) .WillOnce(Return(license_start_time_ + 13598)) @@ -185,7 +183,6 @@ TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) { TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(4) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 5)) .WillOnce(Return(license_start_time_ + 3600)) @@ -213,7 +210,6 @@ TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) { TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(4) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 5)) .WillOnce(Return(license_start_time_ + 3600)) @@ -242,7 +238,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) { TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(4) .WillOnce(Return(license_start_time_ + 10000)) .WillOnce(Return(license_start_time_ + 10005)) .WillOnce(Return(license_start_time_ + 13598)) @@ -271,7 +266,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) { TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(4) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 5)) .WillOnce(Return(license_start_time_ + 3600)) @@ -300,7 +294,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) { TEST_F(PolicyEngineTest, PlaybackOk_Durations0) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(4) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 5)) .WillOnce(Return(license_start_time_ + 604800)) @@ -333,7 +326,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) { TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(5) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + license_duration_ - playback_duration_ + 1)) @@ -366,7 +358,6 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) { TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(6) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + license_duration_ - playback_duration_ + 1)) @@ -406,7 +397,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(6) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + license_duration_ - playback_duration_ + 1)) @@ -450,7 +440,6 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(10) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + license_duration_ - playback_duration_ + 1)) @@ -509,7 +498,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) { TEST_F(PolicyEngineTest, PlaybackOk_RenewedSuccessAfterExpiry) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(10) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + license_duration_ - playback_duration_ + 1)) @@ -569,7 +557,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedSuccessAfterExpiry) { TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(6) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 5)) .WillOnce(Return(license_start_time_ + 10)) @@ -608,7 +595,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) { TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(1) .WillOnce(Return(license_start_time_)); CdmQueryMap query_info; @@ -617,7 +603,6 @@ TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) { TEST_F(PolicyEngineTest, QuerySuccess) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(2) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 100)); @@ -647,10 +632,7 @@ TEST_F(PolicyEngineTest, QuerySuccess) { TEST_F(PolicyEngineTest, QuerySuccess_Offline) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(4) - .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 10)) .WillOnce(Return(license_start_time_ + 100)); LicenseIdentification* id = license_.mutable_id(); @@ -693,7 +675,6 @@ TEST_F(PolicyEngineTest, QuerySuccess_Offline) { TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .Times(4) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + 5)) .WillOnce(Return(license_start_time_ + 10)) diff --git a/libwvdrmengine/cdm/core/test/timer_unittest.cpp b/libwvdrmengine/cdm/core/test/timer_unittest.cpp index df41b8b1..edc63879 100644 --- a/libwvdrmengine/cdm/core/test/timer_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/timer_unittest.cpp @@ -14,7 +14,7 @@ class TestTimerHandler : public TimerHandler { timer_events_++; } - uint32_t GetTimerEvents() { return timer_events_; } + uint32_t timer_events() { return timer_events_; } void ResetTimerEvents() { timer_events_ = 0; } private: @@ -34,21 +34,21 @@ TEST(TimerTest, TimerCheck) { Timer timer; uint32_t duration = 10; - EXPECT_EQ(static_cast(0), handler.GetTimerEvents()); + EXPECT_EQ(0u, handler.timer_events()); EXPECT_FALSE(timer.IsRunning()); EXPECT_TRUE(timer.Start(&handler, 1)); EXPECT_TRUE(timer.IsRunning()); sleep(duration); - EXPECT_TRUE(duration-1 <= handler.GetTimerEvents()); - EXPECT_TRUE(handler.GetTimerEvents() <= duration+1); + EXPECT_LE(duration-1, handler.timer_events()); + EXPECT_LE(handler.timer_events(), duration+1); timer.Stop(); EXPECT_FALSE(timer.IsRunning()); sleep(duration); - EXPECT_TRUE(duration-1 <= handler.GetTimerEvents()); - EXPECT_TRUE(handler.GetTimerEvents() <= duration+1); + EXPECT_LE(duration-1, handler.timer_events()); + EXPECT_LE(handler.timer_events(), duration+1); } } diff --git a/libwvdrmengine/cdm/core/test/url_request.cpp b/libwvdrmengine/cdm/core/test/url_request.cpp index 618c2ccf..2bcc70c2 100644 --- a/libwvdrmengine/cdm/core/test/url_request.cpp +++ b/libwvdrmengine/cdm/core/test/url_request.cpp @@ -2,6 +2,7 @@ #include "url_request.h" +#include #include #include "http_socket.h" @@ -10,28 +11,26 @@ namespace wvcdm { -UrlRequest::UrlRequest(const std::string& url, const std::string& port) - : is_connected_(false), +UrlRequest::UrlRequest(const std::string& url, const std::string& port, + bool secure_connection, bool chunk_transfer_mode) + : chunk_transfer_mode_(chunk_transfer_mode), + is_connected_(false), port_("80"), request_(""), - server_url_(url) -{ + server_url_(url) { if (!port.empty()) { port_.assign(port); } - if (socket_.Connect((server_url_).c_str(), port_, true)) { + if (socket_.Connect((server_url_).c_str(), port_, true, secure_connection)) { LOGD("connected to %s", socket_.domain_name().c_str()); is_connected_ = true; } else { - LOGE("failed to connect to %s, port=%s", - socket_.domain_name().c_str(), port.c_str()); + LOGE("failed to connect to %s, port=%s", socket_.domain_name().c_str(), + port.c_str()); } } -UrlRequest::~UrlRequest() -{ - socket_.CloseSocket(); -} +UrlRequest::~UrlRequest() { socket_.CloseSocket(); } void UrlRequest::AppendChunkToUpload(const std::string& data) { // format of chunk: @@ -49,24 +48,97 @@ void UrlRequest::AppendChunkToUpload(const std::string& data) { request_.append("\r\n"); // marks end of data } -int UrlRequest::GetResponse(std::string& response) { - response.clear(); +// Concatenate all chunks into one blob and returns the response with +// header information. +void UrlRequest::ConcatenateChunkedResponse(const std::string http_response, + std::string* modified_response) { + if (http_response.empty()) return; - const int kTimeoutInMs = 1500 * 2; + modified_response->clear(); + const std::string kChunkedTag = "Transfer-Encoding: chunked\r\n\r\n"; + size_t chunked_tag_pos = http_response.find(kChunkedTag); + if (std::string::npos != chunked_tag_pos) { + // processes chunked encoding + size_t chunk_size = 0; + size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size(); + sscanf(&http_response[chunk_size_pos], "%x", &chunk_size); + if (chunk_size > http_response.size()) { + // precaution, in case we misread chunk size + LOGE("invalid chunk size %u", chunk_size); + return; + } + + // Search for chunks in the following format: + // header + // chunk size\r\n <-- chunk_size_pos @ beginning of chunk size + // chunk data\r\n <-- chunk_pos @ beginning of chunk data + // chunk size\r\n + // chunk data\r\n + // 0\r\n + const std::string kCrLf = "\r\n"; + size_t chunk_pos = http_response.find(kCrLf, chunk_size_pos); + modified_response->assign(http_response, 0, chunk_size_pos); + + while ((chunk_size > 0) && (std::string::npos != chunk_pos)) { + chunk_pos += kCrLf.size(); + modified_response->append(http_response, chunk_pos, chunk_size); + + // Search for next chunk + chunk_size_pos = chunk_pos + chunk_size + kCrLf.size(); + sscanf(&http_response[chunk_size_pos], "%x", &chunk_size); + if (chunk_size > http_response.size()) { + // precaution, in case we misread chunk size + LOGE("invalid chunk size %u", chunk_size); + break; + } + chunk_pos = http_response.find(kCrLf, chunk_size_pos); + } + } else { + // Response is not chunked encoded + modified_response->assign(http_response); + } +} + +void UrlRequest::DumpMessage(const std::string& description, + const std::string& message) { + if (description.empty()) return; + + LOGD("%s (%d bytes):", description.c_str(), message.size()); + + size_t remaining = message.size(); + size_t portion = 0; + size_t start = 0; + while (remaining > 0) { + // LOGX may not get to empty its buffer if it is too large, + // pick an arbitrary small size to be safe + portion = (remaining < 512) ? remaining : 512; + LOGD("%s", message.substr(start, portion).c_str()); + start += portion; + remaining -= portion; + } + LOGD("total bytes dumped(%d)", start); +} + +int UrlRequest::GetResponse(std::string* message) { + message->clear(); + + std::string response; + const int kTimeoutInMs = 3000; int bytes = 0; - int total_bytes = 0; do { memset(buffer_, 0, kHttpBufferSize); bytes = socket_.Read(buffer_, kHttpBufferSize, kTimeoutInMs); if (bytes > 0) { response.append(buffer_, bytes); - total_bytes += bytes; } else { if (bytes < 0) LOGE("read error = ", errno); // bytes == 0 indicates nothing to read } } while (bytes > 0); - return total_bytes; + + ConcatenateChunkedResponse(response, message); + LOGD("%d bytes returned", message->size()); + return message->size(); } int UrlRequest::GetStatusCode(const std::string& response) { @@ -106,6 +178,9 @@ bool UrlRequest::PostRequestChunk(const std::string& data) { } bool UrlRequest::PostRequest(const std::string& data) { + if (chunk_transfer_mode_) { + return PostRequestChunk(data); + } request_.assign("POST /"); request_.append(socket_.resource_path()); request_.append(" HTTP/1.1\r\n"); @@ -149,4 +224,4 @@ bool UrlRequest::PostCertRequestInQueryString(const std::string& data) { return true; } -} // namespace wvcdm +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/url_request.h b/libwvdrmengine/cdm/core/test/url_request.h index 668a55fc..098b43e3 100644 --- a/libwvdrmengine/cdm/core/test/url_request.h +++ b/libwvdrmengine/cdm/core/test/url_request.h @@ -13,12 +13,16 @@ namespace wvcdm { // Only POST request method is implemented. class UrlRequest { public: - UrlRequest(const std::string& url, const std::string& port); + UrlRequest(const std::string& url, const std::string& port, + bool secure_connect, bool chunk_transfer_mode); ~UrlRequest(); void AppendChunkToUpload(const std::string& data); - int GetResponse(std::string& response); - int GetStatusCode(const std::string& response); + void ConcatenateChunkedResponse(const std::string http_response, + std::string* modified_response); + void DumpMessage(const std::string& description, const std::string& message); + int GetResponse(std::string* message); + int GetStatusCode(const std::string& response); bool is_connected() const { return is_connected_; } bool PostRequest(const std::string& data); bool PostRequestChunk(const std::string& data); @@ -27,6 +31,7 @@ class UrlRequest { private: static const unsigned int kHttpBufferSize = 4096; char buffer_[kHttpBufferSize]; + bool chunk_transfer_mode_; bool is_connected_; std::string port_; std::string request_; diff --git a/libwvdrmengine/cdm/core/test/wvcdm_test.cpp b/libwvdrmengine/cdm/core/test/wvcdm_test.cpp deleted file mode 100644 index 55076ba7..00000000 --- a/libwvdrmengine/cdm/core/test/wvcdm_test.cpp +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "base/at_exit.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_test_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "string_conversions.h" -#include "wv_content_decryption_module.h" - -namespace { - -// Default license server to play server, can be configured using --server command line option -std::string gGpLicenseServer = - "https://jmt17.google.com/video-dev/license/GetCencLicense"; -std::string gYtLicenseServer = - "https://www.youtube.com/api/drm/widevine?video_id=03681262dc412c06&source=YOUTUBE"; - -std::string gLicenseServer(gYtLicenseServer); - -// Default key id (pssh), can be configured using --keyid command line option -std::string gKeyID = "000000347073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id - "08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data - -// An invalid key id, expected to fail -std::string kWrongKeyID = "000000347073736800000000" // blob size and psshb - "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id - "0901121094889920E8D6520098577DF8F2DD5546"; // pssh data - -static const char kWidevineKeySystem[] = "com.widevine.alpha"; - -} // namespace - -namespace wvcdm_test { - -class WvCdmDecryptorTest : public testing::Test { - public: - WvCdmDecryptorTest() {} - ~WvCdmDecryptorTest() {} - - protected: - void GenerateKeyRequest(const std::string& key_system, - const std::string& init_data) { - EXPECT_EQ(decryptor_.GenerateKeyRequest(key_system, - init_data, - &key_msg_, - &session_id_), wvcdm::KEY_MESSAGE); - } - - void GenerateRenewalRequest(const std::string& key_system, - const std::string& init_data) { - EXPECT_EQ(decryptor_.GenerateRenewalRequest(key_system, - init_data, - session_id_, &key_msg_), - wvcdm::KEY_MESSAGE); - } - - std::string GetKeyRequestResponse(const std::string& server_url, - int expected_response) { - net::TestDelegate d; - net::TestNetworkDelegate network_delegate; - net::TestURLRequestContext context(true); - context.set_network_delegate(&network_delegate); - scoped_ptr resolver( - net::HostResolver::CreateDefaultResolver(NULL)); - context.set_host_resolver(resolver.get()); - context.Init(); - net::URLRequest r(GURL(server_url), &d, &context); - r.EnableChunkedUpload(); - r.set_method("POST"); - r.AppendChunkToUpload(key_msg_.data(), key_msg_.size(), true); - r.Start(); - EXPECT_TRUE(r.is_pending()); - - MessageLoop::current()->Run(); - - std::string data = d.data_received(); - // Youtube server returns 400 for invalid message while play server returns - // 500, so just test inequity here for invalid message - if (expected_response == 200) { - EXPECT_EQ(200, r.GetResponseCode()) << data; - } else { - EXPECT_NE(200, r.GetResponseCode()) << data; - } - EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status()); - EXPECT_TRUE(d.bytes_received() > 0); - // Extract DRM message: - // https://docs.google.com/a/google.com/document/d/1Xue3bgwv2qIAnuFIZ-HCcix43dvH2UxsOEA_8FCBO3I/edit# - if (r.status().status() == net::URLRequestStatus::SUCCESS) { - size_t pos = data.find("\r\n"); - if (pos != data.npos) data = data.substr(pos+2); - } - return data; - } - - void VerifyKeyRequestResponse(const std::string& server_url, - std::string& init_data, - bool is_renewal) { - std::string resp = GetKeyRequestResponse(server_url, 200); - - std::cout << "Message: " << wvcdm::b2a_hex(key_msg_) << std::endl; - std::cout << "Response: " << wvcdm::b2a_hex(resp) << std::endl; - - if (is_renewal) { - EXPECT_EQ(decryptor_.RenewKey(kWidevineKeySystem, - init_data, resp, - session_id_), wvcdm::KEY_ADDED); - } - else { - EXPECT_EQ(decryptor_.AddKey(kWidevineKeySystem, - init_data, resp, - session_id_), wvcdm::KEY_ADDED); - } - - std::cout << "back from AddKey" << std::endl; - - } - - wvcdm::WvContentDecryptionModule decryptor_; - - std::string key_msg_; - std::string session_id_; -}; - -TEST_F(WvCdmDecryptorTest, BaseMessageTest) -{ - GenerateKeyRequest(kWidevineKeySystem, gKeyID); - - GetKeyRequestResponse(gLicenseServer, 200); -} - -TEST_F(WvCdmDecryptorTest, WrongMessageTest) -{ - std::string wrong_message = wvcdm::a2b_hex(kWrongKeyID); - - GenerateKeyRequest(kWidevineKeySystem, wrong_message); - - GetKeyRequestResponse(gLicenseServer, 500); -} - -TEST_F(WvCdmDecryptorTest, NormalWebMDecryption) { - GenerateKeyRequest(kWidevineKeySystem, gKeyID); - - VerifyKeyRequestResponse(gLicenseServer, gKeyID, false); -} - -TEST_F(WvCdmDecryptorTest, LicenseRenewal) { - GenerateKeyRequest(kWidevineKeySystem, gKeyID); - - VerifyKeyRequestResponse(gLicenseServer, gKeyID, false); - - GenerateRenewalRequest(kWidevineKeySystem, gKeyID); - - VerifyKeyRequestResponse(gLicenseServer, gKeyID, true); -} - -} // namespace wvcdm_test - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - - std::string temp; - std::string license_server(gLicenseServer); - std::string key_id(gKeyID); - for (int i=1; i"; - std::cout << "configure the license server url, please include http[s] in the url" << std::endl; - std::cout << std::setw(30) << std::left << " "; - std::cout << "default: " << license_server << std::endl; - std::cout << std::setw(30) << std::left << " --keyid="; - std::cout << "configure the key id or pssh, in hex format" << std::endl; - std::cout << std::setw(30) << std::left << " "; - std::cout << "default: " << key_id << std::endl << std::endl; - return 0; - } - } - - std::cout << std::endl; - std::cout << "Server: " << gLicenseServer << std::endl; - std::cout << "KeyID: " << gKeyID << std::endl << std::endl; - - gKeyID = wvcdm::a2b_hex(gKeyID); - - base::AtExitManager exit; - MessageLoop ttr(MessageLoop::TYPE_IO); - return RUN_ALL_TESTS(); -} diff --git a/libwvdrmengine/cdm/include/properties_configuration.h b/libwvdrmengine/cdm/include/properties_configuration.h index 852f55ab..cd4087c9 100644 --- a/libwvdrmengine/cdm/include/properties_configuration.h +++ b/libwvdrmengine/cdm/include/properties_configuration.h @@ -26,6 +26,11 @@ const bool kPropertyOemCryptoUseUserSpaceBuffers = false; // and passed as the token in the license request const bool kPropertyUseCertificatesAsIdentification = true; +// If false, extraction of widevine PSSH information from the PSSH box +// takes place external to the CDM. This will become the default behaviour +// once all platforms support it (b/9465346) +const bool kExtractPsshData = true; + } // namespace wvcdm #endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_ diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index f6a8d9ac..17388ba0 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -34,7 +34,7 @@ class WvContentDecryptionModule { // Accept license response and extract key info. virtual CdmResponseType AddKey(const CdmSessionId& session_id, const CdmKeyResponse& key_data, - CdmKeySetId& key_set_id); + CdmKeySetId* key_set_id); // Setup keys for offline usage which were retrived in an earlier key request virtual CdmResponseType RestoreKey(const CdmSessionId& session_id, diff --git a/libwvdrmengine/cdm/src/file_store.cpp b/libwvdrmengine/cdm/src/file_store.cpp index 3df331ad..e8eec4fc 100644 --- a/libwvdrmengine/cdm/src/file_store.cpp +++ b/libwvdrmengine/cdm/src/file_store.cpp @@ -14,31 +14,31 @@ #include "log.h" namespace { - const char* kCurrentDirectory = "."; - const char* kParentDirectory = ".."; -} +const char* kCurrentDirectory = "."; +const char* kParentDirectory = ".."; +} // namespace namespace wvcdm { class File::Impl { public: + Impl() : file_(NULL) {} + Impl(const std::string& file_path) : file_(NULL), file_path_(file_path) {} + virtual ~Impl() {} + FILE* file_; + std::string file_path_; }; File::File() : impl_(new File::Impl()) {} -File::File(const std::string& file_path, int flags) : - impl_(new File::Impl()) { - Open(file_path, flags); -} - File::~File() { Close(); delete impl_; } bool File::Open(const std::string& name, int flags) { - std::string openFlags = ""; + std::string open_flags; if (((flags & File::kTruncate) && Exists(name)) || ((flags & File::kCreate) && !Exists(name))) { @@ -49,39 +49,29 @@ bool File::Open(const std::string& name, int flags) { } if (flags & File::kBinary) { - openFlags = (flags & File::kReadOnly)? "rb" : "rb+"; + open_flags = (flags & File::kReadOnly)? "rb" : "rb+"; } else { - openFlags = (flags & File::kReadOnly)? "r" : "r+"; + open_flags = (flags & File::kReadOnly)? "r" : "r+"; } - impl_->file_ = fopen(name.c_str(), openFlags.c_str()); + impl_->file_ = fopen(name.c_str(), open_flags.c_str()); if (!impl_->file_) { LOGW("File::Open: fopen failed: %d", errno); } - return IsOpen(); + impl_->file_path_ = name; + return impl_->file_ != NULL; } void File::Close() { if (impl_->file_) { fclose(impl_->file_); impl_->file_ = NULL; - } + } } -bool File::IsOpen() { - return impl_->file_ != NULL; -} - -bool File::IsBad() { - if (impl_->file_) - return ferror(impl_->file_) != 0; - else - return true; -} - -ssize_t File::Read(void* buffer, size_t bytes) { +ssize_t File::Read(char* buffer, size_t bytes) { if (impl_->file_) { - size_t len = fread(buffer, 1, bytes, impl_->file_); + size_t len = fread(buffer, sizeof(char), bytes, impl_->file_); if (len == 0) { LOGW("File::Read: fread failed: %d", errno); } @@ -91,9 +81,9 @@ ssize_t File::Read(void* buffer, size_t bytes) { return -1; } -ssize_t File::Write(const void* buffer, size_t bytes) { +ssize_t File::Write(const char* buffer, size_t bytes) { if (impl_->file_) { - size_t len = fwrite(buffer, 1, bytes, impl_->file_); + size_t len = fwrite(buffer, sizeof(char), bytes, impl_->file_); if (len == 0) { LOGW("File::Write: fwrite failed: %d", errno); } @@ -103,27 +93,27 @@ ssize_t File::Write(const void* buffer, size_t bytes) { return -1; } -bool File::Exists(const std::string& file) { +bool File::Exists(const std::string& path) { struct stat buf; - int res = stat(file.c_str(), &buf) == 0; + int res = stat(path.c_str(), &buf) == 0; if (!res) { LOGV("File::Exists: stat failed: %d", errno); } return res; } -bool File::Remove(const std::string& file_path) { - if (IsDirectory(file_path)) { +bool File::Remove(const std::string& path) { + if (IsDirectory(path)) { DIR* dir; - if ((dir = opendir(file_path.c_str())) != NULL) { + if ((dir = opendir(path.c_str())) != NULL) { // 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 file_path_to_remove = file_path + '/'; - file_path_to_remove += entry->d_name; - if (!Remove(file_path_to_remove)) { + std::string path_to_remove = path + '/'; + path_to_remove += entry->d_name; + if (!Remove(path_to_remove)) { closedir(dir); return false; } @@ -131,13 +121,13 @@ bool File::Remove(const std::string& file_path) { } closedir(dir); } - if (rmdir(file_path.c_str())) { + if (rmdir(path.c_str())) { LOGW("File::Remove: rmdir failed: %d", errno); return false; } return true; } else { - if (unlink(file_path.c_str())) { + if (unlink(path.c_str()) && (errno != ENOENT)) { LOGW("File::Remove: unlink failed: %d", errno); return false; } @@ -195,9 +185,9 @@ bool File::IsRegularFile(const std::string& path) { return false; } -ssize_t File::FileSize(const std::string& file_path) { +ssize_t File::FileSize(const std::string& path) { struct stat buf; - if (stat(file_path.c_str(), &buf) == 0) + if (stat(path.c_str(), &buf) == 0) return buf.st_size; else return -1; diff --git a/libwvdrmengine/cdm/src/log.cpp b/libwvdrmengine/cdm/src/log.cpp index 56618032..0446392a 100644 --- a/libwvdrmengine/cdm/src/log.cpp +++ b/libwvdrmengine/cdm/src/log.cpp @@ -10,7 +10,9 @@ namespace wvcdm { -void log_write(LogPriority level, const char *fmt, ...) { +void InitLogging(int argc, const char* const* argv) {} + +void Log(const char* file, int line, LogPriority level, const char* fmt, ...) { va_list ap; char buf[LOG_BUF_SIZE]; va_start(ap, fmt); diff --git a/libwvdrmengine/cdm/src/properties.cpp b/libwvdrmengine/cdm/src/properties.cpp deleted file mode 100644 index 80f44a3b..00000000 --- a/libwvdrmengine/cdm/src/properties.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. - -#include - -#include "cutils/properties.h" -#include "properties.h" - -namespace { - bool GetAndroidProperty(const char* key, std::string& value) { - char val[PROPERTY_VALUE_MAX]; - - if (property_get(key, val, "Unknown") <= 0) - return false; - - value = val; - return true; - } -} - -namespace wvcdm { - -bool Properties::GetCompanyName(std::string& company_name) { - return GetAndroidProperty("ro.product.manufacturer", company_name); -} - -bool Properties::GetModelName(std::string& model_name) { - return GetAndroidProperty("ro.product.model", model_name); -} - -bool Properties::GetArchitectureName(std::string& arch_name) { - return GetAndroidProperty("ro.product.cpu.abi", arch_name); -} - -bool Properties::GetDeviceName(std::string& device_name) { - return GetAndroidProperty("ro.product.device", device_name); -} - -bool Properties::GetProductName(std::string& product_name) { - return GetAndroidProperty("ro.product.name", product_name); -} - -bool Properties::GetBuildInfo(std::string& build_info) { - return GetAndroidProperty("ro.build.fingerprint", build_info); -} - -} // namespace wvcdm diff --git a/libwvdrmengine/cdm/src/properties_android.cpp b/libwvdrmengine/cdm/src/properties_android.cpp new file mode 100644 index 00000000..a3269f4e --- /dev/null +++ b/libwvdrmengine/cdm/src/properties_android.cpp @@ -0,0 +1,95 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "properties.h" + +#include +#include +#include + +#include "cutils/properties.h" +#include "log.h" + +namespace { + +bool GetAndroidProperty(const char* key, std::string* value) { + char val[PROPERTY_VALUE_MAX]; + if (!key) { + LOGW("GetAndroidProperty: Invalid property key parameter"); + return false; + } + + if (!value) { + LOGW("GetAndroidProperty: Invalid property value parameter"); + return false; + } + + if (property_get(key, val, "Unknown") <= 0) return false; + + *value = val; + return true; +} + +} // namespace + +namespace wvcdm { + +bool Properties::GetCompanyName(std::string* company_name) { + if (!company_name) { + LOGW("Properties::GetCompanyName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.manufacturer", company_name); +} + +bool Properties::GetModelName(std::string* model_name) { + if (!model_name) { + LOGW("Properties::GetModelName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.model", model_name); +} + +bool Properties::GetArchitectureName(std::string* arch_name) { + if (!arch_name) { + LOGW("Properties::GetArchitectureName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.cpu.abi", arch_name); +} + +bool Properties::GetDeviceName(std::string* device_name) { + if (!device_name) { + LOGW("Properties::GetDeviceName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.device", device_name); +} + +bool Properties::GetProductName(std::string* product_name) { + if (!product_name) { + LOGW("Properties::GetProductName: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.product.name", product_name); +} + +bool Properties::GetBuildInfo(std::string* build_info) { + if (!build_info) { + LOGW("Properties::GetBuildInfo: Invalid parameter"); + return false; + } + return GetAndroidProperty("ro.build.fingerprint", build_info); +} + +bool Properties::GetDeviceFilesBasePath(std::string* base_path) { + if (!base_path) { + LOGW("Properties::GetDeviceFilesBasePath: Invalid parameter"); + return false; + } + std::stringstream ss; + ss << "/data/mediadrm/IDM" << getuid() << "/"; + *base_path = ss.str(); + return true; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/src/timer.cpp b/libwvdrmengine/cdm/src/timer.cpp index 88bd10d3..97a6200e 100644 --- a/libwvdrmengine/cdm/src/timer.cpp +++ b/libwvdrmengine/cdm/src/timer.cpp @@ -26,7 +26,10 @@ class Timer::Impl : virtual public android::RefBase { private: virtual bool threadLoop() { - sleep(period_); + struct timeval timeout; + timeout.tv_sec = period_; + timeout.tv_usec = 0; + TEMP_FAILURE_RETRY(select(0, NULL, NULL, NULL, &timeout)); handler_->OnTimerEvent(); return true; } diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 7409b61f..814bb931 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -48,7 +48,7 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( app_parameters, key_request, server_url); - if ((license_type == kLicenseTypeRelease) && (sts != KEY_MESSAGE)) { + if (license_type == kLicenseTypeRelease && sts != KEY_MESSAGE) { cdm_engine_->CloseKeySetSession(key_set_id); } return sts; @@ -57,10 +57,10 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( CdmResponseType WvContentDecryptionModule::AddKey( const CdmSessionId& session_id, const CdmKeyResponse& key_data, - CdmKeySetId& key_set_id) { + CdmKeySetId* key_set_id) { CdmResponseType sts = cdm_engine_->AddKey(session_id, key_data, key_set_id); - if ((sts == KEY_ADDED) && session_id.empty()) // license type release - cdm_engine_->CloseKeySetSession(key_set_id); + if (sts == KEY_ADDED && session_id.empty()) // license type release + cdm_engine_->CloseKeySetSession(*key_set_id); return sts; } diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 7aa6e93b..26cc1445 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -24,29 +24,6 @@ std::string g_license_server; std::string g_port; wvcdm::KeyId g_wrong_key_id; int g_use_full_path = 0; // cannot use boolean in getopt_long - -// returns production-rooted certificates that have test bit set, this test -// uses this url -const std::string kProductionTestProvisioningServerUrl = - "https://www.googleapis.com/" - "certificateprovisioning/v1exttest/devicecertificates/create" - "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; - -// url returned by GetProvisioningRequest() -const std::string kProductionProvisioningServerUrl = - "https://www.googleapis.com/" - "certificateprovisioning/v1/devicecertificates/create" - "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; - -// TODO(edwinwong, rfrias): refactor to set these parameters though config -std::string kServerSdkClientAuth = ""; -wvcdm::KeyId kServerSdkKeyId = wvcdm::a2bs_hex( - "000000347073736800000000" - "edef8ba979d64acea3c827dcd51d21ed00000014" - "0801121030313233343536373839414243444546"); -std::string kServerSdkLicenseServer = "http://kir03fcpg174.widevine.net/" - "widevine/cgi-bin/drm.cgi"; - } // namespace namespace wvcdm { @@ -66,10 +43,11 @@ class TestWvCdmEventListener : public WvCdmEventListener { TestWvCdmEventListener() : WvCdmEventListener() {} virtual void onEvent(const CdmSessionId& id, CdmEventType event) { session_id_ = id; - event_type_ = event; + event_type_ = event; } - CdmSessionId session_id() { return session_id_; }; - CdmEventType event_type() { return event_type_; }; + CdmSessionId session_id() { return session_id_; } + CdmEventType event_type() { return event_type_; } + private: CdmSessionId session_id_; CdmEventType event_type_; @@ -88,10 +66,10 @@ class WvCdmRequestLicenseTest : public testing::Test { std::string server_url; std::string key_set_id; EXPECT_EQ(wvcdm::KEY_MESSAGE, - decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data, - license_type, app_parameters, - &key_msg_, &server_url)); - EXPECT_EQ(0, static_cast(server_url.size())); + decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data, + license_type, app_parameters, + &key_msg_, &server_url)); + EXPECT_EQ(0u, server_url.size()); } void GenerateRenewalRequest(const std::string& key_system, @@ -101,14 +79,13 @@ class WvCdmRequestLicenseTest : public testing::Test { std::string init_data; wvcdm::CdmAppParameterMap app_parameters; std::string server_url; - std::string key_set_id; EXPECT_EQ(wvcdm::KEY_MESSAGE, - decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data, - license_type, app_parameters, - &key_msg_, &server_url)); + decryptor_.GenerateKeyRequest(session_id_, key_set_id_, init_data, + license_type, app_parameters, + &key_msg_, &server_url)); // TODO(edwinwong, rfrias): Add tests cases for when license server url // is empty on renewal. Need appropriate key id at the server. - EXPECT_NE(0, static_cast(server_url.size())); + EXPECT_NE(0u, server_url.size()); } void GenerateKeyRelease(CdmKeySetId key_set_id) { @@ -117,120 +94,29 @@ class WvCdmRequestLicenseTest : public testing::Test { wvcdm::CdmAppParameterMap app_parameters; std::string server_url; EXPECT_EQ(wvcdm::KEY_MESSAGE, - decryptor_.GenerateKeyRequest(session_id, key_set_id, init_data, - kLicenseTypeRelease, app_parameters, - &key_msg_, &server_url)); + decryptor_.GenerateKeyRequest(session_id, key_set_id, init_data, + kLicenseTypeRelease, app_parameters, + &key_msg_, &server_url)); } - void DumpResponse(const std::string& description, - const std::string& response) { - if (description.empty()) - return; - - LOGD("%s (%d bytes):", description.c_str(), response.size()); - - size_t remaining = response.size(); - size_t portion = 0; - size_t start = 0; - while (remaining > 0) { - // LOGX may not get to empty its buffer if it is too large, - // pick an arbitrary small size to be safe - portion = (remaining < 512) ? remaining : 512; - LOGD("%s", response.substr(start, portion).c_str()); - start += portion; - remaining -= portion; - } - LOGD("total bytes dumped(%d)", start); - } - - // concatinates all chunks into one blob - // TODO (edwinwong) move this function to url_request class as GetMessageBody - void ConcatenateChunkedResponse(const std::string http_response, - std::string* message_body) { - if (http_response.empty()) - return; - - message_body->clear(); - const std::string kChunkedTag = "Transfer-Encoding: chunked\r\n\r\n"; - size_t chunked_tag_pos = http_response.find(kChunkedTag); - if (std::string::npos != chunked_tag_pos) { - // processes chunked encoding - size_t chunk_size = 0; - size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size(); - sscanf(&http_response[chunk_size_pos], "%x", &chunk_size); - if (chunk_size > http_response.size()) { - // precaution, in case we misread chunk size - LOGE("invalid chunk size %u", chunk_size); - return; - } - - /* - * searches for chunks - * - * header - * chunk size\r\n <-- chunk_size_pos @ beginning of chunk size - * chunk data\r\n <-- chunk_pos @ beginning of chunk data - * chunk size\r\n - * chunk data\r\n - * 0\r\n - */ - const std::string kCrLf = "\r\n"; - size_t chunk_pos = http_response.find(kCrLf, chunk_size_pos) + - kCrLf.size(); - message_body->assign(&http_response[0], chunk_size_pos); - while ((chunk_size > 0) && (std::string::npos != chunk_pos)) { - message_body->append(&http_response[chunk_pos], chunk_size); - - // searches for next chunk - chunk_size_pos = chunk_pos + chunk_size + kCrLf.size(); - sscanf(&http_response[chunk_size_pos], "%x", &chunk_size); - if (chunk_size > http_response.size()) { - // precaution, in case we misread chunk size - LOGE("invalid chunk size %u", chunk_size); - break; - } - chunk_pos = http_response.find(kCrLf, chunk_size_pos) + kCrLf.size(); - } - } else { - // response is not chunked encoded - message_body->assign(http_response); - } - } - - // posts a request and extracts the drm message from the response + // 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) { - std::string port; - if (server_url.find("https") != std::string::npos) { - port.assign("443"); - } else { - port.assign(g_port); - } - UrlRequest url_request(server_url + client_auth, port); - + // Use secure connection and chunk transfer coding. + UrlRequest url_request(server_url + client_auth, g_port, true, true); if (!url_request.is_connected()) { return ""; } - - // TODO(edwinwong, rfrias): need a cleaner solution to handle - // HTTP servers that use chunking vs those that do not - if (server_url.compare(kServerSdkLicenseServer) == 0) - url_request.PostRequest(key_msg_); - else - url_request.PostRequestChunk(key_msg_); - - std::string http_response; - std::string message_body; - int resp_bytes = url_request.GetResponse(http_response); - if (resp_bytes) { - ConcatenateChunkedResponse(http_response, &message_body); - } + url_request.PostRequest(key_msg_); + std::string message; + int resp_bytes = url_request.GetResponse(&message); LOGD("end %d bytes response dump", resp_bytes); + LOGD("end %s ", message.c_str()); // 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_body); + int status_code = url_request.GetStatusCode(message); if (expected_response == 200) { EXPECT_EQ(200, status_code); } else { @@ -240,7 +126,7 @@ class WvCdmRequestLicenseTest : public testing::Test { std::string drm_msg; if (200 == status_code) { LicenseRequest lic_request; - lic_request.GetDrmMessage(message_body, drm_msg); + lic_request.GetDrmMessage(message, drm_msg); LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(), HexEncode(reinterpret_cast(drm_msg.data()), drm_msg.size()).c_str()); @@ -248,61 +134,49 @@ class WvCdmRequestLicenseTest : public testing::Test { return drm_msg; } - // Posts a request and extracts the signed provisioning message from + // 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 port; - if (server_url.find("https") != std::string::npos) { - port.assign("443"); - } else { - port.assign(g_port); - } - UrlRequest url_request(server_url, port); - + // Use secure connection and chunk transfer coding. + UrlRequest url_request(server_url, g_port, true, true); if (!url_request.is_connected()) { return ""; } url_request.PostCertRequestInQueryString(key_msg_); - std::string http_response; - std::string message_body; - int resp_bytes = url_request.GetResponse(http_response); - if (resp_bytes) { - ConcatenateChunkedResponse(http_response, &message_body); - } + std::string message; + 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_body); + int status_code = url_request.GetStatusCode(message); if (expected_response == 200) { EXPECT_EQ(200, status_code); } else { EXPECT_NE(200, status_code); } - return message_body; + 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& init_data, bool is_renewal) { + std::string resp = GetKeyRequestResponse(server_url, client_auth, 200); + if (is_renewal) { // TODO application makes a license request, CDM will renew the license // when appropriate - EXPECT_EQ(decryptor_.AddKey(session_id_, resp, key_set_id_), - wvcdm::KEY_ADDED); - } - else { - EXPECT_EQ(decryptor_.AddKey(session_id_, resp, key_set_id_), - wvcdm::KEY_ADDED); + EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_), + wvcdm::KEY_ADDED); + } else { + EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_), + wvcdm::KEY_ADDED); } } + wvcdm::ConfigTestEnv config_; wvcdm::WvContentDecryptionModule decryptor_; CdmKeyMessage key_msg_; CdmSessionId session_id_; @@ -311,15 +185,14 @@ class WvCdmRequestLicenseTest : public testing::Test { TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { decryptor_.OpenSession(g_key_system, &session_id_); - std::string provisioning_server_url = ""; + std::string provisioning_server_url; - EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(&key_msg_, - &provisioning_server_url)); - EXPECT_STREQ(provisioning_server_url.data(), - kProductionProvisioningServerUrl.data()); + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( + &key_msg_, &provisioning_server_url)); + EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url()); - std::string response = GetCertRequestResponse( - kProductionTestProvisioningServerUrl, 200); + std::string response = + GetCertRequestResponse(config_.provisioning_test_server_url(), 200); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); decryptor_.CloseSession(session_id_); @@ -327,23 +200,26 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) { decryptor_.OpenSession(g_key_system, &session_id_); - std::string provisioning_server_url = ""; + std::string provisioning_server_url; - EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(&key_msg_, - &provisioning_server_url)); - EXPECT_STREQ(provisioning_server_url.data(), - kProductionProvisioningServerUrl.data()); + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( + &key_msg_, &provisioning_server_url)); + EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url()); - EXPECT_EQ(wvcdm::NO_ERROR, - decryptor_.GetProvisioningRequest( - &key_msg_, &provisioning_server_url)); - EXPECT_STREQ(provisioning_server_url.data(), - kProductionProvisioningServerUrl.data()); + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( + &key_msg_, &provisioning_server_url)); + EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url()); - std::string response = GetCertRequestResponse( - kProductionTestProvisioningServerUrl, 200); + std::string response = + GetCertRequestResponse(config_.provisioning_test_server_url(), 200); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); + + response = + GetCertRequestResponse(config_.provisioning_test_server_url(), 200); + EXPECT_NE(0, static_cast(response.size())); + EXPECT_EQ(wvcdm::UNKNOWN_ERROR, + decryptor_.HandleProvisioningResponse(response)); decryptor_.CloseSession(session_id_); } @@ -372,17 +248,16 @@ TEST_F(WvCdmRequestLicenseTest, AddSteamingKeyTest) { TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, false); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, false); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + CdmKeySetId key_set_id = key_set_id_; EXPECT_FALSE(key_set_id_.empty()); decryptor_.CloseSession(session_id_); @@ -395,9 +270,9 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, false); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + CdmKeySetId key_set_id = key_set_id_; EXPECT_FALSE(key_set_id_.empty()); decryptor_.CloseSession(session_id_); @@ -412,15 +287,14 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { key_set_id_.clear(); GenerateKeyRelease(key_set_id); key_set_id_ = key_set_id; - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, false); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); } TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, false); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + CdmKeySetId key_set_id = key_set_id_; EXPECT_FALSE(key_set_id_.empty()); decryptor_.CloseSession(session_id_); @@ -431,8 +305,8 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { CdmSessionId restore_session_id = session_id_; TestWvCdmEventListener listener; EXPECT_TRUE(decryptor_.AttachEventListener(restore_session_id, &listener)); - EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(restore_session_id, - key_set_id)); + EXPECT_EQ(wvcdm::KEY_ADDED, + decryptor_.RestoreKey(restore_session_id, key_set_id)); session_id_.clear(); key_set_id_.clear(); @@ -442,8 +316,7 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { EXPECT_TRUE(listener.session_id().size() != 0); EXPECT_TRUE(listener.session_id().compare(restore_session_id) == 0); EXPECT_TRUE(listener.event_type() == LICENSE_EXPIRED_EVENT); - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, false); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); decryptor_.CloseSession(restore_session_id); } @@ -459,13 +332,11 @@ TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) { TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, false); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); GenerateRenewalRequest(g_key_system, kLicenseTypeOffline); - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, true); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, true); decryptor_.CloseSession(session_id_); } @@ -476,7 +347,8 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { CdmQueryMap query_info; CdmQueryMap::iterator itr; - EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryKeyStatus(session_id_, &query_info)); + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_.QueryKeyStatus(session_id_, &query_info)); itr = query_info.find(wvcdm::QUERY_KEY_LICENSE_TYPE); ASSERT_TRUE(itr != query_info.end()); @@ -507,7 +379,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { itr = query_info.find(wvcdm::QUERY_KEY_RENEWAL_SERVER_URL); ASSERT_TRUE(itr != query_info.end()); - EXPECT_LT(0, (int)itr->second.size()); + EXPECT_LT(0u, itr->second.size()); decryptor_.CloseSession(session_id_); } @@ -519,13 +391,12 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) { itr = query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL); ASSERT_TRUE(itr != query_info.end()); - EXPECT_EQ(2, (int)itr->second.size()); - EXPECT_EQ(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3.at(0), - itr->second.at(0)); + EXPECT_EQ(2u, itr->second.size()); + EXPECT_EQ(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3.at(0), itr->second.at(0)); itr = query_info.find(wvcdm::QUERY_KEY_DEVICE_ID); ASSERT_TRUE(itr != query_info.end()); - EXPECT_GT((int)itr->second.size(), 0); + EXPECT_GT(itr->second.size(), 0u); itr = query_info.find(wvcdm::QUERY_KEY_SYSTEM_ID); ASSERT_TRUE(itr != query_info.end()); @@ -536,9 +407,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) { itr = query_info.find(wvcdm::QUERY_KEY_PROVISIONING_ID); ASSERT_TRUE(itr != query_info.end()); - EXPECT_EQ(16, (int)itr->second.size()); - - decryptor_.CloseSession(session_id_); + EXPECT_EQ(16u, itr->second.size()); } TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) { @@ -548,8 +417,8 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) { CdmQueryMap query_info; CdmQueryMap::iterator itr; - EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryKeyControlInfo(session_id_, - &query_info)); + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_.QueryKeyControlInfo(session_id_, &query_info)); uint32_t oem_crypto_session_id; itr = query_info.find(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID); @@ -596,19 +465,14 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionTest) { size_t encrypt_length = data.encrypt_data.size(); decrypt_buffer.resize(encrypt_length); - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, - data.is_encrypted, - data.is_secure, - data.key_id, - &data.encrypt_data.front(), - encrypt_length, - data.iv, - data.block_offset, - &decrypt_buffer.front(), - 0)); + EXPECT_EQ(NO_ERROR, + decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure, + data.key_id, &data.encrypt_data.front(), + encrypt_length, data.iv, data.block_offset, + &decrypt_buffer.front(), 0)); EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), - decrypt_buffer.begin())); + decrypt_buffer.begin())); decryptor_.CloseSession(session_id_); } @@ -645,19 +509,14 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionNoKeyTest) { size_t encrypt_length = data.encrypt_data.size(); decrypt_buffer.resize(encrypt_length); - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, - data.is_encrypted, - data.is_secure, - data.key_id, - &data.encrypt_data.front(), - encrypt_length, - data.iv, - data.block_offset, - &decrypt_buffer.front(), - 0)); + EXPECT_EQ(NO_ERROR, + decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure, + data.key_id, &data.encrypt_data.front(), + encrypt_length, data.iv, data.block_offset, + &decrypt_buffer.front(), 0)); EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), - decrypt_buffer.begin())); + decrypt_buffer.begin())); decryptor_.CloseSession(session_id_); } @@ -696,131 +555,14 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionTest) { size_t encrypt_length = data.encrypt_data.size(); decrypt_buffer.resize(encrypt_length); - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, - data.is_encrypted, - data.is_secure, - data.key_id, - &data.encrypt_data.front(), - encrypt_length, - data.iv, - data.block_offset, - &decrypt_buffer.front(), - 0)); + EXPECT_EQ(NO_ERROR, + decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure, + data.key_id, &data.encrypt_data.front(), + encrypt_length, data.iv, data.block_offset, + &decrypt_buffer.front(), 0)); EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), - decrypt_buffer.begin())); - decryptor_.CloseSession(session_id_); -} - -TEST_F(WvCdmRequestLicenseTest, OfflineLicenseDecryptionTest) { - decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, false); - - // key 1, encrypted, 256b - DecryptionData data; - data.is_encrypted = true; - data.is_secure = false; - data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546"); - data.encrypt_data = wvcdm::a2b_hex( - "b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561" - "8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771" - "a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93" - "c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe" - "4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785" - "7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412" - "9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff" - "3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c"); - data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6"); - data.block_offset = 0; - data.decrypt_data = wvcdm::a2b_hex( - "cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a" - "d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a" - "e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d" - "8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0" - "d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c" - "a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b" - "5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c" - "e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf"); - - std::vector decrypt_buffer; - size_t encrypt_length = data.encrypt_data.size(); - decrypt_buffer.resize(encrypt_length); - - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, - data.is_encrypted, - data.is_secure, - data.key_id, - &data.encrypt_data.front(), - encrypt_length, - data.iv, - data.block_offset, - &decrypt_buffer.front(), - 0)); - - EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), - decrypt_buffer.begin())); - decryptor_.CloseSession(session_id_); -} - -TEST_F(WvCdmRequestLicenseTest, RestoreOfflineLicenseDecryptionTest) { - decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); - VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, - kServerSdkKeyId, false); - CdmKeySetId key_set_id = key_set_id_; - EXPECT_FALSE(key_set_id_.empty()); - decryptor_.CloseSession(session_id_); - - session_id_.clear(); - decryptor_.OpenSession(g_key_system, &session_id_); - EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); - - // key 1, encrypted, 256b - DecryptionData data; - data.is_encrypted = true; - data.is_secure = false; - data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546"); - data.encrypt_data = wvcdm::a2b_hex( - "b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561" - "8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771" - "a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93" - "c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe" - "4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785" - "7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412" - "9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff" - "3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c"); - data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6"); - data.block_offset = 0; - data.decrypt_data = wvcdm::a2b_hex( - "cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a" - "d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a" - "e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d" - "8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0" - "d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c" - "a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b" - "5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c" - "e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf"); - - std::vector decrypt_buffer; - size_t encrypt_length = data.encrypt_data.size(); - decrypt_buffer.resize(encrypt_length); - - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, - data.is_encrypted, - data.is_secure, - data.key_id, - &data.encrypt_data.front(), - encrypt_length, - data.iv, - data.block_offset, - &decrypt_buffer.front(), - 0)); - - EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), - decrypt_buffer.begin())); - + decrypt_buffer.begin())); decryptor_.CloseSession(session_id_); } @@ -887,20 +629,15 @@ TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) { size_t encrypt_length = data[i].encrypt_data.size(); decrypt_buffer.resize(encrypt_length); - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, - data[i].is_encrypted, - data[i].is_secure, - data[i].key_id, - &data[i].encrypt_data.front(), - encrypt_length, - data[i].iv, - data[i].block_offset, - &decrypt_buffer.front(), - 0)); + EXPECT_EQ( + NO_ERROR, + decryptor_.Decrypt(session_id_, data[i].is_encrypted, data[i].is_secure, + data[i].key_id, &data[i].encrypt_data.front(), + encrypt_length, data[i].iv, data[i].block_offset, + &decrypt_buffer.front(), 0)); EXPECT_TRUE(std::equal(data[i].decrypt_data.begin(), - data[i].decrypt_data.end(), - decrypt_buffer.begin())); + data[i].decrypt_data.end(), decrypt_buffer.begin())); } decryptor_.CloseSession(session_id_); } @@ -932,19 +669,14 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockDecryptionTest) { size_t encrypt_length = data.encrypt_data.size(); decrypt_buffer.resize(encrypt_length); - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, - data.is_encrypted, - data.is_secure, - data.key_id, - &data.encrypt_data.front(), - encrypt_length, - data.iv, - data.block_offset, - &decrypt_buffer.front(), - 0)); + EXPECT_EQ(NO_ERROR, + decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure, + data.key_id, &data.encrypt_data.front(), + encrypt_length, data.iv, data.block_offset, + &decrypt_buffer.front(), 0)); EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), - decrypt_buffer.begin())); + decrypt_buffer.begin())); decryptor_.CloseSession(session_id_); } @@ -975,16 +707,11 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) { size_t encrypt_length = data.encrypt_data.size(); decrypt_buffer.resize(encrypt_length); - EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, - data.is_encrypted, - data.is_secure, - data.key_id, - &data.encrypt_data.front(), - encrypt_length, - data.iv, - data.block_offset, - &decrypt_buffer.front(), - 0)); + EXPECT_EQ(NO_ERROR, + decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure, + data.key_id, &data.encrypt_data.front(), + encrypt_length, data.iv, data.block_offset, + &decrypt_buffer.front(), 0)); EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), decrypt_buffer.begin())); @@ -992,6 +719,118 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) { decryptor_.CloseSession(session_id_); } +TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + + /* + // key 1, encrypted, 256b + DecryptionData data; + data.is_encrypted = true; + data.is_secure = false; + data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546"); + data.encrypt_data = wvcdm::a2b_hex( + "b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561" + "8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771" + "a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93" + "c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe" + "4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785" + "7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412" + "9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff" + "3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c"); + data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6"); + data.block_offset = 0; + data.decrypt_data = wvcdm::a2b_hex( + "cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a" + "d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a" + "e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d" + "8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0" + "d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c" + "a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b" + "5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c" + "e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf"); + + std::vector decrypt_buffer; + size_t encrypt_length = data.encrypt_data.size(); + decrypt_buffer.resize(encrypt_length); + + EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, + data.is_encrypted, + data.is_secure, + data.key_id, + &data.encrypt_data.front(), + encrypt_length, + data.iv, + data.block_offset, + &decrypt_buffer.front(), + 0)); + + EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), + decrypt_buffer.begin())); + */ + decryptor_.CloseSession(session_id_); +} + +TEST_F(WvCdmRequestLicenseTest, DISABLED_RestoreOfflineLicenseDecryptionTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + CdmKeySetId key_set_id = key_set_id_; + EXPECT_FALSE(key_set_id_.empty()); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + decryptor_.OpenSession(g_key_system, &session_id_); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); + /* + // key 1, encrypted, 256b + DecryptionData data; + data.is_encrypted = true; + data.is_secure = false; + data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546"); + data.encrypt_data = wvcdm::a2b_hex( + "b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561" + "8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771" + "a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93" + "c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe" + "4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785" + "7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412" + "9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff" + "3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c"); + data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6"); + data.block_offset = 0; + data.decrypt_data = wvcdm::a2b_hex( + "cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a" + "d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a" + "e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d" + "8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0" + "d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c" + "a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b" + "5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c" + "e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf"); + + std::vector decrypt_buffer; + size_t encrypt_length = data.encrypt_data.size(); + decrypt_buffer.resize(encrypt_length); + + EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, + data.is_encrypted, + data.is_secure, + data.key_id, + &data.encrypt_data.front(), + encrypt_length, + data.iv, + data.block_offset, + &decrypt_buffer.front(), + 0)); + + EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), + decrypt_buffer.begin())); + */ + decryptor_.CloseSession(session_id_); +} + // TODO(rfrias, edwinwong): pending L1 OEMCrypto due to key block handling /* TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) { @@ -1049,7 +888,7 @@ TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) { */ } // namespace wvcdm -int main(int argc, char **argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); wvcdm::ConfigTestEnv config; @@ -1065,16 +904,15 @@ int main(int argc, char **argv) { int show_usage = 0; static const struct option long_options[] = { - { "use_full_path", no_argument, &g_use_full_path, 0 }, - { "keyid", required_argument, NULL, 'k' }, - { "port", required_argument, NULL, 'p' }, - { "server", required_argument, NULL, 's' }, - { NULL, 0, NULL, '\0' } - }; + {"use_full_path", no_argument, &g_use_full_path, 0}, + {"keyid", required_argument, NULL, 'k'}, + {"port", required_argument, NULL, 'p'}, + {"server", required_argument, NULL, 's'}, {NULL, 0, NULL, '\0'}}; int option_index = 0; int opt = 0; - while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) { + while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, + &option_index)) != -1) { switch (opt) { case 'k': { g_key_id.clear(); @@ -1105,8 +943,10 @@ int main(int argc, char **argv) { if (show_usage) { std::cout << std::endl; std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl; - std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl; - std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl; + std::cout << " enclose multiple arguments in '' when using adb shell" + << std::endl; + std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" + << std::endl << std::endl; std::cout << std::setw(30) << std::left << " --port="; std::cout << "specifies the port number, in decimal format" << std::endl; @@ -1114,7 +954,9 @@ int main(int argc, char **argv) { std::cout << "default: " << g_port << std::endl; std::cout << std::setw(30) << std::left << " --server="; - std::cout << "configure the license server url, please include http[s] in the url" << std::endl; + std::cout + << "configure the license server url, please include http[s] in the url" + << std::endl; std::cout << std::setw(30) << std::left << " "; std::cout << "default: " << license_server << std::endl; diff --git a/libwvdrmengine/cdm/test/test_vectors.h b/libwvdrmengine/cdm/test/test_vectors.h new file mode 100644 index 00000000..7ba7c987 --- /dev/null +++ b/libwvdrmengine/cdm/test/test_vectors.h @@ -0,0 +1,18 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +// For platform specific test vectors + +#include + +namespace wvcdm { +namespace test_vectors { + +// for FileStore unit tests +static const std::string kFileExists = "/system/bin/sh"; +static const std::string kDirExists = "/system/bin"; +static const std::string kFileDoesNotExist = "/system/bin/enoext"; +static const std::string kDirDoesNotExist = "/system/bin_enoext"; +static const std::string kTestDir = "/data/mediadrm/IDM0/"; + +} // namespace test_vectors +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/test/unit-test.mk b/libwvdrmengine/cdm/test/unit-test.mk index 129620db..ab9a0fec 100644 --- a/libwvdrmengine/cdm/test/unit-test.mk +++ b/libwvdrmengine/cdm/test/unit-test.mk @@ -21,12 +21,12 @@ LOCAL_C_INCLUDES := \ external/gtest/include \ external/openssl/include \ external/stlport/stlport \ - $(LOCAL_PATH)/core/test/include \ - vendor/widevine/libwvdrmengine/test/gmock/include \ + vendor/widevine/libwvdrmengine/android/cdm/test \ vendor/widevine/libwvdrmengine/cdm/core/include \ vendor/widevine/libwvdrmengine/cdm/core/test \ vendor/widevine/libwvdrmengine/cdm/include \ - vendor/widevine/libwvdrmengine/oemcrypto/include + vendor/widevine/libwvdrmengine/oemcrypto/include \ + vendor/widevine/libwvdrmengine/test/gmock/include LOCAL_C_INCLUDES += external/protobuf/src diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index f795b68e..3b9b7613 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -17,11 +17,15 @@ enum { kErrorCDMGeneric = ERROR_DRM_VENDOR_MIN + 1, kErrorUnsupportedCrypto = ERROR_DRM_VENDOR_MIN + 2, kErrorExpectedUnencrypted = ERROR_DRM_VENDOR_MIN + 3, + kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 3, // Used by crypto test mode kErrorTestMode = ERROR_DRM_VENDOR_MAX, }; +_STLP_STATIC_ASSERT(static_cast(kErrorWVDrmMaxErrorUsed) <= + static_cast(ERROR_DRM_VENDOR_MAX)); + } // namespace wvdrm #endif // WV_ERRORS_H_ diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index fea049db..35606e36 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -206,10 +206,10 @@ status_t WVDrmPlugin::provideKeyResponse( CdmKeyResponse cdmResponse(response.begin(), response.end()); CdmKeySetId cdmKeySetId; - bool isRequest = (memcmp(scope.array(), SESSION_ID_PREFIX.data(), - SESSION_ID_PREFIX.size()) == 0); - bool isRelease = (memcmp(scope.array(), KEY_SET_ID_PREFIX.data(), - KEY_SET_ID_PREFIX.size()) == 0); + bool isRequest = (memcmp(scope.array(), SESSION_ID_PREFIX, + sizeof(SESSION_ID_PREFIX) - 1) == 0); + bool isRelease = (memcmp(scope.array(), KEY_SET_ID_PREFIX, + sizeof(KEY_SET_ID_PREFIX) - 1) == 0); if (isRequest) { cdmSessionId.assign(scope.begin(), scope.end()); @@ -219,7 +219,7 @@ status_t WVDrmPlugin::provideKeyResponse( return android::ERROR_DRM_CANNOT_HANDLE; } - CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, cdmKeySetId); + CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, &cdmKeySetId); if (isRequest && isCdmResponseTypeSuccess(res)) { keySetId.clear(); diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index bb00c18f..fdaf8c14 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -37,7 +37,7 @@ class MockCDM : public WvContentDecryptionModule { MOCK_METHOD3(AddKey, CdmResponseType(const CdmSessionId&, const CdmKeyResponse&, - CdmKeySetId&)); + CdmKeySetId*)); MOCK_METHOD1(CancelKeyRequest, CdmResponseType(const CdmSessionId&)); @@ -117,7 +117,7 @@ class WVDrmPluginTest : public Test { fread(sessionIdRaw, sizeof(uint8_t), kSessionIdSize, fp); fclose(fp); - memcpy(sessionIdRaw, SESSION_ID_PREFIX.data(), SESSION_ID_PREFIX.size()); + memcpy(sessionIdRaw, SESSION_ID_PREFIX, sizeof(SESSION_ID_PREFIX) - 1); sessionId.appendArray(sessionIdRaw, kSessionIdSize); cdmSessionId.assign(sessionId.begin(), sessionId.end()); @@ -149,6 +149,9 @@ TEST_F(WVDrmPluginTest, OpensSessions) { EXPECT_CALL(cdm, DetachEventListener(_, _)) .Times(AtLeast(0)); + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + status_t res = plugin.openSession(sessionId); ASSERT_EQ(OK, res); @@ -185,7 +188,7 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp); fclose(fp); - memcpy(keySetIdRaw, KEY_SET_ID_PREFIX.data(), KEY_SET_ID_PREFIX.size()); + memcpy(keySetIdRaw, KEY_SET_ID_PREFIX, sizeof(KEY_SET_ID_PREFIX) - 1); CdmKeySetId cdmKeySetId(reinterpret_cast(keySetIdRaw), kKeySetIdSize); Vector keySetId; keySetId.appendArray(keySetIdRaw, kKeySetIdSize); @@ -292,7 +295,7 @@ TEST_F(WVDrmPluginTest, AddsKeys) { Vector response; response.appendArray(responseRaw, kResponseSize); - memcpy(keySetIdRaw, KEY_SET_ID_PREFIX.data(), KEY_SET_ID_PREFIX.size()); + memcpy(keySetIdRaw, KEY_SET_ID_PREFIX, sizeof(KEY_SET_ID_PREFIX) - 1); CdmKeySetId cdmKeySetId(reinterpret_cast(keySetIdRaw), kKeySetIdSize); Vector keySetId; @@ -300,11 +303,11 @@ TEST_F(WVDrmPluginTest, AddsKeys) { EXPECT_CALL(cdm, AddKey(cdmSessionId, ElementsAreArray(responseRaw, kResponseSize), _)) - .WillOnce(DoAll(SetArgReferee<2>(cdmKeySetId), + .WillOnce(DoAll(SetArgPointee<2>(cdmKeySetId), Return(wvcdm::KEY_ADDED))); EXPECT_CALL(cdm, AddKey("", ElementsAreArray(responseRaw, kResponseSize), - cdmKeySetId)) + Pointee(cdmKeySetId))) .Times(1); status_t res = plugin.provideKeyResponse(sessionId, response, keySetId); @@ -646,6 +649,9 @@ TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) { EXPECT_CALL(cdm, DetachEventListener(_, _)) .Times(AtLeast(0)); + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + status_t res = plugin.openSession(sessionId); ASSERT_EQ(OK, res); @@ -729,6 +735,9 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) { EXPECT_CALL(cdm, DetachEventListener(_, _)) .Times(AtLeast(0)); + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + status_t res = plugin.openSession(sessionId); ASSERT_EQ(OK, res); @@ -793,6 +802,9 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) { EXPECT_CALL(cdm, DetachEventListener(_, _)) .Times(AtLeast(0)); + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + status_t res = plugin.openSession(sessionId); ASSERT_EQ(OK, res); @@ -859,6 +871,9 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) { EXPECT_CALL(cdm, DetachEventListener(_, _)) .Times(AtLeast(0)); + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + status_t res = plugin.openSession(sessionId); ASSERT_EQ(OK, res); @@ -935,6 +950,9 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) { EXPECT_CALL(cdm, DetachEventListener(_, _)) .Times(AtLeast(0)); + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + status_t res = plugin.openSession(sessionId); ASSERT_EQ(OK, res); @@ -972,6 +990,9 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) { EXPECT_CALL(cdm, DetachEventListener(_, _)) .Times(AtLeast(0)); + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + status_t res = plugin.openSession(sessionId); ASSERT_EQ(OK, res); } @@ -1015,6 +1036,9 @@ TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) { EXPECT_CALL(cdm, AttachEventListener(_, _)) .Times(AtLeast(0)); + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + status_t res = plugin.openSession(sessionId); ASSERT_EQ(OK, res); @@ -1063,6 +1087,9 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) { EXPECT_CALL(cdm, DetachEventListener(_, _)) .Times(AtLeast(0)); + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + status_t res = plugin.setListener(listener); ASSERT_EQ(OK, res); diff --git a/libwvdrmengine/oemcrypto/mock/src/log.cpp b/libwvdrmengine/oemcrypto/mock/src/log.cpp index 9d5559a7..0446392a 100644 --- a/libwvdrmengine/oemcrypto/mock/src/log.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/log.cpp @@ -10,7 +10,9 @@ namespace wvcdm { -void log_write(LogPriority level, const char* fmt, ...) { +void InitLogging(int argc, const char* const* argv) {} + +void Log(const char* file, int line, LogPriority level, const char* fmt, ...) { va_list ap; char buf[LOG_BUF_SIZE]; va_start(ap, fmt); diff --git a/libwvdrmengine/oemcrypto/mock/src/log.h b/libwvdrmengine/oemcrypto/mock/src/log.h index bd14c602..e5bbb519 100644 --- a/libwvdrmengine/oemcrypto/mock/src/log.h +++ b/libwvdrmengine/oemcrypto/mock/src/log.h @@ -17,14 +17,23 @@ typedef enum { LOG_VERBOSE } LogPriority; -void log_write(LogPriority priority, const char* fmt, ...); +// Required to enable/disable verbose logging (LOGV) in Chromium. In Chromium, +// verbose logging level is controlled using command line switches --v (global) +// or --vmodule (per module). This function calls logging::InitLogging to +// initialize logging, which should have already been included in most Chromium +// based binaries. However, it is typically not included by default in +// unittests, in particular, the unittests in CDM core need to call InitLogging +// to be able to control verbose logging in command line. +void InitLogging(int argc, const char* const* argv); + +void Log(const char* file, int line, LogPriority level, const char* fmt, ...); // Log APIs -#define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__)) -#define LOGW(...) ((void)log_write(wvcdm::LOG_WARN, __VA_ARGS__)) -#define LOGI(...) ((void)log_write(wvcdm::LOG_INFO, __VA_ARGS__)) -#define LOGD(...) ((void)log_write(wvcdm::LOG_DEBUG, __VA_ARGS__)) -#define LOGV(...) ((void)log_write(wvcdm::LOG_VERBOSE, __VA_ARGS__)) +#define LOGE(...) Log(__FILE__, __LINE__, wvcdm::LOG_ERROR, __VA_ARGS__) +#define LOGW(...) Log(__FILE__, __LINE__, wvcdm::LOG_WARN, __VA_ARGS__) +#define LOGI(...) Log(__FILE__, __LINE__, wvcdm::LOG_INFO, __VA_ARGS__) +#define LOGD(...) Log(__FILE__, __LINE__, wvcdm::LOG_DEBUG, __VA_ARGS__) +#define LOGV(...) Log(__FILE__, __LINE__, wvcdm::LOG_VERBOSE, __VA_ARGS__) }; // namespace wvcdm diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp index 4dd78dcd..6348ca98 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp @@ -16,7 +16,6 @@ namespace wvoec_mock { bool KeyControlBlock::Validate() { - valid_ = false; if (0x6b63746c != verification_) { // kctl. LOGE("KCB: BAD verification string: %08X (not %08X)", verification_, @@ -35,7 +34,6 @@ bool KeyControlBlock::Validate() { LOGW("KCB: CGMS setting set for refresh."); } } - valid_ = true; return valid_; } diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index d6f52841..1a6338c0 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -135,7 +135,7 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_GenerateNonce(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_GenerateNonce(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } @@ -172,7 +172,7 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } @@ -227,7 +227,7 @@ OEMCryptoResult OEMCrypto_GenerateSignature( SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_GenerateSignature(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_GenerateSignature(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } @@ -292,7 +292,7 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_LoadKeys(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_LoadKeys(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } @@ -393,7 +393,7 @@ OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, size_t num_keys, const OEMCrypto_KeyRefreshObject* key_array) { if (trace_all_calls) { - printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%d)\n", num_keys); + printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%zu)\n", num_keys); } if (NO_ERROR != crypto_engine->ValidateKeybox()) { @@ -403,7 +403,7 @@ OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_RefreshKeys(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_RefreshKeys(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } @@ -491,7 +491,7 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_SelectKey(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_SelectKey(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } @@ -527,8 +527,9 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, break; case OEMCrypto_BufferType_Secure: buffer_type = kBufferTypeSecure; - destination = ((uint8_t*)out_buffer->buffer.secure.handle - + out_buffer->buffer.secure.offset); + destination = + reinterpret_cast(out_buffer->buffer.secure.handle) + + out_buffer->buffer.secure.offset; max_length = out_buffer->buffer.secure.max_length; break; default: @@ -552,7 +553,7 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_DecryptCTR(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_DecryptCTR(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } @@ -562,7 +563,7 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, return OEMCrypto_ERROR_INVALID_CONTEXT; } - if (!crypto_engine->DecryptCTR(session_ctx, iv, (int)block_offset, + if (!crypto_engine->DecryptCTR(session_ctx, iv, block_offset, data_addr, data_length, is_encrypted, destination, buffer_type)) { LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_DECRYPT_FAILED]"); @@ -718,7 +719,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } if (message == NULL || message_length == 0 || signature == NULL @@ -753,7 +754,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, result = OEMCrypto_ERROR_INVALID_RSA_KEY; } size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1]; - if( result == OEMCrypto_SUCCESS) { + if (result == OEMCrypto_SUCCESS) { if (padding > 16) { LOGE("[RewrapRSAKey(): Encrypted RSA has bad padding: %d]", padding); result = OEMCrypto_ERROR_INVALID_RSA_KEY; @@ -761,19 +762,19 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, } size_t rsa_key_length = enc_rsa_key_length - padding; // verify signature, verify RSA key, and load it. - if( result == OEMCrypto_SUCCESS) { + if (result == OEMCrypto_SUCCESS) { if (!session_ctx->LoadRSAKey(pkcs8_rsa_key, rsa_key_length, message, message_length, signature, signature_length)) { result = OEMCrypto_ERROR_SIGNATURE_FAILURE; - // return OEMCrypto_ERROR_INVALID_RSA_KEY; + // return OEMCrypto_ERROR_INVALID_RSA_KEY; } } // Now we generate a wrapped keybox. WrappedRSAKey* wrapped = reinterpret_cast(wrapped_rsa_key); // Pick a random context and IV for generating keys. - if( result == OEMCrypto_SUCCESS) { + if (result == OEMCrypto_SUCCESS) { if (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) { result = OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -784,7 +785,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, const std::vector context(wrapped->context, wrapped->context + sizeof(wrapped->context)); // Generate mac and encryption keys for encrypting the signature. - if( result == OEMCrypto_SUCCESS) { + if (result == OEMCrypto_SUCCESS) { if (!session_ctx->DeriveKeys(crypto_engine->keybox().device_key().value(), context, context)) { result = OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -792,7 +793,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, } // Encrypt rsa key with keybox. - if( result == OEMCrypto_SUCCESS) { + if (result == OEMCrypto_SUCCESS) { if (!session_ctx->EncryptRSAKey(pkcs8_rsa_key, enc_rsa_key_length, wrapped->iv, wrapped->enc_rsa_key)) { result = OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -802,8 +803,8 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, // The wrapped keybox must be signed with the same key we verify with. I'll // pick the server key, so I don't have to modify LoadRSAKey. - if( result == OEMCrypto_SUCCESS) { - size_t sig_length = sizeof(wrapped->signature); + if (result == OEMCrypto_SUCCESS) { + unsigned int sig_length = sizeof(wrapped->signature); if (!HMAC(EVP_sha256(), &session_ctx->mac_key_server()[0], SHA256_DIGEST_LENGTH, wrapped->context, buffer_size - sizeof(wrapped->signature), wrapped->signature, @@ -844,7 +845,7 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } const std::vector context(wrapped->context, @@ -864,7 +865,7 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, result = OEMCrypto_ERROR_INVALID_RSA_KEY; } size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1]; - if( result == OEMCrypto_SUCCESS) { + if (result == OEMCrypto_SUCCESS) { if (padding > 16) { LOGE("[LoadDeviceRSAKey(): Encrypted RSA has bad padding: %d]", padding); result = OEMCrypto_ERROR_INVALID_RSA_KEY; @@ -872,7 +873,7 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, } size_t rsa_key_length = enc_rsa_key_length - padding; // verify signature. - if( result == OEMCrypto_SUCCESS) { + if (result == OEMCrypto_SUCCESS) { if (!session_ctx->LoadRSAKey(pkcs8_rsa_key, rsa_key_length, wrapped->context, wrapped_rsa_key_length - sizeof(wrapped->signature), @@ -908,7 +909,7 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } @@ -938,13 +939,13 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, extern "C" OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( - OEMCrypto_SESSION session, - const uint8_t* enc_session_key, - size_t enc_session_key_length, - const uint8_t* mac_key_context, - size_t mac_key_context_length, - const uint8_t* enc_key_context, - size_t enc_key_context_length) { + OEMCrypto_SESSION session, + const uint8_t* enc_session_key, + size_t enc_session_key_length, + const uint8_t* mac_key_context, + size_t mac_key_context_length, + const uint8_t* enc_key_context, + size_t enc_key_context_length) { if (trace_all_calls) { printf("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n"); dump_hex("enc_session_key", enc_session_key, enc_session_key_length); @@ -958,7 +959,7 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } @@ -1008,7 +1009,7 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } if (in_buffer == NULL || buffer_length == 0 || @@ -1037,7 +1038,7 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } if (!session_ctx->Generic_Decrypt(in_buffer, buffer_length, iv, algorithm, @@ -1065,7 +1066,7 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_Generic_Sign(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_Generic_Sign(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } if (*signature_length < SHA256_DIGEST_LENGTH) { @@ -1096,7 +1097,7 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_Generic_Verify(): ERROR_NO_INVALID_SESSION]"); + LOGE("[OEMCrypto_Generic_Verify(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } if (signature_length != SHA256_DIGEST_LENGTH) { diff --git a/libwvdrmengine/oemcrypto/oemcrypto.gyp b/libwvdrmengine/oemcrypto/oemcrypto.gyp index a9a7024a..bf9b6525 100644 --- a/libwvdrmengine/oemcrypto/oemcrypto.gyp +++ b/libwvdrmengine/oemcrypto/oemcrypto.gyp @@ -15,8 +15,6 @@ 'dependencies': [ 'oec_mrvl', ], - 'libraries': [ - ], }, { 'dependencies': [ 'oec_mock', @@ -24,59 +22,24 @@ }], ], }, - { - 'target_name': 'oec_client', - 'type': 'static_library', - 'sources': [ - 'client/oemcrypto_client.h', - 'client/oemcrypto_client.cpp', - ], - 'dependencies': [ - '../../../base/base.gyp:base', - ], - 'include_dirs': [ - 'client', - '../include/widevine', - '../core/include', - ], - }, { 'target_name': 'oec_mock', 'type': 'static_library', - 'conditions': [ - [ 'use_openssl==1', { - 'sources!': [ - 'mock/src/encryptor_nss.cpp', - ], - }, { - 'sources!': [ - 'mock/src/encryptor_openssl.cpp', - ], - },], - ], 'sources': [ 'mock/src/oemcrypto_mock.cpp', 'mock/src/oemcrypto_engine_mock.cpp', - 'mock/src/oemcrypto_engine_mock.h', 'mock/src/oemcrypto_key_mock.cpp', - 'mock/src/oemcrypto_key_mock.h', 'mock/src/oemcrypto_keybox_mock.cpp', - 'mock/src/oemcrypto_keybox_mock.h', - 'mock/src/encryptor.h', - 'mock/src/encryptor.cpp', - 'mock/src/encryptor_nss.cpp', - 'mock/src/encryptor_openssl.cpp', - 'mock/src/cmac.h', - 'mock/src/cmac.c', - ], - 'dependencies': [ - '../../../base/base.gyp:base', - '../../../crypto/crypto.gyp:crypto', + 'mock/src/wvcrc.cpp', ], 'include_dirs': [ - 'mock/src', - '../include', - '../core/include', + 'include', + ], + 'dependencies': [ + '../../../crypto/crypto.gyp:crypto', + # TODO(kqyang): make it platform independent. + '../chromium/util.gyp:lock', + '../chromium/util.gyp:string_conversions', ], }, { @@ -85,14 +48,14 @@ 'sources': [ 'eureka/src/oemcrypto_mrvl.cpp', ], - 'dependencies': [ - '../../../base/base.gyp:base', - '../../../crypto/crypto.gyp:crypto', - ], 'include_dirs': [ '../include', '../core/include', ], + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../crypto/crypto.gyp:crypto', + ], 'cflags': [ '-Wsign-conversion', ], @@ -115,13 +78,13 @@ 'test/oemcrypto_test.cpp', ], 'include_dirs': [ - '../include', - '../../../testing/gtest/include', + 'include', + 'mock/src', ], 'dependencies': [ 'oec_lib', - '../../../base/base.gyp:base', '../../../testing/gtest.gyp:gtest', + '../../../testing/gtest.gyp:gtest_main', ], }, ], diff --git a/libwvdrmengine/oemcrypto/test/Android.mk b/libwvdrmengine/oemcrypto/test/Android.mk index 937d4bc3..4597888d 100644 --- a/libwvdrmengine/oemcrypto/test/Android.mk +++ b/libwvdrmengine/oemcrypto/test/Android.mk @@ -4,7 +4,8 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - oemcrypto_test.cpp + oemcrypto_test.cpp \ + ../../cdm/src/log.cpp \ LOCAL_MODULE_TAGS := tests diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 16463919..7525e5e2 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -30,11 +30,6 @@ using namespace std; namespace { -// Use random generated or static test vectors -bool g_random = true; -// Enable/disable console output of test vectors -bool g_verbose = false; - const size_t kNumKeys = 4; const size_t kDuration = 2; const size_t kLongDuration = 5; @@ -921,7 +916,6 @@ class Session { "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" "180120002a0c31383836373837343035000000000080"); - OEMCryptoResult sts; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GenerateDerivedKeys( session_id(), @@ -1482,7 +1476,6 @@ Session OEMCryptoClientTest::badSession; // These two tests are first, becuase it might give an idea why other // tests are failing when the device has the wrong keybox installed. TEST_F(OEMCryptoClientTest, VersionNumber) { - OEMCryptoResult sts; testSetUp(); const char* level = OEMCrypto_SecurityLevel(); @@ -1528,6 +1521,7 @@ TEST_F(OEMCryptoClientTest, DISABLED_CheckSystemID) { uint32_t req_len = 256; size_t key_data_len = req_len; sts = OEMCrypto_GetKeyData(key_data, &key_data_len); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); uint32_t* data = reinterpret_cast(key_data); uint32_t system_id = htonl(data[1]); @@ -1741,8 +1735,6 @@ TEST_F(OEMCryptoClientTest, GenerateNonce) { uint32_t nonce; s.GenerateNonce(&nonce); - // std::cout << "GenerateNonce:: nonce=" << nonce << std::endl; - s.close(); ASSERT_TRUE(s.successStatus()); ASSERT_FALSE(s.isOpen()); @@ -1758,9 +1750,6 @@ TEST_F(OEMCryptoClientTest, GenerateTwoNonces) { s.GenerateNonce(&nonce1); s.GenerateNonce(&nonce2); - // std::cout << "GenerateNonce:: nonce1=" << nonce1 << std::endl; - // std::cout << "GenerateNonce:: nonce2=" << nonce2 << std::endl; - ASSERT_TRUE(nonce1 != nonce2); s.close(); @@ -1784,8 +1773,10 @@ TEST_F(OEMCryptoClientTest, GenerateDerivedKeys) { // Define CAN_INSTALL_KEYBOX if you are compiling with the reference // implementation of OEMCrypto, or if your version of OEMCrypto supports -// OEMCrypto_InstallKeybox and OEwith a clear keybox. +// OEMCrypto_InstallKeybox with a clear keybox. // The Below tests are based on a specific keybox which is installed for testing. +// They are disabled by default. Just because you can install a test keybox, +// does not mean you want to install a test keybox. #if defined(CAN_INSTALL_KEYBOX) /////////////////////////////////////////////////// @@ -2822,7 +2813,6 @@ TEST_F(DISABLED_TestKeybox, ValidateRSATestKeys) { } TEST_F(DISABLED_TestKeybox, CertificateProvision) { - OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox, true); Session& s = createSession("ONE"); @@ -2844,7 +2834,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvision) { } TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange1) { - OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox, true); Session& s = createSession("ONE"); @@ -2882,7 +2871,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange1) { } TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange2) { - OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox, true); Session& s = createSession("ONE"); @@ -2922,7 +2910,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange2) { } TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange3) { - OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox, true); Session& s = createSession("ONE"); @@ -2962,7 +2949,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange3) { } TEST_F(DISABLED_TestKeybox, CertificateProvisionBadSignature) { - OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox, true); Session& s = createSession("ONE"); @@ -3000,7 +2986,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadSignature) { } TEST_F(DISABLED_TestKeybox, CertificateProvisionBadNonce) { - OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox, true); Session& s = createSession("ONE"); @@ -3038,7 +3023,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadNonce) { } TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRSAKey) { - OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox, true); Session& s = createSession("ONE"); @@ -3141,7 +3125,6 @@ TEST_F(DISABLED_TestKeybox, RSASignature) { } TEST_F(DISABLED_TestKeybox, LoadRSASessionKey) { - OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox, true); diff --git a/libwvdrmengine/third_party/stringencoders/BUILD b/libwvdrmengine/third_party/stringencoders/BUILD new file mode 100644 index 00000000..eb591150 --- /dev/null +++ b/libwvdrmengine/third_party/stringencoders/BUILD @@ -0,0 +1,8 @@ +# Description: +# Modp base64 encode and decode functions + +licenses(['notice']) + +exports_files(['LICENSE']) + +package(default_visibility = ['//visibility:public']) diff --git a/libwvdrmengine/third_party/stringencoders/LICENSE b/libwvdrmengine/third_party/stringencoders/LICENSE new file mode 100644 index 00000000..857e7f7e --- /dev/null +++ b/libwvdrmengine/third_party/stringencoders/LICENSE @@ -0,0 +1,35 @@ +MODP_B64 - High performance base64 encoder/decoder +http://code.google.com/p/stringencoders/ + +Copyright © 2005, 2006, 2007 Nick Galbreath -- nickg [at] modp [dot] com +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the modp.com nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This is the standard "new" BSD license: +http://www.opensource.org/licenses/bsd-license.php diff --git a/libwvdrmengine/third_party/stringencoders/OWNERS b/libwvdrmengine/third_party/stringencoders/OWNERS new file mode 100644 index 00000000..65ab1913 --- /dev/null +++ b/libwvdrmengine/third_party/stringencoders/OWNERS @@ -0,0 +1,4 @@ +edwinwong@google.com +jtinker@google.com +rfrias@google.com +widevine-eng@google.com diff --git a/libwvdrmengine/third_party/stringencoders/README.google b/libwvdrmengine/third_party/stringencoders/README.google new file mode 100644 index 00000000..fad2fdb4 --- /dev/null +++ b/libwvdrmengine/third_party/stringencoders/README.google @@ -0,0 +1,30 @@ +URL: https://code.google.com/p/stringencoders/ +Version: v3.10.3 +License: BSD +License File: LICENSE + +Description: +This directory contains open source code for modp base64 encode and decode +functions from https://code.google.com/p/stringencoders/. + +Local Modifications: +Comment out #include "config.h" in modep_b64.cpp + +Additional Notes: +Note that the directory structure in third_party/stringencoders mirrors that of +stringencoders-v3.10.3, therefore, the include files are placed in ./src +instead of ./include. + +The following instructions demonstrate how modp_b64_data.h is generated. +modp_b64_data.h contains conversion tables to generate web safe encoded +base64 strings. + + 1. navigate to https://code.google.com/p/stringencoders + 2. download stringencoders-v3.10.3.tar.gz from the "Downloads" tab + 3. extract source to a working folder + 4. change into stringencoders-v3.10.3/ directory + 5. ./configure --with-b64w-chars='-_=' + 6. make + 7. now copy modp_b64w_data.h to third_party/stringencoders/src/. + 8. copy src/modp_b64w.c to third_party/stringencoders/src/*.cpp + 9. copy src/modp_b64w.h to third_party/stringencoders/src/. diff --git a/libwvdrmengine/third_party/stringencoders/src/modp_b64w.cpp b/libwvdrmengine/third_party/stringencoders/src/modp_b64w.cpp new file mode 100644 index 00000000..1881db72 --- /dev/null +++ b/libwvdrmengine/third_party/stringencoders/src/modp_b64w.cpp @@ -0,0 +1,269 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4: */ +/** + * \file modp_b64w.c + *
+ * MODP_B64 - High performance base64 encoder/decoder
+ * http://code.google.com/p/stringencoders/
+ *
+ * Copyright © 2005, 2006, 2007  Nick Galbreath -- nickg [at] modp [dot] com
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ *   Neither the name of the modp.com nor the names of its
+ *   contributors may be used to endorse or promote products derived from
+ *   this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This is the standard "new" BSD license:
+ * http://www.opensource.org/licenses/bsd-license.php
+ * 
+ */ + +/* public header */ +#include "modp_b64w.h" + +/* + * If you are ripping this out of the library, comment out the next + * line and uncomment the next lines as approrpiate + */ +//#include "config.h" + +/* if on motoral, sun, ibm; uncomment this */ +/* #define WORDS_BIGENDIAN 1 */ +/* else for Intel, Amd; uncomment this */ +/* #undef WORDS_BIGENDIAN */ + +#include "modp_b64w_data.h" + +#define BADCHAR 0x01FFFFFF + +/** + * you can control if we use padding by commenting out this + * next line. However, I highly recommend you use padding and not + * using it should only be for compatability with a 3rd party. + * Also, 'no padding' is not tested! + */ +#define DOPAD 1 + +/* + * if we aren't doing padding + * set the pad character to NULL + */ +#ifndef DOPAD +#undef CHARPAD +#define CHARPAD '\0' +#endif + +int modp_b64w_encode(char* dest, const char* str, int len) +{ + int i; + const uint8_t* s = (const uint8_t*) str; + uint8_t* p = (uint8_t*) dest; + + /* unsigned here is important! */ + /* uint8_t is fastest on G4, amd */ + /* uint32_t is fastest on Intel */ + uint32_t t1, t2, t3; + + for (i = 0; i < len - 2; i += 3) { + t1 = s[i]; t2 = s[i+1]; t3 = s[i+2]; + *p++ = e0[t1]; + *p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *p++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *p++ = e2[t3]; + } + + switch (len - i) { + case 0: + break; + case 1: + t1 = s[i]; + *p++ = e0[t1]; + *p++ = e1[(t1 & 0x03) << 4]; + *p++ = CHARPAD; + *p++ = CHARPAD; + break; + default: /* case 2 */ + t1 = s[i]; t2 = s[i+1]; + *p++ = e0[t1]; + *p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *p++ = e2[(t2 & 0x0F) << 2]; + *p++ = CHARPAD; + } + + *p = '\0'; + return (int)(p - (uint8_t*)dest); +} + +#ifdef WORDS_BIGENDIAN /* BIG ENDIAN -- SUN / IBM / MOTOROLA */ +int modp_b64w_decode(char* dest, const char* src, int len) +{ + int i; + if (len == 0) return 0; + +#ifdef DOPAD + /* if padding is used, then the message must be at least + 4 chars and be a multiple of 4. + there can be at most 2 pad chars at the end */ + if (len < 4 || (len % 4 != 0)) return -1; + if (src[len-1] == CHARPAD) { + len--; + if (src[len -1] == CHARPAD) { + len--; + } + } +#endif /* DOPAD */ + + int leftover = len % 4; + int chunks = (leftover == 0) ? len / 4 - 1 : len /4; + + uint8_t* p = (uint8_t*) dest; + uint32_t x = 0; + uint32_t* destInt = (uint32_t*) p; + uint32_t* srcInt = (uint32_t*) src; + uint32_t y = *srcInt++; + for (i = 0; i < chunks; ++i) { + x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] | + d2[y >> 8 & 0xff] | d3[y & 0xff]; + + if (x >= BADCHAR) return -1; + *destInt = x << 8; + p += 3; + destInt = (uint32_t*)p; + y = *srcInt++; + } + + switch (leftover) { + case 0: + x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] | + d2[y >> 8 & 0xff] | d3[y & 0xff]; + if (x >= BADCHAR) return -1; + *p++ = ((uint8_t*)&x)[1]; + *p++ = ((uint8_t*)&x)[2]; + *p = ((uint8_t*)&x)[3]; + return (chunks+1)*3; +#ifndef DOPAD + case 1: /* with padding this is an impossible case */ + x = d3[y >> 24]; + *p = (uint8_t)x; + break; +#endif + case 2: + x = d3[y >> 24] *64 + d3[(y >> 16) & 0xff]; + *p = (uint8_t)(x >> 4); + break; + default: /* case 3 */ + x = (d3[y >> 24] *64 + d3[(y >> 16) & 0xff])*64 + + d3[(y >> 8) & 0xff]; + *p++ = (uint8_t) (x >> 10); + *p = (uint8_t) (x >> 2); + break; + } + + if (x >= BADCHAR) return -1; + return 3*chunks + (6*leftover)/8; +} + +#else /* LITTLE ENDIAN -- INTEL AND FRIENDS */ + +int modp_b64w_decode(char* dest, const char* src, int len) +{ + int i; + if (len == 0) return 0; + +#ifdef DOPAD + /* + * if padding is used, then the message must be at least + * 4 chars and be a multiple of 4 + */ + if (len < 4 || (len % 4 != 0)) return -1; /* error */ + /* there can be at most 2 pad chars at the end */ + if (src[len-1] == CHARPAD) { + len--; + if (src[len -1] == CHARPAD) { + len--; + } + } +#endif + + int leftover = len % 4; + int chunks = (leftover == 0) ? len / 4 - 1 : len /4; + + uint8_t* p = (uint8_t*) dest; + uint32_t x = 0; + uint32_t* destInt = (uint32_t*) p; + uint32_t* srcInt = (uint32_t*) src; + uint32_t y = *srcInt++; + for (i = 0; i < chunks; ++i) { + x = d0[y & 0xff] | + d1[(y >> 8) & 0xff] | + d2[(y >> 16) & 0xff] | + d3[(y >> 24) & 0xff]; + + if (x >= BADCHAR) return -1; + *destInt = x ; + p += 3; + destInt = (uint32_t*)p; + y = *srcInt++;} + + + switch (leftover) { + case 0: + x = d0[y & 0xff] | + d1[(y >> 8) & 0xff] | + d2[(y >> 16) & 0xff] | + d3[(y >> 24) & 0xff]; + + if (x >= BADCHAR) return -1; + *p++ = ((uint8_t*)(&x))[0]; + *p++ = ((uint8_t*)(&x))[1]; + *p = ((uint8_t*)(&x))[2]; + return (chunks+1)*3; + break; +#ifndef DOPAD + case 1: /* with padding this is an impossible case */ + x = d0[y & 0xff]; + *p = *((uint8_t*)(&x)); // i.e. first char/byte in int + break; +#endif + case 2: // * case 2, 1 output byte */ + x = d0[y & 0xff] | d1[y >> 8 & 0xff]; + *p = *((uint8_t*)(&x)); // i.e. first char + break; + default: /* case 3, 2 output bytes */ + x = d0[y & 0xff] | + d1[y >> 8 & 0xff ] | + d2[y >> 16 & 0xff]; /* 0x3c */ + *p++ = ((uint8_t*)(&x))[0]; + *p = ((uint8_t*)(&x))[1]; + break; + } + + if (x >= BADCHAR) return -1; + + return 3*chunks + (6*leftover)/8; +} + +#endif /* if bigendian / else / endif */ diff --git a/libwvdrmengine/third_party/stringencoders/src/modp_b64w.h b/libwvdrmengine/third_party/stringencoders/src/modp_b64w.h new file mode 100644 index 00000000..3fa31a13 --- /dev/null +++ b/libwvdrmengine/third_party/stringencoders/src/modp_b64w.h @@ -0,0 +1,241 @@ +/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4: */ + +/** + * \file + *
+ * High performance WEB-SAFE base64 encoder / decoder
+ *
+ * Copyright © 2005, 2006, 2007 Nick Galbreath -- nickg [at] modp [dot] com
+ * All rights reserved.
+ *
+ * http://code.google.com/p/stringencoders/
+ *
+ * Released under bsd license.  See modp_b64w.c for details.
+ * 
+ * + * This uses a "URL-safe" or "WEB-safe" encoding. THe standard + * base 64 encoding uses the characters '+', '/' and '=' have special + * restrictions when used inside a URL. + * + * This uses "+" to "-", "/" to "_", and "=" to "." as the replacement + * alphabet. + * + * It's easy to change this to use "URL safe" characters and to remove + * padding. See the modp_b64.c source code for details. + * + */ + +#ifndef COM_MODP_STRINGENCODERS_B64W +#define COM_MODP_STRINGENCODERS_B64W + +#ifdef __cplusplus +#define BEGIN_C extern "C" { +#define END_C } +#else +#define BEGIN_C +#define END_C +#endif + +BEGIN_C + +/** + * Encode a raw binary string into web-safe base 64. + * \param[out] dest should be allocated by the caller to contain + * at least modp_b64w_encode_len(len) bytes (see below) + * This will contain the null-terminated b64w encoded result + * \param[in] src contains the bytes + * \param[in] len contains the number of bytes in the src + * \return length of the destination string plus the ending null byte + * i.e. the result will be equal to strlen(dest) + 1 + * + * Example + * + * \code + * char* src = ...; + * int srclen = ...; //the length of number of bytes in src + * char* dest = (char*) malloc(modp_b64w_encode_len); + * int len = modp_b64w_encode(dest, src, sourcelen); + * if (len == -1) { + * printf("Error\n"); + * } else { + * printf("b64w = %s\n", dest); + * } + * \endcode + * + */ +int modp_b64w_encode(char* dest, const char* src, int len); + +/** + * Decode a web-safe base64 encoded string + * + * \param[out] dest should be allocated by the caller to contain at least + * len * 3 / 4 bytes. + * \param[in] src should contain exactly len bytes of b64w characters. + * if src contains -any- non-base characters (such as white + * space, -1 is returned. + * \param[in] len is the length of src + * + * \return the length (strlen) of the output, or -1 if unable to + * decode + * + * \code + * char* src = ...; + * int srclen = ...; // or if you don't know use strlen(src) + * char* dest = (char*) malloc(modp_b64w_decode_len(srclen)); + * int len = modp_b64w_decode(dest, src, sourcelen); + * if (len == -1) { error } + * \endcode + */ +int modp_b64w_decode(char* dest, const char* src, int len); + +/** + * Given a source string of length len, this returns the amount of + * memory the destination string should have. + * + * remember, this is integer math + * 3 bytes turn into 4 chars + * ceiling[len / 3] * 4 + 1 + * + * +1 is for any extra null. + */ +#define modp_b64w_encode_len(A) ((A+2)/3 * 4 + 1) + +/** + * Given a base64 string of length len, + * this returns the amount of memory required for output string + * It maybe be more than the actual number of bytes written. + * NOTE: remember this is integer math + * this allocates a bit more memory than traditional versions of b64w + * decode 4 chars turn into 3 bytes + * floor[len * 3/4] + 2 + */ +#define modp_b64w_decode_len(A) (A / 4 * 3 + 2) + +/** + * Will return the strlen of the output from encoding. + * This may be less than the required number of bytes allocated. + * + * This allows you to 'deserialized' a struct + * \code + * char* b64wencoded = "..."; + * int len = strlen(b64wencoded); + * + * struct datastuff foo; + * if (modp_b64w_encode_strlen(sizeof(struct datastuff)) != len) { + * // wrong size + * return false; + * } else { + * // safe to do; + * if (modp_b64w_decode((char*) &foo, b64wencoded, len) == -1) { + * // bad characters + * return false; + * } + * } + * // foo is filled out now + * \endcode + */ +#define modp_b64w_encode_strlen(A) ((A + 2)/ 3 * 4) + +END_C + +#ifdef __cplusplus +#include +#include + +namespace modp { + + /** \brief b64w encode a cstr with len + * + * \param[in] s the input string to encode + * \param[in] len the length of the input string + * \return a newly allocated b64w string. Empty if failed. + */ + inline std::string b64w_encode(const char* s, size_t len) + { + std::string x(modp_b64w_encode_len(len), '\0'); + int d = modp_b64w_encode(const_cast(x.data()), s, + static_cast(len)); + x.erase(d, std::string::npos); + return x; + } + + /** \brief b64w encode a cstr + * + * \param[in] s the input string to encode + * \return a newly allocated b64w string. Empty if failed. + */ + inline std::string b64w_encode(const char* s) + { + return b64w_encode(s, static_cast(strlen(s))); + } + + /** \brief b64w encode a const std::string + * + * \param[in] s the input string to encode + * \return a newly allocated b64w string. Empty if failed. + */ + inline std::string b64w_encode(const std::string& s) + { + return b64w_encode(s.data(), s.size()); + } + + /** \brief self-modifing b64w encode + * + * web-safe base 64 decode a string (self-modifing) + * On failure, the string is empty. + * + * \param[in,out] s the string to be decoded + * \return a reference to the input string + */ + inline std::string& b64w_encode(std::string& s) + { + std::string x(b64w_encode(s.data(), s.size())); + s.swap(x); + return s; + } + + inline std::string b64w_decode(const char* src, size_t len) + { + std::string x(modp_b64w_decode_len(len)+1, '\0'); + int d = modp_b64w_decode(const_cast(x.data()), src, + static_cast(len)); + if (d < 0) { + x.clear(); + } else { + x.erase(d, std::string::npos); + } + return x; + } + + inline std::string b64w_decode(const char* src) + { + return b64w_decode(src, strlen(src)); + } + + /** + * base 64 decode a string (self-modifing) + * On failure, the string is empty. + * + * This function is for C++ only (duh) + * + * \param[in,out] s the string to be decoded + * \return a reference to the input string + */ + inline std::string& b64w_decode(std::string& s) + { + std::string x(b64w_decode(s.data(), s.size())); + s.swap(x); + return s; + } + + inline std::string b64w_decode(const std::string& s) + { + return b64w_decode(s.data(), s.size()); + } +} + +#endif /* __cplusplus */ + +#endif /* MODP_B64W */ + diff --git a/libwvdrmengine/third_party/stringencoders/src/modp_b64w_data.h b/libwvdrmengine/third_party/stringencoders/src/modp_b64w_data.h new file mode 100644 index 00000000..ecb5723c --- /dev/null +++ b/libwvdrmengine/third_party/stringencoders/src/modp_b64w_data.h @@ -0,0 +1,480 @@ +#include +#define CHAR62 '-' +#define CHAR63 '_' +#define CHARPAD '=' +static const unsigned char e0[256] = { + 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', + 'C', 'C', 'D', 'D', 'D', 'D', 'E', 'E', 'E', 'E', + 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', + 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', + 'K', 'K', 'K', 'K', 'L', 'L', 'L', 'L', 'M', 'M', + 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', + 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', + 'R', 'R', 'S', 'S', 'S', 'S', 'T', 'T', 'T', 'T', + 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', + 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', + 'Z', 'Z', 'Z', 'Z', 'a', 'a', 'a', 'a', 'b', 'b', + 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', + 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', + 'g', 'g', 'h', 'h', 'h', 'h', 'i', 'i', 'i', 'i', + 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', + 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', + 'o', 'o', 'o', 'o', 'p', 'p', 'p', 'p', 'q', 'q', + 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', + 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', + 'v', 'v', 'w', 'w', 'w', 'w', 'x', 'x', 'x', 'x', + 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', + '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', + '3', '3', '3', '3', '4', '4', '4', '4', '5', '5', + '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', + '8', '8', '8', '8', '9', '9', '9', '9', '-', '-', + '-', '-', '_', '_', '_', '_' +}; + +static const unsigned char e1[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '-', '_' +}; + +static const unsigned char e2[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '-', '_' +}; + + + +#ifdef WORDS_BIGENDIAN + + +/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */ + +static const uint32_t d0[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00f80000, 0x01ffffff, 0x01ffffff, +0x00d00000, 0x00d40000, 0x00d80000, 0x00dc0000, 0x00e00000, 0x00e40000, +0x00e80000, 0x00ec0000, 0x00f00000, 0x00f40000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00040000, 0x00080000, 0x000c0000, 0x00100000, 0x00140000, 0x00180000, +0x001c0000, 0x00200000, 0x00240000, 0x00280000, 0x002c0000, 0x00300000, +0x00340000, 0x00380000, 0x003c0000, 0x00400000, 0x00440000, 0x00480000, +0x004c0000, 0x00500000, 0x00540000, 0x00580000, 0x005c0000, 0x00600000, +0x00640000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00fc0000, +0x01ffffff, 0x00680000, 0x006c0000, 0x00700000, 0x00740000, 0x00780000, +0x007c0000, 0x00800000, 0x00840000, 0x00880000, 0x008c0000, 0x00900000, +0x00940000, 0x00980000, 0x009c0000, 0x00a00000, 0x00a40000, 0x00a80000, +0x00ac0000, 0x00b00000, 0x00b40000, 0x00b80000, 0x00bc0000, 0x00c00000, +0x00c40000, 0x00c80000, 0x00cc0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d1[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0003e000, 0x01ffffff, 0x01ffffff, +0x00034000, 0x00035000, 0x00036000, 0x00037000, 0x00038000, 0x00039000, +0x0003a000, 0x0003b000, 0x0003c000, 0x0003d000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, +0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, +0x0000d000, 0x0000e000, 0x0000f000, 0x00010000, 0x00011000, 0x00012000, +0x00013000, 0x00014000, 0x00015000, 0x00016000, 0x00017000, 0x00018000, +0x00019000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0003f000, +0x01ffffff, 0x0001a000, 0x0001b000, 0x0001c000, 0x0001d000, 0x0001e000, +0x0001f000, 0x00020000, 0x00021000, 0x00022000, 0x00023000, 0x00024000, +0x00025000, 0x00026000, 0x00027000, 0x00028000, 0x00029000, 0x0002a000, +0x0002b000, 0x0002c000, 0x0002d000, 0x0002e000, 0x0002f000, 0x00030000, +0x00031000, 0x00032000, 0x00033000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d2[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000f80, 0x01ffffff, 0x01ffffff, +0x00000d00, 0x00000d40, 0x00000d80, 0x00000dc0, 0x00000e00, 0x00000e40, +0x00000e80, 0x00000ec0, 0x00000f00, 0x00000f40, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000040, 0x00000080, 0x000000c0, 0x00000100, 0x00000140, 0x00000180, +0x000001c0, 0x00000200, 0x00000240, 0x00000280, 0x000002c0, 0x00000300, +0x00000340, 0x00000380, 0x000003c0, 0x00000400, 0x00000440, 0x00000480, +0x000004c0, 0x00000500, 0x00000540, 0x00000580, 0x000005c0, 0x00000600, +0x00000640, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000fc0, +0x01ffffff, 0x00000680, 0x000006c0, 0x00000700, 0x00000740, 0x00000780, +0x000007c0, 0x00000800, 0x00000840, 0x00000880, 0x000008c0, 0x00000900, +0x00000940, 0x00000980, 0x000009c0, 0x00000a00, 0x00000a40, 0x00000a80, +0x00000ac0, 0x00000b00, 0x00000b40, 0x00000b80, 0x00000bc0, 0x00000c00, +0x00000c40, 0x00000c80, 0x00000cc0, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d3[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000003e, 0x01ffffff, 0x01ffffff, +0x00000034, 0x00000035, 0x00000036, 0x00000037, 0x00000038, 0x00000039, +0x0000003a, 0x0000003b, 0x0000003c, 0x0000003d, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, +0x00000007, 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, +0x0000000d, 0x0000000e, 0x0000000f, 0x00000010, 0x00000011, 0x00000012, +0x00000013, 0x00000014, 0x00000015, 0x00000016, 0x00000017, 0x00000018, +0x00000019, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000003f, +0x01ffffff, 0x0000001a, 0x0000001b, 0x0000001c, 0x0000001d, 0x0000001e, +0x0000001f, 0x00000020, 0x00000021, 0x00000022, 0x00000023, 0x00000024, +0x00000025, 0x00000026, 0x00000027, 0x00000028, 0x00000029, 0x0000002a, +0x0000002b, 0x0000002c, 0x0000002d, 0x0000002e, 0x0000002f, 0x00000030, +0x00000031, 0x00000032, 0x00000033, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +#else + + +/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */ + +static const uint32_t d0[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, +0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, +0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, +0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, +0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, +0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, +0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, +0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, +0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, +0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, +0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, +0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d1[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, +0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, +0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, +0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, +0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, +0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, +0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, +0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, +0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, +0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, +0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, +0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d2[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, +0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, +0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, +0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, +0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, +0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, +0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, +0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, +0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, +0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, +0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, +0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d3[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, +0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, +0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, +0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, +0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, +0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, +0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, +0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, +0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, +0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, +0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, +0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +#endif