From f3ec8c19d60627bbee65e115280b93be8b2e0281 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Wed, 3 Apr 2013 17:54:20 -0700 Subject: [PATCH] Import updates to the Widevine CENC DRM Plugin This change incorporates the following CLs from the Widevine cdm repository: Update the java request/response test app to match Drm API changes Don't build the mock liboemcrypto.so by default Do not build CDM tests by default Fix Build Break in DrmEngine Unit Tests Fix Build Break in WVDrmPlugin Initial version of roadmap for CDM projects. Implement License Query Implement Generic DRM in OEMCrypto Reference Implementation Add key_data_length field when calling OEMCrypto_LoadKeys Policy engine unittests Generalized DRM API for OEMCrypto Fixes proto buf libraries build. Add Version Number to OEMCrypto API Test key control block duration field in OEMCrypto Add fix for missing crypto offset. Fixed android/media*/test builds and added proto files for Cert. provisioning Refactor and clean up callback code in CDM. Add "device_id" name-value pair to LicenseRequest::ClientIdentification Separate unit and end-to-end tests from the top level makefie. Includes changes for 'fall back to l3 oemcrypto lib' in top level makefile. Fall Back to Level 3 if Level 1 Fails Fix compilation error in wvcdm_unittest. Fix Android build break due to Decrypt() signature change in cdm_engine.h. Wire up callbacks and errors in the Steel proxy. Fix lock assert if there is no keybox on the device. RSA Certificate Unit Test Change Generic_Verify signature to constant. Change-Id: I2e42db9d0b4f8d4e833675ae81d0714509bbfd2c --- libwvdrmengine/Android.mk | 30 +- libwvdrmengine/cdm/Android.mk | 7 +- libwvdrmengine/cdm/core/include/cdm_engine.h | 10 +- libwvdrmengine/cdm/core/include/cdm_session.h | 5 +- libwvdrmengine/cdm/core/include/clock.h | 10 +- .../cdm/core/include/crypto_engine.h | 2 + .../cdm/core/include/crypto_session.h | 5 + libwvdrmengine/cdm/core/include/license.h | 5 +- .../cdm/core/include/policy_engine.h | 46 +- libwvdrmengine/cdm/core/include/properties.h | 53 + .../cdm/core/include/wv_cdm_constants.h | 34 + .../cdm/core/include/wv_cdm_types.h | 3 +- libwvdrmengine/cdm/core/src/cdm_engine.cpp | 81 +- libwvdrmengine/cdm/core/src/cdm_session.cpp | 22 +- .../core/src/certificate_provisioning.proto | 43 + .../cdm/core/src/client_identification.proto | 30 + libwvdrmengine/cdm/core/src/crypto_engine.cpp | 6 + .../cdm/core/src/crypto_session.cpp | 31 + .../cdm/core/src/device_certificate.proto | 117 + libwvdrmengine/cdm/core/src/license.cpp | 14 +- libwvdrmengine/cdm/core/src/policy_engine.cpp | 320 +- libwvdrmengine/cdm/core/src/properties.cpp | 44 + .../cdm/core/test/cdm_engine_test.cpp | 2 +- .../cdm/core/test/license_unittest.cpp | 6 +- .../cdm/core/test/policy_engine_unittest.cpp | 737 +++++ .../cdm/include/properties_configuration.h | 18 + .../include/wv_content_decryption_module.h | 9 +- libwvdrmengine/cdm/src/clock.cpp | 2 +- .../cdm/src/wv_content_decryption_module.cpp | 11 +- libwvdrmengine/cdm/test/Android.mk | 4 + .../cdm/test/request_license_test.cpp | 51 +- libwvdrmengine/cdm/test/unit-test.mk | 10 +- libwvdrmengine/level3/Android.mk | 21 +- libwvdrmengine/level3/arm/entry_points.cpp | 1162 +++++++ libwvdrmengine/mediacrypto/test/Android.mk | 6 +- libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 6 +- libwvdrmengine/mediadrm/test/Android.mk | 6 +- libwvdrmengine/mediadrm/test/MockCDM.h | 4 +- .../mediadrm/test/WVDrmPlugin_test.cpp | 44 +- .../oemcrypto/include/OEMCryptoCENC.h | 305 +- libwvdrmengine/oemcrypto/mock/Android.mk | 1 + libwvdrmengine/oemcrypto/mock/src/log.h | 2 +- .../mock/src/oemcrypto_engine_mock.cpp | 303 +- .../mock/src/oemcrypto_engine_mock.h | 26 +- .../oemcrypto/mock/src/oemcrypto_key_mock.h | 4 + .../oemcrypto/mock/src/oemcrypto_mock.cpp | 191 +- .../oemcrypto/mock/src/string_conversions.cpp | 2 +- libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp | 2 +- libwvdrmengine/oemcrypto/test/Android.mk | 2 +- .../oemcrypto/test/oemcrypto_test.cpp | 2725 ++++++++++++++--- libwvdrmengine/test/unit/Android.mk | 10 +- .../unit/WVCreatePluginFactories_test.cpp | 23 + .../test/unit/WVCryptoFactory_test.cpp | 41 + .../test/unit/WVDrmFactory_test.cpp | 41 + 54 files changed, 5944 insertions(+), 751 deletions(-) create mode 100644 libwvdrmengine/cdm/core/include/properties.h create mode 100644 libwvdrmengine/cdm/core/src/certificate_provisioning.proto create mode 100644 libwvdrmengine/cdm/core/src/client_identification.proto mode change 100644 => 100755 libwvdrmengine/cdm/core/src/crypto_session.cpp create mode 100644 libwvdrmengine/cdm/core/src/device_certificate.proto create mode 100644 libwvdrmengine/cdm/core/src/properties.cpp create mode 100644 libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp create mode 100644 libwvdrmengine/cdm/include/properties_configuration.h create mode 100644 libwvdrmengine/level3/arm/entry_points.cpp create mode 100644 libwvdrmengine/test/unit/WVCreatePluginFactories_test.cpp create mode 100644 libwvdrmengine/test/unit/WVCryptoFactory_test.cpp create mode 100644 libwvdrmengine/test/unit/WVDrmFactory_test.cpp 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"; +}