diff --git a/libwvdrmengine/Android.mk b/libwvdrmengine/Android.mk index 18170ee1..1714936d 100644 --- a/libwvdrmengine/Android.mk +++ b/libwvdrmengine/Android.mk @@ -1,26 +1,22 @@ -# ---------------------------------------------------------------- +# ----------------------------------------------------------------------------- # CDM top level makefile # LOCAL_PATH := $(call my-dir) -########################################################## -# ---------------------------------------------------------------- -# Builds the protobuf static library and generate .pb.cc and .pb.h -# license_protocol.pb.cc -# license_protocol.pb.h -# license_protocol.a +# ----------------------------------------------------------------------------- +# Builds cdm_protos.a +# Generates *.a, *.pb.h and *.pb.cc for *.proto files. # include $(CLEAR_VARS) -LOCAL_MODULE := license_protocol_protos +LOCAL_MODULE := cdm_protos LOCAL_MODULE_CLASS := STATIC_LIBRARIES LOCAL_C_INCLUDES := \ bionic \ external/stlport/stlport -LOCAL_SRC_FILES := \ - $(call all-proto-files-under, cdm/core/src) +LOCAL_SRC_FILES := $(call all-proto-files-under, cdm/core/src) LOCAL_EXPORT_C_INCLUDE_DIRS := \ $(call local-intermediates-dir)/proto/$(LOCAL_PATH)/cdm/core/src @@ -31,8 +27,9 @@ include $(BUILD_STATIC_LIBRARY) # We can use cdm_proto_gen_headers later to establish the dependency. cdm_proto_gen_headers := $(proto_generated_headers) - -########################################################### +# ----------------------------------------------------------------------------- +# Builds libwvdrmengine.so +# include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -40,7 +37,7 @@ LOCAL_SRC_FILES := \ src/WVCreatePluginFactories.cpp \ src/WVCryptoFactory.cpp \ src/WVDrmFactory.cpp \ - src/WVUUID.cpp \ + src/WVUUID.cpp LOCAL_C_INCLUDES := \ bionic \ @@ -55,19 +52,20 @@ LOCAL_C_INCLUDES := \ LOCAL_STATIC_LIBRARIES := \ libcdm \ + libl3crypto \ libprotobuf-cpp-2.3.0-lite \ libwvdrmcryptoplugin \ libwvdrmdrmplugin \ LOCAL_SHARED_LIBRARIES := \ + libcrypto \ libdl \ liblog \ - liboemcrypto \ libstlport \ libutils \ LOCAL_WHOLE_STATIC_LIBRARIES := \ - license_protocol_protos + cdm_protos LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers) @@ -80,9 +78,9 @@ LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) include vendor/widevine/libwvdrmengine/cdm/Android.mk +include vendor/widevine/libwvdrmengine/level3/Android.mk include vendor/widevine/libwvdrmengine/mediacrypto/Android.mk include vendor/widevine/libwvdrmengine/mediadrm/Android.mk -include vendor/widevine/libwvdrmengine/oemcrypto/mock/Android.mk # clean up temp vars cdm_proto_gen_headers := diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index 780dacb2..7b315132 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -15,7 +15,7 @@ LOCAL_C_INCLUDES += \ external/protobuf/src \ ../oemcrypto/include -LOCAL_STATIC_LIBRARIES := license_protocol_protos +LOCAL_STATIC_LIBRARIES := cdm_protos LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers) SRC_DIR := src @@ -28,6 +28,7 @@ LOCAL_SRC_FILES := \ $(CORE_SRC_DIR)/crypto_session.cpp \ $(CORE_SRC_DIR)/license.cpp \ $(CORE_SRC_DIR)/policy_engine.cpp \ + $(CORE_SRC_DIR)/properties.cpp \ $(CORE_SRC_DIR)/string_conversions.cpp \ $(SRC_DIR)/clock.cpp \ $(SRC_DIR)/lock.cpp \ @@ -39,7 +40,3 @@ LOCAL_MODULE := libcdm LOCAL_MODULE_TAGS := optional include $(BUILD_STATIC_LIBRARY) - -######################################################### -# include the tests. -include $(LOCAL_PATH)/test/Android.mk diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 8bd58e18..a3ab2149 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -30,7 +30,7 @@ class CdmEngine : public TimerHandler { const CdmKeySystem& key_system, const CdmInitData& init_data, const CdmLicenseType license_type, - CdmNameValueMap& app_parameters, + CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request); // Accept license response and extract key info. @@ -59,10 +59,12 @@ class CdmEngine : public TimerHandler { const CdmInitData& init_data, const CdmKeyResponse& key_data); + // Query system information + CdmResponseType QueryStatus(CdmQueryMap* info); + // Query license information CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, - CdmNameValueMap* key_info); - + CdmQueryMap* key_info); // Provisioning related methods CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request, @@ -83,7 +85,7 @@ class CdmEngine : public TimerHandler { size_t encrypted_size, const std::vector& iv, size_t block_offset, - void* decrypted_buffer); + uint8_t* decrypted_buffer); // Is the key known to any session? bool IsKeyValid(const KeyId& key_id); diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index 4f950089..b5f9c2c6 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -42,11 +42,14 @@ class CdmSession { // CancelKeyRequest() - Cancel session. CdmResponseType CancelKeyRequest(); + // Query license information + CdmResponseType QueryKeyStatus(CdmQueryMap* key_info); + // Decrypt() - Accept encrypted buffer and return decrypted data. CdmResponseType Decrypt(const uint8_t* encrypted_buffer, size_t encrypted_size, size_t block_offset, - const std::string& iv, + const std::vector& iv, const KeyId& key_id, uint8_t* decrypted_buffer); diff --git a/libwvdrmengine/cdm/core/include/clock.h b/libwvdrmengine/cdm/core/include/clock.h index 0fb0a379..265fad4d 100644 --- a/libwvdrmengine/cdm/core/include/clock.h +++ b/libwvdrmengine/cdm/core/include/clock.h @@ -10,9 +10,15 @@ namespace wvcdm { // Provides time related information. The implementation is platform dependent. +class Clock { -// Provides the number of seconds since an epoch (00:00 hours, Jan 1, 1970 UTC) -int64_t GetCurrentTime(); + public: + Clock() {} + virtual ~Clock() {} + + // Provides the number of seconds since an epoch - 01/01/1970 00:00 UTC + virtual int64_t GetCurrentTime(); +}; }; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/include/crypto_engine.h b/libwvdrmengine/cdm/core/include/crypto_engine.h index 930a7f88..a7789a44 100644 --- a/libwvdrmengine/cdm/core/include/crypto_engine.h +++ b/libwvdrmengine/cdm/core/include/crypto_engine.h @@ -38,6 +38,8 @@ class CryptoEngine { bool GetToken(std::string* token); + CdmResponseType Query(CdmQueryMap* info); + private: void DeleteInstance(); diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index c4b8e210..c084725c 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -56,6 +56,11 @@ class CryptoSession { // Media data path bool SelectKey(const std::string& key_id); bool Decrypt(const InputDescriptor input, OutputDescriptor output); + bool Decrypt(const uint8_t* encrypted_buffer, + size_t encrypted_size, + size_t block_offset, + const std::vector& iv, + uint8_t* decrypted_buffer); private: diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index ee10efff..01b9afca 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -11,6 +11,7 @@ namespace wvcdm { using video_widevine_server::sdk::LicenseIdentification; class CryptoSession; +class PolicyEngine; class CdmLicense { @@ -19,7 +20,8 @@ class CdmLicense { CdmLicense(); ~CdmLicense(); - bool Init(const std::string& token, CryptoSession* session); + bool Init(const std::string& token, CryptoSession* session, + PolicyEngine* policy_engine); bool PrepareKeyRequest(const CdmInitData& init_data, CdmKeyMessage* signed_request); @@ -31,6 +33,7 @@ private: LicenseIdentification license_id_; CryptoSession* session_; + PolicyEngine* policy_engine_; std::string token_; CORE_DISALLOW_COPY_AND_ASSIGN(CdmLicense); diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index 2627d674..54691b01 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -10,6 +10,9 @@ namespace wvcdm { +class Clock; +class PolicyEngineTest; + // This acts as an oracle that basically says "Yes(true) you may still decrypt // or no(false) you may not decrypt this data anymore." class PolicyEngine { @@ -17,8 +20,14 @@ class PolicyEngine { PolicyEngine(); ~PolicyEngine(); - // |current_time| is used to check if license has to be renewed or expired. - void OnTimerEvent(int64_t current_time, bool event_occurred, CdmEventType& event); + // The value returned should be taken as a hint rather than an absolute + // status. It is computed during the last call to either SetLicense/ + // UpdateLicense/OnTimerEvent/BeginDecryption and may be out of sync + // depending on the amount of time elapsed. The current decryption + // status is not calculated to avoid overhead in the decryption path. + inline bool can_decrypt() { return can_decrypt_; } + + void OnTimerEvent(bool& event_occurred, CdmEventType& event); // SetLicense is used in handling the initial license response. It stores // an exact copy of the policy information stored in the license. @@ -26,6 +35,11 @@ class PolicyEngine { // permits playback. void SetLicense(const video_widevine_server::sdk::License& license); + // Call this on first decrypt to set the start of playback. This is + // for cases where usage begins not when the license is received, + // but at the start of playback + void BeginDecryption(void); + // UpdateLicense is used in handling a license response for a renewal request. // The response may only contain any policy fields that have changed. In this // case an exact copy is not what we want to happen. We also will receive an @@ -33,6 +47,8 @@ class PolicyEngine { // kLicenseStateCanPlay if the license permits playback. void UpdateLicense(const video_widevine_server::sdk::License& license); + CdmResponseType Query(CdmQueryMap* key_info); + const video_widevine_server::sdk::LicenseIdentification& license_id() { return license_id_; } @@ -40,14 +56,17 @@ class PolicyEngine { private: typedef enum { kLicenseStateInitial, + kLicenseStateInitialPendingUsage, kLicenseStateCanPlay, - kLicenseStateCannotPlay, kLicenseStateNeedRenewal, kLicenseStateWaitingLicenseUpdate, kLicenseStateExpired } LicenseState; + void Init(Clock* clock); + bool IsLicenseDurationExpired(int64_t current_time); + bool IsPlaybackDurationExpired(int64_t current_time); bool IsRenewalDelayExpired(int64_t current_time); bool IsRenewalRecoveryDurationExpired(int64_t current_time); bool IsRenewalRetryIntervalExpired(int64_t current_time); @@ -55,6 +74,7 @@ class PolicyEngine { void UpdateRenewalRequest(int64_t current_time); LicenseState license_state_; + bool can_decrypt_; // This is the current policy information for this license. This gets updated // as license renewals occur. @@ -69,14 +89,30 @@ class PolicyEngine { // license request or renewal. int64_t license_start_time_; + // This is the time at which the license was received and playback was + // started. These times are based off the local clock in case there is a + // discrepency between local and server time. + int64_t license_received_time_; + int64_t playback_start_time_; + // This is used as a reference point for policy management. This value - // represents an offset from license_start_time_. This is used to calculate - // the time where renewal retries should occur. + // represents an offset from license_received_time_. This is used to + // calculate the time where renewal retries should occur. int64_t next_renewal_time_; int64_t policy_max_duration_seconds_; + bool properties_valid_; + bool begin_license_usage_when_received_; + + Clock* clock_; + + // For testing + friend class PolicyEngineTest; + PolicyEngine(Clock* clock); + CORE_DISALLOW_COPY_AND_ASSIGN(PolicyEngine); }; + } // wvcdm #endif // CDM_BASE_POLICY_ENGINE_H_ diff --git a/libwvdrmengine/cdm/core/include/properties.h b/libwvdrmengine/cdm/core/include/properties.h new file mode 100644 index 00000000..04ad0321 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/properties.h @@ -0,0 +1,53 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_PROPERTIES_H_ +#define CDM_BASE_PROPERTIES_H_ + +#include +#include + +#include "lock.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +typedef std::map CdmBooleanPropertiesMap; + +struct CdmBooleanProperties { + std::string name; + bool value; +}; + +// This class saves information about features and properties enabled +// for a given platform. At initialization it reads in properties from +// property_configuration.h. That file specifies features selected for each +// platform. Core CDM can then query enabled features though the GetProperty +// method and tailor its behaviour in a non-platform specific way. +// +// Additional features can be added at runtime as long as the key names do +// not clash. Also, only boolean properties are supported at this time, though +// it should be trivial to in support for other datatypes. +class Properties { + public: + static Properties* GetInstance(); + + // value argument is only set if the property was found (true is returned) + bool GetProperty(std::string& key, bool& value); + + private: + Properties(); + ~Properties() {} + + void SetProperty(std::string& key, bool value); + + static Properties* instance_; + static Lock properties_lock_; + + CdmBooleanPropertiesMap boolean_properties_; + + CORE_DISALLOW_COPY_AND_ASSIGN(Properties); +}; + +} // namespace wvcdm + +#endif // CDM_BASE_PROPERTIES_H_ diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 8174602a..97958395 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -3,6 +3,8 @@ #ifndef CDM_BASE_WV_CDM_CONSTANTS_H_ #define CDM_BASE_WV_CDM_CONSTANTS_H_ +#include + namespace wvcdm { static const size_t KEY_CONTROL_SIZE = 16; // TODO(kqyang): Key ID size is not fixed in spec, but conventionally we @@ -13,6 +15,38 @@ static const size_t KEY_IV_SIZE = 16; static const size_t KEY_PAD_SIZE = 16; static const size_t KEY_SIZE = 16; static const size_t MAC_KEY_SIZE = 32; + +// define boolean property keys here +// If false begin license usage on first playback +static std::string kPropertyKeyBeginLicenseUsageWhenReceived = + "WVBeginLicenseUsageWhenReceived"; + +// define query keys, values here +static const std::string QUERY_KEY_LICENSE_TYPE = "LicenseType"; + // "Streaming", "Offline" +static const std::string QUERY_KEY_PLAY_ALLOWED = "PlayAllowed"; + // "True", "False" +static const std::string QUERY_KEY_PERSIST_ALLOWED = "PersistAllowed"; + // "True", "False" +static const std::string QUERY_KEY_RENEW_ALLOWED = "RenewAllowed"; + // "True", "False" +static const std::string QUERY_KEY_LICENSE_DURATION_REMAINING = + "LicenseDurationRemaining"; // non-negative integer +static const std::string QUERY_KEY_PLAYBACK_DURATION_REMAINING = + "PlaybackDurationRemaining"; // non-negative integer +static const std::string QUERY_KEY_RENEWAL_SERVER_URL = "RenewalServerUrl"; + // url +static const std::string QUERY_KEY_SECURITY_LEVEL = "SecurityLevel"; + // "L1", "L3" +static const std::string QUERY_VALUE_TRUE = "True"; +static const std::string QUERY_VALUE_FALSE = "False"; +static const std::string QUERY_VALUE_STREAMING = "Streaming"; +static const std::string QUERY_VALUE_OFFLINE = "Offline"; +static const std::string QUERY_VALUE_SECURITY_LEVEL_L1 = "L1"; +static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2"; +static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3"; + + } // namespace wvcdm #endif // CDM_BASE_WV_CDM_CONSTANTS_H_ diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index f7a57ba9..821adaa0 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -20,7 +20,8 @@ typedef std::string RequestId; typedef uint32_t CryptoResult; typedef uint32_t CryptoSessionId; typedef std::string CryptoKeyId; -typedef std::map CdmNameValueMap; +typedef std::map CdmAppParameterMap; +typedef std::map CdmQueryMap; typedef std::vector CdmSecureStops; typedef std::vector CdmSecureStopReleaseMessage; typedef std::string CdmProvisioningRequest; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index fb4726a5..b4e723a2 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -6,6 +6,7 @@ #include "buffer_reader.h" #include "cdm_session.h" +#include "crypto_engine.h" #include "log.h" #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" @@ -70,16 +71,15 @@ CdmResponseType CdmEngine::OpenSession( CdmResponseType CdmEngine::CloseSession(CdmSessionId& session_id) { LOGI("CdmEngine::CloseSession"); - CdmSession* cdm_session = sessions_[session_id]; - - if (!cdm_session) { + CdmSessionIter 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); - cdm_session->DestroySession(); - delete cdm_session; + iter->second->DestroySession(); + delete iter->second; return NO_ERROR; } @@ -89,13 +89,12 @@ CdmResponseType CdmEngine::GenerateKeyRequest( const CdmKeySystem& key_system, const CdmInitData& init_data, const CdmLicenseType license_type, - CdmNameValueMap& app_parameters, + CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request) { LOGI("CdmEngine::GenerateKeyRequest"); - CdmSession* session = sessions_[session_id]; - - if (!session) { + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", session_id.c_str()); return KEY_ERROR; } @@ -123,8 +122,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest( key_request->clear(); // TODO(edwinwong, rfrias): need to pass in license type and app parameters - CdmResponseType sts = session->GenerateKeyRequest(extracted_pssh, - key_request); + CdmResponseType sts = iter->second->GenerateKeyRequest(extracted_pssh, + key_request); if (KEY_MESSAGE != sts) { LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d", @@ -146,9 +145,8 @@ CdmResponseType CdmEngine::AddKey( const CdmKeyResponse& key_data) { LOGI("CdmEngine::AddKey"); - CdmSession* session = sessions_[session_id]; - - if (!session) { + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { LOGE("CdmEngine::AddKey: session_id not found = %s", session_id.c_str()); return KEY_ERROR; } @@ -166,7 +164,7 @@ CdmResponseType CdmEngine::AddKey( return KEY_ERROR; } - CdmResponseType sts = session->AddKey(key_data); + CdmResponseType sts = iter->second->AddKey(key_data); if (KEY_ADDED != sts) { LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts); } @@ -186,9 +184,8 @@ CdmResponseType CdmEngine::CancelKeyRequest( // active sessions. Sessions are currently not being destroyed here. We can // add this logic once the semantics of canceling the key is worked out. - CdmSession* session = sessions_[session_id]; - - if (!session) { + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { LOGE("CdmEngine::CancelKeyRequest: session_id not found = %s", session_id.c_str()); return KEY_ERROR; } @@ -210,9 +207,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest( CdmKeyMessage* key_request) { LOGI("CdmEngine::GenerateRenewalRequest"); - CdmSession* session = sessions_[session_id]; - - if (!session) { + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s", session_id.c_str()); return KEY_ERROR; } @@ -232,7 +228,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest( key_request->clear(); - CdmResponseType sts = session->GenerateRenewalRequest(key_request); + CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request); if (KEY_MESSAGE != sts) { LOGE("CdmEngine::GenerateRenewalRequest: key request generation failed, sts=%d", @@ -251,9 +247,8 @@ CdmResponseType CdmEngine::RenewKey( const CdmKeyResponse& key_data) { LOGI("CdmEngine::RenewKey"); - CdmSession* session = sessions_[session_id]; - - if (!session) { + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str()); return KEY_ERROR; } @@ -271,7 +266,7 @@ CdmResponseType CdmEngine::RenewKey( return KEY_ERROR; } - CdmResponseType sts = session->RenewKey(key_data); + CdmResponseType sts = iter->second->RenewKey(key_data); if (KEY_ADDED != sts) { LOGE("CdmEngine::RenewKey: keys not added, sts=%d", (int)sts); return sts; @@ -280,11 +275,25 @@ CdmResponseType CdmEngine::RenewKey( return KEY_ADDED; } +CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) { + LOGI("CdmEngine::QueryStatus"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + if (crypto_engine) { + return crypto_engine->Query(key_info); + } + return KEY_ERROR; +} + CdmResponseType CdmEngine::QueryKeyStatus( const CdmSessionId& session_id, - CdmNameValueMap* key_info) { - // TODO(edwinwong, rfrias): add implementation - return NO_ERROR; + CdmQueryMap* key_info) { + LOGI("CdmEngine::QueryKeyStatus"); + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { + LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s", session_id.c_str()); + return KEY_ERROR; + } + return iter->second->QueryKeyStatus(key_info); } CdmResponseType CdmEngine::GetProvisioningRequest( @@ -320,16 +329,18 @@ CdmResponseType CdmEngine::Decrypt( size_t encrypted_size, const std::vector& iv, size_t block_offset, - void* decrypted_buffer) { - CdmSession* session = sessions_[session_id]; - - if (!session) { + uint8_t* decrypted_buffer) { + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { LOGW("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str()); return KEY_ERROR; } - // TODO(edwinwong, rfrias): Need to add implemenation and hook up - // decryption though to oem_crypto + if (NO_ERROR != iter->second->Decrypt(encrypted_buffer, encrypted_size, + block_offset, iv, key_id, + decrypted_buffer)) + return UNKNOWN_ERROR; + return NO_ERROR; } diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 9086341e..a7bab9ff 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -30,7 +30,7 @@ bool CdmSession::Init() { std::string token; if (!crypto_engine->GetToken(&token)) return false; - return license_parser_.Init(token, crypto_session_); + return license_parser_.Init(token, crypto_session_, &policy_engine_); } bool CdmSession::DestroySession() { @@ -67,6 +67,10 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { } } +CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) { + return policy_engine_.Query(key_info); +} + // CancelKeyRequest() - Cancel session. CdmResponseType CdmSession::CancelKeyRequest() { // TODO(gmorgan): cancel and clean up session @@ -78,10 +82,20 @@ CdmResponseType CdmSession::CancelKeyRequest() { CdmResponseType CdmSession::Decrypt(const uint8_t* encrypted_buffer, size_t encrypted_size, size_t block_offset, - const std::string& iv, + const std::vector& iv, const KeyId& key_id, uint8_t* decrypted_buffer) { - return UNKNOWN_ERROR; + if (!crypto_session_) + return UNKNOWN_ERROR; + + if (!crypto_session_->SelectKey(key_id)) + return UNKNOWN_ERROR; + + if (!crypto_session_->Decrypt(encrypted_buffer, encrypted_size, + block_offset, iv, decrypted_buffer)) + return UNKNOWN_ERROR; + + return NO_ERROR; } // License renewal @@ -131,7 +145,7 @@ void CdmSession::OnTimerEvent() { bool event_occurred = false; CdmEventType event; - policy_engine_.OnTimerEvent(GetCurrentTime(), event_occurred, event); + policy_engine_.OnTimerEvent(event_occurred, event); if (event_occurred) { for (CdmEventListenerIter iter = listeners_.begin(); diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.proto b/libwvdrmengine/cdm/core/src/certificate_provisioning.proto new file mode 100644 index 00000000..db51b0f2 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.proto @@ -0,0 +1,43 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// Author: tinskip@google.com (Thomas Inskip) +// +// Description: +// Public protocol buffer definitions for Widevine Device Certificate +// Provisioning protocol. + +syntax = "proto2"; + +package video_widevine_server.sdk; + +import "vendor/widevine/libwvdrmengine/cdm/core/src/client_identification.proto"; +option optimize_for = LITE_RUNTIME; + +// Provisioning request sent by client devices to provisioning service. +message ProvisioningRequest { + // Device root of trust and other client identification. Required. + optional ClientIdentification client_id = 1; + // Nonce value used to prevent replay attacks. Required. + optional bytes nonce = 2; +} + +// Provisioning response sent by the provisioning server to client devices. +message ProvisioningResponse { + // AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded. + // Required. + optional bytes device_rsa_key = 1; + // Initialization vector used to encrypt device_rsa_key. Required. + optional bytes device_rsa_key_iv = 2; + // Serialized DeviceCertificate. Required. + optional bytes device_certificate = 3; + // Nonce value matching nonce in ProvisioningRequest. Required. + optional bytes nonce = 4; +} + +// Serialized ProvisioningRequest or ProvisioningResponse signed with +// The message authentication key. +message SignedProvisioningMessage { + // Serialized ProvisioningRequest or ProvisioningResponse. Required. + optional bytes message = 1; + // HMAC-SHA256 signature of message. Required. + optional bytes signature = 2; +} diff --git a/libwvdrmengine/cdm/core/src/client_identification.proto b/libwvdrmengine/cdm/core/src/client_identification.proto new file mode 100644 index 00000000..29ad82da --- /dev/null +++ b/libwvdrmengine/cdm/core/src/client_identification.proto @@ -0,0 +1,30 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// Author: tinskip@google.com (Thomas Inskip) +// +// Description: +// ClientIdentification message used by provisioning and license protocols. + +syntax = "proto2"; + +package video_widevine_server.sdk; +option java_outer_classname = "ClientIdentificationProtos"; +option optimize_for = LITE_RUNTIME; + +// ClientIdentification message used to authenticate the client device. +message ClientIdentification { + enum TokenType { + KEYBOX = 0; + } + + message NameValue { + optional string name = 1; + optional string value = 2; + } + + // Type of factory-provisioned device root of trust. Optional. + optional TokenType type = 1 [default = KEYBOX]; + // Factory-provisioned device root of trust. Required. + optional bytes token = 2; + // Optional client information name/value pairs. + repeated NameValue client_info = 3; +} diff --git a/libwvdrmengine/cdm/core/src/crypto_engine.cpp b/libwvdrmengine/cdm/core/src/crypto_engine.cpp index feed17ba..dc40e643 100644 --- a/libwvdrmengine/cdm/core/src/crypto_engine.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_engine.cpp @@ -175,4 +175,10 @@ bool CryptoEngine::GetToken(std::string* token) { return true; } +CdmResponseType CryptoEngine::Query(CdmQueryMap* key_info) { + LOGV("CryptoEngine::GetToken: Query"); + (*key_info)[QUERY_KEY_SECURITY_LEVEL] = OEMCrypto_SecurityLevel(); + return NO_ERROR; +} + }; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp old mode 100644 new mode 100755 index 9840f272..995b1c81 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -230,6 +230,7 @@ bool CryptoSession::LoadKeys(const std::string& message, ko->key_id_length = ki->key_id().length(); ko->key_data_iv = msg + GetOffset(message, ki->key_data_iv()); ko->key_data = msg + GetOffset(message, ki->key_data()); + ko->key_data_length = ki->key_data().length(); if (ki->HasKeyControl()) { ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv()); ko->key_control = msg + GetOffset(message, ki->key_control()); @@ -324,6 +325,36 @@ bool CryptoSession::Decrypt(const InputDescriptor input, return true; } +// TODO(jfore): Define InputDescriptor and OutputDecriptor and +// remove this method. For now this is a level 3 decrypt. +bool CryptoSession::Decrypt(const uint8_t* encrypted_buffer, + size_t encrypted_size, + size_t block_offset, + const std::vector& iv, + uint8_t* decrypted_buffer) { + LOGV("CryptoSession::Decrypt: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + // TODO(gmorgan): handle inputs and outputs to decrypt call + const uint8_t* data_addr = NULL; + uint32_t data_length = 0; + bool is_encrypted = false; + uint32_t offset = block_offset; + OEMCrypto_DestBufferDesc out_buffer; + + out_buffer.type = OEMCrypto_BufferType_Clear; + out_buffer.buffer.clear.address = decrypted_buffer; + out_buffer.buffer.clear.max_length = encrypted_size; + + OEMCryptoResult sts = OEMCrypto_DecryptCTR(oec_session_id_, encrypted_buffer, + encrypted_size, true, &iv[0], + offset, &out_buffer); + if (OEMCrypto_SUCCESS != sts) { + return false; + } + return true; +} + bool CryptoSession::GenerateNonce(uint32_t* nonce) { if (!nonce) { LOGE("input parameter is null"); diff --git a/libwvdrmengine/cdm/core/src/device_certificate.proto b/libwvdrmengine/cdm/core/src/device_certificate.proto new file mode 100644 index 00000000..20b8318a --- /dev/null +++ b/libwvdrmengine/cdm/core/src/device_certificate.proto @@ -0,0 +1,117 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// Author: tinskip@google.com (Thomas Inskip) +// +// Description: +// Device certificate and certificate status list format definitions. + +syntax = "proto2"; + +package video_widevine_server.sdk; + +option optimize_for = LITE_RUNTIME; +option java_outer_classname = "DeviceCertificateProtos"; +option java_package = "com.google.video.widevine.protos"; + +// Certificate definition for user devices, intermediate, and root certificates. +message DeviceCertificate { + enum CertificateType { + ROOT = 0; + INTERMEDIATE = 1; + USER_DEVICE = 2; + } + + // Type of certificate. Required. + optional CertificateType type = 1; + // 128-bit globally unique serial number of certificate. + // Value is 0 for root certificate. Required. + optional bytes serial_number = 2; + // POSIX time, in seconds, when the certificate was created. Required. + optional uint32 creation_time_seconds = 3; + // Device public key. PKCS#1 ASN.1 DER-encoded. Required. + optional bytes public_key = 4; + // Widevine system ID for the device. Required for intermediate and + // user device certificates. + optional uint32 system_id = 5; + // True if the certificate corresponds to a test (non production) device. + // Optional. + optional bool test_device = 6 [default = false]; +} + +// DeviceCertificate signed with intermediate or root certificate private key. +message SignedDeviceCertificate { + // Serialized DeviceCertificate. Required. + optional bytes device_certificate = 1; + // Signature of device_certificate. Signed with root or intermediate + // certificate private key using RSASSA-PSS. Required. + optional bytes signature = 2; + // Intermediate signing certificate. Present only for user device + // certificates. All others signed with root certificate private key. + optional SignedDeviceCertificate signer = 3; +} + +// Contains device model information for a provisioned device. +message ProvisionedDeviceInfo { + enum WvSecurityLevel { + // Defined in Widevine Security Integration Guide for DASH on Android: + // https://docs.google.com/a/google.com/document/d/1Zum-fcJeoIw6KG1kDP_KepIE5h9gAZg0PaMtemBvk9c/edit#heading=h.1t3h5sf + LEVEL_UNSPECIFIED = 0; + LEVEL_1 = 1; + LEVEL_2 = 2; + LEVEL_3 = 3; + } + + // Widevine system ID for the device. Mandatory. + optional uint32 system_id = 1; + // Name of system-on-a-chip. Optional. + optional string soc = 2; + // Name of manufacturer. Optional. + optional string manufacturer = 3; + // Manufacturer's model name. Matches "brand" in device metadata. Optional. + optional string model = 4; + // Type of device (Phone, Tablet, TV, etc). + optional string device_type = 5; + // Device model year. Optional. + optional uint32 model_year = 6; + // Widevine-defined security level. Optional. + optional WvSecurityLevel security_level = 7 [default = LEVEL_UNSPECIFIED]; + // True if the certificate corresponds to a test (non production) device. + // Optional. + optional bool test_device = 8 [default = false]; +} + +// Contains the status of the root or an intermediate DeviceCertificate. +message DeviceCertificateStatus { + enum CertificateStatus { + VALID = 0; + REVOKED = 1; + }; + + // Serial number of the DeviceCertificate to which this message refers. + // Required. + optional bytes serial_number = 1; + // Status of the certificate. Optional. + optional CertificateStatus status = 2 [default = VALID]; + // Current version of a valid certificate. Present only if status = VALID. + optional uint32 current_certificate_version = 3; + // Device model information about the device to which the certificate + // corresponds. Required. + optional ProvisionedDeviceInfo device_info = 4; +} + +// List of DeviceCertificateStatus. Used to propagate certificate revocation and +// update list. +message DeviceCertificateStatusList { + // POSIX time, in seconds, when the list was created. Required. + optional uint32 creation_time_seconds = 1; + // DeviceCertificateStatus for each certifificate. + repeated DeviceCertificateStatus certificate_status = 2; +} + +// Signed CertificateStatusList +message SignedCertificateStatusList { + // Serialized CertificateStatusList. Required. + optional bytes certificate_status_list = 1; + // Signature of certificate_status_list. Signed with root certificate private + // key using RSASSA-PSS. Required. + optional bytes signature = 2; +} diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index c9e80968..c54c7e4b 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -4,6 +4,7 @@ #include "crypto_session.h" #include "log.h" +#include "policy_engine.h" #include "string_conversions.h" #include "wv_cdm_constants.h" @@ -25,13 +26,16 @@ 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; token_ = token; session_ = session; + policy_engine_ = policy_engine; return true; } @@ -239,9 +243,7 @@ bool CdmLicense::HandleKeyResponse(const CdmKeyResponse& license_response) { if (num_keys == 0) return false; - // TODO(kqyang): move protocol buffer related stuff in policy - // engine to this file. - // policy_engine_.SetLicense(license); + policy_engine_->SetLicense(license); bool status = session_->LoadKeys(signed_response.msg(), signed_response.signature(), @@ -282,9 +284,7 @@ bool CdmLicense::HandleKeyRenewalResponse( //This is the normal case. license_id_.CopyFrom(license.id()); - // TODO(kqyang): should we move protocol buffer related stuff in policy - // engine to this file instead? - // policy_engine_.UpdateLicense(license); + policy_engine_->UpdateLicense(license); } else { // This isn't supposed to happen. // TODO(jfore): Handle wrap? We can miss responses and that should be diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index 2e41b3ec..098fda43 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -4,81 +4,106 @@ #include #include +#include #include #include #include "log.h" +#include "properties.h" +#include "properties_configuration.h" #include "string_conversions.h" +#include "clock.h" namespace wvcdm { -PolicyEngine::PolicyEngine() : - license_state_(kLicenseStateInitial), - license_start_time_(0), - next_renewal_time_(0), - policy_max_duration_seconds_(0) { +PolicyEngine::PolicyEngine() { + Init(new Clock()); +} + +PolicyEngine::PolicyEngine(Clock* clock) { + Init(clock); } PolicyEngine::~PolicyEngine() { + if (clock_) + delete clock_; } -void PolicyEngine::OnTimerEvent(int64_t current_time, bool event_occured, CdmEventType& event) { +void PolicyEngine::Init(Clock* clock) { + license_state_ = kLicenseStateInitial; + can_decrypt_ = false; + license_start_time_ = 0; + license_received_time_ = 0; + playback_start_time_ = 0; + next_renewal_time_ = 0; + policy_max_duration_seconds_ = 0; + clock_ = clock; + properties_valid_ = true; + + if (!Properties::GetInstance()->GetProperty( + kPropertyKeyBeginLicenseUsageWhenReceived, + begin_license_usage_when_received_)) { + LOGW("PolicyEngine::PolicyEngine: Unable to access property - begin license usage"); + properties_valid_ = false; + } +} + +void PolicyEngine::OnTimerEvent(bool& event_occured, CdmEventType& event) { event_occured = false; + int64_t current_time = clock_->GetCurrentTime(); // License expiration trumps all. - if (IsLicenseDurationExpired(current_time) && - license_state_ != kLicenseStateExpired) { + if ((IsLicenseDurationExpired(current_time) || + IsPlaybackDurationExpired(current_time)) && + license_state_ != kLicenseStateExpired) { license_state_ = kLicenseStateExpired; + can_decrypt_ = false; event = LICENSE_EXPIRED_EVENT; event_occured = true; return; } + bool renewal_needed = false; + // Test to determine if renewal should be attempted. switch (license_state_) { + case kLicenseStateInitialPendingUsage: case kLicenseStateCanPlay: { - if (IsRenewalDelayExpired(current_time)) { - license_state_ = kLicenseStateNeedRenewal; - UpdateRenewalRequest(current_time); - event = LICENSE_RENEWAL_NEEDED_EVENT; - event_occured = true; - } - return; + if (IsRenewalDelayExpired(current_time)) + renewal_needed = true; + break; } case kLicenseStateNeedRenewal: { - UpdateRenewalRequest(current_time); - event = LICENSE_RENEWAL_NEEDED_EVENT; - event_occured = true; - return; + renewal_needed = true; + break; } case kLicenseStateWaitingLicenseUpdate: { - if (IsRenewalRetryIntervalExpired(current_time)) { - UpdateRenewalRequest(current_time); - event = LICENSE_RENEWAL_NEEDED_EVENT; - event_occured = true; - } - return; + if (IsRenewalRetryIntervalExpired(current_time)) + renewal_needed = true; + break; } case kLicenseStateInitial: case kLicenseStateExpired: { - return; + break; } default: { - license_state_ = kLicenseStateCannotPlay; - return; + license_state_ = kLicenseStateExpired; + can_decrypt_ = false; + break; } } + + if (renewal_needed) { + UpdateRenewalRequest(current_time); + event = LICENSE_RENEWAL_NEEDED_EVENT; + event_occured = true; + } } -// TODO: Fix up differences between Eureka and other platforms' use cases. -// Eureka does not get calls to decrypt. license usage begins -// when we receive the initial license. renew_with_usage will cause renewal to -// occur on the first call to OnTimeEvent after PolicyEngine::SetLicense is -// called. void PolicyEngine::SetLicense( const video_widevine_server::sdk::License& license) { license_id_.Clear(); @@ -89,89 +114,158 @@ void PolicyEngine::SetLicense( void PolicyEngine::UpdateLicense( const video_widevine_server::sdk::License& license) { - if (!license.has_policy() || kLicenseStateExpired == license_state_) + if (!license.has_policy() || kLicenseStateExpired == license_state_ || + !properties_valid_) return; policy_.MergeFrom(license.policy()); - policy_max_duration_seconds_ = 0; - - // Calculate policy_max_duration_seconds_. policy_max_duration_seconds_ - // will be set to the minimum of the following policies : - // rental_duration_seconds, playback_duration_seconds, and - // license_duration_seconds. The value is used to determine - // when the license expires. - if (policy_.has_rental_duration_seconds()) - policy_max_duration_seconds_ = policy_.rental_duration_seconds(); - - if ((policy_.has_playback_duration_seconds() && - (policy_.playback_duration_seconds() < policy_max_duration_seconds_)) || - !policy_max_duration_seconds_) { - policy_max_duration_seconds_ = policy_.playback_duration_seconds(); - } - - if ((policy_.has_license_duration_seconds() && - (policy_.license_duration_seconds() < policy_max_duration_seconds_)) || - !policy_max_duration_seconds_) { - policy_max_duration_seconds_ = policy_.license_duration_seconds(); - } switch (license_state_) { - case kLicenseStateInitial: { - // Process initial license. - license_start_time_ = license.license_start_time(); - if (policy_.can_play()) { - license_state_ = - policy_.renew_with_usage() ? - kLicenseStateNeedRenewal : kLicenseStateCanPlay; - } else { - license_state_ = kLicenseStateExpired; - } - next_renewal_time_ = license_start_time_ - + policy_.renewal_delay_seconds(); - } - break; - case kLicenseStateExpired: // Ignore policy updates. return; - default: { - // Process license renewal. - if (license.id().version() > license_id_.version()) { - // This is the normal case. - policy_.MergeFrom(license.policy()); - license_id_.CopyFrom(license.id()); - } else { - // This isn't supposed to happen. - // TODO(jfore): Handle wrap? We can miss responses and that should be - // considered normal until retries are exhausted. - policy_.set_can_play(false); + case kLicenseStateInitial: + case kLicenseStateInitialPendingUsage: + case kLicenseStateCanPlay: + case kLicenseStateNeedRenewal: + case kLicenseStateWaitingLicenseUpdate: + if (!policy_.can_play()) { + license_state_ = kLicenseStateExpired; + return; } - if (license.has_license_start_time()) { - // license start has been updated. Transition back to the - // normal kLicenseStateCanPlay state if playback is allowed by - // the updated license. - license_start_time_ = license.license_start_time(); - next_renewal_time_ = license_start_time_ - + policy_.renewal_delay_seconds(); - license_state_ = - policy_.can_play() ? kLicenseStateCanPlay : kLicenseStateExpired; - } else { - // license start was not updated. Continue sending renewel requests - // at the specified retry rate. To perform this we transition directly - // to kLicenseStateWaitingLicenseUpdate. While in this state - // IsRenewalRetryIntervalExpired will always return false if retries are - // not allowed. Note that next_renewal_time_ was updated when this - // renewal was requested. - license_state_ = - policy_.can_play() ? - kLicenseStateWaitingLicenseUpdate : kLicenseStateExpired; + // some basic license validation + if (license_state_ == kLicenseStateInitial) { + // license start time needs to be present in the initial response + if (!license.has_license_start_time()) + return; } + else { + // TODO(edwingwong, rfrias): Check back with Thomas and see if + // we need to enforce that all duration windows are absent if + // license_start_time is not present. This is a TBD. + + // if renewal, discard license if version has not been updated + if (license.id().version() > license_id_.version()) + license_id_.CopyFrom(license.id()); + else + return; + } + + // Update time information + int64_t current_time = clock_->GetCurrentTime(); + // TODO(edwingwong, rfrias): Check back with Thomas and see if + // we need to enforce that all duration windows are absent if + // license_start_time is not present. This is a TBD. + if (license.has_license_start_time()) + license_start_time_ = license.license_start_time(); + license_received_time_ = current_time; + next_renewal_time_ = current_time + + policy_.renewal_delay_seconds(); + + // Calculate policy_max_duration_seconds_. policy_max_duration_seconds_ + // will be set to the minimum of the following policies : + // rental_duration_seconds and license_duration_seconds. + // The value is used to determine when the license expires. + policy_max_duration_seconds_ = 0; + + if (policy_.has_rental_duration_seconds()) + policy_max_duration_seconds_ = policy_.rental_duration_seconds(); + + if ((policy_.license_duration_seconds() > 0) && + ((policy_.license_duration_seconds() < + policy_max_duration_seconds_) || + policy_max_duration_seconds_ == 0)) { + policy_max_duration_seconds_ = policy_.license_duration_seconds(); + } + + if (begin_license_usage_when_received_) + playback_start_time_ = current_time; + + // Update state + if (begin_license_usage_when_received_) { + if (policy_.renew_with_usage()) { + license_state_ = kLicenseStateNeedRenewal; + } + else { + license_state_ = kLicenseStateCanPlay; + can_decrypt_ = true; + } + } + else { + if (license_state_ == kLicenseStateInitial) { + license_state_ = kLicenseStateInitialPendingUsage; + } + else { + license_state_ = kLicenseStateCanPlay; + can_decrypt_ = true; + } + } + + break; + } +} + +void PolicyEngine::BeginDecryption() { + if ((playback_start_time_ == 0) && + (!begin_license_usage_when_received_)) { + switch (license_state_) { + case kLicenseStateInitialPendingUsage: + case kLicenseStateNeedRenewal: + case kLicenseStateWaitingLicenseUpdate: + playback_start_time_ = clock_->GetCurrentTime(); + + if (policy_.renew_with_usage()) { + license_state_ = kLicenseStateNeedRenewal; + } + else { + license_state_ = kLicenseStateCanPlay; + can_decrypt_ = true; + } + break; + case kLicenseStateCanPlay: + case kLicenseStateInitial: + case kLicenseStateExpired: + default: + break; } } } +CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) { + std::stringstream ss; + int64_t current_time = clock_->GetCurrentTime(); + + if (license_state_ == kLicenseStateInitial) + return UNKNOWN_ERROR; + + (*key_info)[QUERY_KEY_LICENSE_TYPE] = + license_id_.type() == video_widevine_server::sdk::STREAMING ? + QUERY_VALUE_STREAMING : QUERY_VALUE_OFFLINE; + (*key_info)[QUERY_KEY_PLAY_ALLOWED] = policy_.can_play() ? + QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; + (*key_info)[QUERY_KEY_PERSIST_ALLOWED] = policy_.can_persist() ? + QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; + (*key_info)[QUERY_KEY_RENEW_ALLOWED] = policy_.can_renew() ? + QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; + int64_t remaining_time = policy_max_duration_seconds_ + + license_received_time_ - current_time; + if (remaining_time < 0) + remaining_time = 0; + ss << remaining_time; + (*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str(); + remaining_time = policy_.playback_duration_seconds() + playback_start_time_ - + current_time; + if (remaining_time < 0) + remaining_time = 0; + ss << remaining_time; + (*key_info)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str(); + (*key_info)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url(); + + return NO_ERROR; +} + void PolicyEngine::UpdateRenewalRequest(int64_t current_time) { license_state_ = kLicenseStateWaitingLicenseUpdate; next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds(); @@ -182,30 +276,38 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) { // will always return false if the value is 0. bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) { return policy_max_duration_seconds_ && - license_start_time_ + policy_max_duration_seconds_ <= + license_received_time_ + policy_max_duration_seconds_ <= + current_time; +} + +bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) { + return (policy_.playback_duration_seconds() > 0) && + playback_start_time_ && + playback_start_time_ + policy_.playback_duration_seconds() <= current_time; } bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) { - return (policy_.renewal_delay_seconds() > 0) && - license_start_time_ + policy_.renewal_delay_seconds() <= + return policy_.can_renew() && + (policy_.renewal_delay_seconds() > 0) && + license_received_time_ + policy_.renewal_delay_seconds() <= current_time; } -// TODO(jfore): there is some gray around how this should be -// implemented. It currently is not. +// TODO(jfore, edwinwong, rfrias): This field is in flux and currently +// not implemented. Will address after possible updates from Thomas. bool PolicyEngine::IsRenewalRecoveryDurationExpired( int64_t current_time) { return (policy_.renewal_recovery_duration_seconds() > 0) && - license_start_time_ + policy_.renewal_recovery_duration_seconds() <= + license_received_time_ + policy_.renewal_recovery_duration_seconds() <= current_time; } bool PolicyEngine::IsRenewalRetryIntervalExpired( int64_t current_time) { - return (policy_.renewal_retry_interval_seconds() > 0) && - next_renewal_time_ <= current_time; + return policy_.can_renew() && + (policy_.renewal_retry_interval_seconds() > 0) && + next_renewal_time_ <= current_time; } } // wvcdm - diff --git a/libwvdrmengine/cdm/core/src/properties.cpp b/libwvdrmengine/cdm/core/src/properties.cpp new file mode 100644 index 00000000..e2dccc21 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/properties.cpp @@ -0,0 +1,44 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "properties.h" +#include "properties_configuration.h" +#include "wv_cdm_constants.h" + +namespace wvcdm { + +Properties* Properties::instance_ = NULL; +Lock Properties::properties_lock_; + +Properties::Properties() { + // Read in static properties/features + uint32_t size = sizeof(kCdmBooleanProperties)/sizeof(CdmBooleanProperties); + for (uint32_t i = 0; i < size; i++) { + boolean_properties_[kCdmBooleanProperties[i].name] = + kCdmBooleanProperties[i].value; + } +} + +Properties* Properties::GetInstance() { + AutoLock auto_lock(properties_lock_); + if (instance_ == NULL) { + instance_ = new Properties(); + } + return instance_; +} + +bool Properties::GetProperty(std::string& key, bool& value) { + CdmBooleanPropertiesMap::iterator itr = boolean_properties_.find(key); + + if (itr == boolean_properties_.end()) { + return false; + } + + value = itr->second; + return true; +} + +void Properties::SetProperty(std::string& key, bool value) { + boolean_properties_[key] = value; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index e7715c1e..8568d297 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -33,7 +33,7 @@ class WvCdmEngineTest : public testing::Test { protected: void GenerateKeyRequest(const std::string& key_system, const std::string& init_data) { - wvcdm::CdmNameValueMap app_parameters; + wvcdm::CdmAppParameterMap app_parameters; EXPECT_EQ(cdm_engine_.GenerateKeyRequest(session_id_, true, // is_key_system_present key_system, diff --git a/libwvdrmengine/cdm/core/test/license_unittest.cpp b/libwvdrmengine/cdm/core/test/license_unittest.cpp index 794115b9..470745d4 100644 --- a/libwvdrmengine/cdm/core/test/license_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/license_unittest.cpp @@ -4,6 +4,7 @@ #include "crypto_session.h" #include "license.h" #include "gtest/gtest.h" +#include "policy_engine.h" #include "string_conversions.h" namespace { @@ -57,7 +58,7 @@ class LicenseTest : public ::testing::Test { EXPECT_TRUE(crypto_engine->GetToken(&token)); EXPECT_TRUE(session_->IsOpen()); - EXPECT_TRUE(license_.Init(token, session_)); + EXPECT_TRUE(license_.Init(token, session_, &policy_engine_)); } virtual void TearDown() { @@ -67,11 +68,12 @@ class LicenseTest : public ::testing::Test { CryptoSession* session_; CdmLicense license_; + PolicyEngine policy_engine_; }; TEST(LicenseTestSession, InitNullSession) { CdmLicense license; - EXPECT_FALSE(license.Init("Dummy", NULL)); + EXPECT_FALSE(license.Init("Dummy", NULL, NULL)); } TEST_F(LicenseTest, PrepareKeyRequest) { diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp new file mode 100644 index 00000000..05c1cb71 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -0,0 +1,737 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#include + +#include "clock.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "license.h" +#include "policy_engine.h" +#include "wv_cdm_constants.h" + +namespace wvcdm { + +//protobuf generated classes. +using video_widevine_server::sdk::License; +using video_widevine_server::sdk::License_Policy; +using video_widevine_server::sdk::LicenseIdentification; +using video_widevine_server::sdk::STREAMING; +using video_widevine_server::sdk::OFFLINE; + +// gmock methods +using ::testing::Return; +using ::testing::AtLeast; + + +class MockClock : public Clock { + public: + MOCK_METHOD0(GetCurrentTime, int64_t()); +}; + +class PolicyEngineTest : public ::testing::Test { + protected: + virtual void SetUp() { + mock_clock_ = new MockClock(); + policy_engine_ = new PolicyEngine(mock_clock_); + + license_start_time_ = 1413517500; // ~ 01/01/2013 + license_renewal_delay_ = 604200; // 7 days - 10 minutes + license_renewal_retry_interval_ = 30; + license_duration_ = 604800; // 7 days + playback_duration_ = 86400; // 24 hours + + license_.set_license_start_time(license_start_time_); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(1); + id->set_type(STREAMING); + + License_Policy* policy = license_.mutable_policy(); + policy = license_.mutable_policy(); + policy->set_can_play(true); + policy->set_can_persist(true); + policy->set_can_renew(true); + policy->set_rental_duration_seconds(license_duration_); + policy->set_playback_duration_seconds(playback_duration_); + policy->set_license_duration_seconds(license_duration_); + policy->set_renewal_recovery_duration_seconds(license_duration_ - + license_renewal_delay_); // 10 minutes + policy->set_renewal_server_url( + "https://jmt17.google.com/video-dev/license/GetCencLicense"); + policy->set_renewal_delay_seconds(license_renewal_delay_); + policy->set_renewal_retry_interval_seconds( + license_renewal_retry_interval_); + policy->set_renew_with_usage(false); + } + + virtual void TearDown() { + } + + MockClock* mock_clock_; + PolicyEngine* policy_engine_; + License license_; + License_Policy* policy_; + + int64_t license_start_time_; + int64_t license_renewal_delay_; + int64_t license_renewal_retry_interval_; + int64_t license_duration_; + int64_t playback_duration_; +}; + +TEST_F(PolicyEngineTest, NoLicense) { + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +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)); + + policy_engine_->SetLicense(license_); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); +} + +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)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_play(false); + + policy_engine_->SetLicense(license_); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->BeginDecryption(); + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +// TODO(edwinwong, rfrias): persist license verification test needed + +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)) + .WillOnce(Return(license_start_time_ + 3601)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(3600); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +// TODO(edwinwong, rfrias): tests needed when begin license usage when received +// is enabled + +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)) + .WillOnce(Return(license_start_time_ + 13602)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_playback_duration_seconds(3600); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, PlaybackFails_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)) + .WillOnce(Return(license_start_time_ + 3601)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_license_duration_seconds(3600); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, 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)) + .WillOnce(Return(license_start_time_ + 3601)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(0); + policy->set_license_duration_seconds(3600); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, 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)) + .WillOnce(Return(license_start_time_ + 13602)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_playback_duration_seconds(0); + policy->set_license_duration_seconds(3600); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, 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)) + .WillOnce(Return(license_start_time_ + 3601)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_license_duration_seconds(0); + policy->set_rental_duration_seconds(3600); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, 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)) + .WillOnce(Return(license_start_time_ + 604810)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(0); + policy->set_playback_duration_seconds(0); + policy->set_license_duration_seconds(0); + policy->set_renewal_delay_seconds(604900); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + EXPECT_TRUE(policy_engine_->can_decrypt()); +} + + +// TODO(edwinwong, rfrias): renewal url test needed + +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)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) + .WillOnce(Return(license_start_time_ + license_duration_ + 10)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, 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)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 15)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + + license_renewal_retry_interval_ + 10)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + license_.set_license_start_time(license_start_time_ + + license_renewal_delay_ + 15); + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy_engine_->UpdateLicense(license_); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + EXPECT_TRUE(policy_engine_->can_decrypt()); +} + +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)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) + .WillOnce(Return(license_start_time_ + license_duration_ + 10)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + license_.set_license_start_time(license_start_time_ + + license_renewal_delay_ + 15); + policy_engine_->UpdateLicense(license_); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +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)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80)) + .WillOnce(Return(license_start_time_ + license_duration_ + 15)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, 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)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 55)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 67)) + .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 200)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + license_.set_license_start_time(license_start_time_ + + license_renewal_delay_ + 55); + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy_engine_->UpdateLicense(license_); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + EXPECT_TRUE(policy_engine_->can_decrypt()); +} + +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)) + .WillOnce(Return(license_start_time_ + 20)) + .WillOnce(Return(license_start_time_ + 40)) + .WillOnce(Return(license_start_time_ + 50)); + + License_Policy* policy = license_.mutable_policy(); + policy->set_renew_with_usage(true); + + policy_engine_->SetLicense(license_); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->BeginDecryption(); + EXPECT_FALSE(policy_engine_->can_decrypt()); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + license_.set_license_start_time(license_start_time_ + 30); + policy->set_renew_with_usage(false); + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy_engine_->UpdateLicense(license_); + + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + EXPECT_TRUE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .Times(1) + .WillOnce(Return(license_start_time_)); + + CdmQueryMap query_info; + EXPECT_EQ(UNKNOWN_ERROR, policy_engine_->Query(&query_info)); +} + +TEST_F(PolicyEngineTest, QuerySuccess) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .Times(2) + .WillOnce(Return(license_start_time_ + 1)) + .WillOnce(Return(license_start_time_ + 100)); + + License_Policy* policy = license_.mutable_policy(); + + policy_engine_->SetLicense(license_); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_LT(0, remaining_time); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_LT(0, remaining_time); + + EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL], + policy->renewal_server_url()); +} + +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(); + id->set_type(OFFLINE); + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_play(false); + policy->set_can_persist(false); + policy->set_can_renew(false); + + policy_engine_->SetLicense(license_); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->BeginDecryption(); + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + + EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL], + policy->renewal_server_url()); +} + +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)) + .WillOnce(Return(license_start_time_ + license_duration_ + 20)); + + LicenseIdentification* id = license_.mutable_id(); + id->set_type(OFFLINE); + + License_Policy* policy = license_.mutable_policy(); + + policy_engine_->SetLicense(license_); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(event_occurred, event); + EXPECT_FALSE(event_occurred); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + + EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL], + policy->renewal_server_url()); +} + +} // wvcdm diff --git a/libwvdrmengine/cdm/include/properties_configuration.h b/libwvdrmengine/cdm/include/properties_configuration.h new file mode 100644 index 00000000..2b222c62 --- /dev/null +++ b/libwvdrmengine/cdm/include/properties_configuration.h @@ -0,0 +1,18 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_PROPERTIES_CONFIGURATION_H_ +#define CDM_BASE_PROPERTIES_CONFIGURATION_H_ + +#include "wv_cdm_constants.h" +#include "properties.h" + +namespace wvcdm { + +// set property values below +static CdmBooleanProperties kCdmBooleanProperties[] = { + { .name = kPropertyKeyBeginLicenseUsageWhenReceived, .value = false } +}; + +} // 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 a3781df3..1801aaf0 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -26,7 +26,7 @@ class WvContentDecryptionModule { virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, const CdmInitData& init_data, const CdmLicenseType license_type, - CdmNameValueMap& app_parameters, + CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request); // Accept license response and extract key info. @@ -36,9 +36,12 @@ class WvContentDecryptionModule { // Cancel session virtual CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); + // Query system information + virtual CdmResponseType QueryStatus(CdmQueryMap* key_info); + // Query license information virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, - CdmNameValueMap* key_info); + CdmQueryMap* key_info); // Provisioning related methods virtual CdmResponseType GetProvisioningRequest( @@ -61,7 +64,7 @@ class WvContentDecryptionModule { size_t encrypted_size, const std::vector& iv, size_t block_offset, - void* decrypted_buffer); + uint8_t* decrypted_buffer); // Event listener related methods virtual bool AttachEventListener(CdmSessionId& session_id, diff --git a/libwvdrmengine/cdm/src/clock.cpp b/libwvdrmengine/cdm/src/clock.cpp index a1fc398b..4dd0e6dc 100644 --- a/libwvdrmengine/cdm/src/clock.cpp +++ b/libwvdrmengine/cdm/src/clock.cpp @@ -8,7 +8,7 @@ namespace wvcdm { -int64_t GetCurrentTime() { +int64_t Clock::GetCurrentTime() { struct timeval tv; tv.tv_sec = tv.tv_usec = 0; gettimeofday(&tv, NULL); diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 206981d2..97515b3c 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -33,7 +33,7 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( const CdmSessionId& session_id, const CdmInitData& init_data, const CdmLicenseType license_type, - CdmNameValueMap& app_parameters, + CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request) { CdmKeySystem key_system; return cdm_engine_->GenerateKeyRequest(session_id, false, key_system, @@ -56,9 +56,14 @@ CdmResponseType WvContentDecryptionModule::CancelKeyRequest( return cdm_engine_->CancelKeyRequest(session_id, false, key_system); } +CdmResponseType WvContentDecryptionModule::QueryStatus( + CdmQueryMap* key_info) { + return cdm_engine_->QueryStatus(key_info); +} + CdmResponseType WvContentDecryptionModule::QueryKeyStatus( const CdmSessionId& session_id, - CdmNameValueMap* key_info) { + CdmQueryMap* key_info) { return cdm_engine_->QueryKeyStatus(session_id, key_info); } @@ -91,7 +96,7 @@ CdmResponseType WvContentDecryptionModule::Decrypt( size_t encrypted_size, const std::vector& iv, size_t block_offset, - void* decrypted_buffer) { + uint8_t* decrypted_buffer) { return cdm_engine_->Decrypt(session_id, is_encrypted, key_id, encrypted_buffer, encrypted_size, iv, block_offset, decrypted_buffer); diff --git a/libwvdrmengine/cdm/test/Android.mk b/libwvdrmengine/cdm/test/Android.mk index 726d5843..4cce1e40 100644 --- a/libwvdrmengine/cdm/test/Android.mk +++ b/libwvdrmengine/cdm/test/Android.mk @@ -15,6 +15,10 @@ test_name := license_unittest test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk +test_name := policy_engine_unittest +test_src_dir := ../core/test +include $(LOCAL_PATH)/unit-test.mk + test_name := request_license_test test_src_dir := . include $(LOCAL_PATH)/unit-test.mk diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 4772a753..d5ddd7f0 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -9,6 +9,7 @@ #include "log.h" #include "string_conversions.h" #include "url_request.h" +#include "wv_cdm_constants.h" #include "wv_content_decryption_module.h" namespace { @@ -33,7 +34,7 @@ class WvCdmRequestLicenseTest : public testing::Test { protected: void GenerateKeyRequest(const std::string& key_system, const std::string& init_data) { - wvcdm::CdmNameValueMap app_parameters; + wvcdm::CdmAppParameterMap app_parameters; EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, init_data, kLicenseTypeStreaming, @@ -45,7 +46,7 @@ class WvCdmRequestLicenseTest : public testing::Test { const std::string& init_data) { // TODO application makes a license request, CDM will renew the license // when appropriate. - wvcdm::CdmNameValueMap app_parameters; + wvcdm::CdmAppParameterMap app_parameters; EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, init_data, kLicenseTypeStreaming, @@ -100,7 +101,7 @@ class WvCdmRequestLicenseTest : public testing::Test { if (is_renewal) { // TODO application makes a license request, CDM will renew the license // when appropriate - wvcdm::CdmNameValueMap app_parameters; + wvcdm::CdmAppParameterMap app_parameters; EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, init_data, kLicenseTypeStreaming, @@ -153,6 +154,50 @@ TEST_F(WvCdmRequestLicenseTest, LicenseRenewal) { } #endif +TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + + CdmQueryMap query_info; + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryKeyStatus(session_id_, &query_info)); + + EXPECT_EQ(wvcdm::QUERY_VALUE_STREAMING, + query_info[wvcdm::QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE, + query_info[wvcdm::QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(wvcdm::QUERY_VALUE_FALSE, + query_info[wvcdm::QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE, + query_info[wvcdm::QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_LE(0, remaining_time); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_LE(0, remaining_time); + + EXPECT_LE(0, (int)query_info[QUERY_KEY_RENEWAL_SERVER_URL].size()); + + decryptor_.CloseSession(session_id_); +} + +TEST_F(WvCdmRequestLicenseTest, QueryStatus) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + + CdmQueryMap query_info; + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryStatus(&query_info)); + + EXPECT_EQ(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3, + query_info[wvcdm::QUERY_KEY_SECURITY_LEVEL]); + decryptor_.CloseSession(session_id_); +} + } // namespace wvcdm int main(int argc, char **argv) { diff --git a/libwvdrmengine/cdm/test/unit-test.mk b/libwvdrmengine/cdm/test/unit-test.mk index eed4addc..921336e2 100644 --- a/libwvdrmengine/cdm/test/unit-test.mk +++ b/libwvdrmengine/cdm/test/unit-test.mk @@ -20,6 +20,8 @@ LOCAL_C_INCLUDES := \ bionic \ external/gtest/include \ external/stlport/stlport \ + $(LOCAL_PATH)/core/test/include \ + vendor/widevine/libwvdrmengine/test/gmock/include \ vendor/widevine/libwvdrmengine/cdm/core/include \ vendor/widevine/libwvdrmengine/cdm/core/test \ vendor/widevine/libwvdrmengine/cdm/include @@ -31,18 +33,20 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers) LOCAL_STATIC_LIBRARIES := \ libcdm \ + libgmock \ libgtest \ libgtest_main \ + libl3crypto \ libprotobuf-cpp-2.3.0-lite LOCAL_WHOLE_STATIC_LIBRARIES := \ - license_protocol_protos + cdm_protos LOCAL_SHARED_LIBRARIES := \ - libstlport \ libchromium_net \ libcrypto \ - liboemcrypto \ + libdl \ + libstlport \ libutils include $(BUILD_EXECUTABLE) diff --git a/libwvdrmengine/level3/Android.mk b/libwvdrmengine/level3/Android.mk index b648db5f..3f8a09ba 100644 --- a/libwvdrmengine/level3/Android.mk +++ b/libwvdrmengine/level3/Android.mk @@ -2,18 +2,20 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) - +# TODO(fredgc): remove mock code as real code starts working. LOCAL_SRC_FILES := \ - $(TARGET_ARCH)/l3crypto_engine_mock.cpp \ - $(TARGET_ARCH)/l3crypto_key_mock.cpp \ - $(TARGET_ARCH)/l3crypto_keybox_mock.cpp \ - $(TARGET_ARCH)/l3crypto_mock.cpp \ - $(TARGET_ARCH)/lock.cpp \ - $(TARGET_ARCH)/log.cpp \ - $(TARGET_ARCH)/string_conversions.cpp \ - $(TARGET_ARCH)/wvcrc.cpp \ + $(TARGET_ARCH)/entry_points.cpp \ + ../oemcrypto/mock/src/oemcrypto_engine_mock.cpp \ + ../oemcrypto/mock/src/oemcrypto_key_mock.cpp \ + ../oemcrypto/mock/src/oemcrypto_keybox_mock.cpp \ + ../oemcrypto/mock/src/lock.cpp \ + ../oemcrypto/mock/src/log.cpp \ + ../oemcrypto/mock/src/string_conversions.cpp \ + ../oemcrypto/mock/src/wvcrc.cpp \ +# TODO(fredgc): remove mock include when real code starts working. LOCAL_C_INCLUDES += \ + vendor/widevine/libwvdrmengine/oemcrypto/mock/src \ bionic \ external/openssh \ external/openssl/include \ @@ -29,7 +31,6 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libdl \ liblog \ - liboemcrypto \ libstlport \ libutils \ libz \ diff --git a/libwvdrmengine/level3/arm/entry_points.cpp b/libwvdrmengine/level3/arm/entry_points.cpp new file mode 100644 index 00000000..9ab1ad2d --- /dev/null +++ b/libwvdrmengine/level3/arm/entry_points.cpp @@ -0,0 +1,1162 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#include "OEMCryptoCENC.h" + +#include +#include +#include +#include +#include + +#include "log.h" +#include "oemcrypto_engine_mock.h" +#include "openssl/rand.h" +#include "openssl/sha.h" +#include "wv_cdm_constants.h" + +namespace wvoec_mock { + +// TODO(fredgc): These are in the level 3 directory, but they are entry points for +// both level 3 and level 1. That is confusing. Fix it. +typedef OEMCryptoResult (*L1_Initialize_t)(void); +typedef OEMCryptoResult (*L1_Terminate_t)(void); +typedef OEMCryptoResult (*L1_OpenSession_t)(OEMCrypto_SESSION *session); +typedef OEMCryptoResult (*L1_CloseSession_t)(OEMCrypto_SESSION session); +typedef OEMCryptoResult (*L1_GenerateDerivedKeys_t)(OEMCrypto_SESSION session, + const uint8_t *mac_key_context, + uint32_t mac_key_context_length, + const uint8_t *enc_key_context, + uint32_t enc_key_context_length); +typedef OEMCryptoResult (*L1_GenerateNonce_t)(OEMCrypto_SESSION session, + uint32_t* nonce); +typedef OEMCryptoResult (*L1_GenerateSignature_t)(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); +typedef OEMCryptoResult (*L1_LoadKeys_t)(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint8_t* enc_mac_key_iv, + const uint8_t* enc_mac_key, + size_t num_keys, + const OEMCrypto_KeyObject* key_array); +typedef OEMCryptoResult (*L1_RefreshKeys_t)(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array); +typedef OEMCryptoResult (*L1_SelectKey_t)(const OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_length); +typedef OEMCryptoResult (*L1_DecryptCTR_t)(OEMCrypto_SESSION session, + const uint8_t *data_addr, + size_t data_length, + bool is_encrypted, + const uint8_t *iv, + size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer); +typedef OEMCryptoResult (*L1_InstallKeybox_t)(const uint8_t *keybox, + size_t keyBoxLength); +typedef OEMCryptoResult (*L1_IsKeyboxValid_t)(void); +typedef OEMCryptoResult (*L1_GetDeviceID_t)(uint8_t* deviceID, + size_t *idLength); +typedef OEMCryptoResult (*L1_GetKeyData_t)(uint8_t* keyData, + size_t *keyDataLength); +typedef OEMCryptoResult (*L1_GetRandom_t)(uint8_t* randomData, + size_t dataLength); +typedef OEMCryptoResult (*L1_WrapKeybox_t)(const uint8_t *keybox, + size_t keyBoxLength, + uint8_t *wrappedKeybox, + size_t *wrappedKeyBoxLength, + const uint8_t *transportKey, + size_t transportKeyLength); +typedef OEMCryptoResult (*L1_RewrapDeviceRSAKey_t)(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint32_t *nonce, + const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + uint8_t* wrapped_rsa_key, + size_t *wrapped_rsa_key_length); +typedef OEMCryptoResult (*L1_LoadDeviceRSAKey_t)(OEMCrypto_SESSION session, + const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length); +typedef OEMCryptoResult (*L1_GenerateRSASignature_t)(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t *signature_length); +typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)(OEMCrypto_SESSION session, + const uint8_t* enc_session_key, + size_t enc_session_key_length, + const uint8_t *mac_key_context, + size_t mac_key_context_length, + const uint8_t *enc_key_context, + size_t enc_key_context_length); +typedef OEMCryptoResult (*L1_Generic_Encrypt_t)(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); +typedef OEMCryptoResult (*L1_Generic_Decrypt_t)(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); + +typedef OEMCryptoResult (*L1_Generic_Sign_t)(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length); + +typedef OEMCryptoResult (*L1_Generic_Verify_t)(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length); +typedef uint8_t (*L1_APIVersion_t)(); +typedef const char* (*L1_SecurityLevel_t)(); + +struct FunctionPointers { + void* library; + L1_Initialize_t OEMCrypto_Initialize; + L1_Terminate_t OEMCrypto_Terminate; + L1_OpenSession_t OEMCrypto_OpenSession; + L1_CloseSession_t OEMCrypto_CloseSession; + L1_GenerateDerivedKeys_t OEMCrypto_GenerateDerivedKeys; + L1_GenerateNonce_t OEMCrypto_GenerateNonce; + L1_GenerateSignature_t OEMCrypto_GenerateSignature; + L1_LoadKeys_t OEMCrypto_LoadKeys; + L1_RefreshKeys_t OEMCrypto_RefreshKeys; + L1_SelectKey_t OEMCrypto_SelectKey; + L1_DecryptCTR_t OEMCrypto_DecryptCTR; + L1_InstallKeybox_t OEMCrypto_InstallKeybox; + L1_IsKeyboxValid_t OEMCrypto_IsKeyboxValid; + L1_GetDeviceID_t OEMCrypto_GetDeviceID; + L1_GetKeyData_t OEMCrypto_GetKeyData; + L1_GetRandom_t OEMCrypto_GetRandom; + L1_WrapKeybox_t OEMCrypto_WrapKeybox; + L1_RewrapDeviceRSAKey_t OEMCrypto_RewrapDeviceRSAKey; + L1_LoadDeviceRSAKey_t OEMCrypto_LoadDeviceRSAKey; + L1_GenerateRSASignature_t OEMCrypto_GenerateRSASignature; + L1_DeriveKeysFromSessionKey_t OEMCrypto_DeriveKeysFromSessionKey; + L1_APIVersion_t OEMCrypto_APIVersion; + L1_SecurityLevel_t OEMCrypto_SecurityLevel; + L1_Generic_Encrypt_t OEMCrypto_Generic_Encrypt; + L1_Generic_Decrypt_t OEMCrypto_Generic_Decrypt; + L1_Generic_Sign_t OEMCrypto_Generic_Sign; + L1_Generic_Verify_t OEMCrypto_Generic_Verify; +}; +static struct FunctionPointers level1; + +#define QUOTE_DEFINE(A) #A +#define QUOTE(A) QUOTE_DEFINE(A) +#define LOOKUP(Type, Name) \ + level1.Name = (Type)dlsym(level1.library, QUOTE(Name)); \ + if (!level1.Name) { \ + dll_valid = false; \ + } + +typedef struct { + uint8_t signature[wvcdm::MAC_KEY_SIZE]; + uint8_t context[wvcdm::MAC_KEY_SIZE]; + uint8_t iv[wvcdm::KEY_IV_SIZE]; + uint8_t enc_rsa_key[]; +} WrappedRSAKey; + +static CryptoEngine* crypto_engine = NULL; + +extern "C" +OEMCryptoResult OEMCrypto_Initialize(void) { + crypto_engine = new CryptoEngine; + if (!crypto_engine || !crypto_engine->Initialized()) { + LOGE("[OEMCrypto_Initialize(): failed]"); + return OEMCrypto_ERROR_INIT_FAILED; + } + LOGD("OEMCrypto_Initialize Level 3 success. Now I will try to load Level 1"); + + level1.library = dlopen("liboemcrypto.so", RTLD_NOW); + if (level1.library == NULL) { + LOGW("Could not load liboemcrypto.so. Falling Back to L3."); + return OEMCrypto_SUCCESS; + } + bool dll_valid = true; + LOOKUP(L1_Initialize_t, OEMCrypto_Initialize); + LOOKUP(L1_Terminate_t, OEMCrypto_Terminate); + LOOKUP(L1_OpenSession_t, OEMCrypto_OpenSession); + LOOKUP(L1_CloseSession_t, OEMCrypto_CloseSession); + LOOKUP(L1_GenerateDerivedKeys_t, OEMCrypto_GenerateDerivedKeys); + LOOKUP(L1_GenerateNonce_t, OEMCrypto_GenerateNonce); + LOOKUP(L1_GenerateSignature_t, OEMCrypto_GenerateSignature); + LOOKUP(L1_LoadKeys_t, OEMCrypto_LoadKeys); + LOOKUP(L1_RefreshKeys_t, OEMCrypto_RefreshKeys); + LOOKUP(L1_SelectKey_t, OEMCrypto_SelectKey); + LOOKUP(L1_DecryptCTR_t, OEMCrypto_DecryptCTR); + LOOKUP(L1_InstallKeybox_t, OEMCrypto_InstallKeybox); + LOOKUP(L1_IsKeyboxValid_t, OEMCrypto_IsKeyboxValid); + LOOKUP(L1_GetDeviceID_t, OEMCrypto_GetDeviceID); + LOOKUP(L1_GetKeyData_t, OEMCrypto_GetKeyData); + LOOKUP(L1_GetRandom_t, OEMCrypto_GetRandom); + LOOKUP(L1_WrapKeybox_t, OEMCrypto_WrapKeybox); + + // TODO(fredgc): Move the validity check from here to below after we have + // an L1 library that matches current version. + if (!dll_valid) { + dlclose(level1.library); + level1.library = NULL; + LOGW("Could not load functions from liboemcrypto.so. Falling Back to L3."); + return OEMCrypto_SUCCESS; + } + + LOOKUP(L1_RewrapDeviceRSAKey_t, OEMCrypto_RewrapDeviceRSAKey); + LOOKUP(L1_LoadDeviceRSAKey_t, OEMCrypto_LoadDeviceRSAKey); + LOOKUP(L1_GenerateRSASignature_t, OEMCrypto_GenerateRSASignature); + LOOKUP(L1_DeriveKeysFromSessionKey_t, OEMCrypto_DeriveKeysFromSessionKey); + LOOKUP(L1_APIVersion_t, OEMCrypto_APIVersion); + LOOKUP(L1_SecurityLevel_t, OEMCrypto_SecurityLevel); + LOOKUP(L1_Generic_Decrypt_t, OEMCrypto_Generic_Decrypt); + LOOKUP(L1_Generic_Encrypt_t, OEMCrypto_Generic_Encrypt); + LOOKUP(L1_Generic_Sign_t, OEMCrypto_Generic_Sign); + LOOKUP(L1_Generic_Verify_t, OEMCrypto_Generic_Verify); + + // TODO(fredgc): Move the validity check from above to here after we have + // a current L1 library. + + OEMCryptoResult st = level1.OEMCrypto_Initialize(); + if (st != OEMCrypto_SUCCESS) { + LOGW("Could not initialize liboemcrypto.so. Falling Back to L3."); + dlclose(level1.library); + level1.library = NULL; + return OEMCrypto_SUCCESS; + } + if (level1.OEMCrypto_APIVersion ) { + uint32_t level1_version = level1.OEMCrypto_APIVersion(); + if( level1_version > oec_latest_version ) { // Check for foward jump. + LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.", + level1_version, oec_latest_version); + dlclose(level1.library); + level1.library = NULL; + return OEMCrypto_SUCCESS; + } + } + LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1."); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_Terminate(void) { + if (level1.library) { + OEMCryptoResult st = level1.OEMCrypto_Terminate(); + dlclose(level1.library); + level1.library = NULL; + return st; + } + if (!crypto_engine) { + LOGE("[OEMCrypto_Terminate(): failed]"); + return OEMCrypto_ERROR_TERMINATE_FAILED; + } + + if (crypto_engine->Initialized()) { + crypto_engine->Terminate(); + } + + delete crypto_engine; + crypto_engine = NULL; + LOGD("[OEMCrypto_Terminate(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) { + if (level1.library) { + return level1.OEMCrypto_OpenSession(session); + } + SessionId sid = crypto_engine->CreateSession(); + *session = (OEMCrypto_SESSION)sid; + LOGD("[OEMCrypto_OpenSession(): SID=%08x]", sid); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { + if (level1.library) { + return level1.OEMCrypto_CloseSession(session); + } + if (!crypto_engine->DestroySession((SessionId)session)) { + LOGD("[OEMCrypto_CloseSession(SID=%08X): failed]", session); + return OEMCrypto_ERROR_CLOSE_SESSION_FAILED; + } else { + LOGD("[OEMCrypto_CloseSession(SID=%08X): success]", session); + return OEMCrypto_SUCCESS; + } +} + +extern "C" +OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, + uint32_t* nonce) { + if (level1.library) { + return level1.OEMCrypto_GenerateNonce(session, nonce); + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateNonce(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + uint32_t nonce_value; + uint8_t* nonce_string = reinterpret_cast(&nonce_value); + + // Generate 4 bytes of random data + if (!RAND_bytes(nonce_string, 4)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + session_ctx->AddNonce(nonce_value); + *nonce = nonce_value; + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, + const uint8_t* mac_key_context, + uint32_t mac_key_context_length, + const uint8_t* enc_key_context, + uint32_t enc_key_context_length) { + if (level1.library) { + return level1.OEMCrypto_GenerateDerivedKeys(session, mac_key_context, + mac_key_context_length, + enc_key_context, + enc_key_context_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector mac_ctx_str(mac_key_context, + mac_key_context + mac_key_context_length); + const std::vector enc_ctx_str(enc_key_context, + enc_key_context + enc_key_context_length); + + // Generate mac and encryption keys for current session context + if (!session_ctx->DeriveKeys(mac_ctx_str, enc_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + if (level1.library) { + return level1.OEMCrypto_GenerateSignature( session, message, message_length, + signature, signature_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_GenerateSignature(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateSignature(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (session_ctx->GenerateSignature(message, + message_length, + signature, + signature_length)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE;; +} + +bool RangeCheck(const uint8_t* message, + uint32_t message_length, + const uint8_t* field, + uint32_t field_length, + bool allow_null) { + if (field == NULL) return allow_null; + if (field < message) return false; + if (field + field_length > message + message_length) return false; + return true; +} + +extern "C" +OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint8_t* enc_mac_key_iv, + const uint8_t* enc_mac_key, + size_t num_keys, + const OEMCrypto_KeyObject* key_array) { + if (level1.library) { + return level1.OEMCrypto_LoadKeys(session, message, message_length, signature, + signature_length, enc_mac_key_iv, enc_mac_key, + num_keys, key_array); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_LoadKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_LoadKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0 || + key_array == NULL || num_keys == 0) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + if (!RangeCheck(message, message_length, enc_mac_key, + wvcdm::MAC_KEY_SIZE, true) || + !RangeCheck(message, message_length, enc_mac_key_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + for (unsigned int i = 0; i < num_keys; i++) { + if (!RangeCheck(message, message_length, key_array[i].key_id, + key_array[i].key_id_length, false) || + !RangeCheck(message, message_length, key_array[i].key_data, + wvcdm::KEY_SIZE, false) || + !RangeCheck(message, message_length, key_array[i].key_data_iv, + wvcdm::KEY_IV_SIZE, false) || + !RangeCheck(message, message_length, key_array[i].key_control, + wvcdm::KEY_CONTROL_SIZE, true) || + !RangeCheck(message, message_length, key_array[i].key_control_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE -range check %d]", i); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + + // Validate message signature + if (!session_ctx->ValidateMessage(message, message_length, signature, signature_length)) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + session_ctx->StartTimer(); + + // Decrypt and install keys in key object + // Each key will have a key control block. They will all have the same nonce. + std::vector key_id; + std::vector enc_key_data; + std::vector key_data_iv; + std::vector key_control; + std::vector key_control_iv; + for (unsigned int i = 0; i < num_keys; i++) { + key_id.assign(key_array[i].key_id, + key_array[i].key_id + key_array[i].key_id_length); + enc_key_data.assign(key_array[i].key_data, + key_array[i].key_data + wvcdm::KEY_SIZE); + key_data_iv.assign(key_array[i].key_data_iv, + key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE); + if (key_array[i].key_control == NULL) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + key_control.assign(key_array[i].key_control, + key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); + key_control_iv.assign(key_array[i].key_control_iv, + key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); + + if (!session_ctx->InstallKey(key_id, enc_key_data, key_data_iv, key_control, + key_control_iv)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + + // All keys processed. Flush nonce table + session_ctx->FlushNonces(); + + // enc_mac_key can be NULL if license renewal is not supported + if (enc_mac_key == NULL) return OEMCrypto_SUCCESS; + + // V2 license protocol: update mac key after processing license response + const std::vector enc_mac_key_str = std::vector( + enc_mac_key, enc_mac_key + wvcdm::MAC_KEY_SIZE); + const std::vector enc_mac_key_iv_str = std::vector( + enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE); + + if (!session_ctx->UpdateMacKey(enc_mac_key_str, enc_mac_key_iv_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array) { + if (level1.library) { + return level1.OEMCrypto_RefreshKeys(session, message, message_length, signature, + signature_length, num_keys, key_array); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_RefreshKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_RefreshKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + for (unsigned int i = 0; i < num_keys; i++) { + if (!RangeCheck(message, message_length, key_array[i].key_id, + key_array[i].key_id_length, true) || + !RangeCheck(message, message_length, key_array[i].key_control, + wvcdm::KEY_CONTROL_SIZE, true) || + !RangeCheck(message, message_length, key_array[i].key_control_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + + // Validate message signature + if (!session_ctx->ValidateMessage(message, message_length, + signature, signature_length)) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + // Decrypt and refresh keys in key refresh object + std::vector key_id; + std::vector key_control; + std::vector key_control_iv; + for (unsigned int i = 0; i < num_keys; i++) { + // TODO(gmorgan): key_id may be null if special control key type (TBS) + if (key_array[i].key_id != NULL) { + key_id.assign(key_array[i].key_id, + key_array[i].key_id + key_array[i].key_id_length); + } else { + key_id.clear(); + } + if (key_array[i].key_control != NULL) { + key_control.assign(key_array[i].key_control, + key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); + key_control_iv.assign(key_array[i].key_control_iv, + key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); + } else { + key_control.clear(); + key_control_iv.clear(); + } + + if (!session_ctx->RefreshKey(key_id, key_control, key_control_iv)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + session_ctx->StartTimer(); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_length) { + if (level1.library) { + return level1.OEMCrypto_SelectKey(session, key_id, key_id_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_SelectKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_SelectKey(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector key_id_str = std::vector(key_id, key_id + key_id_length); + if (!session_ctx->SelectContentKey(key_id_str)) { + LOGE("[OEMCrypto_SelectKey(): FAIL]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, + const uint8_t* data_addr, + size_t data_length, + bool is_encrypted, + const uint8_t* iv, + size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer) { + if (level1.library) { + return level1.OEMCrypto_DecryptCTR( session, data_addr, data_length, + is_encrypted, iv, offset, out_buffer); + } + wvoec_mock::BufferType buffer_type = kBufferTypeDirect; + void* destination = NULL; + size_t max_length = 0; + switch (out_buffer->type) { + case OEMCrypto_BufferType_Clear: + buffer_type = kBufferTypeClear; + destination = out_buffer->buffer.clear.address; + max_length = out_buffer->buffer.clear.max_length; + break; + case OEMCrypto_BufferType_Secure: + buffer_type = kBufferTypeSecure; + destination = out_buffer->buffer.secure.handle; + max_length = out_buffer->buffer.secure.max_length; + break; + default: + case OEMCrypto_BufferType_Direct: + buffer_type = kBufferTypeDirect; + destination = NULL; + break; + } + + if (buffer_type != kBufferTypeDirect && max_length < data_length) { + LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_DecryptCTR(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_DecryptCTR(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (data_addr == NULL || data_length == 0 || + iv == NULL || out_buffer == NULL) { + LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + std::vector iv_v(iv, iv + 16); + std::vector content(data_addr, data_addr + data_length); + + if (!crypto_engine->DecryptCTR(session_ctx, iv_v, (int)offset, + content, is_encrypted, + destination, buffer_type)) { + LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, + size_t keyBoxLength) { + if (level1.library) { + return level1.OEMCrypto_InstallKeybox(keybox, keyBoxLength); + } + if (crypto_engine->keybox().InstallKeybox(keybox, keyBoxLength)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_WRITE_KEYBOX; +} + +extern "C" +OEMCryptoResult OEMCrypto_IsKeyboxValid(void) { + if (level1.library) { + return level1.OEMCrypto_IsKeyboxValid(); + } + switch(crypto_engine->ValidateKeybox()) { + case NO_ERROR: return OEMCrypto_SUCCESS; + case BAD_CRC: return OEMCrypto_ERROR_BAD_CRC; + case BAD_MAGIC: return OEMCrypto_ERROR_BAD_MAGIC; + default: + case OTHER_ERROR: return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + +extern "C" +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, + size_t* idLength) { + if (level1.library) { + return level1.OEMCrypto_GetDeviceID(deviceID, idLength); + } + std::vector dev_id_string = crypto_engine->keybox().device_id(); + if (dev_id_string.empty()) { + LOGE("[OEMCrypto_GetDeviceId(): Keybox Invalid]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + size_t dev_id_len = dev_id_string.size(); + if (*idLength < dev_id_len) { + *idLength = dev_id_len; + LOGE("[OEMCrypto_GetDeviceId(): ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memset(deviceID, 0, *idLength); + memcpy(deviceID, &dev_id_string[0], dev_id_len); + *idLength = dev_id_len; + LOGD("[OEMCrypto_GetDeviceId(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, + size_t* keyDataLength) { + if (level1.library) { + return level1.OEMCrypto_GetKeyData(keyData, keyDataLength); + } + size_t length = crypto_engine->keybox().key_data_length(); + if (*keyDataLength < length) { + *keyDataLength = length; + LOGE("[OEMCrypto_GetKeyData(): ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memset(keyData, 0, *keyDataLength); + memcpy(keyData, crypto_engine->keybox().key_data(), length); + *keyDataLength = length; + LOGD("[OEMCrypto_GetKeyData(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) { + if (level1.library) { + return level1.OEMCrypto_GetRandom(randomData, dataLength); + } + if (!randomData) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (RAND_bytes(randomData, dataLength)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +extern "C" +OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox, + size_t keyBoxLength, + uint8_t* wrappedKeybox, + size_t* wrappedKeyBoxLength, + const uint8_t* transportKey, + size_t transportKeyLength) { + if (level1.library) { + return level1.OEMCrypto_WrapKeybox(keybox, keyBoxLength, wrappedKeybox, + wrappedKeyBoxLength, transportKey, + transportKeyLength); + } + if (!keybox || !wrappedKeybox || !wrappedKeyBoxLength + || (keyBoxLength != *wrappedKeyBoxLength)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // This implementation ignores the transport key. For test keys, we + // don't need to encrypt the keybox. + memcpy(wrappedKeybox, keybox, keyBoxLength); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint32_t* nonce, + const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + uint8_t* wrapped_rsa_key, + size_t* wrapped_rsa_key_length) { + if (level1.library) { + if (!level1.OEMCrypto_RewrapDeviceRSAKey ) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return level1.OEMCrypto_RewrapDeviceRSAKey( session, message, message_length, + signature, signature_length, nonce, + enc_rsa_key, enc_rsa_key_length, + enc_rsa_key_iv, wrapped_rsa_key, + wrapped_rsa_key_length); + } + if (wrapped_rsa_key_length == NULL) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // For the reference implementation, the wrapped key and the encrypted + // key are the same size -- just encrypted with different keys. + // We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature. + // Important: This layout must match OEMCrypto_LoadDeviceRSAKey below. + size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); + + if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) { + LOGW("[OEMCrypto_RewrapDeviceRSAKey(): Wrapped Keybox Short Buffer]"); + *wrapped_rsa_key_length = buffer_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used. + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (message == NULL || message_length == 0 || signature == NULL + || signature_length == 0 || nonce == NULL || enc_rsa_key == NULL) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + if (!RangeCheck(message, message_length, reinterpret_cast(nonce), + sizeof(uint32_t), true) || + !RangeCheck(message, message_length, enc_rsa_key, enc_rsa_key_length, + true) || + !RangeCheck(message, message_length, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE, + true)) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): - range check.]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + + // Validate nonce + if (!session_ctx->CheckNonce(*nonce)) { + return OEMCrypto_ERROR_INVALID_NONCE; + } + session_ctx->FlushNonces(); + + // Decrypt key and verify signature. + if (!session_ctx->LoadRSAKey(enc_rsa_key, enc_rsa_key_length, + enc_rsa_key_iv, message, message_length, + signature, signature_length)) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + // 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 (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!RAND_bytes(wrapped->iv, sizeof(wrapped->iv))) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::vector mac_ctx_str(wrapped->context, + wrapped->context + sizeof(wrapped->context)); + // Generate mac and encryption keys for encrypting the signature. + if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Encrypt rsa key with keybox. + if (!session_ctx->EncryptRSAKey(wrapped->enc_rsa_key, + enc_rsa_key_length, + wrapped->iv)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + size_t sig_length = sizeof(wrapped->signature); + if (!session_ctx->GenerateSignature(wrapped->context, // start signing here. + buffer_size - sizeof(wrapped->signature), + wrapped->signature, + &sig_length)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length) { + if (level1.library) { + if (!level1.OEMCrypto_LoadDeviceRSAKey ) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return level1.OEMCrypto_LoadDeviceRSAKey( session, wrapped_rsa_key, + wrapped_rsa_key_length); + } + const WrappedRSAKey* wrapped + = reinterpret_cast(wrapped_rsa_key); + if (wrapped_rsa_key == NULL) { + LOGE("[OEMCrypto_LoadDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + const std::vector mac_ctx_str(wrapped->context, + wrapped->context + sizeof(wrapped->context)); + // Generate mac and encryption keys for encrypting the signature. + if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Decrypt key and verify signature. + if (!session_ctx->LoadRSAKey(wrapped->enc_rsa_key, + wrapped_rsa_key_length - sizeof(WrappedRSAKey), + wrapped->iv, + wrapped->context, + wrapped_rsa_key_length - sizeof(wrapped->signature), + wrapped->signature, + sizeof(wrapped->signature))) { + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + if (level1.library) { + if (!level1.OEMCrypto_GenerateRSASignature ) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return level1.OEMCrypto_GenerateRSASignature(session, message, message_length, + signature, signature_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + if (signature_length == 0) { + LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + size_t required_size = session_ctx->RSASignatureSize(); + if (*signature_length < required_size) { + *signature_length = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (session_ctx->GenerateRSASignature(message, + message_length, + signature, + signature_length)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE;; +} + +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) { + if (level1.library) { + if (!level1.OEMCrypto_DeriveKeysFromSessionKey ) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return level1.OEMCrypto_DeriveKeysFromSessionKey(session, enc_session_key, + enc_session_key_length, + mac_key_context, + mac_key_context_length, + enc_key_context, + enc_key_context_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector ssn_key_str(enc_session_key, + enc_session_key + enc_session_key_length); + const std::vector mac_ctx_str(mac_key_context, + mac_key_context + mac_key_context_length); + const std::vector enc_ctx_str(enc_key_context, + enc_key_context + enc_key_context_length); + + // Generate mac and encryption keys for current session context + if (!session_ctx->RSADeriveKeys(ssn_key_str, mac_ctx_str, enc_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +extern "C" +uint32_t OEMCrypto_APIVersion() { + if (level1.library) { + if (!level1.OEMCrypto_APIVersion ) { + return 5; + } + return level1.OEMCrypto_APIVersion(); + } + return oec_latest_version; +} + +extern "C" +const char* OEMCrypto_SecurityLevel() { + if (level1.library) { + if (!level1.OEMCrypto_SecurityLevel ) { + return "Unknown"; + } + return level1.OEMCrypto_SecurityLevel(); + } + return "L3"; +} + +extern "C" +OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + + if (level1.library) { + if (!level1.OEMCrypto_Generic_Encrypt ) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return level1.OEMCrypto_Generic_Encrypt(session, in_buffer, buffer_length, + iv, algorithm, out_buffer); + } + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" +OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + if (level1.library) { + if (!level1.OEMCrypto_Generic_Decrypt ) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return level1.OEMCrypto_Generic_Decrypt(session, in_buffer, buffer_length, + iv, algorithm, out_buffer); + } + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" +OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length) { + if (level1.library) { + if (!level1.OEMCrypto_Generic_Sign ) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return level1.OEMCrypto_Generic_Sign(session, in_buffer, buffer_length, + algorithm, signature, + signature_length); + } + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" +OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length) { + if (level1.library) { + if (!level1.OEMCrypto_Generic_Verify ) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return level1.OEMCrypto_Generic_Verify(session, in_buffer, buffer_length, + algorithm, signature, + signature_length); + } + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +}; // namespace wvoec_mock diff --git a/libwvdrmengine/mediacrypto/test/Android.mk b/libwvdrmengine/mediacrypto/test/Android.mk index 6b109f9f..878fba4f 100644 --- a/libwvdrmengine/mediacrypto/test/Android.mk +++ b/libwvdrmengine/mediacrypto/test/Android.mk @@ -21,12 +21,14 @@ LOCAL_STATIC_LIBRARIES := \ libgmock \ libgmock_main \ libgtest \ + libl3crypto \ libprotobuf-cpp-2.3.0-lite \ libwvdrmcryptoplugin \ LOCAL_SHARED_LIBRARIES := \ + libcrypto \ + libdl \ liblog \ - liboemcrypto \ libstlport \ libutils \ @@ -43,7 +45,7 @@ LOCAL_C_INCLUDES += \ LOCAL_ADDITIONAL_DEPENDENCIES += $(proto_generated_headers) LOCAL_WHOLE_STATIC_LIBRARIES := \ - license_protocol_protos \ + cdm_protos # End protobuf section diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index 61e3eb13..16519a2f 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -69,7 +69,7 @@ status_t WVDrmPlugin::getKeyRequest( CdmInitData cdmInitData(initData.begin(), initData.end()); // TODO: Do something with mimeType? - CdmNameValueMap cdmParameters; + CdmAppParameterMap cdmParameters; for (size_t i = 0; i < optionalParameters.size(); ++i) { const String8& key = optionalParameters.keyAt(i); const String8& value = optionalParameters.valueAt(i); @@ -132,7 +132,7 @@ status_t WVDrmPlugin::queryKeyStatus( const Vector& sessionId, KeyedVector& infoMap) const { CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); - CdmNameValueMap cdmLicenseInfo; + CdmQueryMap cdmLicenseInfo; CdmResponseType res = mCDM->QueryKeyStatus(cdmSessionId, &cdmLicenseInfo); @@ -141,7 +141,7 @@ status_t WVDrmPlugin::queryKeyStatus( } infoMap.clear(); - for (CdmNameValueMap::const_iterator iter = cdmLicenseInfo.begin(); + for (CdmQueryMap::const_iterator iter = cdmLicenseInfo.begin(); iter != cdmLicenseInfo.end(); ++iter) { const string& cdmKey = iter->first; diff --git a/libwvdrmengine/mediadrm/test/Android.mk b/libwvdrmengine/mediadrm/test/Android.mk index f08508bf..8a44ad9c 100644 --- a/libwvdrmengine/mediadrm/test/Android.mk +++ b/libwvdrmengine/mediadrm/test/Android.mk @@ -21,12 +21,14 @@ LOCAL_STATIC_LIBRARIES := \ libgmock \ libgmock_main \ libgtest \ + libl3crypto \ libprotobuf-cpp-2.3.0-lite \ libwvdrmdrmplugin \ LOCAL_SHARED_LIBRARIES := \ + libcrypto \ + libdl \ liblog \ - liboemcrypto \ libstlport \ libutils \ @@ -43,7 +45,7 @@ LOCAL_C_INCLUDES += \ LOCAL_ADDITIONAL_DEPENDENCIES += $(proto_generated_headers) LOCAL_WHOLE_STATIC_LIBRARIES := \ - license_protocol_protos \ + cdm_protos # End protobuf section diff --git a/libwvdrmengine/mediadrm/test/MockCDM.h b/libwvdrmengine/mediadrm/test/MockCDM.h index 8ffd979a..deb36c8b 100644 --- a/libwvdrmengine/mediadrm/test/MockCDM.h +++ b/libwvdrmengine/mediadrm/test/MockCDM.h @@ -25,7 +25,7 @@ class MockCDM : public WvContentDecryptionModule { MOCK_METHOD5(GenerateKeyRequest, CdmResponseType(const CdmSessionId&, const CdmInitData&, const CdmLicenseType, - CdmNameValueMap&, + CdmAppParameterMap&, CdmKeyMessage*)); MOCK_METHOD2(AddKey, CdmResponseType(const CdmSessionId&, @@ -34,7 +34,7 @@ class MockCDM : public WvContentDecryptionModule { MOCK_METHOD1(CancelKeyRequest, CdmResponseType(const CdmSessionId&)); MOCK_METHOD2(QueryKeyStatus, CdmResponseType(const CdmSessionId&, - CdmNameValueMap*)); + CdmQueryMap*)); MOCK_METHOD2(GetProvisioningRequest, CdmResponseType(CdmProvisioningRequest*, std::string*)); diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index 9a944f1c..f30abff1 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -91,7 +91,7 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { Vector request; KeyedVector parameters; - CdmNameValueMap cdmParameters; + CdmAppParameterMap cdmParameters; parameters.add(String8("paddingScheme"), String8("PKCS7")); cdmParameters["paddingScheme"] = "PKCS7"; @@ -110,7 +110,7 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { kInitDataSize), kLicenseTypeOffline, cdmParameters, _)) .WillOnce(DoAll(SetArgPointee<4>(cdmRequest), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::KEY_MESSAGE))); EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, ElementsAreArray(initDataRaw, @@ -118,21 +118,21 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { kLicenseTypeStreaming, cdmParameters, _)) .WillOnce(DoAll(SetArgPointee<4>(cdmRequest), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::KEY_MESSAGE))); } - status_t res = plugin.getLicenseRequest(sessionId, initData, - String8("video/h264"), - DrmPlugin::kLicenseType_Offline, - parameters, request, defaultUrl); + status_t res = plugin.getKeyRequest(sessionId, initData, + String8("video/h264"), + DrmPlugin::kKeyType_Offline, + parameters, request, defaultUrl); ASSERT_EQ(OK, res); EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); EXPECT_TRUE(defaultUrl.isEmpty()); - res = plugin.getLicenseRequest(sessionId, initData, String8("video/h264"), - DrmPlugin::kLicenseType_Streaming, parameters, - request, defaultUrl); + res = plugin.getKeyRequest(sessionId, initData, String8("video/h264"), + DrmPlugin::kKeyType_Streaming, parameters, + request, defaultUrl); ASSERT_EQ(OK, res); EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); @@ -152,33 +152,27 @@ TEST_F(WVDrmPluginTest, AddsKeys) { Vector response; response.appendArray(responseRaw, kResponseSize); + // TODO: Do something with the key set ID. + Vector ignoredKeySetId; + EXPECT_CALL(cdm, AddKey(cdmSessionId, ElementsAreArray(responseRaw, kResponseSize))) - .Times(1); + .WillOnce(Return(wvcdm::KEY_ADDED)); - status_t res = plugin.provideLicenseResponse(sessionId, response); + status_t res = plugin.provideKeyResponse(sessionId, response, + ignoredKeySetId); ASSERT_EQ(OK, res); } -TEST_F(WVDrmPluginTest, CancelsKeyRequests) { - MockCDM cdm; - WVDrmPlugin plugin(&cdm); - - EXPECT_CALL(cdm, CancelKeyRequest(cdmSessionId)) - .Times(1); - - status_t res = plugin.removeLicense(sessionId); - - ASSERT_EQ(OK, res); -} +// TODO: Reinstate removeKeys() test once its behavior is finalized. TEST_F(WVDrmPluginTest, QueriesKeyStatus) { MockCDM cdm; WVDrmPlugin plugin(&cdm); KeyedVector expectedLicenseStatus; - CdmNameValueMap cdmLicenseStatus; + CdmQueryMap cdmLicenseStatus; expectedLicenseStatus.add(String8("areTheKeysAllRight"), String8("yes")); cdmLicenseStatus["areTheKeysAllRight"] = "yes"; @@ -193,7 +187,7 @@ TEST_F(WVDrmPluginTest, QueriesKeyStatus) { KeyedVector licenseStatus; - status_t res = plugin.queryLicenseStatus(sessionId, licenseStatus); + status_t res = plugin.queryKeyStatus(sessionId, licenseStatus); ASSERT_EQ(OK, res); diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index d3043916..ef0bdb83 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -16,8 +16,9 @@ extern "C" { #endif -#define OEMCRYPTO_VERSION "5.0" +#define OEMCRYPTO_VERSION "7.0" static const char oec_version[] = OEMCRYPTO_VERSION; +static const uint32_t oec_latest_version = 7; typedef uint32_t OEMCrypto_SESSION; @@ -144,6 +145,7 @@ typedef struct { size_t key_id_length; const uint8_t* key_data_iv; const uint8_t* key_data; + size_t key_data_length; const uint8_t* key_control_iv; const uint8_t* key_control; } OEMCrypto_KeyObject; @@ -153,12 +155,12 @@ typedef struct { * Points to the relevant fields for renewing a content key. The fields are * extracted from the License Renewal Response message offered to * OEMCrypto_RefreshKeys(). Each field points to one of the components of - * the key. All fields are 128 bits (16 bytes): + * the key. * key_id - the unique id of this key. * key_control_iv - the IV for performing AES-128-CBC decryption of the - * key_control field. + * key_control field. 16 bytes. * key_control - the key control block. It is encrypted (AES-128-CBC) with - * the content key from the key_data field. + * the content key from the key_data field. 16 bytes. * * The key_data is unchanged from the original OEMCrypto_LoadKeys() call. Some * Key Control Block fields, especially those related to key lifetime, may @@ -174,6 +176,17 @@ typedef struct { const uint8_t* key_control; } OEMCrypto_KeyRefreshObject; +/* + * OEMCrypto_Algorithm + * This is a list of valid algorithms for OEMCrypto_Generic_* functions. + * Some are valid for encryption/decryption, and some for signing/verifying. + */ +typedef enum OEMCrypto_Algorithm { + OEMCrypto_AES_CBC_128_NO_PADDING = 0, + OEMCrypto_HMAC_SHA256 = 1, +} OEMCrypto_Algorithm; + +/* Obfuscation Renames. */ #define OEMCrypto_Initialize _oecc01 #define OEMCrypto_Terminate _oecc02 #define OEMCrypto_InstallKeybox _oecc03 @@ -195,6 +208,12 @@ typedef struct { #define OEMCrypto_LoadDeviceRSAKey _oecc19 #define OEMCrypto_GenerateRSASignature _oecc20 #define OEMCrypto_DeriveKeysFromSessionKey _oecc21 +#define OEMCrypto_APIVersion _oecc22 +#define OEMCrypto_SecurityLevel _oecc23 +#define OEMCrypto_Generic_Encrypt _oecc24 +#define OEMCrypto_Generic_Decrypt _oecc25 +#define OEMCrypto_Generic_Sign _oecc26 +#define OEMCrypto_Generic_Virify _oecc27 /* * OEMCrypto_Initialize @@ -212,6 +231,9 @@ typedef struct { * Returns: * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_Initialize(void); @@ -232,6 +254,9 @@ OEMCryptoResult OEMCrypto_Initialize(void); * Returns: * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto hardware + * + * Version: + * This method is all API versions. */ OEMCryptoResult OEMCrypto_Terminate(void); @@ -253,6 +278,9 @@ OEMCryptoResult OEMCrypto_Terminate(void); * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_TOO_MANY_SESSIONS failed because too many sessions are open * OEMCrypto_ERROR_OPEN_SESSION_FAILED failed to initialize the crypto session + * + * Version: + * This method changed in API version 5. */ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session); @@ -274,6 +302,9 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session); * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. * OEMCrypto_ERROR_CLOSE_SESSION_FAILED failed to terminate the crypto session + * + * Version: + * This method changed in API version 5. */ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); @@ -315,6 +346,9 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); * OEMCrypto_ERROR_INVALID_SESSION * OEMCrypto_ERROR_UNKNOWN_FAILURE * OEMCrypto_ERROR_INVALID_CONTEXT + * + * Version: + * This method changed in API version 5. */ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( OEMCrypto_SESSION session, @@ -351,6 +385,9 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( * OEMCrypto_ERROR_INVALID_SESSION * OEMCrypto_ERROR_UNKNOWN_FAILURE * OEMCrypto_ERROR_INVALID_CONTEXT + * + * Version: + * This method changed in API version 5. */ OEMCryptoResult OEMCrypto_GenerateNonce( OEMCrypto_SESSION session, @@ -384,8 +421,12 @@ OEMCryptoResult OEMCrypto_GenerateNonce( * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_NO_DEVICE_KEY * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_SHORT_BUFFER * OEMCrypto_ERROR_UNKNOWN_FAILURE * OEMCrypto_ERROR_INVALID_CONTEXT + * + * Version: + * This method changed in API version 5. */ OEMCryptoResult OEMCrypto_GenerateSignature( OEMCrypto_SESSION session, @@ -459,6 +500,9 @@ OEMCryptoResult OEMCrypto_GenerateSignature( * OEMCrypto_ERROR_SIGNATURE_FAILURE * OEMCrypto_ERROR_INVALID_NONCE * OEMCrypto_ERROR_TOO_MANY_KEYS + * + * Version: + * This method changed in API version 5. */ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, const uint8_t* message, @@ -510,6 +554,9 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, * OEMCrypto_ERROR_INVALID_CONTEXT * OEMCrypto_ERROR_INVALID_NONCE * OEMCrypto_ERROR_SIGNATURE_FAILURE + * + * Version: + * This method changed in API version 5. */ OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, @@ -564,6 +611,9 @@ OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, * OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key * OEMCrypto_ERROR_CONTROL_INVALID invalid or unsupported control input * OEMCrypto_ERROR_KEYBOX_INVALID cannot decrypt and read from Keybox + * + * Version: + * This method changed in API version 5. */ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, const uint8_t* key_id, @@ -636,6 +686,9 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, * OEMCrypto_ERROR_UNKNOWN_FAILURE * OEMCrypto_ERROR_INVALID_CONTEXT * OEMCrypto_ERROR_DECRYPT_FAILED + * + * Version: + * This method changed in API version 5. */ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, @@ -667,6 +720,9 @@ OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, * Returns: * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_WRITE_KEYBOX failed to handle and store Keybox + * + * Version: + * This method is all API versions. */ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox, size_t keyBoxLength); @@ -693,6 +749,9 @@ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox, * OEMCrypto_SUCCESS * OEMCrypto_ERROR_BAD_MAGIC * OEMCrypto_ERROR_BAD_CRC + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_IsKeyboxValid(void); @@ -714,6 +773,9 @@ OEMCryptoResult OEMCrypto_IsKeyboxValid(void); * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_SHORT_BUFFER buffer is too small to return the device ID * OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t *idLength); @@ -743,6 +805,9 @@ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_SHORT_BUFFER the buffer is too small to return the KeyData * OEMCrypto_ERROR_NO_KEYDATA failed to return KeyData + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t *keyDataLength); @@ -766,6 +831,9 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_RNG_FAILED failed to generate random number * OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength); @@ -795,6 +863,9 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_WRAP_KEYBOX failed to wrap Keybox * OEMCrypto_ERROR_NOT_IMPLEMENTED + * + * Version: + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox, size_t keyBoxLength, @@ -848,6 +919,9 @@ OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox, * OEMCrypto_ERROR_SIGNATURE_FAILURE * OEMCrypto_ERROR_INVALID_NONCE * OEMCrypto_ERROR_SHORT_BUFFER + * + * Version: + * This method changed in API versions 6. */ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, @@ -855,7 +929,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, size_t message_length, const uint8_t* signature, size_t signature_length, - uint32_t *nonce, + const uint32_t *nonce, const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, @@ -889,6 +963,9 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, * OEMCrypto_ERROR_INVALID_SESSION * OEMCrypto_ERROR_UNKNOWN_FAILURE * OEMCrypto_ERROR_INVALID_RSA_KEY + * + * Version: + * This method changed in API version 6. */ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key, @@ -923,6 +1000,9 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, * OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small. * OEMCrypto_ERROR_CLOSE_SESSION_FAILED illegal/unrecognized handle or the * security engine is not properly initialized. + * + * Version: + * This method changed in API version 6. */ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, const uint8_t* message, @@ -966,6 +1046,9 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, * OEMCrypto_ERROR_INVALID_SESSION * OEMCrypto_ERROR_UNKNOWN_FAILURE * OEMCrypto_ERROR_INVALID_CONTEXT + * + * Version: + * This method changed in API version 6. */ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, const uint8_t* enc_session_key, @@ -975,6 +1058,218 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, const uint8_t *enc_key_context, size_t enc_key_context_length); + +/* + * OEMCrypto_APIVersion() + * + * Description: + * This function returns the current API version number. Because this + * API is part of a shared library, the version number allows the calling + * application to avoid version mis-match errors. + * + * There is a possibility that some API methods will be backwards compatible, + * or backwards compatible at a reduced security level. + * + * There is no plan to introduce forward-compatibility. I.e. applications + * will reject a library with a newer version of the API. + * + * Returns: + * The current version number. + * + * Version: + * This method should change in all API versions. + */ +uint32_t OEMCrypto_APIVersion(); + +/* + * OEMCrypto_SecurityLevel() + * + * Description: + * This function returns the security level of the OEMCrypto library. + * + * Since this function is spoofable, it is not relied on for security + * purposes. It is for information only. + * + * Returns: + * A null terminated string. Useful values are "L1", "L2" or "L3". + * + * Version: + * This method changed in API version 6. + */ +const char* OEMCrypto_SecurityLevel(); + +/* + * OEMCryptoResult OEMCrypto_Generic_Encrypt + * + * This function encrypts a generic buffer of data using the current key. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and the data is not encrypted. + * + * The control bit for the current key shall have the Allow_Encrypt set. If + * not, return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * + * Parameters: + * [in] session: crypto session identifier. + * [in] in_buffer: pointer to memory containing data to be encrypted. + * [in] buffer_length: length of the buffer, in bytes. + * [in] iv: IV for encrypting data. Size is specified by the algorithm. + * [in] algorithm: Specifies which encryption algorithm to use. See + * OEMCrypto_Algorithm for valid values. + * [out] out_buffer: pointer to buffer in which encrypted data should be stored. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Version: + * This method changed in API version 7. + */ +OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); + +/* + * OEMCrypto_Generic_Decrypt + * + * This function decrypts a generic buffer of data using the current key. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and the data is not decrypted. + * + * The control bit for the current key shall have the Allow_Decrypt set. If + * not, return OEMCrypto_ERROR_DECRYPT_FAILED. + * If the current key’s control block has the Data_Path_Type bit set, then + * return OEMCrypto_ERROR_DECRYPT_FAILED. + * If the current key’s control block has the HDCP bit set, then return + * OEMCrypto_ERROR_DECRYPT_FAILED. + * + * Parameters: + * [in] session: crypto session identifier. + * [in] in_buffer: pointer to memory containing data to be encrypted. + * [in] buffer_length: length of the buffer, in bytes. + * [in] iv: IV for encrypting data. Size depends on the algorithm. + * [in] algorithm: Specifies which encryption algorithm to use. See + * OEMCrypto_Algorithm for valid values. + * [out] out_buffer: pointer to buffer in which decrypted data should be stored. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Version: + * This method changed in API version 7. + */ +OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); + +/* + * OEMCrypto_Generic_Sign + * + * This function signs a generic buffer of data using the current key. + * + * Verification + * The following checks should be performed. If any check fails, + * an error is returned, and the signature is not generated. + * + * The control bit for the current key shall have the Allow_Sign set. + * + * Parameters + * [in] session: crypto session identifier. + * [in] in_buffer: pointer to memory containing data to be encrypted. + * [in] buffer_length: length of the buffer, in bytes. + * [in] algorithm: Specifies which algorithm to use. See + * OEMCrypto_Algorithm for valid values. + * [out] signature: pointer to buffer in which signature should be stored. + * [in/out] signature_length: (in) length of the signature buffer, in bytes. + * (out) actual length of the signature + * + * Returns + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to hold + * buffer. + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * Threading + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Version: + * This method changed in API version 7. + */ +OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length); + +/* + * OEMCrypto_Generic_Verify + * This function verfies the signature of a generic buffer of data using the + * current key. + * + * Verification + * The following checks should be performed. If any check fails, an error is + * returned, and the data is not signed. + * + * The control bit for the current key shall have the Allow_Verify set. + * The signature of the message shall be computed, and the API shall verify the + * computed signature matches the signature passed in. If not, return + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * + * Parameters + * [in] session: crypto session identifier. + * [in] in_buffer: pointer to memory containing data to be encrypted. + * [in] buffer_length: length of the buffer, in bytes. + * [in] algorithm: Specifies which algorithm to use. Current valid value is + * HMAC_SHA256. + * [in] signature: pointer to signature buffer. + * [in] signature_length: length of the signature buffer, in bytes. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Version: + * This method changed in API version 7. + */ +OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length); + #ifdef __cplusplus } #endif diff --git a/libwvdrmengine/oemcrypto/mock/Android.mk b/libwvdrmengine/oemcrypto/mock/Android.mk index bd19649a..8501f15f 100644 --- a/libwvdrmengine/oemcrypto/mock/Android.mk +++ b/libwvdrmengine/oemcrypto/mock/Android.mk @@ -32,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libz \ +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES) LOCAL_MODULE := liboemcrypto include $(BUILD_SHARED_LIBRARY) diff --git a/libwvdrmengine/oemcrypto/mock/src/log.h b/libwvdrmengine/oemcrypto/mock/src/log.h index ce4204f3..bd14c602 100644 --- a/libwvdrmengine/oemcrypto/mock/src/log.h +++ b/libwvdrmengine/oemcrypto/mock/src/log.h @@ -17,7 +17,7 @@ typedef enum { LOG_VERBOSE } LogPriority; -void log_write(LogPriority priority, const char *fmt, ...); +void log_write(LogPriority priority, const char* fmt, ...); // Log APIs #define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__)) diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index 48c0e87b..82d48055 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -25,6 +25,9 @@ #include "string_conversions.h" #include "wv_cdm_constants.h" + +static const int kPssSaltLength = 20; + namespace { // Increment counter for AES-CTR void ctr128_inc(uint8_t* counter) { @@ -36,7 +39,7 @@ void ctr128_inc(uint8_t* counter) { void dump_openssl_error() { while (unsigned long err = ERR_get_error()) { char buffer[120]; - LOGE("openssl error: %lu: %s", + LOGE("openssl error -- %lu -- %s", err, ERR_error_string(err, buffer)); } } @@ -139,6 +142,13 @@ bool SessionContext::DeriveKeys(const std::vector& mac_key_context, return false; } +#if 0 // Print Derived Keys to stdout. + std::cout << " mac_key_context = " << wvcdm::b2a_hex(mac_key_context) << std::endl; + std::cout << " enc_key_context = " << wvcdm::b2a_hex(enc_key_context) << std::endl; + std::cout << " mac_key = " << wvcdm::b2a_hex(mac_key) << std::endl; + std::cout << " enc_key = " << wvcdm::b2a_hex(enc_key) << std::endl; +#endif + set_mac_key(mac_key); set_encryption_key(enc_key); return true; @@ -151,21 +161,40 @@ bool SessionContext::RSADeriveKeys(const std::vector& enc_session_key, LOGE("[RSADeriveKeys(): no RSA key set]"); return false; } - session_key_.resize(wvcdm::KEY_SIZE); - if (-1 == RSA_private_decrypt(wvcdm::KEY_SIZE, &enc_session_key[0], - &session_key_[0], rsa_key_, - RSA_PKCS1_OAEP_PADDING)) { + if (enc_session_key.size() != static_cast(RSA_size(rsa_key_))) { + LOGE("[RSADeriveKeys(): encrypted session key is wrong size:%zu, should be %d]", + enc_session_key.size(), RSA_size(rsa_key_)); + dump_openssl_error(); + return false; + } + session_key_.resize(RSA_size(rsa_key_)); + int decrypted_size = RSA_private_decrypt(enc_session_key.size(), + &enc_session_key[0], + &session_key_[0], rsa_key_, + RSA_PKCS1_OAEP_PADDING); + if (-1 == decrypted_size) { LOGE("[RSADeriveKeys(): error decrypting session key.]"); dump_openssl_error(); return false; } + session_key_.resize(decrypted_size); + if (decrypted_size != static_cast(wvcdm::KEY_SIZE)) { + LOGE("[RSADeriveKeys(): error. session key is wrong size: %d.]", + decrypted_size); + dump_openssl_error(); + session_key_.clear(); + return false; + } + // Generate derived key for mac key std::vector mac_key; std::vector mac_key_part2; if (!DeriveKey(session_key_, mac_key_context, 1, &mac_key)) { + session_key_.clear(); return false; } if (!DeriveKey(session_key_, mac_key_context, 2, &mac_key_part2)) { + session_key_.clear(); return false; } mac_key.insert(mac_key.end(), mac_key_part2.begin(), mac_key_part2.end()); @@ -173,9 +202,18 @@ bool SessionContext::RSADeriveKeys(const std::vector& enc_session_key, // Generate derived key for encryption key std::vector enc_key; if (!DeriveKey(session_key_, enc_key_context, 1, &enc_key)) { + session_key_.clear(); return false; } +#if 0 // Print Derived Keys to stdout. + std::cout << " mac_key_context = " << wvcdm::b2a_hex(mac_key_context) << std::endl; + std::cout << " enc_key_context = " << wvcdm::b2a_hex(enc_key_context) << std::endl; + std::cout << " session_key = " << wvcdm::b2a_hex(session_key_) << std::endl; + std::cout << " mac_key = " << wvcdm::b2a_hex(mac_key) << std::endl; + std::cout << " enc_key = " << wvcdm::b2a_hex(enc_key) << std::endl; +#endif + set_mac_key(mac_key); set_encryption_key(enc_key); return true; @@ -212,6 +250,14 @@ bool SessionContext::GenerateSignature(const uint8_t* message, return false; } +size_t SessionContext::RSASignatureSize() { + if (!rsa_key_) { + LOGE("[GenerateRSASignature(): no RSA key set]"); + return 0; + } + return static_cast(RSA_size(rsa_key_)); +} + bool SessionContext::GenerateRSASignature(const uint8_t* message, size_t message_length, uint8_t* signature, @@ -229,19 +275,30 @@ bool SessionContext::GenerateRSASignature(const uint8_t* message, *signature_length = RSA_size(rsa_key_); return false; } - // TODO(fredgc): This uses the wrong algorithm for signing. - // This code needs to be fixed!! - LOGE("COmputing signature using RSASSA-PKCS1v1.5 instead of RSASSA-PSS"); + + // Hash the message using SHA1. uint8_t hash[SHA_DIGEST_LENGTH]; if (!SHA1(message, message_length, hash)) { LOGE("[GeneratRSASignature(): error creating signature hash.]"); dump_openssl_error(); return false; } - int ret = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, - signature, signature_length, rsa_key_); - if (ret != 1) { - LOGE("[GeneratRSASignature(): error signing signature hash.]"); + + // Add PSS padding. + uint8_t padded_digest[*signature_length]; + int status = RSA_padding_add_PKCS1_PSS(rsa_key_, padded_digest, hash, + EVP_sha1(), kPssSaltLength); + if (status == -1) { + LOGE("[GeneratRSASignature(): error padding hash.]"); + dump_openssl_error(); + return false; + } + + // Encrypt PSS padded digest. + status = RSA_private_encrypt(*signature_length, padded_digest, signature, + rsa_key_, RSA_NO_PADDING); + if (status == -1) { + LOGE("[GeneratRSASignature(): error in private encrypt.]"); dump_openssl_error(); return false; } @@ -387,10 +444,17 @@ bool SessionContext::EncryptRSAKey(uint8_t* wrapped_rsa_key, uint8_t* p = &buffer[0]; int len = i2d_RSAPrivateKey(rsa_key_, &p); if (len < 0) { - LOGE("[RewrapRSAKey(): Could not decode rsa key]"); + LOGE("[EncryptRSAKey(): Could not decode rsa key]"); dump_openssl_error(); return false; } + if (static_cast(len) >= wrapped_rsa_key_length) { + LOGE("[EncryptRSAKey(): padding is wrong size: len=%d, size=%zu", + len, wrapped_rsa_key_length); + return false; + } + size_t padding = wrapped_rsa_key_length - len; + memset(&buffer[len], static_cast(padding), padding); // Encrypt rsa key with keybox. uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE]; @@ -409,28 +473,35 @@ bool SessionContext::LoadRSAKey(const uint8_t* enc_rsa_key, size_t message_length, const uint8_t* signature, size_t signature_length) { - std::vector enc_rsa_key_v(enc_rsa_key, - enc_rsa_key + enc_rsa_key_length); - std::vector iv(enc_rsa_key_iv, enc_rsa_key_iv + wvcdm::KEY_IV_SIZE); - std::vector rsa_key; // unencrypted. // Validate message signature if (!ValidateMessage(message, message_length, signature, signature_length)) { LOGE("[LoadRSAKey(): Could not verify signature]"); return false; } - if (!ce_->DecryptMessage(this, encryption_key_, iv, - enc_rsa_key_v, &rsa_key)) { - LOGE("[LoadRSAKey(): Could not decrypt key data]"); + + uint8_t iv[wvcdm::KEY_IV_SIZE]; + uint8_t* clear = new uint8_t[enc_rsa_key_length]; + memcpy(iv, enc_rsa_key_iv, 16); + AES_KEY aes_key; + AES_set_decrypt_key(&encryption_key_[0], 128, &aes_key); + AES_cbc_encrypt(enc_rsa_key, clear, enc_rsa_key_length, &aes_key, + iv, AES_DECRYPT); + size_t padding = clear[enc_rsa_key_length - 1]; + if (padding > 16) { + LOGE("[LoadRSAKey(): Encrypted RSA has bad padding]"); return false; } + size_t rsa_key_length = enc_rsa_key_length - padding; + if (rsa_key_) { RSA_free(rsa_key_); rsa_key_ = NULL; } - uint8_t const* p = &rsa_key[0]; - RSA* rsa = d2i_RSAPrivateKey(0, &p, rsa_key.size()); - rsa_key_ = rsa; + uint8_t const* p = clear; + rsa_key_ = d2i_RSAPrivateKey(0, &p, rsa_key_length); + delete[] clear; + if (! rsa_key_) { LOGE("[LoadRSAKey(): Could decode unencrypted rsa key]"); dump_openssl_error(); @@ -450,6 +521,191 @@ bool SessionContext::LoadRSAKey(const uint8_t* enc_rsa_key, } } +bool SessionContext::Generic_Encrypt(const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Generic_Encrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return false; + } + const std::vector& key = current_content_key()->value(); + const KeyControlBlock& control = current_content_key()->control(); + // Set the AES key. + if (static_cast(key.size()) != AES_BLOCK_SIZE) { + LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size."); + return false; + } + if (!(control.control_bits() & kControlAllowEncrypt)) { + LOGE("[Generic_Encrypt(): control bit says not allowed."); + return false; + } + if (control.duration() > 0) { + if (control.duration() < CurrentTimer()) { + LOGE("[Generic_Encrypt(): key expired."); + return false; + } + } + if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) { + LOGE("[Generic_Encrypt(): algorithm bad."); + return false; + } + if( buffer_length % AES_BLOCK_SIZE != 0 ) { + LOGE("[Generic_Encrypt(): buffers size bad."); + return false; + } + const uint8_t* key_u8 = &key[0]; + AES_KEY aes_key; + if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[Generic_Encrypt(): FAILURE]"); + return false; + } + uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE); + AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, + &aes_key, iv_buffer, AES_ENCRYPT); + return true; +} + +bool SessionContext::Generic_Decrypt(const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Generic_Decrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return false; + } + const std::vector& key = current_content_key()->value(); + const KeyControlBlock& control = current_content_key()->control(); + // Set the AES key. + if (static_cast(key.size()) != AES_BLOCK_SIZE) { + LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size."); + return false; + } + if (!(control.control_bits() & kControlAllowDecrypt)) { + LOGE("[Generic_Decrypt(): control bit says not allowed."); + return false; + } + if (control.duration() > 0) { + if (control.duration() < CurrentTimer()) { + LOGE("[Generic_Decrypt(): key expired."); + return false; + } + } + if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) { + LOGE("[Generic_Decrypt(): bad algorithm."); + return false; + } + if( buffer_length % AES_BLOCK_SIZE != 0 ) { + LOGE("[Generic_Decrypt(): bad buffer size."); + return false; + } + const uint8_t* key_u8 = &key[0]; + AES_KEY aes_key; + if (AES_set_decrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[Generic_Decrypt(): FAILURE]"); + return false; + } + uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE); + AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, + &aes_key, iv_buffer, AES_DECRYPT); + return true; +} + +bool SessionContext::Generic_Sign(const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Generic_Sign(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return false; + } + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + LOGE("[Generic_Sign(): bad signature length."); + return false; + } + const std::vector& key = current_content_key()->value(); + const KeyControlBlock& control = current_content_key()->control(); + if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { + LOGE("[Generic_Sign(): CONTENT_KEY has wrong size."); + return false; + } + if (!(control.control_bits() & kControlAllowSign)) { + LOGE("[Generic_Sign(): control bit says not allowed."); + return false; + } + if (control.duration() > 0) { + if (control.duration() < CurrentTimer()) { + LOGE("[Generic_Sign(): key expired."); + return false; + } + } + if( algorithm != OEMCrypto_HMAC_SHA256 ) { + LOGE("[Generic_Sign(): bad algorithm."); + return false; + } + unsigned int md_len = *signature_length; + if (HMAC(EVP_sha256(), &key[0], SHA256_DIGEST_LENGTH, + in_buffer, buffer_length, signature, &md_len)) { + *signature_length = md_len; + return true; + } + LOGE("[Generic_Sign(): hmac failed."); + dump_openssl_error(); + return false; +} + +bool SessionContext::Generic_Verify(const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Decrypt_Verify(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return false; + } + if (signature_length < SHA256_DIGEST_LENGTH) { + return false; + } + const std::vector& key = current_content_key()->value(); + const KeyControlBlock& control = current_content_key()->control(); + if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { + LOGE("[Generic_Verify(): CONTENT_KEY has wrong size."); + return false; + } + if (!(control.control_bits() & kControlAllowVerify)) { + LOGE("[Generic_Verify(): control bit says not allowed."); + return false; + } + if (control.duration() > 0) { + if (control.duration() < CurrentTimer()) { + LOGE("[Generic_Verify(): key expired."); + return false; + } + } + if( algorithm != OEMCrypto_HMAC_SHA256 ) { + LOGE("[Generic_Verify(): bad algorithm."); + return false; + } + unsigned int md_len = signature_length; + uint8_t computed_signature[SHA256_DIGEST_LENGTH]; + if (HMAC(EVP_sha256(), &key[0], SHA256_DIGEST_LENGTH, + in_buffer, buffer_length, computed_signature, &md_len)) { + return (0 == memcmp( signature, computed_signature, SHA256_DIGEST_LENGTH)); + } + LOGE("[Generic_Verify(): HMAC failed."); + dump_openssl_error(); + return false; +} bool SessionContext::RefreshKey(const KeyId& key_id, const std::vector& key_control, @@ -582,7 +838,6 @@ bool CryptoEngine::DecryptMessage(SessionContext* session, LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return false; } - decrypted->resize(message.size()); uint8_t iv_buffer[16]; memcpy(iv_buffer, &iv[0], 16); diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index 9e9a8d7d..fc55b99a 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -20,6 +20,10 @@ #include "oemcrypto_keybox_mock.h" #include "wv_cdm_types.h" +// TODO(fredgc,gmorgan): Revisit the need to keep interface separate. +// For now, we need to include the enum OEMCrypto_Algorithm. +#include "OEMCryptoCENC.h" + namespace wvoec_mock { enum BufferType { @@ -100,15 +104,35 @@ class SessionContext { size_t message_length, uint8_t* signature, size_t* signature_length); + size_t RSASignatureSize(); bool GenerateRSASignature(const uint8_t* message, size_t message_length, uint8_t* signature, size_t* signature_length); - bool ValidateMessage(const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length); + bool Generic_Encrypt(const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); + bool Generic_Decrypt(const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); + bool Generic_Sign(const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length); + bool Generic_Verify(const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length); void StartTimer(); uint32_t CurrentTimer(); // (seconds). bool InstallKey(const KeyId& key_id, diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h index d16a7076..845706cb 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h @@ -29,6 +29,10 @@ enum KeyType { const uint32_t kControlObserveDataPath = (1<<31); const uint32_t kControlObserveHDCP = (1<<30); const uint32_t kControlObserveCGMS = (1<<29); +const uint32_t kControlAllowEncrypt = (1<<8); +const uint32_t kControlAllowDecrypt = (1<<7); +const uint32_t kControlAllowSign = (1<<6); +const uint32_t kControlAllowVerify = (1<<5); const uint32_t kControlDataPathSecure = (1<<4); const uint32_t kControlNonceEnabled = (1<<3); const uint32_t kControlHDCPRequired = (1<<2); diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index 3a0ebf99..bebeb44a 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -15,6 +15,7 @@ #include "log.h" #include "oemcrypto_engine_mock.h" #include "openssl/rand.h" +#include "openssl/sha.h" #include "wv_cdm_constants.h" namespace wvoec_mock { @@ -207,6 +208,11 @@ OEMCryptoResult OEMCrypto_GenerateSignature( return OEMCrypto_ERROR_KEYBOX_INVALID; } + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (message == NULL || message_length == 0 || signature == NULL || signature_length == 0) { LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); @@ -304,7 +310,7 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, if (!RangeCheck(message, message_length, key_array[i].key_id, key_array[i].key_id_length, false) || !RangeCheck(message, message_length, key_array[i].key_data, - wvcdm::KEY_SIZE, false) || + key_array[i].key_data_length, false) || !RangeCheck(message, message_length, key_array[i].key_data_iv, wvcdm::KEY_IV_SIZE, false) || !RangeCheck(message, message_length, key_array[i].key_control, @@ -334,7 +340,7 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, key_id.assign(key_array[i].key_id, key_array[i].key_id + key_array[i].key_id_length); enc_key_data.assign(key_array[i].key_data, - key_array[i].key_data + wvcdm::KEY_SIZE); + key_array[i].key_data + key_array[i].key_data_length); key_data_iv.assign(key_array[i].key_data_iv, key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE); if (key_array[i].key_control == NULL) { @@ -653,7 +659,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, size_t message_length, const uint8_t* signature, size_t signature_length, - uint32_t* nonce, + const uint32_t* nonce, const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, @@ -673,12 +679,12 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, } // For the reference implementation, the wrapped key and the encrypted // key are the same size -- just encrypted with different keys. - // We add 32 bytes for a context, and 32 bytes for a signature. + // We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature. // Important: This layout must match OEMCrypto_LoadDeviceRSAKey below. size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) { - LOGW("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); + LOGW("[OEMCrypto_RewrapDeviceRSAKey(): Wrapped Keybox Short Buffer]"); *wrapped_rsa_key_length = buffer_size; return OEMCrypto_ERROR_SHORT_BUFFER; } @@ -699,13 +705,13 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, } // Range check - if (!RangeCheck(message, message_length, reinterpret_cast(nonce), + if (!RangeCheck(message, message_length, reinterpret_cast(nonce), sizeof(uint32_t), true) || !RangeCheck(message, message_length, enc_rsa_key, enc_rsa_key_length, true) || !RangeCheck(message, message_length, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE, true)) { - LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]"); + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): - range check.]"); return OEMCrypto_ERROR_SIGNATURE_FAILURE; } @@ -739,13 +745,6 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - size_t sig_length = sizeof(wrapped->signature); - if (!session_ctx->GenerateSignature(wrapped->context, // start signing here. - buffer_size - sizeof(wrapped->signature), - wrapped->signature, - &sig_length)) { - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } // Encrypt rsa key with keybox. if (!session_ctx->EncryptRSAKey(wrapped->enc_rsa_key, @@ -754,8 +753,19 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } + size_t sig_length = sizeof(wrapped->signature); + if (!session_ctx->GenerateSignature(wrapped->context, // start signing here. + buffer_size - sizeof(wrapped->signature), + wrapped->signature, + &sig_length)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (trace_all_calls) { dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length); + dump_hex("signature", wrapped->signature, sizeof(wrapped->signature)); + dump_hex("context", wrapped->context, sizeof(wrapped->context)); + dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); } return OEMCrypto_SUCCESS; } @@ -764,9 +774,14 @@ extern "C" OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length) { + const WrappedRSAKey* wrapped + = reinterpret_cast(wrapped_rsa_key); if (trace_all_calls) { printf("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n"); dump_hex("wrapped_rsa_key", wrapped_rsa_key, wrapped_rsa_key_length); + dump_hex("signature", wrapped->signature, sizeof(wrapped->signature)); + dump_hex("context", wrapped->context, sizeof(wrapped->context)); + dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); } if (wrapped_rsa_key == NULL) { @@ -784,9 +799,6 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } - // Now we generate a wrapped keybox. - const WrappedRSAKey* wrapped - = reinterpret_cast(wrapped_rsa_key); const std::vector mac_ctx_str(wrapped->context, wrapped->context + sizeof(wrapped->context)); // Generate mac and encryption keys for encrypting the signature. @@ -821,8 +833,7 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, return OEMCrypto_ERROR_KEYBOX_INVALID; } - if (message == NULL || message_length == 0 || - signature == NULL || signature_length == 0) { + if (signature_length == 0) { LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } @@ -832,6 +843,19 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_NO_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } + + size_t required_size = session_ctx->RSASignatureSize(); + if (*signature_length < required_size) { + *signature_length = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (session_ctx->GenerateRSASignature(message, message_length, signature, @@ -890,4 +914,133 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( return OEMCrypto_SUCCESS; } +extern "C" +uint32_t OEMCrypto_APIVersion() { + return oec_latest_version; +} + +extern "C" +const char* OEMCrypto_SecurityLevel() { + return "L3"; +} + +extern "C" +OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (in_buffer == NULL || buffer_length == 0 || + iv == NULL || out_buffer == NULL) { + LOGE("[OEMCrypto_Generic_Enrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!session_ctx->Generic_Encrypt(in_buffer, buffer_length, iv, algorithm, + out_buffer)) { + LOGE("[OEMCrypto_Generic_Enrypt(): OEMCrypto_ERROR_UNKNOWN_FAILURE]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (!session_ctx->Generic_Decrypt(in_buffer, buffer_length, iv, algorithm, + out_buffer)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (in_buffer == NULL || buffer_length == 0 || + iv == NULL || out_buffer == NULL) { + LOGE("[OEMCrypto_Generic_Decrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length) { + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_Generic_Sign(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_Generic_Sign(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (in_buffer == NULL || buffer_length == 0 || signature == NULL) { + LOGE("[OEMCrypto_Generic_Sign(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!session_ctx->Generic_Sign(in_buffer, buffer_length, algorithm, + signature, signature_length)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length) { + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_Generic_Verify(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_Generic_Verify(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (signature_length != SHA256_DIGEST_LENGTH) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (in_buffer == NULL || buffer_length == 0 || signature == NULL) { + LOGE("[OEMCrypto_Generic_Verify(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!session_ctx->Generic_Verify(in_buffer, buffer_length, algorithm, + signature, signature_length)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + }; // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/string_conversions.cpp b/libwvdrmengine/oemcrypto/mock/src/string_conversions.cpp index 4fe9cc8a..5df30027 100644 --- a/libwvdrmengine/oemcrypto/mock/src/string_conversions.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/string_conversions.cpp @@ -38,7 +38,7 @@ std::vector a2b_hex(const std::string& byte) { unsigned char lsb = 0; // least significant 4 bits if (!CharToDigit(byte[i * 2], &msb) || !CharToDigit(byte[i * 2 + 1], &lsb)) { - LOGE("Invalid hex value %c%c at index %d", byte[i*2], byte[i*2+1], i); + LOGE("Invalid hex value %c%c at index %d", byte[i * 2], byte[i * 2 + 1], i); return array; } array.push_back((msb << 4) | lsb); diff --git a/libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp b/libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp index 79d5fe05..506315a5 100644 --- a/libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp @@ -80,7 +80,7 @@ uint32_t wvrunningcrc32(uint8_t* p_begin, int i_count, uint32_t i_crc) { /* Calculate the CRC */ while (i_count > 0) { - i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin) ]; + i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin)]; p_begin++; i_count--; } diff --git a/libwvdrmengine/oemcrypto/test/Android.mk b/libwvdrmengine/oemcrypto/test/Android.mk index 7ef20f49..9ddd4cf7 100644 --- a/libwvdrmengine/oemcrypto/test/Android.mk +++ b/libwvdrmengine/oemcrypto/test/Android.mk @@ -23,13 +23,13 @@ LOCAL_C_INCLUDES += \ LOCAL_STATIC_LIBRARIES := \ libgtest \ libgtest_main \ + libl3crypto \ LOCAL_SHARED_LIBRARIES := \ libcrypto \ libcutils \ libdl \ liblog \ - liboemcrypto \ libstlport \ libutils \ libz \ diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 0aa7ebfa..edad844d 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -5,6 +5,13 @@ // #include // TODO(fredgc): Add ntoh to wv_cdm_utilities.h #include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -14,11 +21,6 @@ #include "OEMCryptoCENC.h" #include "oemcrypto_key_mock.h" -#include "openssl/aes.h" -#include "openssl/cmac.h" -#include "openssl/hmac.h" -#include "openssl/rand.h" -#include "openssl/sha.h" #include "string_conversions.h" #include "wv_cdm_constants.h" #include "wv_keybox.h" @@ -37,7 +39,8 @@ typedef struct { const size_t kTestKeyIdLength = 12; // pick a length. any length. typedef struct { uint8_t key_id[kTestKeyIdLength]; - uint8_t key_data[wvcdm::KEY_SIZE]; + uint8_t key_data[wvcdm::MAC_KEY_SIZE]; + size_t key_data_length; uint8_t key_iv[wvcdm::KEY_IV_SIZE]; uint8_t control_iv[wvcdm::KEY_IV_SIZE]; KeyControlBlock control; @@ -50,14 +53,22 @@ struct MessageData { uint8_t mac_key[wvcdm::MAC_KEY_SIZE]; }; +const size_t kMaxTestRSAKeyLength = 2000; // Rough estimate. +struct RSAPrivateKeyMessage { + uint8_t rsa_key[kMaxTestRSAKeyLength]; + uint8_t rsa_key_iv[wvcdm::KEY_IV_SIZE]; + size_t rsa_key_length; + uint32_t nonce; +}; + const wvoec_mock::WidevineKeybox kDefaultKeybox = { // Sample keybox used for test vectors { // deviceID - 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 - 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ }, { // key 0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e, @@ -82,10 +93,664 @@ const wvoec_mock::WidevineKeybox kDefaultKeybox = { } }; +static const uint8_t kTestRsaPrivateKey1_3072[] = { + 0x30, 0x82, 0x06, 0xe3, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x81, 0x00, 0xa5, 0x62, 0x07, 0xdf, + 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, + 0x78, 0x76, 0xbe, 0x13, 0x3b, 0xe6, 0x2c, 0x09, + 0x9d, 0x35, 0x3f, 0xf3, 0x0f, 0xe9, 0x61, 0x96, + 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, + 0xca, 0xe4, 0xdd, 0xd5, 0x96, 0xaf, 0x9a, 0xd7, + 0x08, 0x47, 0xe4, 0x55, 0x1b, 0x83, 0xbe, 0x10, + 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, + 0x46, 0xc2, 0x65, 0x97, 0xa6, 0xcc, 0x4b, 0xa4, + 0x08, 0xc3, 0x04, 0x17, 0x01, 0xb5, 0x11, 0x53, + 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, + 0x5c, 0xb4, 0x7a, 0x1d, 0x5d, 0x6c, 0x58, 0xc2, + 0x82, 0xa0, 0x92, 0xf1, 0x14, 0xf1, 0x22, 0xff, + 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, + 0xcd, 0xa0, 0x0a, 0x63, 0x08, 0xdd, 0x60, 0x5d, + 0xfd, 0xa4, 0x01, 0xe3, 0xb6, 0x0e, 0x85, 0xe4, + 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, + 0xde, 0xf2, 0x59, 0x11, 0xe3, 0x5b, 0x02, 0x9f, + 0x24, 0xb9, 0xb0, 0xbb, 0x31, 0xa0, 0xee, 0x6a, + 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, + 0x3a, 0xae, 0xb2, 0x2e, 0x84, 0xa0, 0x47, 0x42, + 0x51, 0xbb, 0xfa, 0xbb, 0x90, 0x97, 0x2c, 0x77, + 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, + 0x49, 0x94, 0x53, 0x5d, 0x37, 0xaf, 0x86, 0x47, + 0xda, 0xe2, 0xbd, 0xf0, 0x5f, 0x07, 0x53, 0x8a, + 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, + 0xda, 0xea, 0x1e, 0x2e, 0x54, 0xec, 0x44, 0xde, + 0x3a, 0xe1, 0xc8, 0xdb, 0x17, 0xe8, 0xc9, 0x3a, + 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, + 0x30, 0xb7, 0xf5, 0x1b, 0x03, 0x86, 0x21, 0xa9, + 0xf5, 0xca, 0x15, 0x26, 0xaf, 0x39, 0xf3, 0x5d, + 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, + 0x16, 0x9c, 0xee, 0xc3, 0xbd, 0xcc, 0xdb, 0x02, + 0x82, 0xd0, 0x60, 0x0b, 0x42, 0x72, 0x85, 0xec, + 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, + 0xf9, 0xa6, 0x82, 0x40, 0xd3, 0xc5, 0xc9, 0xf9, + 0x6b, 0xc9, 0x12, 0x64, 0xe4, 0x3a, 0x3b, 0xc9, + 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, + 0x4a, 0xe9, 0x4c, 0x46, 0x2b, 0xb6, 0xe1, 0xed, + 0x82, 0xb2, 0xf0, 0xd1, 0x72, 0x71, 0x04, 0x35, + 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, + 0xde, 0x8f, 0xe1, 0xc1, 0x49, 0x68, 0x0c, 0xc8, + 0xce, 0x6d, 0x87, 0x50, 0x04, 0xb5, 0xd7, 0x24, + 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, + 0x1b, 0x38, 0xff, 0x2f, 0x71, 0xf5, 0x30, 0x18, + 0x1e, 0x6f, 0xd7, 0xf0, 0x33, 0x61, 0x53, 0x7e, + 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, + 0x67, 0xd5, 0xf0, 0x2e, 0x23, 0x23, 0x60, 0x0b, + 0x83, 0x9c, 0xc2, 0x87, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x80, 0x5a, 0x09, 0x3f, + 0x9e, 0x2e, 0x4d, 0x26, 0x50, 0x7b, 0x70, 0x21, + 0xb0, 0x0c, 0x25, 0x21, 0x1f, 0xd9, 0x89, 0x5a, + 0xca, 0x35, 0x23, 0x0b, 0x58, 0xa9, 0x7d, 0xf6, + 0x19, 0xc4, 0x29, 0x87, 0xc7, 0xd4, 0x94, 0x85, + 0xb4, 0x2c, 0xaf, 0x62, 0xb1, 0xe8, 0x62, 0x5b, + 0xda, 0xdb, 0x70, 0x40, 0x37, 0xb1, 0x4e, 0x0c, + 0xc8, 0x62, 0xee, 0xa2, 0xfc, 0x3c, 0xd2, 0x39, + 0x90, 0x15, 0x2c, 0xba, 0x20, 0x50, 0xb7, 0x82, + 0x2a, 0xa0, 0x76, 0x83, 0x20, 0x7f, 0x56, 0x73, + 0x43, 0x8a, 0x9b, 0xa7, 0x6c, 0x63, 0xb6, 0xad, + 0x56, 0xb2, 0x8a, 0xb2, 0xbc, 0x8f, 0xe2, 0xef, + 0x83, 0x9d, 0x98, 0x0b, 0xc7, 0x62, 0x0e, 0x51, + 0x6e, 0x57, 0x1d, 0x1b, 0x0e, 0x3a, 0xea, 0x3b, + 0x76, 0x63, 0x35, 0xd0, 0xd1, 0xcf, 0xbe, 0xad, + 0xbb, 0x1d, 0xde, 0x0f, 0x05, 0x48, 0x55, 0x29, + 0xc1, 0xbc, 0x21, 0xc7, 0x87, 0xf2, 0x75, 0x12, + 0x7d, 0x92, 0x9e, 0xbf, 0xad, 0x04, 0x68, 0xc4, + 0xc9, 0x9d, 0x35, 0xd6, 0xa8, 0x62, 0xc1, 0x69, + 0x6a, 0xb6, 0x41, 0xb7, 0x37, 0x66, 0xdf, 0xb2, + 0xb9, 0x8c, 0x8b, 0x15, 0x08, 0x4c, 0x3d, 0xf1, + 0xed, 0x82, 0x0f, 0xe3, 0xd5, 0xff, 0x46, 0xbd, + 0xf7, 0x85, 0x43, 0xc0, 0x8b, 0xba, 0x47, 0xf1, + 0x41, 0x57, 0xc3, 0x7f, 0x8b, 0x0d, 0x48, 0xea, + 0xc2, 0xed, 0xc0, 0x69, 0x84, 0xb6, 0x32, 0x08, + 0x49, 0x74, 0x14, 0x84, 0xa4, 0x1b, 0x48, 0x5b, + 0xec, 0xd3, 0x0b, 0x12, 0x2b, 0x4c, 0x9e, 0x5c, + 0x01, 0x60, 0xad, 0xef, 0xcb, 0x2b, 0x56, 0x84, + 0x07, 0xfa, 0x62, 0xc6, 0x08, 0x92, 0x98, 0x70, + 0xc9, 0x5b, 0x18, 0xc8, 0xfa, 0x27, 0x0c, 0xe2, + 0xbd, 0xfb, 0x3e, 0x43, 0xa5, 0xb7, 0x06, 0x2c, + 0x4e, 0xf1, 0x07, 0x5d, 0x8d, 0xdd, 0x53, 0xc5, + 0x8c, 0x4a, 0xf2, 0x2f, 0x8e, 0x80, 0x96, 0x16, + 0xc0, 0xfc, 0xf9, 0x20, 0x4f, 0x35, 0xc7, 0x53, + 0x8b, 0x2d, 0x37, 0x43, 0x93, 0x3d, 0x74, 0x3f, + 0x63, 0xf7, 0x0b, 0xbd, 0x46, 0xe4, 0x51, 0x67, + 0x33, 0x57, 0x15, 0xf5, 0x59, 0x27, 0x66, 0xe8, + 0xe2, 0x4b, 0xa3, 0x93, 0x03, 0x8a, 0x9c, 0x05, + 0x13, 0xf2, 0xcb, 0xf7, 0x9c, 0x68, 0xe7, 0x16, + 0x4b, 0x8e, 0x59, 0x71, 0x2b, 0x73, 0x9b, 0xb9, + 0xae, 0x50, 0xfa, 0xd7, 0xd3, 0x34, 0x17, 0x1d, + 0x62, 0x88, 0xbd, 0x8c, 0xba, 0x5a, 0x6b, 0x6a, + 0x5e, 0xb3, 0xa5, 0x80, 0xca, 0xbb, 0xb9, 0xb5, + 0xa8, 0x2e, 0xb1, 0x61, 0x6e, 0xd5, 0xd6, 0x62, + 0x98, 0x4a, 0xb0, 0xb8, 0x76, 0xa9, 0x19, 0x5c, + 0xe2, 0xbe, 0xb3, 0x9b, 0x4a, 0x39, 0xf5, 0xe6, + 0xbb, 0x11, 0x6e, 0x13, 0x13, 0x38, 0xb8, 0x1f, + 0x21, 0x19, 0xf5, 0xa7, 0x76, 0x93, 0xb3, 0x56, + 0xfa, 0xcc, 0x74, 0xbc, 0x19, 0x02, 0x81, 0xc1, + 0x00, 0xd1, 0xd1, 0x72, 0x57, 0xe5, 0xb0, 0x1c, + 0x09, 0x05, 0xbb, 0x55, 0x89, 0x3c, 0x4a, 0x81, + 0x90, 0x9a, 0xf9, 0x32, 0x63, 0x41, 0xad, 0x6a, + 0x5f, 0x65, 0x94, 0x92, 0xcc, 0xf7, 0xc7, 0x53, + 0x93, 0xa0, 0xf7, 0xbe, 0x48, 0x82, 0x63, 0x31, + 0x7b, 0xd0, 0x82, 0x09, 0xbb, 0x0a, 0xbc, 0x60, + 0xc9, 0x4d, 0x83, 0xe4, 0x5d, 0x50, 0xe6, 0x5f, + 0x8b, 0x47, 0x07, 0xa3, 0x3a, 0x36, 0x97, 0xaa, + 0x21, 0x70, 0x7f, 0xd5, 0x6c, 0xb0, 0x56, 0xf5, + 0x5c, 0x48, 0x74, 0x2a, 0xdd, 0xfe, 0x94, 0x83, + 0x05, 0xe0, 0x3d, 0x5d, 0xdd, 0x5a, 0x05, 0xcb, + 0x47, 0xd7, 0xf9, 0x89, 0x55, 0xaa, 0x0b, 0x21, + 0xc0, 0x71, 0x5d, 0xe1, 0x4c, 0x6a, 0x45, 0x86, + 0x86, 0xf2, 0xb9, 0x38, 0x6a, 0x56, 0x51, 0x0d, + 0x7d, 0xac, 0x30, 0x31, 0xca, 0x2d, 0xaa, 0xaa, + 0xba, 0xcc, 0x12, 0x40, 0xc1, 0x0d, 0xa6, 0xc1, + 0x7d, 0x22, 0xec, 0xb6, 0x51, 0x45, 0xfe, 0x4e, + 0xbb, 0x4a, 0xd2, 0xba, 0x9b, 0xa2, 0xcc, 0x28, + 0x2b, 0x01, 0x53, 0x53, 0xf3, 0xa9, 0x5a, 0x8f, + 0xeb, 0xb7, 0xb8, 0x62, 0x6b, 0x8a, 0x79, 0x24, + 0xcc, 0x86, 0x34, 0x45, 0xe2, 0xad, 0x1d, 0xd0, + 0x4c, 0xc9, 0x77, 0x2a, 0xf9, 0x1a, 0xe8, 0x58, + 0x78, 0x51, 0x8a, 0xea, 0x3f, 0x90, 0x36, 0x46, + 0x2a, 0xc0, 0x71, 0x41, 0x83, 0x2c, 0x48, 0xee, + 0xc5, 0x02, 0x81, 0xc1, 0x00, 0xc9, 0xc8, 0xce, + 0xc4, 0x50, 0xb2, 0x26, 0xcb, 0x35, 0x78, 0x55, + 0x3c, 0xcc, 0xf0, 0x7e, 0xba, 0xad, 0xeb, 0x58, + 0xe9, 0xb5, 0x78, 0x2f, 0x43, 0x5f, 0x07, 0x47, + 0x56, 0x05, 0x41, 0x38, 0x71, 0xe1, 0x58, 0x62, + 0xb1, 0x8e, 0xbc, 0xf9, 0x80, 0x04, 0x22, 0x39, + 0x22, 0x24, 0x28, 0x86, 0x9c, 0x00, 0x44, 0x5f, + 0xc4, 0x97, 0xe6, 0x71, 0x5f, 0x1f, 0x58, 0xea, + 0x75, 0x18, 0x0c, 0x23, 0x63, 0x09, 0xc5, 0x98, + 0xc4, 0x6d, 0x23, 0xc2, 0x2c, 0x93, 0x6a, 0x26, + 0xe4, 0x3d, 0x8d, 0xa1, 0x39, 0x70, 0x34, 0x25, + 0xcd, 0xbc, 0x82, 0x78, 0x2b, 0xf3, 0x7e, 0x81, + 0xb6, 0x5f, 0xc5, 0x69, 0xd0, 0x81, 0x69, 0x50, + 0x2f, 0x17, 0x0c, 0x17, 0x3c, 0x0b, 0x45, 0x38, + 0xce, 0xe3, 0xbf, 0x8a, 0x50, 0x0a, 0x00, 0x74, + 0x7e, 0x7a, 0xd8, 0x55, 0x52, 0x6b, 0x82, 0xfb, + 0x34, 0x15, 0x73, 0x6a, 0xf4, 0x51, 0x9b, 0x9f, + 0xa0, 0x45, 0xb9, 0x76, 0xe5, 0xd3, 0xd5, 0xf4, + 0xa9, 0xa4, 0xcd, 0x42, 0x2f, 0x29, 0x89, 0xec, + 0x28, 0x5f, 0x03, 0x45, 0x27, 0xaf, 0x8c, 0x39, + 0x3e, 0x59, 0x9d, 0xaf, 0x27, 0x5d, 0x17, 0x53, + 0x17, 0xeb, 0x8d, 0x7f, 0x3d, 0xb8, 0x2a, 0x50, + 0x1e, 0xb5, 0xc5, 0x04, 0xab, 0x9c, 0xa7, 0xaa, + 0x86, 0x41, 0xb9, 0x36, 0x29, 0x9e, 0xd2, 0xd8, + 0xde, 0x5f, 0xde, 0x80, 0xdb, 0x02, 0x81, 0xc0, + 0x03, 0xf3, 0x5f, 0xa5, 0xcc, 0x0b, 0x5e, 0xdb, + 0xc4, 0xa1, 0xdc, 0x60, 0x73, 0x24, 0x2c, 0x00, + 0x5f, 0x0a, 0xa6, 0x2a, 0x3c, 0x48, 0x59, 0xa2, + 0x66, 0x35, 0x3f, 0xf6, 0x60, 0x0b, 0xfe, 0xc4, + 0xde, 0xd9, 0x0b, 0x5a, 0x2e, 0x2a, 0x53, 0xfa, + 0x32, 0xd8, 0xdf, 0xfa, 0x07, 0x9f, 0xb8, 0x6a, + 0xd1, 0xec, 0xd3, 0xd5, 0xf5, 0xfa, 0x00, 0x7e, + 0x8c, 0xdd, 0xd5, 0xf2, 0xf8, 0xa8, 0x2e, 0x69, + 0xe6, 0xc6, 0x61, 0x6c, 0x64, 0x7d, 0x9e, 0xad, + 0x18, 0x28, 0x27, 0xce, 0x7a, 0x46, 0xad, 0x98, + 0xe4, 0xba, 0x03, 0x14, 0x71, 0xe7, 0x7e, 0x06, + 0x62, 0x48, 0xae, 0x8f, 0x50, 0x5e, 0x59, 0x4a, + 0x58, 0x58, 0x1e, 0x2f, 0xe4, 0x28, 0x5e, 0xfa, + 0x17, 0x83, 0xe9, 0x4e, 0x07, 0x46, 0x0b, 0x6c, + 0xfc, 0x5b, 0x03, 0xf4, 0xfc, 0x9b, 0x24, 0x0f, + 0xd4, 0x5b, 0xdb, 0xa0, 0x46, 0xf3, 0x86, 0xdd, + 0x26, 0x55, 0x32, 0xb1, 0xa1, 0x11, 0xc2, 0xc5, + 0xc0, 0x08, 0xeb, 0xbe, 0x96, 0x78, 0x25, 0xa1, + 0x79, 0xaa, 0xe9, 0xff, 0xc2, 0x86, 0x94, 0x03, + 0x2a, 0x38, 0x6c, 0x91, 0xfd, 0xcf, 0x7e, 0x23, + 0xe3, 0xbb, 0x04, 0x3d, 0xda, 0x68, 0x9f, 0x4d, + 0x72, 0xd5, 0xad, 0x97, 0x77, 0x2c, 0x3c, 0xce, + 0x37, 0x2a, 0xd8, 0x72, 0x4d, 0xf2, 0xd7, 0xab, + 0x62, 0x68, 0x3f, 0x85, 0x8a, 0xc5, 0xec, 0xc9, + 0x02, 0x81, 0xc1, 0x00, 0x92, 0x43, 0x0c, 0x1d, + 0x20, 0xa1, 0x01, 0x9d, 0xaa, 0x54, 0x5e, 0xf4, + 0x83, 0x58, 0x8f, 0x83, 0xa1, 0x2d, 0x46, 0x75, + 0xa1, 0x24, 0x4c, 0x9d, 0xf8, 0xf3, 0xbd, 0xb1, + 0x8c, 0x7d, 0x89, 0xfc, 0x81, 0xeb, 0x1f, 0x1e, + 0xb4, 0xe8, 0x25, 0xb1, 0xb5, 0x4d, 0x59, 0x3c, + 0x76, 0x19, 0x29, 0xf9, 0x49, 0xf8, 0x45, 0xb2, + 0xaa, 0xa8, 0x4e, 0xe5, 0x34, 0x43, 0xaf, 0x2e, + 0xd1, 0x0f, 0x7b, 0x56, 0xfe, 0x6e, 0x4c, 0x1d, + 0x95, 0x3e, 0xa6, 0x30, 0xc9, 0x69, 0xd8, 0x66, + 0xf8, 0x77, 0x00, 0xb6, 0x31, 0xae, 0x9a, 0xf8, + 0x55, 0xfb, 0xfc, 0x3f, 0x5f, 0x70, 0x03, 0x75, + 0xbe, 0x55, 0xca, 0x2d, 0x68, 0xa0, 0x7d, 0x8e, + 0xa4, 0x96, 0x0f, 0x01, 0x66, 0xe9, 0xf6, 0x13, + 0x80, 0xe2, 0x05, 0xcf, 0x9e, 0x70, 0x56, 0x00, + 0x97, 0xea, 0xd7, 0x6d, 0xb6, 0xa0, 0x6a, 0x95, + 0x86, 0x36, 0xf2, 0xff, 0xc5, 0x67, 0x98, 0x7d, + 0x04, 0x0d, 0x3b, 0x31, 0xbc, 0x2b, 0x09, 0xfd, + 0x2d, 0x87, 0xda, 0xc1, 0x74, 0xca, 0x94, 0x73, + 0x6e, 0xeb, 0x5f, 0xe5, 0x34, 0x49, 0xdf, 0xf4, + 0x61, 0xe0, 0xfa, 0x64, 0xfe, 0x05, 0x3a, 0x25, + 0xcc, 0x87, 0xf4, 0x03, 0x38, 0xca, 0xf2, 0xe8, + 0x4f, 0xb9, 0x4f, 0x79, 0x55, 0x43, 0xf3, 0x46, + 0xfd, 0xbc, 0xd2, 0x95, 0xb8, 0x99, 0xfc, 0xb8, + 0xb3, 0xa5, 0x04, 0xa1, 0x02, 0x81, 0xc0, 0x47, + 0xc6, 0x9c, 0x18, 0x54, 0xe5, 0xbb, 0xf9, 0xf4, + 0x38, 0xd2, 0xc0, 0xd1, 0x1a, 0xcc, 0xdb, 0x06, + 0x87, 0x75, 0x1f, 0x13, 0xa2, 0x7f, 0x8b, 0x45, + 0x54, 0xcb, 0x43, 0xf8, 0xbb, 0x94, 0xd6, 0x2e, + 0x56, 0x5c, 0x69, 0x6d, 0x83, 0xb5, 0x45, 0x46, + 0x68, 0x5c, 0x76, 0x1e, 0x6c, 0x0c, 0x53, 0x59, + 0xcc, 0x19, 0xc7, 0x81, 0x62, 0x66, 0x92, 0x02, + 0x8f, 0xa6, 0xdb, 0x50, 0x1c, 0x67, 0xfc, 0x82, + 0x56, 0x2b, 0x4b, 0x1f, 0x97, 0x87, 0xc4, 0x7d, + 0x20, 0xda, 0xd3, 0x3f, 0x28, 0xf9, 0x55, 0xfe, + 0x84, 0x50, 0xc5, 0x3b, 0xd4, 0xaf, 0xf5, 0x3d, + 0x43, 0xce, 0xdc, 0x55, 0x11, 0x87, 0xdb, 0x72, + 0x66, 0xcc, 0x83, 0xc4, 0x8b, 0x20, 0xae, 0x59, + 0x4d, 0xeb, 0xac, 0xb5, 0x4a, 0xec, 0x66, 0x09, + 0x37, 0x55, 0x14, 0x21, 0x57, 0xff, 0x0a, 0xac, + 0xda, 0xb1, 0xae, 0x31, 0xab, 0x41, 0x30, 0x65, + 0x02, 0x83, 0xd1, 0xdb, 0x65, 0xb7, 0x52, 0xa7, + 0x21, 0x9f, 0x1f, 0x8f, 0x69, 0x23, 0x3b, 0xb8, + 0xf9, 0x6d, 0xe7, 0xc1, 0x53, 0x9f, 0x8f, 0x67, + 0xfc, 0x6e, 0x20, 0x18, 0x31, 0x89, 0xe7, 0xbb, + 0xd4, 0xc1, 0x03, 0x67, 0xd6, 0xa5, 0x76, 0xc9, + 0xea, 0x97, 0x93, 0x02, 0xca, 0x44, 0x52, 0x55, + 0x0f, 0xed, 0x55, 0xb5, 0x49, 0xd6, 0x94, 0x59, + 0xee, 0xcc, 0x1b, 0x5a, 0x00, 0x3d, 0xcd }; + +static const uint8_t kTestRsaPublicKey1_3072[] = { + 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, + 0x00, 0xa5, 0x62, 0x07, 0xdf, 0xc8, 0x84, 0x74, + 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, 0x76, 0xbe, + 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, + 0xf3, 0x0f, 0xe9, 0x61, 0x96, 0x20, 0x53, 0x6e, + 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, 0xe4, 0xdd, + 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, + 0x55, 0x1b, 0x83, 0xbe, 0x10, 0x66, 0x74, 0x08, + 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, 0xc2, 0x65, + 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, + 0x17, 0x01, 0xb5, 0x11, 0x53, 0xe9, 0x68, 0x34, + 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, 0xb4, 0x7a, + 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, + 0xf1, 0x14, 0xf1, 0x22, 0xff, 0x64, 0xde, 0xdf, + 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, 0xa0, 0x0a, + 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, + 0xe3, 0xb6, 0x0e, 0x85, 0xe4, 0xc3, 0x37, 0x61, + 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, 0xf2, 0x59, + 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, + 0xbb, 0x31, 0xa0, 0xee, 0x6a, 0x2c, 0xb4, 0x30, + 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, 0xae, 0xb2, + 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, + 0xbb, 0x90, 0x97, 0x2c, 0x77, 0x45, 0xee, 0x2c, + 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, 0x94, 0x53, + 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, + 0xf0, 0x5f, 0x07, 0x53, 0x8a, 0x10, 0xd0, 0x9a, + 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, 0xea, 0x1e, + 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, + 0xdb, 0x17, 0xe8, 0xc9, 0x3a, 0x81, 0x11, 0x4d, + 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, 0xb7, 0xf5, + 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, + 0x26, 0xaf, 0x39, 0xf3, 0x5d, 0x01, 0x7d, 0xe3, + 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, 0x9c, 0xee, + 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, + 0x0b, 0x42, 0x72, 0x85, 0xec, 0xdc, 0x41, 0x7c, + 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, 0xa6, 0x82, + 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, + 0x64, 0xe4, 0x3a, 0x3b, 0xc9, 0x8f, 0x3c, 0xd0, + 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, 0xe9, 0x4c, + 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, + 0xd1, 0x72, 0x71, 0x04, 0x35, 0x19, 0xc1, 0x16, + 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, 0x8f, 0xe1, + 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, + 0x50, 0x04, 0xb5, 0xd7, 0x24, 0xf4, 0x2e, 0x0c, + 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, 0x38, 0xff, + 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, + 0xf0, 0x33, 0x61, 0x53, 0x7e, 0x55, 0x7f, 0x0d, + 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, 0xd5, 0xf0, + 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, + 0x87, 0x02, 0x03, 0x01, 0x00, 0x01 }; + +static const uint8_t kTestRsaPrivateKey2_2048[] = { + 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, + 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, 0x40, 0xb4, + 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, + 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, + 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, 0x56, 0x7e, + 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, + 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, + 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, 0x34, 0xf7, + 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, + 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, + 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, 0x3e, 0x68, + 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, + 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, + 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, + 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, + 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, + 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, 0xb3, 0x4e, + 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, + 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, + 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, 0x96, 0x88, + 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, + 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, + 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, 0x4c, 0xc8, + 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, + 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, + 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, 0xca, 0x2e, + 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, + 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, + 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, 0x27, 0x29, + 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, + 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, + 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, + 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x5e, 0x79, 0x65, + 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, 0x45, 0x0f, + 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, 0xd5, 0xde, + 0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97, 0xeb, 0x3f, + 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, 0x3b, 0x5c, + 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, 0x46, 0xf5, + 0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc, 0x45, 0x38, + 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, 0x5e, 0x79, + 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, 0xc1, 0x6b, + 0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9, 0x0a, 0xfc, + 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, 0x68, 0x7b, + 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, 0xa7, 0x79, + 0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7, 0x00, 0x21, + 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, 0xbe, 0x09, + 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, 0x0e, 0x97, + 0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88, 0x7a, 0xd1, + 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, 0x25, 0x0b, + 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, 0xb6, 0xda, + 0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14, 0x46, 0xc7, + 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, 0xde, 0x00, + 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, 0x68, 0x71, + 0x17, 0x66, 0x12, 0x1a, 0x87, 0x27, 0xf7, 0xef, + 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, 0x6f, 0x35, + 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, 0x13, 0x86, + 0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64, 0xe8, 0x86, + 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, 0x1f, 0x51, + 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, 0x70, 0x98, + 0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f, 0x45, 0x6a, + 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, 0xf6, 0x06, + 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, 0x96, 0xa9, + 0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21, 0xe0, 0xac, + 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, 0xb2, 0x51, + 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, 0x81, 0x81, + 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, 0x45, 0x2d, + 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, 0x78, 0xcc, + 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, 0x64, 0x60, + 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, 0xb6, 0x7d, + 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, 0x84, 0x68, + 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, 0xde, 0x51, + 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, 0xe6, 0x4d, + 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, 0x3a, 0x83, + 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, 0x93, 0xd7, + 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, 0x9e, 0xf7, + 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, 0x18, 0x6c, + 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, 0x90, 0x8f, + 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, 0x06, 0x74, + 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, 0x3c, 0x2d, + 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, 0x33, 0x85, + 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, 0xf8, 0xcd, + 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, 0x15, 0x43, + 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, 0xc3, 0x41, + 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, 0x23, 0x87, + 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, 0xcb, 0x43, + 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, 0xbb, 0xdb, + 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, 0x6d, 0x61, + 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, 0xa7, 0x5b, + 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, 0x91, 0x99, + 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, 0xd8, 0x3e, + 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, 0xc1, 0xee, + 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, 0x58, 0xf4, + 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, 0x46, 0xcd, + 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, 0x0d, 0x6c, + 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, 0xda, 0xfb, + 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, 0xab, 0x54, + 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, 0x53, 0xa8, + 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, 0x81, 0x80, + 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, 0xff, 0x73, + 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, 0x0a, 0x2d, + 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, 0xf3, 0x90, + 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, 0x79, 0xa0, + 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, 0xeb, 0xdb, + 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, 0xb8, 0xe1, + 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, 0x33, 0xb2, + 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, 0x37, 0x43, + 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, 0x20, 0x82, + 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, 0x0f, 0x62, + 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, 0xf9, 0xca, + 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, 0xc8, 0x28, + 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, 0x02, 0x71, + 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, 0xb1, 0x31, + 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, 0x35, 0x95, + 0x41, 0x29, 0x40, 0x19, 0x83, 0x35, 0x24, 0x69, + 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, 0xcc, 0x3b, + 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, 0x50, 0x76, + 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, 0xcb, 0x47, + 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, 0x6a, 0xab, + 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, 0x8a, 0xa2, + 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, 0xa8, 0xcd, + 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, 0x5f, 0xdf, + 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, 0xe4, 0x8b, + 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, 0x4b, 0x81, + 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, 0x77, 0xe6, + 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, 0x6c, 0xfa, + 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, 0x3b, 0x70, + 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, 0xd9, 0xbe, + 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, 0x66, 0xff, + 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, 0x6f, 0x44, + 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, 0xac, 0x21, + 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, 0x2c, 0x7c, + 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, 0xe7, 0xa6, + 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, 0x28, 0x66, + 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, 0xae, 0xb7, + 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, 0x81, 0x7b, + 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, 0x70, 0xcf, + 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, 0x75, 0x61, + 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, 0xf0, 0xad, + 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, 0x50, 0xcb, + 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, 0xd3, 0x29, + 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, 0x40, 0xda, + 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, 0xa2, 0x0d, + 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, 0xf4, 0xd4, + 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, 0x47, 0xb2, + 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, 0x72, 0x2c, + 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe, + 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 }; + +static const uint8_t kTestRsaPublicKey2_2048[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, + 0x54, 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, + 0x58, 0x11, 0x4f, 0x94, 0x58, 0xdd, 0xde, 0xa7, + 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, + 0x57, 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, + 0x59, 0x34, 0x9a, 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, + 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, + 0x4e, 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, + 0x10, 0x47, 0x4f, 0x28, 0xda, 0x3f, 0xce, 0x31, + 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, + 0xf9, 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, + 0x64, 0x4c, 0xf3, 0x29, 0xf2, 0x73, 0x9e, 0x39, + 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, + 0x8e, 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, + 0xca, 0xb6, 0x04, 0xcd, 0x9a, 0x13, 0x8b, 0x54, + 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, + 0x67, 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, + 0xa8, 0x4a, 0x67, 0x98, 0x56, 0x57, 0x54, 0x71, + 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, + 0x8b, 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, + 0x2a, 0xbc, 0x53, 0xc9, 0x83, 0x06, 0x51, 0x5a, + 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, + 0xf1, 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, + 0xae, 0x08, 0x5e, 0x2d, 0x5f, 0xf8, 0x12, 0x7f, + 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, + 0x40, 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, + 0xdd, 0x76, 0x87, 0x82, 0x46, 0x0b, 0x3a, 0x77, + 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, + 0x86, 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, + 0x62, 0xef, 0x44, 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, + 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, + 0x6b, 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, + 0x05, 0x02, 0x03, 0x01, 0x00, 0x01 }; + +static const uint8_t kTestRsaPrivateKey3_2048[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e, + 0x0e, 0x2d, 0xfb, 0x43, 0x51, 0x99, 0xea, 0x40, + 0x1e, 0x2d, 0x89, 0xe4, 0xa2, 0x3e, 0xfc, 0x51, + 0x3d, 0x0e, 0x83, 0xa7, 0xe0, 0xa5, 0x41, 0x04, + 0x1e, 0x14, 0xc5, 0xa7, 0x5c, 0x61, 0x36, 0x44, + 0xb3, 0x08, 0x05, 0x5b, 0x14, 0xde, 0x01, 0x0c, + 0x32, 0x3c, 0x9a, 0x91, 0x00, 0x50, 0xa8, 0x1d, + 0xcc, 0x9f, 0x8f, 0x35, 0xb7, 0xc2, 0x75, 0x08, + 0x32, 0x8b, 0x10, 0x3a, 0x86, 0xf9, 0xd7, 0x78, + 0xa3, 0x9d, 0x74, 0x10, 0xc6, 0x24, 0xb1, 0x7f, + 0xa5, 0xbf, 0x5f, 0xc2, 0xd7, 0x15, 0xa3, 0x1d, + 0xe0, 0x15, 0x6b, 0x1b, 0x0e, 0x38, 0xba, 0x34, + 0xbc, 0x95, 0x47, 0x94, 0x40, 0x70, 0xac, 0x99, + 0x1f, 0x0b, 0x8e, 0x56, 0x93, 0x36, 0x2b, 0x6d, + 0x04, 0xe7, 0x95, 0x1a, 0x37, 0xda, 0x16, 0x57, + 0x99, 0xee, 0x03, 0x68, 0x16, 0x31, 0xaa, 0xc3, + 0xb7, 0x92, 0x75, 0x53, 0xfc, 0xf6, 0x20, 0x55, + 0x44, 0xf8, 0xd4, 0x8d, 0x78, 0x15, 0xc7, 0x1a, + 0xb6, 0xde, 0x6c, 0xe8, 0x49, 0x5d, 0xaf, 0xa8, + 0x4e, 0x6f, 0x7c, 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, + 0x8c, 0x8f, 0x0b, 0x5d, 0x3a, 0x09, 0xd6, 0xb3, + 0x44, 0xab, 0xe0, 0x35, 0x52, 0x7c, 0x66, 0x85, + 0xa4, 0x40, 0xd7, 0x20, 0xec, 0x24, 0x05, 0x06, + 0xd9, 0x84, 0x51, 0x5a, 0xd2, 0x38, 0xd5, 0x1d, + 0xea, 0x70, 0x2a, 0x21, 0xe6, 0x82, 0xfd, 0xa4, + 0x46, 0x1c, 0x4f, 0x59, 0x6e, 0x29, 0x3d, 0xae, + 0xb8, 0x8e, 0xee, 0x77, 0x1f, 0x15, 0x33, 0xcf, + 0x94, 0x1d, 0x87, 0x3c, 0x37, 0xc5, 0x89, 0xe8, + 0x7d, 0x85, 0xb3, 0xbc, 0xe8, 0x62, 0x6a, 0x84, + 0x7f, 0xfe, 0x9a, 0x85, 0x3f, 0x39, 0xe8, 0xaa, + 0x16, 0xa6, 0x8f, 0x87, 0x7f, 0xcb, 0xc1, 0xd6, + 0xf2, 0xec, 0x2b, 0xa7, 0xdd, 0x49, 0x98, 0x7b, + 0x6f, 0xdd, 0x69, 0x6d, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x43, 0x8f, 0x19, + 0x83, 0xb1, 0x27, 0x4e, 0xee, 0x98, 0xba, 0xcb, + 0x54, 0xa0, 0x77, 0x11, 0x6d, 0xd4, 0x25, 0x31, + 0x8c, 0xb0, 0x01, 0xcf, 0xe6, 0x80, 0x83, 0x14, + 0x40, 0x67, 0x39, 0x33, 0x67, 0x03, 0x1e, 0xa0, + 0x8b, 0xd1, 0x1d, 0xfd, 0x80, 0xa4, 0xb9, 0xe7, + 0x57, 0x5e, 0xc8, 0x8e, 0x79, 0x71, 0xd5, 0x6b, + 0x09, 0xe9, 0x2b, 0x41, 0xa0, 0x33, 0x64, 0xc9, + 0x66, 0x33, 0xa1, 0xb1, 0x55, 0x07, 0x55, 0x98, + 0x53, 0x10, 0xe6, 0xc0, 0x39, 0x6d, 0x61, 0xd9, + 0xe8, 0x16, 0x52, 0x28, 0xe4, 0x2b, 0xda, 0x27, + 0x01, 0xaf, 0x21, 0x4a, 0xe8, 0x55, 0x1d, 0x0b, + 0xd1, 0x1c, 0xdc, 0xfd, 0xb3, 0x0b, 0xa6, 0x5c, + 0xcc, 0x6e, 0x77, 0xb8, 0xe0, 0xd1, 0x4e, 0x0a, + 0xd7, 0x7a, 0x5e, 0x18, 0xc3, 0xfb, 0xe9, 0xa1, + 0x9c, 0xc3, 0x9c, 0xd4, 0x4a, 0x7e, 0x70, 0x72, + 0x11, 0x18, 0x24, 0x56, 0x24, 0xdf, 0xf8, 0xba, + 0xac, 0x5b, 0x54, 0xd3, 0xc4, 0x65, 0x69, 0xc8, + 0x79, 0x94, 0x16, 0x88, 0x9a, 0x68, 0x1c, 0xbc, + 0xd4, 0xca, 0xec, 0x5e, 0x07, 0x4a, 0xc9, 0x54, + 0x7a, 0x4b, 0xdb, 0x19, 0x88, 0xf6, 0xbe, 0x50, + 0x9d, 0x9e, 0x9d, 0x88, 0x5b, 0x4a, 0x23, 0x86, + 0x2b, 0xa9, 0xa6, 0x6c, 0x70, 0x7d, 0xe1, 0x11, + 0xba, 0xbf, 0x03, 0x2e, 0xf1, 0x46, 0x7e, 0x1b, + 0xed, 0x06, 0x11, 0x57, 0xad, 0x4a, 0xcb, 0xe5, + 0xb1, 0x11, 0x05, 0x0a, 0x30, 0xb1, 0x73, 0x79, + 0xcd, 0x7a, 0x04, 0xcc, 0x70, 0xe9, 0x95, 0xe4, + 0x27, 0xc2, 0xd5, 0x2d, 0x92, 0x44, 0xdf, 0xb4, + 0x94, 0xa8, 0x73, 0xa1, 0x4a, 0xc3, 0xcc, 0xc4, + 0x0e, 0x8d, 0xa1, 0x6a, 0xc2, 0xd8, 0x03, 0x7f, + 0xfa, 0xa7, 0x76, 0x0d, 0xad, 0x87, 0x88, 0xa0, + 0x77, 0xaf, 0x3b, 0x23, 0xd1, 0x66, 0x0b, 0x31, + 0x2b, 0xaf, 0xef, 0xd5, 0x41, 0x02, 0x81, 0x81, + 0x00, 0xdb, 0xc1, 0xe7, 0xdd, 0xba, 0x3c, 0x1f, + 0x9c, 0x64, 0xca, 0xa0, 0x63, 0xdb, 0xd2, 0x47, + 0x5c, 0x6e, 0x8a, 0xa3, 0x16, 0xd5, 0xda, 0xc2, + 0x25, 0x64, 0x0a, 0x02, 0xbc, 0x7d, 0x7f, 0x50, + 0xab, 0xe0, 0x66, 0x03, 0x53, 0x7d, 0x77, 0x6d, + 0x6c, 0x61, 0x58, 0x09, 0x73, 0xcd, 0x18, 0xe9, + 0x53, 0x0b, 0x5c, 0xa2, 0x71, 0x14, 0x02, 0xfd, + 0x55, 0xda, 0xe9, 0x77, 0x24, 0x7c, 0x2a, 0x4e, + 0xb9, 0xd9, 0x5d, 0x58, 0xf6, 0x26, 0xd0, 0xd8, + 0x3d, 0xcf, 0x8c, 0x89, 0x65, 0x6c, 0x35, 0x19, + 0xb6, 0x63, 0xff, 0xa0, 0x71, 0x49, 0xcd, 0x6d, + 0x5b, 0x3d, 0x8f, 0xea, 0x6f, 0xa9, 0xba, 0x43, + 0xe5, 0xdd, 0x39, 0x3a, 0x78, 0x8f, 0x07, 0xb8, + 0xab, 0x58, 0x07, 0xb7, 0xd2, 0xf8, 0x07, 0x02, + 0x9b, 0x79, 0x26, 0x32, 0x22, 0x38, 0x91, 0x01, + 0x90, 0x81, 0x29, 0x94, 0xad, 0x77, 0xeb, 0x86, + 0xb9, 0x02, 0x81, 0x81, 0x00, 0xc1, 0x29, 0x88, + 0xbd, 0x96, 0x31, 0x33, 0x7b, 0x77, 0x5d, 0x32, + 0x12, 0x5e, 0xdf, 0x28, 0x0c, 0x96, 0x0d, 0xa8, + 0x22, 0xdf, 0xd3, 0x35, 0xd7, 0xb0, 0x41, 0xcb, + 0xe7, 0x94, 0x8a, 0xa4, 0xed, 0xd2, 0xfb, 0xd2, + 0xf3, 0xf2, 0x95, 0xff, 0xd8, 0x33, 0x3f, 0x8c, + 0xd7, 0x65, 0xe4, 0x0c, 0xcc, 0xfe, 0x32, 0x66, + 0xfa, 0x50, 0xe2, 0xcf, 0xf0, 0xbe, 0x05, 0xb1, + 0xbc, 0xbe, 0x44, 0x09, 0xb4, 0xfe, 0x95, 0x06, + 0x18, 0xd7, 0x59, 0xc6, 0xef, 0x2d, 0x22, 0xa0, + 0x73, 0x5e, 0x77, 0xdf, 0x8d, 0x09, 0x2c, 0xb8, + 0xcc, 0xeb, 0x10, 0x4d, 0xa7, 0xd0, 0x4b, 0x46, + 0xba, 0x7d, 0x8b, 0x6a, 0x55, 0x47, 0x55, 0xd3, + 0xd7, 0xb1, 0x88, 0xfd, 0x27, 0x3e, 0xf9, 0x5b, + 0x7b, 0xae, 0x6d, 0x08, 0x9f, 0x0c, 0x2a, 0xe1, + 0xdd, 0xb9, 0xe3, 0x55, 0x13, 0x55, 0xa3, 0x6d, + 0x06, 0xbb, 0xe0, 0x1e, 0x55, 0x02, 0x81, 0x80, + 0x61, 0x73, 0x3d, 0x64, 0xff, 0xdf, 0x05, 0x8d, + 0x8e, 0xcc, 0xa4, 0x0f, 0x64, 0x3d, 0x7d, 0x53, + 0xa9, 0xd9, 0x64, 0xb5, 0x0d, 0xa4, 0x72, 0x8f, + 0xae, 0x2b, 0x1a, 0x47, 0x87, 0xc7, 0x5b, 0x78, + 0xbc, 0x8b, 0xc0, 0x51, 0xd7, 0xc3, 0x8c, 0x0c, + 0x91, 0xa6, 0x3e, 0x9a, 0xd1, 0x8a, 0x88, 0x7d, + 0x40, 0xfe, 0x95, 0x32, 0x5b, 0xd3, 0x6f, 0x90, + 0x11, 0x01, 0x92, 0xc9, 0xe5, 0x1d, 0xc5, 0xc7, + 0x78, 0x72, 0x82, 0xae, 0xb5, 0x4b, 0xcb, 0x78, + 0xad, 0x7e, 0xfe, 0xb6, 0xb1, 0x23, 0x63, 0x01, + 0x94, 0x9a, 0x99, 0x05, 0x63, 0xda, 0xea, 0xf1, + 0x98, 0xfd, 0x26, 0xd2, 0xd9, 0x8b, 0x35, 0xec, + 0xcb, 0x0b, 0x43, 0xb8, 0x8e, 0x84, 0xb8, 0x09, + 0x93, 0x81, 0xe8, 0xac, 0x6f, 0x3c, 0x7c, 0x95, + 0x81, 0x45, 0xc4, 0xd9, 0x94, 0x08, 0x09, 0x8f, + 0x91, 0x17, 0x65, 0x4c, 0xff, 0x6e, 0xbc, 0x51, + 0x02, 0x81, 0x81, 0x00, 0xc1, 0x0d, 0x9d, 0xd8, + 0xbd, 0xaf, 0x56, 0xe0, 0xe3, 0x1f, 0x85, 0xd7, + 0xce, 0x72, 0x02, 0x38, 0xf2, 0x0f, 0x9c, 0x27, + 0x9e, 0xc4, 0x1d, 0x60, 0x00, 0x8d, 0x02, 0x19, + 0xe5, 0xdf, 0xdb, 0x8e, 0xc5, 0xfb, 0x61, 0x8e, + 0xe6, 0xb8, 0xfc, 0x07, 0x3c, 0xd1, 0x1b, 0x16, + 0x7c, 0x83, 0x3c, 0x37, 0xf5, 0x26, 0xb2, 0xbd, + 0x22, 0xf2, 0x4d, 0x19, 0x33, 0x11, 0xc5, 0xdd, + 0xf9, 0xdb, 0x4e, 0x48, 0x52, 0xd8, 0xe6, 0x4b, + 0x15, 0x90, 0x68, 0xbe, 0xca, 0xc1, 0x7c, 0xd3, + 0x51, 0x6b, 0x45, 0x46, 0x54, 0x11, 0x1a, 0x71, + 0xd3, 0xcd, 0x6b, 0x8f, 0x79, 0x22, 0x83, 0x02, + 0x08, 0x4f, 0xba, 0x6a, 0x98, 0xed, 0x32, 0xd8, + 0xb4, 0x5b, 0x51, 0x88, 0x53, 0xec, 0x2c, 0x7e, + 0xa4, 0x89, 0xdc, 0xbf, 0xf9, 0x0d, 0x32, 0xc8, + 0xc3, 0xec, 0x6d, 0x2e, 0xf1, 0xbc, 0x70, 0x4e, + 0xf6, 0x9e, 0xbc, 0x31, 0x02, 0x81, 0x81, 0x00, + 0xd3, 0x35, 0x1b, 0x19, 0x75, 0x3f, 0x61, 0xf2, + 0x55, 0x03, 0xce, 0x25, 0xa9, 0xdf, 0x0c, 0x0a, + 0x3b, 0x47, 0x42, 0xdc, 0x38, 0x4b, 0x13, 0x4d, + 0x1f, 0x86, 0x58, 0x4f, 0xd8, 0xee, 0xfa, 0x76, + 0x15, 0xfb, 0x6e, 0x55, 0x31, 0xf2, 0xd2, 0x62, + 0x32, 0xa5, 0xc4, 0x23, 0x5e, 0x08, 0xa9, 0x83, + 0x07, 0xac, 0x8c, 0xa3, 0x7e, 0x18, 0xc0, 0x1c, + 0x57, 0x63, 0x8d, 0x05, 0x17, 0x47, 0x1b, 0xd3, + 0x74, 0x73, 0x20, 0x04, 0xfb, 0xc8, 0x1a, 0x43, + 0x04, 0x36, 0xc8, 0x19, 0xbe, 0xdc, 0xa6, 0xe5, + 0x0f, 0x25, 0x62, 0x24, 0x96, 0x92, 0xb6, 0xb3, + 0x97, 0xad, 0x57, 0x9a, 0x90, 0x37, 0x4e, 0x31, + 0x44, 0x74, 0xfa, 0x7c, 0xb4, 0xea, 0xfc, 0x15, + 0xa7, 0xb0, 0x51, 0xcc, 0xee, 0x1e, 0xed, 0x5b, + 0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b, + 0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8, 0xee, 0x24 }; + +static const uint8_t kTestRsaPublicKey3_2048[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb, + 0x43, 0x51, 0x99, 0xea, 0x40, 0x1e, 0x2d, 0x89, + 0xe4, 0xa2, 0x3e, 0xfc, 0x51, 0x3d, 0x0e, 0x83, + 0xa7, 0xe0, 0xa5, 0x41, 0x04, 0x1e, 0x14, 0xc5, + 0xa7, 0x5c, 0x61, 0x36, 0x44, 0xb3, 0x08, 0x05, + 0x5b, 0x14, 0xde, 0x01, 0x0c, 0x32, 0x3c, 0x9a, + 0x91, 0x00, 0x50, 0xa8, 0x1d, 0xcc, 0x9f, 0x8f, + 0x35, 0xb7, 0xc2, 0x75, 0x08, 0x32, 0x8b, 0x10, + 0x3a, 0x86, 0xf9, 0xd7, 0x78, 0xa3, 0x9d, 0x74, + 0x10, 0xc6, 0x24, 0xb1, 0x7f, 0xa5, 0xbf, 0x5f, + 0xc2, 0xd7, 0x15, 0xa3, 0x1d, 0xe0, 0x15, 0x6b, + 0x1b, 0x0e, 0x38, 0xba, 0x34, 0xbc, 0x95, 0x47, + 0x94, 0x40, 0x70, 0xac, 0x99, 0x1f, 0x0b, 0x8e, + 0x56, 0x93, 0x36, 0x2b, 0x6d, 0x04, 0xe7, 0x95, + 0x1a, 0x37, 0xda, 0x16, 0x57, 0x99, 0xee, 0x03, + 0x68, 0x16, 0x31, 0xaa, 0xc3, 0xb7, 0x92, 0x75, + 0x53, 0xfc, 0xf6, 0x20, 0x55, 0x44, 0xf8, 0xd4, + 0x8d, 0x78, 0x15, 0xc7, 0x1a, 0xb6, 0xde, 0x6c, + 0xe8, 0x49, 0x5d, 0xaf, 0xa8, 0x4e, 0x6f, 0x7c, + 0xe2, 0x6a, 0x4c, 0xd5, 0xe7, 0x8c, 0x8f, 0x0b, + 0x5d, 0x3a, 0x09, 0xd6, 0xb3, 0x44, 0xab, 0xe0, + 0x35, 0x52, 0x7c, 0x66, 0x85, 0xa4, 0x40, 0xd7, + 0x20, 0xec, 0x24, 0x05, 0x06, 0xd9, 0x84, 0x51, + 0x5a, 0xd2, 0x38, 0xd5, 0x1d, 0xea, 0x70, 0x2a, + 0x21, 0xe6, 0x82, 0xfd, 0xa4, 0x46, 0x1c, 0x4f, + 0x59, 0x6e, 0x29, 0x3d, 0xae, 0xb8, 0x8e, 0xee, + 0x77, 0x1f, 0x15, 0x33, 0xcf, 0x94, 0x1d, 0x87, + 0x3c, 0x37, 0xc5, 0x89, 0xe8, 0x7d, 0x85, 0xb3, + 0xbc, 0xe8, 0x62, 0x6a, 0x84, 0x7f, 0xfe, 0x9a, + 0x85, 0x3f, 0x39, 0xe8, 0xaa, 0x16, 0xa6, 0x8f, + 0x87, 0x7f, 0xcb, 0xc1, 0xd6, 0xf2, 0xec, 0x2b, + 0xa7, 0xdd, 0x49, 0x98, 0x7b, 0x6f, 0xdd, 0x69, + 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01 }; + +static void dump_openssl_error() { + while (unsigned long err = ERR_get_error()) { + char buffer[120]; + cout << "openssl error -- " << ERR_error_string(err, buffer) << "\n"; + } +} + class OEMCryptoClientTest : public ::testing::Test { - protected: - class Session; OEMCryptoClientTest() : alive_(false) {} @@ -126,15 +791,25 @@ class OEMCryptoClientTest : public ::testing::Test { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } + + void CreateWrappedRSAKey(vector* wrapped_key) { + Session& s = createSession("RSA_Session"); + s.open(); + s.GenerateDerivedKeys(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + s.MakeRSACertificate(&encrypted, &signature); + s.RewrapRSAKey(encrypted, signature, wrapped_key); + s.close(); + } + void testTearDown() { destroySessions(); terminate(); } - bool validateKeybox() { - OEMCryptoResult result; - result = OEMCrypto_IsKeyboxValid(); - return (OEMCrypto_SUCCESS == result); + void validateKeybox() { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); } class Session { @@ -143,7 +818,7 @@ class OEMCryptoClientTest : public ::testing::Test { Session(string sname) : valid_(true), open_(false), sname_(sname), mac_key_(wvcdm::MAC_KEY_SIZE), - enc_key_(wvcdm::KEY_SIZE) {} + enc_key_(wvcdm::KEY_SIZE), public_rsa_(0) {} bool isValid() { return valid_; } bool isOpen() { return open_; } @@ -157,7 +832,8 @@ class OEMCryptoClientTest : public ::testing::Test { } void open() { - EXPECT_TRUE(valid_ && !open_); + EXPECT_TRUE(valid_); + EXPECT_TRUE(!open_); session_status_ = OEMCrypto_OpenSession(&session_id_); if (OEMCrypto_SUCCESS == session_status_) { open_ = true; @@ -172,17 +848,13 @@ class OEMCryptoClientTest : public ::testing::Test { } } - bool GenerateNonce(uint32_t* nonce) { - OEMCryptoResult sts; - sts = OEMCrypto_GenerateNonce(session_id(), nonce); - return (OEMCrypto_SUCCESS == sts); + void GenerateNonce(uint32_t* nonce) { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GenerateNonce(session_id(), nonce)); } - bool GenerateDerivedKeys() { - if( !GenerateNonce(&nonce_) ) { - cout << "Generate Nonce failed." << endl; - return false; - } + void GenerateDerivedKeys() { + GenerateNonce(&nonce_); vector mac_context = wvcdm::a2b_hex( "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" @@ -196,192 +868,44 @@ class OEMCryptoClientTest : public ::testing::Test { "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" "180120002a0c31383836373837343035000000000080"); OEMCryptoResult sts; - sts = OEMCrypto_GenerateDerivedKeys( - session_id(), - &mac_context[0], mac_context.size(), - &enc_context[0], enc_context.size()); - if (sts != OEMCrypto_SUCCESS) { - cout << "GenerateDerivedKeys: Failed OEMCrypto_GenerateDerivedKeys with " - << static_cast(sts) << endl; - } + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GenerateDerivedKeys( + session_id(), + &mac_context[0], mac_context.size(), + &enc_context[0], enc_context.size())); mac_key_ = wvcdm::a2b_hex( "9D41F0A77A76E071841C33B06104D106641421E651FBE55F0AED453CDA7713AC"); enc_key_ = wvcdm::a2b_hex("D0BFC35DA9E33436E81C4229E78CB9F4"); - return (OEMCrypto_SUCCESS == sts); } - bool LoadTestKeys() { - if (!GenerateDerivedKeys()) { - cout << "LoadTestKeys: Failed GenerateDerivedKeys" << endl; - return false; - } + void LoadTestKeys() { + const unsigned int kNumKeys = 3; + MessageData data; + FillSimpleMessage(&data); + MessageData encrypted; + EncryptMessage(data, &encrypted); + std::vector signature; + SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; - return CreateAndLoadKeyMessage(); - } + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + FillKeyArray(encrypted, key_array); - bool CertificateProvision(vector* wrappedKey) { - vector context = wvcdm::a2b_hex( - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "38373430350000"); - - OEMCryptoResult sts; - - // Generate signature - size_t gen_signature_length = 0; - sts = OEMCrypto_GenerateSignature(session_id(), &context[0], - context.size(), NULL, - &gen_signature_length); - uint8_t* gen_signature = new uint8_t[gen_signature_length]; - sts = OEMCrypto_GenerateSignature(session_id(), &context[0], - context.size(), gen_signature, - &gen_signature_length); - - if (sts != OEMCrypto_SUCCESS) { - return false; - } - - // Rewrap Canned Response - - // In the real world, the signature above would just have been used to - // contact the certificate provisioning server to get this response. - // TODO: This is not a valid test vector - vector message = wvcdm::a2b_hex( - "000000001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637"); - - memcpy(&message[0], &nonce_, sizeof(uint32_t)); - uint32_t* messageNonce = reinterpret_cast(&message[0]); - uint8_t* key = &message[32]; - size_t key_length = 7; - uint8_t* key_iv = &message[64]; - - // TODO: In practice, we need to generate a signature here after inserting - // the right nonce into the message. - vector signature = wvcdm::a2b_hex( - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840"); - - size_t wrapped_key_length; - - sts = OEMCrypto_RewrapDeviceRSAKey(session_id(), &message[0], - message.size(), &signature[0], - signature.size(), messageNonce, key, - key_length, key_iv, NULL, - &wrapped_key_length); - wrappedKey->clear(); - wrappedKey->resize(wrapped_key_length); - sts = OEMCrypto_RewrapDeviceRSAKey(session_id(), &message[0], - message.size(), &signature[0], - signature.size(), messageNonce, key, - key_length, key_iv, - &(wrappedKey->front()), - &wrapped_key_length); - delete[] gen_signature; - return (sts == OEMCrypto_SUCCESS); - } - - bool CertificateLicense() { - OEMCryptoResult sts; - vector wrappedKey; - - if (!CertificateProvision(&wrappedKey)) { - return false; - } - - // Load the Wrapped Key - sts = OEMCrypto_LoadDeviceRSAKey(session_id(), &wrappedKey[0], - wrappedKey.size()); - if (sts != OEMCrypto_SUCCESS) { - return false; - } - - // Sign a Message - vector licenseRequest = wvcdm::a2b_hex( - "ba711a51e0c4c995440c28057f7f5e2f2e9c3a1edeb7549aca21e6050b059ac8" - "6ad64ec1a528eef17b4f5ce781af488d50fb0e60d04b48c78d55847a4e14243c" - "0023c553b46a2f53995870f351295e3aa2237f153f1415e817ad23e662e547b1" - "4708b303473813f93ee192353ff22bee54dd0f558bbe4b61b75b387bc310e9d6" - "8ff2cb3482689c0688570809b756dba4c2697be3132a2da782aa877ed64d8c7d" - "506525a382bad14d7e797c256c3617c22fa4165482b9742e9b54ffb6c52eda1d"); - size_t signature_length = 0; - - sts = OEMCrypto_GenerateRSASignature(session_id(), &licenseRequest[0], - licenseRequest.size(), NULL, - &signature_length); - uint8_t* signature = new uint8_t[signature_length]; - sts = OEMCrypto_GenerateRSASignature(session_id(), &licenseRequest[0], - licenseRequest.size(), signature, - &signature_length); - if (sts != OEMCrypto_SUCCESS) { - return false; - } - - // Rewrap Canned Response - - // In the real world, the signature above would just have been used to contact - // the license server to get this response. - // TODO: This is not a valid test vector - vector sessionKey = wvcdm::a2b_hex( - "6fa479c731d2770b6a61a5d1420bb9d1"); - vector mac_context = wvcdm::a2b_hex( - "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" - "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" - "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" - "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" - "34333231180120002a0c31383836373837343035000000000100"); - vector enc_context = wvcdm::a2b_hex( - "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" - "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" - "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" - "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" - "180120002a0c31383836373837343035000000000080"); - - sts = OEMCrypto_DeriveKeysFromSessionKey(session_id(), - &sessionKey[0], - sessionKey.size(), - &mac_context[0], - mac_context.size(), - &enc_context[0], - enc_context.size()); - if (sts != OEMCrypto_SUCCESS) { - return false; - } - - delete[] signature; - return CreateAndLoadKeyMessage(); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(session_id(), message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, encrypted.mac_key, + kNumKeys, key_array)); } template - void fill_simple_message(MessageData* data) { + void FillSimpleMessage(MessageData* data) { OEMCrypto_GetRandom(data->mac_key_iv, wvcdm::KEY_IV_SIZE); OEMCrypto_GetRandom(data->mac_key, wvcdm::KEY_IV_SIZE); for (unsigned int i = 0; i < kNumberKeys; i++) { memset(data->keys[i].key_id, i, kTestKeyIdLength); - OEMCrypto_GetRandom(data->keys[i].key_data, wvcdm::KEY_SIZE); + OEMCrypto_GetRandom(data->keys[i].key_data, wvcdm::MAC_KEY_SIZE); + data->keys[i].key_data_length = wvcdm::KEY_SIZE; OEMCrypto_GetRandom(data->keys[i].key_iv, wvcdm::KEY_IV_SIZE); OEMCrypto_GetRandom(data->keys[i].control_iv, wvcdm::KEY_IV_SIZE); memcpy(data->keys[i].control.verification, "kctl", 4); @@ -392,12 +916,11 @@ class OEMCryptoClientTest : public ::testing::Test { // For the canned decryption content, The first key is: vector key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D"); memcpy(data->keys[0].key_data, &key[0], key.size()); - } template - void encrypt_message(const MessageData& data, - MessageData* encrypted) { + void EncryptMessage(const MessageData& data, + MessageData* encrypted) { *encrypted = data; uint8_t iv_buffer[16]; @@ -416,64 +939,233 @@ class OEMCryptoClientTest : public ::testing::Test { memcpy(iv_buffer, &data.keys[i].key_iv[0], wvcdm::KEY_IV_SIZE); AES_set_encrypt_key(&enc_key_[0], 128, &aes_key); - AES_cbc_encrypt(&data.keys[i].key_data[0], &encrypted->keys[i].key_data[0], - wvcdm::KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT); + AES_cbc_encrypt(&data.keys[i].key_data[0], + &encrypted->keys[i].key_data[0], data.keys[i].key_data_length, + &aes_key, iv_buffer, AES_ENCRYPT); } - } - template - void sign_message(MessageData data, - std::vector* signature) { + void EncryptMessage(RSAPrivateKeyMessage* data, + RSAPrivateKeyMessage* encrypted) { + *encrypted = *data; + size_t padding = wvcdm::KEY_SIZE-(data->rsa_key_length % wvcdm::KEY_SIZE); + memset(data->rsa_key + data->rsa_key_length, + static_cast(padding), padding); + encrypted->rsa_key_length = data->rsa_key_length + padding; + + uint8_t iv_buffer[16]; + memcpy(iv_buffer, &data->rsa_key_iv[0], wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&enc_key_[0], 128, &aes_key); + AES_cbc_encrypt(&data->rsa_key[0], &encrypted->rsa_key[0], + encrypted->rsa_key_length, &aes_key, iv_buffer, + AES_ENCRYPT); + } + + template + void SignMessage(const T& data, std::vector* signature) { signature->resize(SHA256_DIGEST_LENGTH); unsigned int md_len = SHA256_DIGEST_LENGTH; HMAC(EVP_sha256(), &mac_key_[0], SHA256_DIGEST_LENGTH, - reinterpret_cast(&data), sizeof(data), + reinterpret_cast(&data), sizeof(data), &(signature->front()), &md_len); } template - void fill_key_array(const MessageData& data, - OEMCrypto_KeyObject* key_array) { + void FillKeyArray(const MessageData& data, + OEMCrypto_KeyObject* key_array) { for (unsigned int i = 0; i < kNumberKeys; i++) { key_array[i].key_id = data.keys[i].key_id; key_array[i].key_id_length = kTestKeyIdLength; key_array[i].key_data_iv = data.keys[i].key_iv; key_array[i].key_data = data.keys[i].key_data; + key_array[i].key_data_length = data.keys[i].key_data_length; key_array[i].key_control_iv = data.keys[i].control_iv; key_array[i].key_control - = reinterpret_cast(&data.keys[i].control); + = reinterpret_cast(&data.keys[i].control); } } + void MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted, + std::vector* signature) { + vector context = wvcdm::a2b_hex( + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "38373430350000"); + + OEMCryptoResult sts; + + // Generate signature + size_t gen_signature_length = 0; + sts = OEMCrypto_GenerateSignature(session_id(), &context[0], + context.size(), NULL, + &gen_signature_length); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_EQ(static_cast(32), gen_signature_length); + static const uint32_t SignatureBufferMaxLength = 256; + uint8_t gen_signature[SignatureBufferMaxLength]; + sts = OEMCrypto_GenerateSignature(session_id(), &context[0], + context.size(), gen_signature, + &gen_signature_length); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Rewrap Canned Response + + // In the real world, the signature above would just have been used to + // contact the certificate provisioning server to get this response. + + struct RSAPrivateKeyMessage message; + memcpy(message.rsa_key, kTestRsaPrivateKey1_3072, + sizeof(kTestRsaPrivateKey1_3072)); + OEMCrypto_GetRandom(message.rsa_key_iv, wvcdm::KEY_IV_SIZE); + message.rsa_key_length = sizeof(kTestRsaPrivateKey1_3072); + message.nonce = nonce_; + + EncryptMessage(&message, encrypted); + SignMessage(*encrypted, signature); + } + + void RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted, + const std::vector& signature, + vector* wrapped_key) { + size_t wrapped_key_length = 0; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey(session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key->clear(); + wrapped_key->resize(wrapped_key_length); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_RewrapDeviceRSAKey(session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, + &(wrapped_key->front()), + &wrapped_key_length)); + } + + bool PreparePublicKey(const uint8_t key[], size_t length) { + uint8_t const* p = key; + public_rsa_ = d2i_RSAPublicKey(0, &p , length); + if (!public_rsa_) { + cout << "d2i_RSAPrivateKey failed. "; + dump_openssl_error(); + return false; + } + return true; + } + + bool VerifyRSASignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + if (!public_rsa_) { + cout << "No public RSA key loaded in test code.\n"; + return false; + } + if (*signature_length != static_cast(RSA_size(public_rsa_))) { + cout << "Signature size is wrong. " << *signature_length + << ", should be " << RSA_size(public_rsa_) << "\n"; + return false; + } + + // Hash the message using SHA1. + uint8_t hash[SHA_DIGEST_LENGTH]; + if (!SHA1(message, message_length, hash)) { + cout << "Error computing SHA1. "; + dump_openssl_error(); + return false; + } + + // Decrypt signature to padded digest. + uint8_t padded_digest[*signature_length]; + int status; + status = RSA_public_decrypt(*signature_length, signature, padded_digest, + public_rsa_, RSA_NO_PADDING); + if (status == -1) { + cout << "VerifyRSASignature. in RSA_Public_digest "; + dump_openssl_error(); + return false; + } + status = RSA_verify_PKCS1_PSS(public_rsa_, hash, EVP_sha1(), + padded_digest, SHA_DIGEST_LENGTH); + if (status != 1) { + cout << "VerifyRSASignature. in RSA_verify_PKCS1_PSS "; + dump_openssl_error(); + return false; + } + return true; + } + + bool GenerateRSASessionKey(vector* enc_session_key, + vector* mac_context, + vector* enc_context) { + if (!public_rsa_) { + cout << "No public RSA key loaded in test code.\n"; + return false; + } + vector session_key = wvcdm::a2b_hex( + "6fa479c731d2770b6a61a5d1420bb9d1"); + *mac_context = wvcdm::a2b_hex( + "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" + "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" + "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" + "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" + "34333231180120002a0c31383836373837343035000000000100"); + *enc_context = wvcdm::a2b_hex( + "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" + "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" + "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" + "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" + "180120002a0c31383836373837343035000000000080"); + + enc_session_key->assign(RSA_size(public_rsa_), 0); + int status = RSA_public_encrypt(session_key.size(), + &session_key[0], + &(enc_session_key->front()), + public_rsa_, RSA_PKCS1_OAEP_PADDING); + if (status != RSA_size(public_rsa_)) { + cout << "GenerateRSASessionKey error encrypting session key. "; + dump_openssl_error(); + return false; + } + return true; + } + + void InstallRSASessionTestKey(const vector& wrapped_rsa_key) { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadDeviceRSAKey(session_id(), &wrapped_rsa_key[0], + wrapped_rsa_key.size())); + GenerateNonce(&nonce_); + vector enc_session_key; + vector mac_context; + vector enc_context; + ASSERT_TRUE(PreparePublicKey(kTestRsaPublicKey1_3072, + sizeof(kTestRsaPublicKey1_3072))); + ASSERT_TRUE(GenerateRSASessionKey(&enc_session_key, &mac_context, + &enc_context)); + + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_DeriveKeysFromSessionKey( + session_id(), &enc_session_key[0], enc_session_key.size(), + &mac_context[0], mac_context.size(), + &enc_context[0], enc_context.size())); + + mac_key_ = wvcdm::a2b_hex( + "B09CB4482675123B66F7A8303D803F6042F43404ED3DE020811CFC13BCDF4C65"); + enc_key_ = wvcdm::a2b_hex("CB477D09014D72C9B8DCE76C33EA43B3"); + } + private: - bool CreateAndLoadKeyMessage() { - const unsigned int num_keys = 3; - MessageData data; - fill_simple_message(&data); - MessageData encrypted; - encrypt_message(data, &encrypted); - std::vector signature; - sign_message(encrypted, &signature); - OEMCrypto_KeyObject key_array[num_keys]; - - const uint8_t* message_ptr = reinterpret_cast(&encrypted); - fill_key_array(encrypted, key_array); - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - session_id(), - message_ptr, sizeof(encrypted), - &signature[0], signature.size(), - encrypted.mac_key_iv, - encrypted.mac_key, - num_keys, key_array); - if (sts != OEMCrypto_SUCCESS) { - cout << "LoadTestKeys: Failed OEMCrypto_LoadKeys with " - << static_cast(sts) << endl; - } - return sts == OEMCrypto_SUCCESS; - } - bool valid_; bool open_; string sname_; @@ -482,6 +1174,7 @@ class OEMCryptoClientTest : public ::testing::Test { vector mac_key_; vector enc_key_; uint32_t nonce_; + RSA* public_rsa_; }; static Session badSession; @@ -551,7 +1244,7 @@ TEST_F(OEMCryptoClientTest, KeyboxValid) { bool success; success = init(); EXPECT_TRUE(success); - success = validateKeybox(); + validateKeybox(); ASSERT_TRUE(success); success = terminate(); ASSERT_TRUE(success); @@ -741,7 +1434,7 @@ TEST_F(OEMCryptoClientTest, GenerateNonce) { s.open(); uint32_t nonce; - ASSERT_TRUE(s.GenerateNonce(&nonce)); + s.GenerateNonce(&nonce); std::cout << "GenerateNonce:: nonce=" << nonce << std::endl; s.close(); @@ -757,8 +1450,8 @@ TEST_F(OEMCryptoClientTest, GenerateTwoNonces) { uint32_t nonce1; uint32_t nonce2; - ASSERT_TRUE(s.GenerateNonce(&nonce1)); - ASSERT_TRUE(s.GenerateNonce(&nonce2)); + s.GenerateNonce(&nonce1); + s.GenerateNonce(&nonce2); std::cout << "GenerateNonce:: nonce1=" << nonce1 << std::endl; std::cout << "GenerateNonce:: nonce2=" << nonce2 << std::endl; @@ -775,7 +1468,7 @@ TEST_F(OEMCryptoClientTest, GenerateDerivedKeys) { testSetUp(); s.open(); - ASSERT_TRUE(s.GenerateDerivedKeys()); + s.GenerateDerivedKeys(); s.close(); ASSERT_TRUE(s.successStatus()); @@ -788,7 +1481,7 @@ TEST_F(OEMCryptoClientTest, GenerateSignature) { testSetUp(); s.open(); - ASSERT_TRUE(s.GenerateDerivedKeys()); + s.GenerateDerivedKeys(); vector context = wvcdm::a2b_hex( "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" @@ -833,8 +1526,8 @@ TEST_F(OEMCryptoClientTest, LoadKeyNoNonce) { InstallKeybox(kDefaultKeybox); Session& s = createSession("ONE"); s.open(); - ASSERT_TRUE(s.GenerateDerivedKeys()); - ASSERT_TRUE(s.LoadTestKeys()); + s.GenerateDerivedKeys(); + s.LoadTestKeys(); s.close(); testTearDown(); } @@ -845,33 +1538,275 @@ TEST_F(OEMCryptoClientTest, LoadKeyWithNonce) { Session& s = createSession("ONE"); s.open(); - ASSERT_TRUE(s.GenerateDerivedKeys()); + s.GenerateDerivedKeys(); - const unsigned int num_keys = 3; - MessageData data; - s.fill_simple_message(&data); - data.keys[0].control.control_bits = wvoec_mock::kControlNonceEnabled; - data.keys[1].control.control_bits = wvoec_mock::kControlNonceEnabled; - data.keys[2].control.control_bits = wvoec_mock::kControlNonceEnabled; + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + data.keys[0].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); + data.keys[1].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); + data.keys[2].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); - MessageData encrypted; - s.encrypt_message(data, &encrypted); + MessageData encrypted; + s.EncryptMessage(data, &encrypted); std::vector signature; - s.sign_message(encrypted, &signature); - OEMCrypto_KeyObject key_array[num_keys]; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; const uint8_t* message_ptr = reinterpret_cast(&encrypted); - s.fill_key_array(encrypted, key_array); - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), - message_ptr, sizeof(encrypted), - &signature[0], signature.size(), - encrypted.mac_key_iv, - encrypted.mac_key, - num_keys, key_array); - + s.FillKeyArray(encrypted, key_array); + OEMCryptoResult sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); ASSERT_EQ(OEMCrypto_SUCCESS, sts); + s.close(); + testTearDown(); +} +/* The Bad Range tests verify that OEMCrypto_LoadKeys checks the range + of all the pointers. It should reject a message if the pointer does + not point into the message buffer */ +TEST_F(OEMCryptoClientTest, LoadKeyWithBadRange1) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + + vector mac_key(encrypted.mac_key, + encrypted.mac_key+sizeof(encrypted.mac_key)); + + OEMCryptoResult sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + &mac_key[0], // Not pointing into buffer. + kNumKeys, key_array); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeyWithBadRange2) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + + vector mac_key_iv(encrypted.mac_key_iv, + encrypted.mac_key_iv+sizeof(encrypted.mac_key_iv)); + + OEMCryptoResult sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + &mac_key_iv[0], // bad. + encrypted.mac_key, + kNumKeys, key_array); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeyWithBadRange3) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + + vector bad_buffer(encrypted.keys[0].key_id, + encrypted.keys[0].key_id+kTestKeyIdLength); + key_array[0].key_id = &bad_buffer[0]; + + OEMCryptoResult sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeyWithBadRange4) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + + vector bad_buffer(encrypted.keys[1].key_data, + encrypted.keys[1].key_data+wvcdm::KEY_SIZE); + key_array[1].key_data = &bad_buffer[0]; + + OEMCryptoResult sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeyWithBadRange5) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + + vector bad_buffer(encrypted.keys[1].key_iv, + encrypted.keys[1].key_iv+sizeof(encrypted.keys[1].key_iv)); + key_array[1].key_data_iv = &bad_buffer[0]; + + OEMCryptoResult sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeyWithBadRange6) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + + vector bad_buffer(key_array[2].key_control, + key_array[2].key_control+sizeof(encrypted.keys[1].control)); + key_array[2].key_control = &bad_buffer[0]; + + OEMCryptoResult sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeyWithBadRange7) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + + vector bad_buffer(key_array[2].key_control_iv, + key_array[2].key_control_iv+sizeof(encrypted.keys[1].control_iv)); + key_array[2].key_control_iv = &bad_buffer[0]; + + OEMCryptoResult sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); + ASSERT_NE(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); } @@ -882,23 +1817,23 @@ TEST_F(OEMCryptoClientTest, LoadKeyWithBadNonce) { Session& s = createSession("ONE"); s.open(); - ASSERT_TRUE(s.GenerateDerivedKeys()); + s.GenerateDerivedKeys(); - const unsigned int num_keys = 3; - MessageData data; - s.fill_simple_message(&data); - data.keys[0].control.control_bits = wvoec_mock::kControlNonceEnabled; - data.keys[1].control.control_bits = wvoec_mock::kControlNonceEnabled; - data.keys[2].control.control_bits = wvoec_mock::kControlNonceEnabled; + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + data.keys[0].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); + data.keys[1].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); + data.keys[2].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); data.keys[1].control.nonce = 42; // This one is bad. - MessageData encrypted; - s.encrypt_message(data, &encrypted); + MessageData encrypted; + s.EncryptMessage(data, &encrypted); std::vector signature; - s.sign_message(encrypted, &signature); - OEMCrypto_KeyObject key_array[num_keys]; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; const uint8_t* message_ptr = reinterpret_cast(&encrypted); - s.fill_key_array(encrypted, key_array); + s.FillKeyArray(encrypted, key_array); OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), @@ -906,7 +1841,42 @@ TEST_F(OEMCryptoClientTest, LoadKeyWithBadNonce) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_key, - num_keys, key_array); + kNumKeys, key_array); + + ASSERT_NE(OEMCrypto_SUCCESS, sts); + + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeyWithBadVerification) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + data.keys[1].control.verification[2] = 'Z'; + + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); ASSERT_NE(OEMCrypto_SUCCESS, sts); @@ -920,19 +1890,19 @@ TEST_F(OEMCryptoClientTest, LoadKeysBadSignature) { Session& s = createSession("ONE"); s.open(); - ASSERT_TRUE(s.GenerateDerivedKeys()); + s.GenerateDerivedKeys(); - const unsigned int num_keys = 3; - MessageData data; - s.fill_simple_message(&data); + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); - MessageData encrypted; - s.encrypt_message(data, &encrypted); + MessageData encrypted; + s.EncryptMessage(data, &encrypted); std::vector signature; - s.sign_message(encrypted, &signature); - OEMCrypto_KeyObject key_array[num_keys]; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; const uint8_t* message_ptr = reinterpret_cast(&encrypted); - s.fill_key_array(encrypted, key_array); + s.FillKeyArray(encrypted, key_array); signature[0] = 42; // Bad signature. @@ -942,7 +1912,7 @@ TEST_F(OEMCryptoClientTest, LoadKeysBadSignature) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_key, - num_keys, key_array); + kNumKeys, key_array); ASSERT_NE(OEMCrypto_SUCCESS, sts); @@ -956,19 +1926,19 @@ TEST_F(OEMCryptoClientTest, LoadKeysWithNoDerivedKeys) { Session& s = createSession("ONE"); s.open(); - // ASSERT_TRUE(s.GenerateDerivedKeys()); + // s.GenerateDerivedKeys(); - const unsigned int num_keys = 3; - MessageData data; - s.fill_simple_message(&data); + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); - MessageData encrypted; - s.encrypt_message(data, &encrypted); + MessageData encrypted; + s.EncryptMessage(data, &encrypted); std::vector signature; - s.sign_message(encrypted, &signature); - OEMCrypto_KeyObject key_array[num_keys]; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; const uint8_t* message_ptr = reinterpret_cast(&encrypted); - s.fill_key_array(encrypted, key_array); + s.FillKeyArray(encrypted, key_array); OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), @@ -976,7 +1946,7 @@ TEST_F(OEMCryptoClientTest, LoadKeysWithNoDerivedKeys) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_key, - num_keys, key_array); + kNumKeys, key_array); ASSERT_NE(OEMCrypto_SUCCESS, sts); @@ -995,9 +1965,10 @@ TEST_F(OEMCryptoClientTest, Decrypt) { Session& s = createSession("ONE"); s.open(); - ASSERT_TRUE(s.LoadTestKeys()); + s.GenerateDerivedKeys(); + s.LoadTestKeys(); - // Select the key (from fill_simple_message) + // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); @@ -1050,9 +2021,10 @@ TEST_F(OEMCryptoClientTest, DecryptWithOffset) { Session& s = createSession("ONE"); s.open(); - ASSERT_TRUE(s.LoadTestKeys()); + s.GenerateDerivedKeys(); + s.LoadTestKeys(); - // Select the key (from fill_simple_message) + // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], @@ -1107,9 +2079,10 @@ TEST_F(OEMCryptoClientTest, DecryptUnencrypted) { Session& s = createSession("ONE"); s.open(); - ASSERT_TRUE(s.LoadTestKeys()); + s.GenerateDerivedKeys(); + s.LoadTestKeys(); - // Select the key (from fill_simple_message) + // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); @@ -1146,138 +2119,495 @@ TEST_F(OEMCryptoClientTest, DecryptUnencrypted) { testTearDown(); } +TEST_F(OEMCryptoClientTest, DecryptSecureToClear) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + s.GenerateDerivedKeys(); + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + data.keys[0].control.control_bits = htonl(wvoec_mock::kControlObserveDataPath + | wvoec_mock::kControlDataPathSecure); + + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Select the key (from FillSimpleMessage) + vector keyId = wvcdm::a2b_hex("000000000000000000000000"); + sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Set up our expected input and output + vector encryptedData = wvcdm::a2b_hex( + "ec261c115f9d5cda1d5cc7d33c4e37362d1397c89efdd1da5f0065c4848b0462" + "337ba14693735203c9b4184e362439c0cea5e5d1a628425eddf8a6bf9ba901ca" + "46f5a9fd973cffbbe3c276af9919e2e8f6f3f420538b7a0d6dc41487874d96b8" + "efaedb45a689b91beb8c20d36140ad467d9d620b19a5fc6f223b57e0e6a7f913" + "00fd899e5e1b89963e83067ca0912aa5b79df683e2530b55a9645be341bc5f07" + "cffc724790af635c959e2644e51ba7f23bae710eb55a1f2f4e060c3c1dd1387c" + "74415dc880492dd1d5b9ecf3f01de48a44baeb4d3ea5cc4f8d561d0865afcabb" + "fc14a9ab9647e6e31adabb72d792f0c9ba99dc3e9205657d28fc7771d64e6d4b"); + vector encryptionIv = wvcdm::a2b_hex( + "719dbcb253b2ec702bb8c1b1bc2f3bc6"); + vector unencryptedData = wvcdm::a2b_hex( + "19ef4361e16e6825b336e2012ad8ffc9ce176ab2256e1b98aa15b7877bd8c626" + "fa40b2e88373457cbcf4f1b4b9793434a8ac03a708f85974cff01bddcbdd7a8e" + "e33fd160c1d5573bfd8104efd23237edcf28205c3673920553f8dd5e916604b0" + "1082345181dceeae5ea39d829c7f49e1850c460645de33c288723b7ae3d91a17" + "a3f04195cd1945ba7b0f37fef7e82368be30f04365d877766f6d56f67d22a244" + "ef2596d3053f657c1b5d90b64e11797edf1c198a23a7bfc20e4d44c74ae41280" + "a8317f443255f4020eda850ff0954e308f53a634cbce799ae58911bc59ccd6a5" + "de2ac53ee0fa7ea15fc692cc892acc0090865dc57becacddf362a092dfd3040b"); + + // Describe the output + uint8_t outputBuffer[256]; + OEMCrypto_DestBufferDesc destBuffer; + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = outputBuffer; + destBuffer.buffer.clear.max_length = sizeof(outputBuffer); + + // Decrypt the data + sts = OEMCrypto_DecryptCTR(s.session_id(), &encryptedData[0], + encryptedData.size(), true, &encryptionIv[0], 0, + &destBuffer); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NE(0, memcmp(&unencryptedData[0], outputBuffer, + unencryptedData.size())); + + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, KeyDuration) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + const unsigned int kNumKeys = 3; + MessageData data; + s.FillSimpleMessage(&data); + data.keys[0].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); + data.keys[0].control.duration = htonl(2); // 2 seconds. + MessageData encrypted; + s.EncryptMessage(data, &encrypted); + std::vector signature; + s.SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.FillKeyArray(encrypted, key_array); + sts = OEMCrypto_LoadKeys(s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Select the key (from FillSimpleMessage) + vector keyId = wvcdm::a2b_hex("000000000000000000000000"); + sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Set up our expected input and output + vector encryptedData = wvcdm::a2b_hex( + "ec261c115f9d5cda1d5cc7d33c4e37362d1397c89efdd1da5f0065c4848b0462" + "337ba14693735203c9b4184e362439c0cea5e5d1a628425eddf8a6bf9ba901ca" + "46f5a9fd973cffbbe3c276af9919e2e8f6f3f420538b7a0d6dc41487874d96b8" + "efaedb45a689b91beb8c20d36140ad467d9d620b19a5fc6f223b57e0e6a7f913" + "00fd899e5e1b89963e83067ca0912aa5b79df683e2530b55a9645be341bc5f07" + "cffc724790af635c959e2644e51ba7f23bae710eb55a1f2f4e060c3c1dd1387c" + "74415dc880492dd1d5b9ecf3f01de48a44baeb4d3ea5cc4f8d561d0865afcabb" + "fc14a9ab9647e6e31adabb72d792f0c9ba99dc3e9205657d28fc7771d64e6d4b"); + vector encryptionIv = wvcdm::a2b_hex( + "719dbcb253b2ec702bb8c1b1bc2f3bc6"); + vector unencryptedData = wvcdm::a2b_hex( + "19ef4361e16e6825b336e2012ad8ffc9ce176ab2256e1b98aa15b7877bd8c626" + "fa40b2e88373457cbcf4f1b4b9793434a8ac03a708f85974cff01bddcbdd7a8e" + "e33fd160c1d5573bfd8104efd23237edcf28205c3673920553f8dd5e916604b0" + "1082345181dceeae5ea39d829c7f49e1850c460645de33c288723b7ae3d91a17" + "a3f04195cd1945ba7b0f37fef7e82368be30f04365d877766f6d56f67d22a244" + "ef2596d3053f657c1b5d90b64e11797edf1c198a23a7bfc20e4d44c74ae41280" + "a8317f443255f4020eda850ff0954e308f53a634cbce799ae58911bc59ccd6a5" + "de2ac53ee0fa7ea15fc692cc892acc0090865dc57becacddf362a092dfd3040b"); + + // Describe the output + uint8_t outputBuffer[256]; + OEMCrypto_DestBufferDesc destBuffer; + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = outputBuffer; + destBuffer.buffer.clear.max_length = sizeof(outputBuffer); + // Decrypt the data + sts = OEMCrypto_DecryptCTR(s.session_id(), &encryptedData[0], + encryptedData.size(), true, &encryptionIv[0], 0, + &destBuffer); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer, + unencryptedData.size())); + + sleep(1); // Should still be valid key. + + memset(outputBuffer, 0, sizeof(outputBuffer)); + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = outputBuffer; + destBuffer.buffer.clear.max_length = sizeof(outputBuffer); + + // Decrypt the data + sts = OEMCrypto_DecryptCTR(s.session_id(), &encryptedData[0], + encryptedData.size(), true, &encryptionIv[0], 0, + &destBuffer); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer, + unencryptedData.size())); + + sleep(2); // Should be expired key. + + memset(outputBuffer, 0, sizeof(outputBuffer)); + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = outputBuffer; + destBuffer.buffer.clear.max_length = sizeof(outputBuffer); + + // Decrypt the data + sts = OEMCrypto_DecryptCTR(s.session_id(), &encryptedData[0], + encryptedData.size(), true, &encryptionIv[0], 0, + &destBuffer); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NE(0, memcmp(&unencryptedData[0], outputBuffer, + unencryptedData.size())); + + s.close(); + testTearDown(); +} + /////////////////////////////////////////////////// // Certificate Root of Trust Tests /////////////////////////////////////////////////// +void TestKey(const uint8_t key[], size_t length) { + uint8_t const* p = key; + RSA* rsa = d2i_RSAPrivateKey(0, &p , length); + if (!rsa) { + cout << "d2i_RSAPrivateKey failed. "; + dump_openssl_error(); + ASSERT_TRUE(false); + } + switch (RSA_check_key(rsa)) { + case 1: // valid. + ASSERT_TRUE(true); + return; + case 0: // not valid. + cout << "[TestKey(): rsa key not valid] "; + dump_openssl_error(); + ASSERT_TRUE(false); + default: // -1 == check failed. + cout << "[TestKey(): error checking rsa key] "; + dump_openssl_error(); + ASSERT_TRUE(false); + } +} +TEST_F(OEMCryptoClientTest, ValidateRSATestKeys) { + TestKey(kTestRsaPrivateKey1_3072, sizeof(kTestRsaPrivateKey1_3072)); + TestKey(kTestRsaPrivateKey2_2048, sizeof(kTestRsaPrivateKey2_2048)); + TestKey(kTestRsaPrivateKey3_2048, sizeof(kTestRsaPrivateKey3_2048)); +} + TEST_F(OEMCryptoClientTest, CertificateProvision) { OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox); Session& s = createSession("ONE"); s.open(); - - ASSERT_TRUE(s.GenerateDerivedKeys()); - uint32_t nonce = s.getNonce(); - - vector context = wvcdm::a2b_hex( - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "38373430350000"); - - // Generate signature - size_t gen_signature_length = 0; - sts = OEMCrypto_GenerateSignature(s.session_id(), &context[0], context.size(), - NULL, &gen_signature_length); - - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - ASSERT_EQ(static_cast(256), gen_signature_length); - - uint8_t* gen_signature = new uint8_t[gen_signature_length]; - sts = OEMCrypto_GenerateSignature(s.session_id(), &context[0], context.size(), - gen_signature, &gen_signature_length); - - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - - // Rewrap Canned Response - - // In the real world, the signature above would just have been used to - // contact the certificate provisioning server to get this response. - // TODO: This is not a valid test vector - vector message = wvcdm::a2b_hex( - "000000001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637"); - - memcpy(&message[0], &nonce, sizeof(uint32_t)); - uint32_t* messageNonce = reinterpret_cast(&message[0]); - uint8_t* key = &message[32]; - size_t key_length = 7; - uint8_t* key_iv = &message[64]; - - // TODO: In practice, we need to generate a signature here after inserting - // the right nonce into the message. - vector signature = wvcdm::a2b_hex( - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840"); - - size_t wrapped_key_length; - - sts = OEMCrypto_RewrapDeviceRSAKey(s.session_id(), &message[0], - message.size(), &signature[0], - signature.size(), messageNonce, key, - key_length, key_iv, NULL, - &wrapped_key_length); - - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - ASSERT_NE(static_cast(0), wrapped_key_length); - + s.GenerateDerivedKeys(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + s.MakeRSACertificate(&encrypted, &signature); vector wrapped_key; - wrapped_key.resize(wrapped_key_length); + s.RewrapRSAKey(encrypted, signature, &wrapped_key); - sts = OEMCrypto_RewrapDeviceRSAKey(s.session_id(), &message[0], - message.size(), &signature[0], - signature.size(), messageNonce, key, - key_length, key_iv, &wrapped_key[0], - &wrapped_key_length); - - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - - // TODO: This is not a valid test vector - vector clear_key = wvcdm::a2b_hex( - "1558497b6d994be343ed1c6d6313e0537b843e9a9c0836d1e83fe33154191ce9" - "a14d8d95bebaddc03bd471827170f527c0a166b9068b273d1bc57fbb13975ee4" - "f6b9a31743da6c447acbb712e81b13eddfd4e96c76010ac9b8aa1b6b3152b0fc" - "39ad33e5719656069f9ede9ebba7a77dd2e2074eec5c1b7ffc427a6f1be168f0" - "b5857713a44623862c903284bc53417e23c65602b52c1cb699e8352453eb9698" - "0b31459b90c26c907b549c1ab293725e414d4e45f5b30af7a55f95499a7dc89f" - "7d13ba90b34aef6b49484b0701bf96ea8b660c24bb4e92a2d1c43beb434fa386" - "1071380388799ac31d79285f5817687ed3e2eeb73a30744e77b757686c9ba5ad");; + vector clear_key(kTestRsaPrivateKey1_3072, + kTestRsaPrivateKey1_3072 + sizeof(kTestRsaPrivateKey1_3072)); ASSERT_EQ(NULL, find(wrapped_key, clear_key)); s.close(); testTearDown(); - - delete[] gen_signature; } -TEST_F(OEMCryptoClientTest, CertificateLicense) { +TEST_F(OEMCryptoClientTest, CertificateProvisionBadRange1) { OEMCryptoResult sts; - InstallKeybox(kDefaultKeybox); testSetUp(); + InstallKeybox(kDefaultKeybox); Session& s = createSession("ONE"); s.open(); + s.GenerateDerivedKeys(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + s.MakeRSACertificate(&encrypted, &signature); + vector wrapped_key; - vector wrappedKey; + size_t wrapped_key_length = 0; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key.clear(); + wrapped_key.resize(wrapped_key_length); + uint32_t nonce = encrypted.nonce; + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, + & (wrapped_key.front()), + &wrapped_key_length)); + s.close(); + testTearDown(); +} - ASSERT_TRUE(s.GenerateDerivedKeys()); - ASSERT_TRUE(s.CertificateProvision(&wrappedKey)); +TEST_F(OEMCryptoClientTest, CertificateProvisionBadRange2) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + s.MakeRSACertificate(&encrypted, &signature); + vector wrapped_key; - // Load the Wrapped Key - sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrappedKey[0], - wrappedKey.size()); + size_t wrapped_key_length = 0; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key.clear(); + wrapped_key.resize(wrapped_key_length); + vector bad_buffer(encrypted.rsa_key, + encrypted.rsa_key+sizeof(encrypted.rsa_key)); + + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + &bad_buffer[0], + encrypted.rsa_key_length, + encrypted.rsa_key_iv, + & (wrapped_key.front()), + &wrapped_key_length)); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, CertificateProvisionBadRange3) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + s.MakeRSACertificate(&encrypted, &signature); + vector wrapped_key; + + size_t wrapped_key_length = 0; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key.clear(); + wrapped_key.resize(wrapped_key_length); + vector bad_buffer(encrypted.rsa_key, + encrypted.rsa_key+sizeof(encrypted.rsa_key)); + + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + &bad_buffer[0], + & (wrapped_key.front()), + &wrapped_key_length)); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, CertificateProvisionBadSignature) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + s.MakeRSACertificate(&encrypted, &signature); + vector wrapped_key; + + size_t wrapped_key_length = 0; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key.clear(); + wrapped_key.resize(wrapped_key_length); + signature[4] = 42; // bad signature. + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, + & (wrapped_key.front()), + &wrapped_key_length)); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, CertificateProvisionBadNonce) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + s.MakeRSACertificate(&encrypted, &signature); + vector wrapped_key; + + size_t wrapped_key_length = 0; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key.clear(); + wrapped_key.resize(wrapped_key_length); + encrypted.nonce = 42; // Almost surely a bad nonce. + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, + & (wrapped_key.front()), + &wrapped_key_length)); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, CertificateProvisionBadRSAKey) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + s.MakeRSACertificate(&encrypted, &signature); + vector wrapped_key; + + size_t wrapped_key_length = 0; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key.clear(); + wrapped_key.resize(wrapped_key_length); + encrypted.rsa_key[1] = 42; // Almost surely a bad key. + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_RewrapDeviceRSAKey(s.session_id(), message_ptr, + sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, + encrypted.rsa_key, + encrypted.rsa_key_length, + encrypted.rsa_key_iv, + & (wrapped_key.front()), + &wrapped_key_length)); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadWrappedRSAKey) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + std::vector wrapped_rsa_key; + CreateWrappedRSAKey(&wrapped_rsa_key); + + Session& s = createSession("ONE"); + s.open(); + sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrapped_rsa_key[0], + wrapped_rsa_key.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, RSASignature) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + std::vector wrapped_rsa_key; + CreateWrappedRSAKey(&wrapped_rsa_key); + + Session& s = createSession("ONE"); + s.open(); + sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrapped_rsa_key[0], + wrapped_rsa_key.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); // Sign a Message @@ -1304,34 +2634,13 @@ TEST_F(OEMCryptoClientTest, CertificateLicense) { &signature_length); ASSERT_EQ(OEMCrypto_SUCCESS, sts); - // TODO: Validate the signature - - // Rewrap Canned Response - // In the real world, the signature above would just have been used to contact // the license server to get this response. - // TODO: This is not a valid test vector - vector sessionKey = wvcdm::a2b_hex( - "6fa479c731d2770b6a61a5d1420bb9d1"); - vector mac_context = wvcdm::a2b_hex( - "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" - "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" - "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" - "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" - "34333231180120002a0c31383836373837343035000000000100"); - vector enc_context = wvcdm::a2b_hex( - "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" - "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" - "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" - "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" - "180120002a0c31383836373837343035000000000080"); + ASSERT_TRUE(s.PreparePublicKey(kTestRsaPublicKey1_3072, + sizeof(kTestRsaPublicKey1_3072))); + ASSERT_TRUE(s.VerifyRSASignature(&licenseRequest[0], licenseRequest.size(), + signature, &signature_length)); - sts = OEMCrypto_DeriveKeysFromSessionKey(s.session_id(), &sessionKey[0], - sessionKey.size(), &mac_context[0], - mac_context.size(), &enc_context[0], - enc_context.size()); - - ASSERT_EQ(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); @@ -1339,17 +2648,33 @@ TEST_F(OEMCryptoClientTest, CertificateLicense) { delete[] signature; } +TEST_F(OEMCryptoClientTest, LoadRSASessionKey) { + OEMCryptoResult sts; + testSetUp(); + + InstallKeybox(kDefaultKeybox); + std::vector wrapped_rsa_key; + CreateWrappedRSAKey(&wrapped_rsa_key); + Session& s = createSession("ONE"); + s.open(); + s.InstallRSASessionTestKey(wrapped_rsa_key); + s.close(); + testTearDown(); +} + TEST_F(OEMCryptoClientTest, CertificateDecrypt) { OEMCryptoResult sts; testSetUp(); InstallKeybox(kDefaultKeybox); + std::vector wrapped_rsa_key; + CreateWrappedRSAKey(&wrapped_rsa_key); Session& s = createSession("ONE"); s.open(); - ASSERT_TRUE(s.GenerateDerivedKeys()); - ASSERT_TRUE(s.CertificateLicense()); + s.InstallRSASessionTestKey(wrapped_rsa_key); + s.LoadTestKeys(); - // Select the key (from fill_simple_message) + // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); @@ -1396,6 +2721,520 @@ TEST_F(OEMCryptoClientTest, CertificateDecrypt) { } #endif // CAN_INSTALL_KEYBOX +TEST_F(OEMCryptoClientTest, VersionNumber) { + OEMCryptoResult sts; + testSetUp(); + + const char* level = OEMCrypto_SecurityLevel(); + ASSERT_NE((char *)NULL, level); + ASSERT_EQ('L', level[0]); + cout << "OEMCrypto Security Level is "<< level << endl; + uint32_t version = OEMCrypto_APIVersion(); + cout << "OEMCrypto API version is " << version << endl; + ASSERT_LT((uint32_t)5, version); + + testTearDown(); +} + +class OEMCryptoGenericDRMTest : public OEMCryptoClientTest { + protected: + static const unsigned int kNumKeys = 4; + MessageData message_data_; + + static const size_t kBufferSize = 160; // multiple of encryption block size. + uint8_t clear_buffer_[kBufferSize]; + uint8_t encrypted_buffer_[kBufferSize]; + uint8_t iv_[wvcdm::KEY_IV_SIZE]; + + + void MakeFourKeys(Session* s) { + s->FillSimpleMessage(&message_data_); + message_data_.keys[0].control.control_bits = htonl(wvoec_mock::kControlAllowEncrypt); + message_data_.keys[1].control.control_bits = htonl(wvoec_mock::kControlAllowDecrypt); + message_data_.keys[2].control.control_bits = htonl(wvoec_mock::kControlAllowSign); + message_data_.keys[3].control.control_bits = htonl(wvoec_mock::kControlAllowVerify); + + message_data_.keys[2].key_data_length = wvcdm::MAC_KEY_SIZE; + message_data_.keys[3].key_data_length = wvcdm::MAC_KEY_SIZE; + + for(size_t i=0; i < kBufferSize; i++) { + clear_buffer_[i] = 1 + i %250; + } + for(size_t i=0; i < wvcdm::KEY_IV_SIZE; i++) { + iv_[i] = i; + } + + } + + void LoadFourKeys(Session* s) { + MessageData encrypted; + s->EncryptMessage(message_data_, &encrypted); + std::vector signature; + s->SignMessage(encrypted, &signature); + OEMCrypto_KeyObject key_array[kNumKeys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s->FillKeyArray(encrypted, key_array); + + OEMCryptoResult sts = OEMCrypto_LoadKeys(s->session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + kNumKeys, key_array); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + } + + void EncryptBuffer(unsigned int key_index, const uint8_t* in_buffer, + uint8_t *out_buffer) { + + AES_KEY aes_key; + ASSERT_EQ(0, AES_set_encrypt_key(message_data_.keys[key_index].key_data, + AES_BLOCK_SIZE * 8, &aes_key)); + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, iv_, wvcdm::KEY_IV_SIZE); + AES_cbc_encrypt(in_buffer, out_buffer, kBufferSize, + &aes_key, iv_buffer, AES_ENCRYPT); + } + + // Sign the buffer with the specified key. + void SignBuffer(unsigned int key_index, const uint8_t* in_buffer, + uint8_t signature[SHA256_DIGEST_LENGTH]) { + unsigned int md_len = SHA256_DIGEST_LENGTH; + HMAC(EVP_sha256(), message_data_.keys[key_index].key_data, + SHA256_DIGEST_LENGTH, in_buffer, kBufferSize, signature, &md_len); + } + + void BadEncrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, + size_t buffer_length) { + OEMCryptoResult sts; + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + LoadFourKeys(&s); + uint8_t expected_encrypted[kBufferSize]; + EncryptBuffer(key_index, clear_buffer_, expected_encrypted); + sts = OEMCrypto_SelectKey(s.session_id(), + message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + uint8_t encrypted[kBufferSize]; + sts = OEMCrypto_Generic_Encrypt(s.session_id(), clear_buffer_, + buffer_length, iv_, + algorithm, encrypted); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NE(0, memcmp(encrypted, expected_encrypted, buffer_length)); + s.close(); + } + + void BadDecrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, + size_t buffer_length) { + OEMCryptoResult sts; + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + LoadFourKeys(&s); + uint8_t encrypted[kBufferSize]; + EncryptBuffer(key_index, clear_buffer_, encrypted); + sts = OEMCrypto_SelectKey(s.session_id(), + message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + uint8_t resultant[kBufferSize]; + sts = OEMCrypto_Generic_Decrypt(s.session_id(), encrypted, + buffer_length, iv_, + algorithm, resultant); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NE(0, memcmp(clear_buffer_, resultant, buffer_length)); + s.close(); + } + + void BadSign(unsigned int key_index, OEMCrypto_Algorithm algorithm) { + OEMCryptoResult sts; + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + LoadFourKeys(&s); + uint8_t expected_signature[SHA256_DIGEST_LENGTH]; + SignBuffer(key_index, clear_buffer_, expected_signature); + + sts = OEMCrypto_SelectKey(s.session_id(), + message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + size_t signature_length = (size_t)SHA256_DIGEST_LENGTH; + uint8_t signature[SHA256_DIGEST_LENGTH]; + sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_, kBufferSize, + algorithm, signature, &signature_length); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NE(0, memcmp(signature, expected_signature, SHA256_DIGEST_LENGTH)); + s.close(); + } + + void BadVerify(unsigned int key_index, OEMCrypto_Algorithm algorithm, + size_t signature_size, bool alter_data) { + OEMCryptoResult sts; + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + LoadFourKeys(&s); + uint8_t signature[SHA256_DIGEST_LENGTH]; + SignBuffer(key_index, clear_buffer_, signature); + if( alter_data ) { + signature[0] = 43; + } + + sts = OEMCrypto_SelectKey(s.session_id(), + message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_, kBufferSize, + algorithm,signature, + signature_size); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + s.close(); + } +}; + +TEST_F(OEMCryptoGenericDRMTest, GenericKeyLoad) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + LoadFourKeys(&s); + + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, GenericKeyEncrypt) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + LoadFourKeys(&s); + unsigned int key_index = 0; + uint8_t expected_encrypted[kBufferSize]; + EncryptBuffer(key_index, clear_buffer_, expected_encrypted); + sts = OEMCrypto_SelectKey(s.session_id(), + message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + uint8_t encrypted[kBufferSize]; + sts = OEMCrypto_Generic_Encrypt(s.session_id(), clear_buffer_, kBufferSize, iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, encrypted); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(encrypted, expected_encrypted, kBufferSize)); + s.close(); + testTearDown(); +} + + +TEST_F(OEMCryptoGenericDRMTest, GenericKeyBadEncrypt) { + testSetUp(); + BadEncrypt(0, OEMCrypto_HMAC_SHA256, kBufferSize); + BadEncrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize-10); + BadEncrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); + BadEncrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); + BadEncrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, GenericKeyDecrypt) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + LoadFourKeys(&s); + unsigned int key_index = 1; + uint8_t encrypted[kBufferSize]; + EncryptBuffer(key_index, clear_buffer_, encrypted); + sts = OEMCrypto_SelectKey(s.session_id(), message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + uint8_t resultant[kBufferSize]; + sts = OEMCrypto_Generic_Decrypt(s.session_id(), encrypted, kBufferSize, iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, resultant); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(clear_buffer_, resultant, kBufferSize)); + s.close(); + testTearDown(); +} + + +TEST_F(OEMCryptoGenericDRMTest, GenericKeyBadDecrypt) { + testSetUp(); + BadDecrypt(1, OEMCrypto_HMAC_SHA256, kBufferSize); + BadDecrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize-10); + BadDecrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); + BadDecrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); + BadDecrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, GenericKeySign) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + LoadFourKeys(&s); + unsigned int key_index = 2; + uint8_t expected_signature[SHA256_DIGEST_LENGTH]; + SignBuffer(key_index, clear_buffer_, expected_signature); + + sts = OEMCrypto_SelectKey(s.session_id(), message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + size_t gen_signature_length = 0; + sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_, kBufferSize, + OEMCrypto_HMAC_SHA256, NULL, + &gen_signature_length); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); + uint8_t signature[SHA256_DIGEST_LENGTH]; + sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_, kBufferSize, + OEMCrypto_HMAC_SHA256,signature, + &gen_signature_length); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(signature, expected_signature, SHA256_DIGEST_LENGTH)); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, GenericKeyBadSign) { + testSetUp(); + BadSign(0, OEMCrypto_HMAC_SHA256); // Can't sign with encrypt key. + BadSign(1, OEMCrypto_HMAC_SHA256); // Can't sign with decrypt key. + BadSign(3, OEMCrypto_HMAC_SHA256); // Can't sign with verify key. + BadSign(2, OEMCrypto_AES_CBC_128_NO_PADDING); // Bad signing algorithm. + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, GenericKeyVerify) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + LoadFourKeys(&s); + unsigned int key_index = 3; + uint8_t signature[SHA256_DIGEST_LENGTH]; + SignBuffer(key_index, clear_buffer_, signature); + + sts = OEMCrypto_SelectKey(s.session_id(), message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_, kBufferSize, + OEMCrypto_HMAC_SHA256,signature, + SHA256_DIGEST_LENGTH); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, GenericKeyBadVerify) { + testSetUp(); + BadVerify(0, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); + BadVerify(1, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); + BadVerify(2, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); + BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, true); + BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH - 1, false); + BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH + 1, false); + BadVerify(3, OEMCrypto_AES_CBC_128_NO_PADDING, SHA256_DIGEST_LENGTH, false); + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, KeyDurationEncrypt) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + message_data_.keys[0].control.duration = htonl(2); // 2 seconds. + message_data_.keys[1].control.duration = htonl(2); // 2 seconds. + message_data_.keys[2].control.duration = htonl(2); // 2 seconds. + message_data_.keys[3].control.duration = htonl(2); // 2 seconds. + LoadFourKeys(&s); + + uint8_t expected_encrypted[kBufferSize]; + EncryptBuffer(0, clear_buffer_, expected_encrypted); + unsigned int key_index = 0; + uint8_t encrypted[kBufferSize]; + + sleep(1); // Should still be valid key. + + memset(encrypted, 0, kBufferSize); + sts = OEMCrypto_SelectKey(s.session_id(), message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + sts = OEMCrypto_Generic_Encrypt(s.session_id(), clear_buffer_, kBufferSize, iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, encrypted); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(encrypted, expected_encrypted, kBufferSize)); + + + sleep(2); // Should be expired key. + + memset(encrypted, 0, kBufferSize); + sts = OEMCrypto_Generic_Encrypt(s.session_id(), clear_buffer_, kBufferSize, iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, encrypted); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NE(0, memcmp(encrypted, expected_encrypted, kBufferSize)); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, KeyDurationDecrypt) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + message_data_.keys[0].control.duration = htonl(2); // 2 seconds. + message_data_.keys[1].control.duration = htonl(2); // 2 seconds. + message_data_.keys[2].control.duration = htonl(2); // 2 seconds. + message_data_.keys[3].control.duration = htonl(2); // 2 seconds. + LoadFourKeys(&s); + + unsigned int key_index = 1; + uint8_t encrypted[kBufferSize]; + EncryptBuffer(key_index, clear_buffer_, encrypted); + sts = OEMCrypto_SelectKey(s.session_id(), message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + uint8_t resultant[kBufferSize]; + sleep(1); // Should still be valid key. + + memset(resultant, 0, kBufferSize); + sts = OEMCrypto_Generic_Decrypt(s.session_id(), encrypted, kBufferSize, iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, resultant); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(clear_buffer_, resultant, kBufferSize)); + + sleep(2); // Should be expired key. + + memset(resultant, 0, kBufferSize); + sts = OEMCrypto_Generic_Decrypt(s.session_id(), encrypted, kBufferSize, iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, resultant); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NE(0, memcmp(clear_buffer_, resultant, kBufferSize)); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, KeyDurationSign) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + + message_data_.keys[0].control.duration = htonl(2); // 2 seconds. + message_data_.keys[1].control.duration = htonl(2); // 2 seconds. + message_data_.keys[2].control.duration = htonl(2); // 2 seconds. + message_data_.keys[3].control.duration = htonl(2); // 2 seconds. + + LoadFourKeys(&s); + + unsigned int key_index = 2; + uint8_t expected_signature[SHA256_DIGEST_LENGTH]; + uint8_t signature[SHA256_DIGEST_LENGTH]; + size_t signature_length = SHA256_DIGEST_LENGTH; + SignBuffer(key_index, clear_buffer_, expected_signature); + + sts = OEMCrypto_SelectKey(s.session_id(), message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + sleep(1); // Should still be valid key. + + memset(signature, 0, SHA256_DIGEST_LENGTH); + sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_, kBufferSize, + OEMCrypto_HMAC_SHA256,signature, + &signature_length); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(signature, expected_signature, SHA256_DIGEST_LENGTH)); + + sleep(2); // Should be expired key. + + memset(signature, 0, SHA256_DIGEST_LENGTH); + sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_, kBufferSize, + OEMCrypto_HMAC_SHA256,signature, + &signature_length); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NE(0, memcmp(signature, expected_signature, SHA256_DIGEST_LENGTH)); + + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoGenericDRMTest, KeyDurationVerify) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + s.GenerateDerivedKeys(); + MakeFourKeys(&s); + message_data_.keys[0].control.duration = htonl(2); // 2 seconds. + message_data_.keys[1].control.duration = htonl(2); // 2 seconds. + message_data_.keys[2].control.duration = htonl(2); // 2 seconds. + message_data_.keys[3].control.duration = htonl(2); // 2 seconds. + LoadFourKeys(&s); + + unsigned int key_index = 3; + uint8_t signature[SHA256_DIGEST_LENGTH]; + SignBuffer(key_index, clear_buffer_, signature); + + sts = OEMCrypto_SelectKey(s.session_id(), message_data_.keys[key_index].key_id, + kTestKeyIdLength); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + sleep(1); // Should still be valid key. + + sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_, kBufferSize, + OEMCrypto_HMAC_SHA256,signature, + SHA256_DIGEST_LENGTH); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + sleep(2); // Should be expired key. + + sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_, kBufferSize, + OEMCrypto_HMAC_SHA256,signature, + SHA256_DIGEST_LENGTH); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + + s.close(); + testTearDown(); +} + // TODO(kqyang): Add more unit tests -} // namespace wvoec +} // namespace wvoec diff --git a/libwvdrmengine/test/unit/Android.mk b/libwvdrmengine/test/unit/Android.mk index 18be3315..82b2d3a9 100644 --- a/libwvdrmengine/test/unit/Android.mk +++ b/libwvdrmengine/test/unit/Android.mk @@ -2,10 +2,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - WVCreateDrmPluginFactory_test.cpp \ - WVDrmPluginFactory_test.cpp \ - ../../src/WVCreateDrmPluginFactory.cpp \ - ../../src/WVDrmPluginFactory.cpp \ + WVCreatePluginFactories_test.cpp \ + WVCryptoFactory_test.cpp \ + WVDrmFactory_test.cpp \ LOCAL_C_INCLUDES := \ bionic \ @@ -14,17 +13,18 @@ LOCAL_C_INCLUDES := \ frameworks/av/include \ frameworks/native/include \ vendor/widevine/libwvdrmengine/include \ - vendor/widevine/libwvdrmengine/oemcrypto/include \ LOCAL_STATIC_LIBRARIES := \ libgtest \ libgtest_main \ LOCAL_SHARED_LIBRARIES := \ + libcrypto \ libdl \ liblog \ libstlport \ libutils \ + libwvdrmengine \ LOCAL_MODULE := libwvdrmengine_test diff --git a/libwvdrmengine/test/unit/WVCreatePluginFactories_test.cpp b/libwvdrmengine/test/unit/WVCreatePluginFactories_test.cpp new file mode 100644 index 00000000..8c758202 --- /dev/null +++ b/libwvdrmengine/test/unit/WVCreatePluginFactories_test.cpp @@ -0,0 +1,23 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#include "gtest/gtest.h" +#include "utils/UniquePtr.h" +#include "WVCreatePluginFactories.h" + +using namespace android; + +TEST(CreatePluginFactoriesTest, CreatesDrmFactory) { + UniquePtr factory(createDrmFactory()); + + EXPECT_NE((DrmFactory*)NULL, factory.get()) << + "createDrmFactory() returned null"; +} + +TEST(CreatePluginFactoriesTest, CreatesCryptoFactory) { + UniquePtr factory(createCryptoFactory()); + + EXPECT_NE((CryptoFactory*)NULL, factory.get()) << + "createCryptoFactory() returned null"; +} diff --git a/libwvdrmengine/test/unit/WVCryptoFactory_test.cpp b/libwvdrmengine/test/unit/WVCryptoFactory_test.cpp new file mode 100644 index 00000000..26c0c1d5 --- /dev/null +++ b/libwvdrmengine/test/unit/WVCryptoFactory_test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#include "gtest/gtest.h" +#include "utils/UniquePtr.h" +#include "WVCryptoFactory.h" + +using namespace wvdrm; + +const uint8_t kWidevineUUID[16] = { + 0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE, + 0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED +}; + +const uint8_t kOldNetflixWidevineUUID[16] = { + 0x29,0x70,0x1F,0xE4,0x3C,0xC7,0x4A,0x34, + 0x8C,0x5B,0xAE,0x90,0xC7,0x43,0x9A,0x47 +}; + +const uint8_t kUnknownUUID[16] = { + 0x6A,0x7F,0xAA,0xB0,0x83,0xC7,0x9E,0x20, + 0x08,0xBC,0xEF,0x32,0x34,0x1A,0x9A,0x26 +}; + +TEST(WVCryptoFactoryTest, SupportsSupportedCryptoSchemes) { + UniquePtr factory(new WVCryptoFactory()); + + EXPECT_TRUE(factory->isCryptoSchemeSupported(kWidevineUUID)) << + "WVPluginFactory does not support Widevine's UUID"; + + EXPECT_TRUE(factory->isCryptoSchemeSupported(kOldNetflixWidevineUUID)) << + "WVPluginFactory does not support the old Netflix Widevine UUID"; +} + +TEST(WVCryptoFactoryTest, DoesNotSupportUnsupportedCryptoSchemes) { + UniquePtr factory(new WVCryptoFactory()); + + EXPECT_FALSE(factory->isCryptoSchemeSupported(kUnknownUUID)) << + "WVPluginFactory incorrectly claims to support an unknown UUID"; +} diff --git a/libwvdrmengine/test/unit/WVDrmFactory_test.cpp b/libwvdrmengine/test/unit/WVDrmFactory_test.cpp new file mode 100644 index 00000000..a742cdbd --- /dev/null +++ b/libwvdrmengine/test/unit/WVDrmFactory_test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#include "gtest/gtest.h" +#include "utils/UniquePtr.h" +#include "WVDrmFactory.h" + +using namespace wvdrm; + +const uint8_t kWidevineUUID[16] = { + 0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE, + 0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED +}; + +const uint8_t kOldNetflixWidevineUUID[16] = { + 0x29,0x70,0x1F,0xE4,0x3C,0xC7,0x4A,0x34, + 0x8C,0x5B,0xAE,0x90,0xC7,0x43,0x9A,0x47 +}; + +const uint8_t kUnknownUUID[16] = { + 0x6A,0x7F,0xAA,0xB0,0x83,0xC7,0x9E,0x20, + 0x08,0xBC,0xEF,0x32,0x34,0x1A,0x9A,0x26 +}; + +TEST(WVDrmFactoryTest, SupportsSupportedCryptoSchemes) { + UniquePtr factory(new WVDrmFactory()); + + EXPECT_TRUE(factory->isCryptoSchemeSupported(kWidevineUUID)) << + "WVPluginFactory does not support Widevine's UUID"; + + EXPECT_TRUE(factory->isCryptoSchemeSupported(kOldNetflixWidevineUUID)) << + "WVPluginFactory does not support the old Netflix Widevine UUID"; +} + +TEST(WVDrmFactoryTest, DoesNotSupportUnsupportedCryptoSchemes) { + UniquePtr factory(new WVDrmFactory()); + + EXPECT_FALSE(factory->isCryptoSchemeSupported(kUnknownUUID)) << + "WVPluginFactory incorrectly claims to support an unknown UUID"; +}