diff --git a/libwvdrmengine/Android.mk b/libwvdrmengine/Android.mk index 09a983c0..d88c9c64 100644 --- a/libwvdrmengine/Android.mk +++ b/libwvdrmengine/Android.mk @@ -68,7 +68,7 @@ LOCAL_MODULE := libcdm_utils LOCAL_MODULE_CLASS := STATIC_LIBRARIES LOCAL_PROPRIETARY_MODULE := true -LOCAL_STATIC_LIBRARIES := libcrypto_static +LOCAL_STATIC_LIBRARIES := libcrypto LOCAL_C_INCLUDES := \ vendor/widevine/libwvdrmengine/cdm/core/include \ @@ -152,7 +152,7 @@ LOCAL_STATIC_LIBRARIES := \ libcdm \ libcdm_protos \ libcdm_utils \ - libcrypto_static \ + libcrypto \ libjsmn \ libwvdrmcryptoplugin \ libwvdrmdrmplugin \ @@ -212,7 +212,7 @@ LOCAL_STATIC_LIBRARIES := \ libcdm \ libcdm_protos \ libcdm_utils \ - libcrypto_static \ + libcrypto \ libjsmn \ libwvdrmcryptoplugin_hidl \ libwvdrmdrmplugin_hidl \ diff --git a/libwvdrmengine/build_and_run_all_unit_tests.sh b/libwvdrmengine/build_and_run_all_unit_tests.sh index c8c94d5d..ec4273f1 100755 --- a/libwvdrmengine/build_and_run_all_unit_tests.sh +++ b/libwvdrmengine/build_and_run_all_unit_tests.sh @@ -78,6 +78,7 @@ try_adb_push() { try_adb_push base64_test try_adb_push buffer_reader_test try_adb_push cdm_engine_test +try_adb_push cdm_feature_test try_adb_push cdm_extended_duration_test try_adb_push cdm_session_unittest try_adb_push device_files_unittest @@ -102,6 +103,7 @@ try_adb_push request_license_test try_adb_push service_certificate_unittest try_adb_push timer_unittest try_adb_push usage_table_header_unittest +try_adb_push wv_cdm_metrics_test # Run the tests using run_all_unit_tests.sh cd $ANDROID_BUILD_TOP/vendor/widevine/libwvdrmengine diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index 83b7b5f2..59a74a87 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -17,7 +17,7 @@ LOCAL_C_INCLUDES += \ LOCAL_HEADER_LIBRARIES := \ libutils_headers -LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto_static +LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto SRC_DIR := src CORE_SRC_DIR := core/src diff --git a/libwvdrmengine/cdm/core/include/crypto_key.h b/libwvdrmengine/cdm/core/include/crypto_key.h index 921517e6..0da57d56 100644 --- a/libwvdrmengine/cdm/core/include/crypto_key.h +++ b/libwvdrmengine/cdm/core/include/crypto_key.h @@ -17,6 +17,9 @@ class CryptoKey { const std::string& key_data_iv() const { return key_data_iv_; } const std::string& key_control() const { return key_control_; } const std::string& key_control_iv() const { return key_control_iv_; } + const std::string& sub_session_key_id() const {return sub_session_key_id_;} + const std::string& sub_session_key() const {return sub_session_key_;} + const std::string& track_label() const { return track_label_; } CdmCipherMode cipher_mode() const { return cipher_mode_; } void set_key_id(const std::string& key_id) { key_id_ = key_id; } void set_key_data(const std::string& key_data) { key_data_ = key_data; } @@ -28,6 +31,15 @@ class CryptoKey { void set_cipher_mode(CdmCipherMode cipher_mode) { cipher_mode_ = cipher_mode; } + void set_sub_session_key_id(const std::string& sub_session_key_id) { + sub_session_key_id_ = sub_session_key_id; + } + void set_sub_session_key(const std::string& sub_session_key) { + sub_session_key_ = sub_session_key; + } + void set_track_label(const std::string& track_label) { + track_label_ = track_label; + } bool HasKeyControl() const { return key_control_.size() >= 16; } @@ -37,6 +49,9 @@ class CryptoKey { std::string key_data_; std::string key_control_; std::string key_control_iv_; + std::string sub_session_key_id_; + std::string track_label_; + std::string sub_session_key_; CdmCipherMode cipher_mode_; }; diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 6e687769..66827882 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -11,6 +11,8 @@ #include "lock.h" #include "metrics_collections.h" #include "oemcrypto_adapter.h" +#include "OEMCryptoCENC.h" +#include "scoped_ptr.h" #include "timer_metric.h" #include "wv_cdm_types.h" @@ -22,6 +24,35 @@ class UsageTableHeader; typedef std::map CryptoKeyMap; typedef std::map SubLicenseSessionMap; +class KeySession { + protected: + KeySession(metrics::CryptoMetrics* metrics) : metrics_(metrics) {} + + public: + typedef enum { kDefault, kSubLicense } KeySessionType; + virtual ~KeySession() {} + virtual KeySessionType Type() = 0; + virtual bool GenerateDerivedKeys(const std::string& message) = 0; + virtual bool GenerateDerivedKeys(const std::string& message, + const std::string& session_key) = 0; + virtual OEMCryptoResult LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, + CdmCipherMode* cipher_mode, + const std::string& srm_requirement) = 0; + virtual OEMCryptoResult SelectKey(const std::string& key_id) = 0; + virtual OEMCryptoResult Decrypt( + const CdmDecryptionParameters& params, + OEMCrypto_DestBufferDesc& buffer_descriptor, + OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) = 0; + + protected: + metrics::CryptoMetrics* metrics_; +}; + class CryptoSession { public: typedef OEMCrypto_HDCP_Capability HdcpCapability; @@ -84,8 +115,6 @@ class CryptoSession { virtual bool GenerateDerivedKeys(const std::string& message); virtual bool GenerateDerivedKeys(const std::string& message, const std::string& session_key); - - virtual bool RewrapCertificate(const std::string& signed_message, const std::string& signature, const std::string& nonce, @@ -95,7 +124,7 @@ class CryptoSession { std::string* wrapped_private_key); // Media data path - virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); + virtual CdmResponseType Decrypt(const CdmDecryptionParameters& params); // Usage related methods virtual bool UsageInformationSupport(bool* has_support); @@ -181,6 +210,10 @@ class CryptoSession { virtual metrics::CryptoMetrics* GetCryptoMetrics() { return metrics_; } virtual CdmResponseType AddSubSession(const std::string& sub_session_key_id); + // TODO(jfore): exists is set based on whether a sub session exists. For now, + // that is not assumed to be an error. + virtual bool GenerateSubSessionNonce(const std::string& sub_session_key_id, + bool* exists, uint32_t* nonce); private: bool GetProvisioningMethod(CdmClientTokenType* token_type); @@ -188,13 +221,9 @@ class CryptoSession { void Terminate(); bool GetTokenFromKeybox(std::string* token); bool GetTokenFromOemCert(std::string* token); - void GenerateMacContext(const std::string& input_context, - std::string* deriv_context); - void GenerateEncryptContext(const std::string& input_context, - std::string* deriv_context); bool GenerateSignature(const std::string& message, std::string* signature); bool GenerateRsaSignature(const std::string& message, std::string* signature); - size_t GetOffset(std::string message, std::string field); + bool SetDestinationBufferType(); bool RewrapDeviceRSAKey( @@ -230,7 +259,7 @@ class CryptoSession { static void IncrementIV(uint64_t increase_by, std::vector* iv_out); static const size_t kAes128BlockSize = 16; // Block size for AES_CBC_128 - static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature + static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature static Lock crypto_lock_; static bool initialized_; static int session_count_; @@ -240,10 +269,13 @@ class CryptoSession { bool open_; CdmClientTokenType pre_provision_token_type_; - std::string oem_token_; // Cached OEMCrypto Public Key + std::string oem_token_; // Cached OEMCrypto Public Key bool update_usage_table_after_close_session_; CryptoSessionId oec_session_id_; SubLicenseSessionMap sub_license_oec_sessions_; + // Used for sub license sessions. + std::string wrapped_key_; + scoped_ptr key_session_; OEMCryptoBufferType destination_buffer_type_; bool is_destination_buffer_type_valid_; diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index fc7e88e1..9488aa95 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -22,6 +22,7 @@ class CryptoSession; class PolicyEngine; class ServiceCertificate; class CdmSession; +class CryptoKey; class CdmLicense { public: @@ -45,6 +46,7 @@ class CdmLicense { const CdmKeyResponse& license_response); virtual CdmResponseType HandleKeyUpdateResponse( bool is_renewal, const CdmKeyResponse& license_response); + virtual CdmResponseType HandleSubLicense(const InitializationData& init_data); virtual bool RestoreOfflineLicense( const CdmKeyMessage& license_request, @@ -113,6 +115,11 @@ class CdmLicense { // For testing // CdmLicense takes ownership of the clock. CdmLicense(const CdmSessionId& session_id, Clock* clock); + + // For sublicense key embedding. This key array will be initilized with any + // sub session keys we may have received in a license response. These keys + // may be used to support key rotation. + std::vector sub_session_key_array_; #if defined(UNIT_TEST) friend class CdmLicenseTest; #endif diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index f5f5296e..113253af 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -50,6 +50,8 @@ class PolicyEngine { // permits playback. virtual void SetLicense(const video_widevine::License& license); + virtual void UpdateLicenseKeys(const video_widevine::License& license); + // SetLicenseForRelease is used when releasing a license. The keys in this // license will be ignored, and any old keys will be expired. virtual void SetLicenseForRelease( diff --git a/libwvdrmengine/cdm/core/include/properties.h b/libwvdrmengine/cdm/core/include/properties.h index 9b28198d..6b593547 100644 --- a/libwvdrmengine/cdm/core/include/properties.h +++ b/libwvdrmengine/cdm/core/include/properties.h @@ -109,6 +109,7 @@ class Properties { FRIEND_TEST(CdmSessionTest, InitFailCryptoError); FRIEND_TEST(CdmSessionTest, InitNeedsProvisioning); FRIEND_TEST(CdmLicenseTest, PrepareKeyRequestValidation); + FRIEND_TEST(SubLicenseTest, VerifySubSessionData); #endif private: diff --git a/libwvdrmengine/cdm/core/include/service_certificate.h b/libwvdrmengine/cdm/core/include/service_certificate.h index e9f0e88b..80da4f4e 100644 --- a/libwvdrmengine/cdm/core/include/service_certificate.h +++ b/libwvdrmengine/cdm/core/include/service_certificate.h @@ -38,6 +38,13 @@ class ServiceCertificate { virtual CdmResponseType VerifySignedMessage(const std::string& message, const std::string& signature); + // Encrypt data using RSA with OAEP padding. + // |plaintext| is the data to be encrypted. |ciphertext| is a pointer to a + // string to contain the decrypted data on return, and may not be null. + // returns NO_ERROR if successful or an appropriate error code otherwise. + virtual CdmResponseType EncryptRsaOaep(const std::string& plaintext, + std::string* ciphertext); + // Encrypt the ClientIdentification message for a provisioning or // licensing request. Encryption is performed using the current // service certificate. Return a failure if the service certificate is diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 27997e60..205f791b 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -318,6 +318,7 @@ enum CdmResponseType { USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE, RELEASE_ALL_USAGE_INFO_ERROR_6, /* 275 */ RELEASE_ALL_USAGE_INFO_ERROR_7, + LICENSE_REQUEST_INVALID_SUBLICENSE, }; enum CdmKeyStatus { @@ -341,6 +342,7 @@ enum CdmLicenseType { // Like Streaming, but stricter. Does not permit storage of any kind. // Named after the 'temporary' session type in EME, which has this behavior. kLicenseTypeTemporary, + kLicenseTypeSubSession }; enum SecurityLevel { diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index bd2f131c..aad20fa6 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -245,7 +245,7 @@ CdmResponseType CdmEngine::OpenKeySetSession( } CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) { - LOGI("CdmEngine::CloseSession"); + LOGV("CdmEngine::CloseSession: %s", session_id.c_str()); if (!session_map_.CloseSession(session_id)) { LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_1; diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 2da42ab3..9fbc70a9 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -363,6 +363,8 @@ CdmResponseType CdmSession::GenerateKeyRequest( case kLicenseTypeRelease: is_release_ = true; break; + case kLicenseTypeSubSession: + return license_parser_->HandleSubLicense(init_data); default: LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld", license_type); @@ -390,6 +392,17 @@ CdmResponseType CdmSession::GenerateKeyRequest( return KEY_REQUEST_ERROR_1; } + std::vector embedded_key_data = + init_data.ExtractEmbeddedKeys(); + for (size_t i = 0; i < embedded_key_data.size(); ++i) { + CdmResponseType sts = crypto_session_->AddSubSession( + embedded_key_data[i].sub_session_key_id()); + if (NO_ERROR != sts) { + LOGE("CdmSession::GenerateKeyRequest: Unable to generate sub session"); + return sts; + } + } + app_parameters_ = app_parameters; CdmResponseType status = license_parser_->PrepareKeyRequest( init_data, license_type, @@ -450,12 +463,13 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { !provider_session_token.empty() && usage_table_header_ != nullptr) { if (sts != KEY_ADDED) { - CdmResponseType sts = + CdmResponseType delete_sts = usage_table_header_->DeleteEntry(usage_entry_number_, file_handle_.get(), crypto_metrics_); - if (sts != NO_ERROR) { - LOGW("CdmSession::AddKey: Delete usage entry failed = %d", sts); + if (delete_sts != NO_ERROR) { + LOGW("CdmSession::AddKey: Delete usage entry failed = %d", + delete_sts); } } } @@ -891,8 +905,11 @@ CdmResponseType CdmSession::UpdateUsageEntryInformation() { if (usage_support_type_ != kUsageEntrySupport || !has_provider_session_token() || usage_table_header_ == nullptr) { - LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected usage type " - "supported: %d", usage_support_type_); + LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected state, " + "usage support type: %d, PST present: %s, usage table header available" + ": %s", usage_support_type_, + has_provider_session_token() ? "yes" : "no", + usage_table_header_ == nullptr ? "no" : "yes"); return INCORRECT_USAGE_SUPPORT_TYPE_2; } diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index f98e9454..0484fef6 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -83,7 +83,8 @@ using video_widevine::SignedProvisioningMessage; */ bool CertificateProvisioning::GetProvisioningTokenType( ClientIdentification::TokenType* token_type) { - switch (crypto_session_.GetPreProvisionTokenType()) { + CdmClientTokenType token = crypto_session_.GetPreProvisionTokenType(); + switch (token) { case kClientTokenKeybox: *token_type = ClientIdentification::KEYBOX; return true; @@ -93,6 +94,8 @@ bool CertificateProvisioning::GetProvisioningTokenType( case kClientTokenDrmCert: default: // shouldn't happen + LOGE("CertificateProvisioning::GetProvisioningTokenType: unexpected " + "provisioning type: %d", token); return false; } } @@ -107,7 +110,7 @@ bool CertificateProvisioning::SetSpoidParameter( const std::string& origin, const std::string& spoid, ProvisioningRequest* request) { if (!request) { - LOGE("CertificateProvisioning::SetSpoidParameter : No request buffer " + LOGE("CertificateProvisioning::SetSpoidParameter: No request buffer " "passed to method."); return false; } diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index f0f89afd..10b22d41 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -29,6 +29,47 @@ std::string EncodeUint32(unsigned int u) { s.append(1, (u >> 0) & 0xFF); return s; } +size_t GetOffset(std::string message, std::string field) { + size_t pos = message.find(field); + if (pos == std::string::npos) { + LOGE("CryptoSession::GetOffset : Cannot find offset for %s", field.c_str()); + pos = 0; + } + return pos; +} +void GenerateMacContext(const std::string& input_context, + std::string* deriv_context) { + if (!deriv_context) { + LOGE("CryptoSession::GenerateMacContext : No output destination provided."); + return; + } + + const std::string kSigningKeyLabel = "AUTHENTICATION"; + const size_t kSigningKeySizeBits = wvcdm::MAC_KEY_SIZE * 8; + + deriv_context->assign(kSigningKeyLabel); + deriv_context->append(1, '\0'); + deriv_context->append(input_context); + deriv_context->append(EncodeUint32(kSigningKeySizeBits * 2)); +} + +void GenerateEncryptContext(const std::string& input_context, + std::string* deriv_context) { + if (!deriv_context) { + LOGE( + "CryptoSession::GenerateEncryptContext : No output destination " + "provided."); + return; + } + + const std::string kEncryptionKeyLabel = "ENCRYPTION"; + const size_t kEncryptionKeySizeBits = wvcdm::KEY_SIZE * 8; + + deriv_context->assign(kEncryptionKeyLabel); + deriv_context->append(1, '\0'); + deriv_context->append(input_context); + deriv_context->append(EncodeUint32(kEncryptionKeySizeBits)); +} const uint32_t kRsaSignatureLength = 256; const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB const size_t kEstimatedInitialUsageTableHeader = 40; @@ -44,6 +85,611 @@ uint64_t CryptoSession::request_id_index_ = 0; UsageTableHeader* CryptoSession::usage_table_header_l1_ = NULL; UsageTableHeader* CryptoSession::usage_table_header_l3_ = NULL; +// TODO(jfore, rfrias, gmorgan): This object should be split off into it's own +// module or refactored out. +class DefaultKeySession : public KeySession { + public: + DefaultKeySession(CryptoSessionId oec_session_id, + metrics::CryptoMetrics* metrics) + : KeySession(metrics), + oec_session_id_(oec_session_id) {} + virtual ~DefaultKeySession() {} + + KeySessionType Type() { return kDefault; } + + // Generate Derived Keys for DefaultKeySession + bool GenerateDerivedKeys(const std::string& message) { + std::string mac_deriv_message; + std::string enc_deriv_message; + GenerateMacContext(message, &mac_deriv_message); + GenerateEncryptContext(message, &enc_deriv_message); + + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_GenerateDerivedKeys( + oec_session_id_, + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, + oemcrypto_generate_derived_keys_, + sts); + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts); + return false; + } + + return true; + } + + // Generate Derived Keys (from session key) for DefaultKeySession + bool GenerateDerivedKeys(const std::string& message, + const std::string& session_key) { + std::string mac_deriv_message; + std::string enc_deriv_message; + GenerateMacContext(message, &mac_deriv_message); + GenerateEncryptContext(message, &enc_deriv_message); + + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DeriveKeysFromSessionKey( + oec_session_id_, + reinterpret_cast(session_key.data()), + session_key.size(), + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, + oemcrypto_derive_keys_from_session_key_, + sts); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", + sts); + return false; + } + + return true; + } + + // Load Keys for DefaultKeySession + OEMCryptoResult LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, + CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { + const uint8_t* msg = reinterpret_cast(message.data()); + const uint8_t* enc_mac_key = NULL; + const uint8_t* enc_mac_key_iv = NULL; + if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { + enc_mac_key = msg + GetOffset(message, mac_key); + enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); + } else { + LOGV("DefaultKeySession::LoadKeys: enc_mac_key not set"); + } + std::vector load_keys(keys.size()); + for (size_t i = 0; i < keys.size(); ++i) { + const CryptoKey* ki = &keys[i]; + OEMCrypto_KeyObject* ko = &load_keys[i]; + ko->key_id = msg + GetOffset(message, ki->key_id()); + 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()); + } else { + LOGE("For key %d: XXX key has no control block. size=%d", i, + ki->key_control().size()); + ko->key_control_iv = NULL; + ko->key_control = NULL; + } + ko->cipher_mode = ki->cipher_mode() == kCipherModeCbc + ? OEMCrypto_CipherMode_CBC + : OEMCrypto_CipherMode_CTR; + *cipher_mode = ki->cipher_mode(); + } + + uint8_t* pst = NULL; + if (!provider_session_token.empty()) { + pst = const_cast(msg) + + GetOffset(message, provider_session_token); + } + + uint8_t* srm_req = NULL; + if (!srm_requirement.empty()) { + srm_req = const_cast(msg) + + GetOffset(message, srm_requirement); + } + + LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_LoadKeys( + oec_session_id_, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), enc_mac_key_iv, enc_mac_key, keys.size(), + &load_keys[0], pst, provider_session_token.length(), + srm_req), + metrics_, + oemcrypto_load_keys_, + sts); + return sts; + } + + // Select Key for DefaultKeySession + OEMCryptoResult SelectKey(const std::string& key_id) { + // Crypto session lock already locked. + if (!cached_key_id_.empty() && cached_key_id_ == key_id) { + // Already using the desired key. + return OEMCrypto_SUCCESS; + } + + cached_key_id_ = key_id; + + const uint8_t* key_id_string = + reinterpret_cast(cached_key_id_.data()); + + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_SelectKey( + oec_session_id_, + key_id_string, + cached_key_id_.size()), + metrics_, + oemcrypto_select_key_, + sts); + + if (OEMCrypto_SUCCESS != sts) { + cached_key_id_.clear(); + } + return sts; + } + + // Decrypt for DefaultKeySession + OEMCryptoResult Decrypt( + const CdmDecryptionParameters& params, + OEMCrypto_DestBufferDesc& buffer_descriptor, + OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) { + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DecryptCENC( + oec_session_id_, + params.encrypt_buffer, + params.encrypt_length, + params.is_encrypted, + &(*params.iv).front(), + params.block_offset, + &buffer_descriptor, + &pattern_descriptor, + params.subsample_flags), + metrics_, + oemcrypto_decrypt_cenc_, + sts, + metrics::Pow2Bucket(params.encrypt_length)); + return sts; + } + + private: + CryptoSessionId oec_session_id_; + KeyId cached_key_id_; +}; + +// TODO(jfore, rfrias, gmorgan): This object should be split off into it's own +// module or refactored out. +class SubLicenseKeySession : public KeySession { + typedef enum { + kInitializing, + kInitialLicenseLoaded, + kInitialLicenseFailed, + } SubLicenseState; + + public: + SubLicenseKeySession(SubLicenseSessionMap& sub_license_oec_sessions, + metrics::CryptoMetrics* metrics, + std::string& wrapped_private_device_key, + SecurityLevel requested_security_level) + : KeySession(metrics), + state_(kInitializing), + wrapped_private_device_key_(wrapped_private_device_key), + sub_license_oec_sessions_(sub_license_oec_sessions), + requested_security_level_(requested_security_level) {} + + virtual ~SubLicenseKeySession() { + for (SubLicenseSessionMap::iterator oec_session = + sub_license_oec_sessions_.begin(); + oec_session != sub_license_oec_sessions_.end(); oec_session++) { + OEMCryptoResult close_sts; + M_TIME( + close_sts = OEMCrypto_CloseSession( + oec_session->second), + metrics_, + oemcrypto_close_session_, + close_sts); + } + sub_license_oec_sessions_.clear(); + } + + KeySessionType Type() { return kSubLicense; } + + // This version of GenerateDerivedKeys is for devices using keyboxes. It is + // not supported using sub licenses. + bool GenerateDerivedKeys(const std::string&) { + return false; + } + + // GenerateDerivedKeys is called for each open oemcrypto session and is only + // called once. + bool GenerateDerivedKeys(const std::string& message, + const std::string& session_key) { + std::string mac_deriv_message; + std::string enc_deriv_message; + GenerateMacContext(message, &mac_deriv_message); + GenerateEncryptContext(message, &enc_deriv_message); + + for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin(); + it != sub_license_oec_sessions_.end(); it++) { + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DeriveKeysFromSessionKey( + it->second, + reinterpret_cast(session_key.data()), + session_key.size(), + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, + oemcrypto_derive_keys_from_session_key_, + sts); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", + sts); + return false; + } + } + + return true; + } + + // Load the keys in |keys|. The initial keys are saved for key rotation. + OEMCryptoResult LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, + CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { + if (state_ == kInitializing) { + state_ = kInitialLicenseLoaded; + keys_ = keys; + OEMCryptoResult sts = DoLoadKeys(message, + signature, + mac_key_iv, + mac_key, + keys, + provider_session_token, + cipher_mode, + srm_requirement); + if (OEMCrypto_SUCCESS != sts) { + state_ = kInitialLicenseFailed; + } + return sts; + } + return DoSubLicenseLoadKeys(message, signature, mac_key_iv, mac_key, + keys[0], provider_session_token, + cipher_mode, srm_requirement); + } + + // Each oemcrypto session contains a single key. Find the right sub session + // and save it's id as the selected oemcrypto session. + OEMCryptoResult SelectKey(const std::string& key_id) { + for (size_t i = 0; i < keys_.size(); ++i) { + if (keys_[i].key_id() == key_id) { + cached_sub_session_key_id_ = keys_[i].sub_session_key_id(); + } + } + return OEMCrypto_SUCCESS; + } + + // Decrypt performs the decryption using the selected oemcrypto session. + // TODO(jfore): Support DecryptInChunks. + OEMCryptoResult Decrypt( + const CdmDecryptionParameters& params, + OEMCrypto_DestBufferDesc& buffer_descriptor, + OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) { + SubLicenseSessionMap::iterator it = + sub_license_oec_sessions_.find(cached_sub_session_key_id_); + if (it == sub_license_oec_sessions_.end()) { + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DecryptCENC( + it->second, + params.encrypt_buffer, + params.encrypt_length, + params.is_encrypted, + &(*params.iv).front(), + params.block_offset, + &buffer_descriptor, + &pattern_descriptor, + params.subsample_flags), + metrics_, + oemcrypto_decrypt_cenc_, + sts, + metrics::Pow2Bucket(params.encrypt_length)); + return sts; + } + + private: + // Destroy each open oemcrypto session and relace them with new ones. + OEMCryptoResult ResetCryptoSessions() { + for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin(); + it != sub_license_oec_sessions_.end(); it++) { + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_CloseSession(it->second), + metrics_, oemcrypto_close_session_, sts); + if (OEMCrypto_SUCCESS != sts) { + return sts; + } + M_TIME( + sts = OEMCrypto_OpenSession(&it->second, requested_security_level_), + metrics_, oemcrypto_open_session_, sts, requested_security_level_); + if (OEMCrypto_SUCCESS != sts) { + return sts; + } + M_TIME( + sts = OEMCrypto_LoadDeviceRSAKey( + it->second, + reinterpret_cast( + wrapped_private_device_key_.data()), + wrapped_private_device_key_.size()), + metrics_, oemcrypto_load_device_rsa_key_, sts); + if (OEMCrypto_SUCCESS != sts) { + return sts; + } + } + return OEMCrypto_SUCCESS; + } + + // DoLoadKeys loads a single key into each oemcrypto session. + OEMCryptoResult DoLoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& keys, + const std::string& provider_session_token, + CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { + const uint8_t* msg = reinterpret_cast(message.data()); + const uint8_t* enc_mac_key = NULL; + const uint8_t* enc_mac_key_iv = NULL; + if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { + enc_mac_key = msg + GetOffset(message, mac_key); + enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); + } else { + LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); + } + + uint8_t* pst = NULL; + if (!provider_session_token.empty()) { + pst = const_cast(msg) + + GetOffset(message, provider_session_token); + } + + uint8_t* srm_req = NULL; + if (!srm_requirement.empty()) { + srm_req = const_cast(msg) + + GetOffset(message, srm_requirement); + } + + // TODO(jfore): Use C++ 11 range loop? + for (size_t i = 0; i < keys.size(); i++) { + OEMCrypto_KeyObject key_object; + const CryptoKey& key_data = keys[i]; + key_object.key_id = msg + GetOffset(message, key_data.key_id()); + key_object.key_id_length = key_data.key_id().length(); + key_object.key_data_iv = msg + GetOffset(message, key_data.key_data_iv()); + key_object.key_data = msg + GetOffset(message, key_data.key_data()); + key_object.key_data_length = key_data.key_data().length(); + if (key_data.HasKeyControl()) { + key_object.key_control_iv = + msg + GetOffset(message, key_data.key_control_iv()); + key_object.key_control = + msg + GetOffset(message, key_data.key_control()); + } else { + LOGE("For key %s: XXX key has no control block. size=%d", + key_data.key_id().c_str(), key_data.key_control().size()); + key_object.key_control_iv = NULL; + key_object.key_control = NULL; + } + key_object.cipher_mode = key_data.cipher_mode() == kCipherModeCbc + ? OEMCrypto_CipherMode_CBC + : OEMCrypto_CipherMode_CTR; + *cipher_mode = key_data.cipher_mode(); + + SubLicenseSessionMap::iterator oec_session_id = + sub_license_oec_sessions_.find(key_data.sub_session_key_id()); + if (oec_session_id == sub_license_oec_sessions_.end()) { + LOGE("CryptoSession::LoadKeys: Unrecognized sub session %s", + key_data.sub_session_key_id().c_str()); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_LoadKeys( + oec_session_id->second, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, + pst, provider_session_token.length(), srm_req), + metrics_, + oemcrypto_load_keys_, + sts); + + if (sts != OEMCrypto_SUCCESS) { + return sts; + } + + M_TIME( + sts = OEMCrypto_SelectKey( + oec_session_id->second, + reinterpret_cast(key_data.key_id().data()), + key_data.key_id().size()), + metrics_, + oemcrypto_select_key_, + sts); + + if (sts != OEMCrypto_SUCCESS) { + return sts; + } + } + keys_ = keys; + return OEMCrypto_SUCCESS; + } + + // DoLoadKeys loads a single key into each oemcrypto session. + OEMCryptoResult DoSubLicenseLoadKeys( + const std::string& message, const std::string& signature, + const std::string& mac_key_iv, const std::string& mac_key, + const CryptoKey& key, const std::string& provider_session_token, + CdmCipherMode*, const std::string& srm_requirement) { + + SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.end(); + size_t key_index = 0; + for (; key_index < keys_.size(); key_index++) { + if (keys_[key_index].track_label() == key.track_label()) { + it = sub_license_oec_sessions_.find( + keys_[key_index].sub_session_key_id()); + CryptoKey tmp = key; + tmp.set_sub_session_key_id(keys_[key_index].sub_session_key_id()); + tmp.set_sub_session_key(keys_[key_index].sub_session_key()); + keys_[key_index] = tmp; + break; + } + } + if (it == sub_license_oec_sessions_.end()) { + return OEMCrypto_SUCCESS; + } + + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second); + + std::string mac_deriv_message; + std::string enc_deriv_message; + GenerateMacContext(key.track_label(), &mac_deriv_message); + GenerateEncryptContext(key.track_label(), &enc_deriv_message); + + const uint8_t* msg = reinterpret_cast(message.data()); + const uint8_t* enc_mac_key = NULL; + const uint8_t* enc_mac_key_iv = NULL; + if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { + enc_mac_key = msg + GetOffset(message, mac_key); + enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); + } else { + LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); + } + + uint8_t* pst = NULL; + if (!provider_session_token.empty()) { + pst = const_cast(msg) + + GetOffset(message, provider_session_token); + } + + uint8_t* srm_req = NULL; + if (!srm_requirement.empty()) { + srm_req = const_cast(msg) + + GetOffset(message, srm_requirement); + } + + OEMCryptoResult sts; + const std::string& sub_session_key = keys_[key_index].sub_session_key(); + LOGE("ssksize = %d", sub_session_key.size()); + + M_TIME( + sts = OEMCrypto_DeriveKeysFromSessionKey( + it->second, + reinterpret_cast(sub_session_key.data()), + sub_session_key.size(), + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, + oemcrypto_derive_keys_from_session_key_, + sts); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", + sts); + return sts; + } + + OEMCrypto_KeyObject key_object; + key_object.key_id = msg + GetOffset(message, keys_[key_index].key_id()); + key_object.key_id_length = keys_[key_index].key_id().length(); + key_object.key_data_iv = msg + GetOffset( + message, keys_[key_index].key_data_iv()); + key_object.key_data = msg + GetOffset(message, keys_[key_index].key_data()); + key_object.key_data_length = keys_[key_index].key_data().length(); + if (key.HasKeyControl()) { + key_object.key_control_iv = + msg + GetOffset(message, keys_[key_index].key_control_iv()); + key_object.key_control = msg + GetOffset( + message, keys_[key_index].key_control()); + } + key_object.cipher_mode = + (keys_[key_index].cipher_mode() == kCipherModeCbc) ? + OEMCrypto_CipherMode_CBC : OEMCrypto_CipherMode_CTR; + + M_TIME( + sts = OEMCrypto_LoadKeys( + it->second, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, + pst, provider_session_token.length(), srm_req), + metrics_, + oemcrypto_load_keys_, + sts); + + if (sts != OEMCrypto_SUCCESS) { + return sts; + } + + M_TIME( + sts = OEMCrypto_SelectKey( + it->second, + reinterpret_cast(keys_[key_index].key_id().data()), + keys_[key_index].key_id().size()), + metrics_, + oemcrypto_select_key_, + sts); + + return sts; + } + + SubLicenseState state_; + std::string cached_sub_session_key_id_; + std::string wrapped_private_device_key_; + std::string message_; + std::string session_key_; + std::vector keys_; + SubLicenseSessionMap& sub_license_oec_sessions_; + SecurityLevel requested_security_level_; +}; + CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics) : metrics_(metrics), open_(false), @@ -164,6 +810,10 @@ bool CryptoSession::GetTokenFromKeybox(std::string* token) { } bool CryptoSession::GetTokenFromOemCert(std::string* token) { + if (token == NULL) { + LOGE("CryptoSession::GetTokenFromOemCert: token not provided "); + return false; + } OEMCryptoResult status; if (!oem_token_.empty()) { token->assign(oem_token_); @@ -179,7 +829,7 @@ bool CryptoSession::GetTokenFromOemCert(std::string* token) { if (OEMCrypto_SUCCESS == status) { temp_buffer.resize(buf_size); oem_token_.assign(temp_buffer); - token->swap(temp_buffer); + token->assign(temp_buffer); return true; } if (OEMCrypto_ERROR_SHORT_BUFFER && !retrying) { @@ -464,13 +1114,8 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { requested_security_level_ = requested_security_level; OEMCryptoResult sts; M_TIME( - sts = OEMCrypto_OpenSession( - &sid, - requested_security_level), - metrics_, - oemcrypto_open_session_, - sts, - requested_security_level); + sts = OEMCrypto_OpenSession(&sid, requested_security_level), + metrics_, oemcrypto_open_session_, sts, requested_security_level); if (OEMCrypto_SUCCESS == sts) { oec_session_id_ = static_cast(sid); LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_); @@ -525,6 +1170,9 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { } } + // TODO(gmorgan, jfore): resolve handling of usage records in sublicenses + key_session_.reset(new DefaultKeySession(oec_session_id_, metrics_)); + return NO_ERROR; } @@ -538,12 +1186,10 @@ void CryptoSession::Close() { AutoLock auto_lock(crypto_lock_); if (!open_) return; + key_session_.reset(); M_TIME( - close_sts = OEMCrypto_CloseSession( - oec_session_id_), - metrics_, - oemcrypto_close_session_, - close_sts); + close_sts = OEMCrypto_CloseSession(oec_session_id_), + metrics_, oemcrypto_close_session_, close_sts); if (OEMCrypto_SUCCESS == close_sts) open_ = false; update_usage_table = update_usage_table_after_close_session_; @@ -611,49 +1257,6 @@ bool CryptoSession::PrepareRenewalRequest(const std::string& message, return true; } -void CryptoSession::GenerateMacContext(const std::string& input_context, - std::string* deriv_context) { - if (!deriv_context) { - LOGE("CryptoSession::GenerateMacContext : No output destination provided."); - return; - } - - const std::string kSigningKeyLabel = "AUTHENTICATION"; - const size_t kSigningKeySizeBits = MAC_KEY_SIZE * 8; - - deriv_context->assign(kSigningKeyLabel); - deriv_context->append(1, '\0'); - deriv_context->append(input_context); - deriv_context->append(EncodeUint32(kSigningKeySizeBits * 2)); -} - -void CryptoSession::GenerateEncryptContext(const std::string& input_context, - std::string* deriv_context) { - if (!deriv_context) { - LOGE( - "CryptoSession::GenerateEncryptContext : No output destination " - "provided."); - return; - } - - const std::string kEncryptionKeyLabel = "ENCRYPTION"; - const size_t kEncryptionKeySizeBits = KEY_SIZE * 8; - - deriv_context->assign(kEncryptionKeyLabel); - deriv_context->append(1, '\0'); - deriv_context->append(input_context); - deriv_context->append(EncodeUint32(kEncryptionKeySizeBits)); -} - -size_t CryptoSession::GetOffset(std::string message, std::string field) { - size_t pos = message.find(field); - if (pos == std::string::npos) { - LOGE("CryptoSession::GetOffset : Cannot find offset for %s", field.c_str()); - pos = 0; - } - return pos; -} - CdmResponseType CryptoSession::LoadKeys( const std::string& message, const std::string& signature, const std::string& mac_key_iv, const std::string& mac_key, @@ -663,65 +1266,11 @@ CdmResponseType CryptoSession::LoadKeys( LOGV("CryptoSession::LoadKeys: Lock"); AutoLock auto_lock(crypto_lock_); - const uint8_t* msg = reinterpret_cast(message.data()); - const uint8_t* enc_mac_key = NULL; - const uint8_t* enc_mac_key_iv = NULL; - if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { - enc_mac_key = msg + GetOffset(message, mac_key); - enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); - } else { - LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); - } - std::vector load_keys(keys.size()); - for (size_t i = 0; i < keys.size(); ++i) { - const CryptoKey* ki = &keys[i]; - OEMCrypto_KeyObject* ko = &load_keys[i]; - ko->key_id = msg + GetOffset(message, ki->key_id()); - 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()); - } else { - LOGE("For key %d: XXX key has no control block. size=%d", i, - ki->key_control().size()); - ko->key_control_iv = NULL; - ko->key_control = NULL; - } - ko->cipher_mode = ki->cipher_mode() == kCipherModeCbc - ? OEMCrypto_CipherMode_CBC - : OEMCrypto_CipherMode_CTR; - cipher_mode_ = ki->cipher_mode(); - } - uint8_t* pst = NULL; - if (!provider_session_token.empty()) { - pst = const_cast(msg) + GetOffset(message, provider_session_token); - } - uint8_t* srm_req = NULL; - if (!srm_requirement.empty()) - srm_req = const_cast(msg) + GetOffset(message, srm_requirement); - LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_); - OEMCryptoResult sts; - M_TIME( - sts = OEMCrypto_LoadKeys( - oec_session_id_, - msg, - message.size(), - reinterpret_cast(signature.data()), - signature.size(), - enc_mac_key_iv, - enc_mac_key, - keys.size(), - &load_keys[0], - pst, - provider_session_token.length(), - srm_req), - metrics_, - oemcrypto_load_keys_, - sts); + OEMCryptoResult sts = key_session_->LoadKeys( + message, signature, mac_key_iv, mac_key, keys, provider_session_token, + &cipher_mode_, srm_requirement); + CdmResponseType result = KEY_ADDED; if (OEMCrypto_SUCCESS == sts) { if (!provider_session_token.empty()) @@ -762,7 +1311,6 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size); LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_); - M_TIME( sts = OEMCrypto_LoadDeviceRSAKey( oec_session_id_, @@ -776,6 +1324,7 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { LOGE("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts); return false; } + wrapped_key_ = wrapped_key; return true; } @@ -827,31 +1376,8 @@ bool CryptoSession::RefreshKeys(const std::string& message, CdmResponseType CryptoSession::SelectKey(const std::string& key_id) { // Crypto session lock already locked. - if (!cached_key_id_.empty() && cached_key_id_ == key_id) { - // Already using the desired key. - return NO_ERROR; - } - cached_key_id_ = key_id; - - const uint8_t* key_id_string = - reinterpret_cast(cached_key_id_.data()); - - OEMCryptoResult sts; - - M_TIME( - sts = OEMCrypto_SelectKey( - oec_session_id_, - key_id_string, - cached_key_id_.size()), - metrics_, - oemcrypto_select_key_, - sts); - - - if (OEMCrypto_SUCCESS != sts) { - cached_key_id_.clear(); - } + OEMCryptoResult sts = key_session_->SelectKey(key_id); switch (sts) { case OEMCrypto_SUCCESS: @@ -880,59 +1406,12 @@ CdmResponseType CryptoSession::SelectKey(const std::string& key_id) { } bool CryptoSession::GenerateDerivedKeys(const std::string& message) { - std::string mac_deriv_message; - std::string enc_deriv_message; - GenerateMacContext(message, &mac_deriv_message); - GenerateEncryptContext(message, &enc_deriv_message); - - LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); - OEMCryptoResult sts; - M_TIME( - sts = OEMCrypto_GenerateDerivedKeys( - oec_session_id_, - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()), - metrics_, - oemcrypto_generate_derived_keys_, - sts); - if (OEMCrypto_SUCCESS != sts) { - LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts); - return false; - } - - return true; + return key_session_->GenerateDerivedKeys(message); } bool CryptoSession::GenerateDerivedKeys(const std::string& message, const std::string& session_key) { - std::string mac_deriv_message; - std::string enc_deriv_message; - GenerateMacContext(message, &mac_deriv_message); - GenerateEncryptContext(message, &enc_deriv_message); - - LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); - OEMCryptoResult sts; - M_TIME( - sts = OEMCrypto_DeriveKeysFromSessionKey( - oec_session_id_, - reinterpret_cast(session_key.data()), - session_key.size(), - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()), - metrics_, - oemcrypto_derive_keys_from_session_key_, - sts); - - if (OEMCrypto_SUCCESS != sts) { - LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts); - return false; - } - - return true; + return key_session_->GenerateDerivedKeys(message, session_key); } bool CryptoSession::GenerateSignature(const std::string& message, @@ -1095,27 +1574,13 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { if (result != NO_ERROR) return result; } - M_TIME( - sts = OEMCrypto_DecryptCENC( - oec_session_id_, - params.encrypt_buffer, - params.encrypt_length, - params.is_encrypted, - &(*params.iv).front(), - params.block_offset, - &buffer_descriptor, - &pattern_descriptor, - params.subsample_flags), - metrics_, - oemcrypto_decrypt_cenc_, - sts, - metrics::Pow2Bucket(params.encrypt_length)); + sts = key_session_->Decrypt(params, buffer_descriptor, pattern_descriptor); if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) { - // OEMCrypto_DecryptCENC rejected the buffer as too large, so chunk it up - // into sections no more than 100 KiB. The exact chunk size needs to be - // an even number of pattern repetitions long or else the pattern will get - // out of sync. + // OEMCrypto_DecryptCENC rejected the buffer as too large, so chunk it + // up into sections no more than 100 KiB. The exact chunk size needs to + // be an even number of pattern repetitions long or else the pattern + // will get out of sync. const size_t pattern_length = (pattern_descriptor.encrypt + pattern_descriptor.skip) * kAes128BlockSize; @@ -1631,7 +2096,7 @@ bool CryptoSession::RewrapDeviceRSAKey30(const std::string& message, wrapped_private_key->resize(wrapped_private_key_length); if (OEMCrypto_SUCCESS != status) { - LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status); + LOGE("OEMCrypto_RewrapDeviceRSAKey30 fails with %d", status); return false; } return true; @@ -1642,7 +2107,9 @@ bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current, LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_); if (!initialized_) return false; if (current == NULL || max == NULL) { - LOGE("CryptoSession::GetHdcpCapabilities: |current|, |max| cannot be NULL"); + LOGE( + "CryptoSession::GetHdcpCapabilities: |current|, |max| cannot be " + "NULL"); return false; } OEMCryptoResult status; @@ -2278,14 +2745,14 @@ bool CryptoSession::CreateOldUsageEntry( return false; } - OEMCrypto_Usage_Entry_Status status; + OEMCrypto_Usage_Entry_Status status = kUnused; switch (usage_duration_status) { case kUsageDurationsInvalid: status = kUnused; break; case kUsageDurationPlaybackNotBegun: status = kInactiveUnused; break; case kUsageDurationsValid: status = kActive; break; default: - LOGE("CreateOldUsageEntry: Unrecognized usage entry status: %d", status); - status = kUnused; + LOGE("CreateOldUsageEntry: Unrecognized usage duration status: %d", + usage_duration_status); return false; } @@ -2336,9 +2803,8 @@ CdmResponseType CryptoSession::AddSubSession( return UNKNOWN_ERROR; } - OEMCryptoResult sts; CryptoSessionId sid; - sts = OEMCrypto_OpenSession(&sid,requested_security_level_); + OEMCryptoResult sts = OEMCrypto_OpenSession(&sid, requested_security_level_); if (OEMCrypto_ERROR_TOO_MANY_SESSIONS == sts) { LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts, session_count_, (int)initialized_); @@ -2349,12 +2815,43 @@ CdmResponseType CryptoSession::AddSubSession( return UNKNOWN_ERROR; } - // TODO(jfore): Is there some session count metrics that should be add or - // update? + sts = OEMCrypto_LoadDeviceRSAKey( + sid, reinterpret_cast(wrapped_key_.data()), + wrapped_key_.size()); + sub_license_oec_sessions_[sub_session_key_id] = sid; + if (key_session_->Type() != KeySession::kSubLicense) { + key_session_.reset( + new SubLicenseKeySession(sub_license_oec_sessions_, metrics_, + wrapped_key_, requested_security_level_)); + } return NO_ERROR; } +bool CryptoSession::GenerateSubSessionNonce( + const std::string& sub_session_key_id, bool* exists, uint32_t* nonce) { + if (!exists || !nonce) { + LOGE("input parameter is null"); + return false; + } + + LOGV("CryptoSession::GenerateSubSessionNonce: Lock"); + AutoLock auto_lock(crypto_lock_); + + SubLicenseSessionMap::iterator it = + sub_license_oec_sessions_.find(sub_session_key_id); + if (it == sub_license_oec_sessions_.end()) { + // A subsession does not exist. Indicate that and return success. + *exists = false; + return false; + } + *exists = true; + + OEMCryptoResult result; + result = OEMCrypto_GenerateNonce(it->second, nonce); + return OEMCrypto_SUCCESS == result; +} + OEMCrypto_Algorithm CryptoSession::GenericSigningAlgorithm( CdmSigningAlgorithm algorithm) { if (kSigningAlgorithmHmacSha256 == algorithm) { @@ -2396,8 +2893,8 @@ OEMCryptoResult CryptoSession::CopyBufferInChunks( const size_t additional_offset = params.encrypt_length - remaining_encrypt_length; - // Update the remaining length of the original buffer only after calculating - // the new values. + // Update the remaining length of the original buffer only after + // calculating the new values. remaining_encrypt_length -= chunk_size; // Update the destination buffer with the new offset. @@ -2476,8 +2973,8 @@ OEMCryptoResult CryptoSession::DecryptInChunks( // calculating the new values. remaining_encrypt_length -= chunk_size; - // Update the destination buffer with the new offset. Because OEMCrypto can - // modify the OEMCrypto_DestBufferDesc during the call to + // Update the destination buffer with the new offset. Because OEMCrypto + // can modify the OEMCrypto_DestBufferDesc during the call to // OEMCrypto_DecryptCENC, (and is known to do so on some platforms) a new // OEMCrypto_DestBufferDesc must be allocated for each call. OEMCrypto_DestBufferDesc buffer_descriptor = full_buffer_descriptor; @@ -2561,8 +3058,8 @@ OEMCryptoResult CryptoSession::DecryptInChunks( // For cbcs, we must look for the last encrypted block, which is // probably not the last block of the subsample. Luckily, since the // buffer size is guaranteed to be an even number of pattern - // repetitions long, we can use the pattern to know how many blocks to - // look back. + // repetitions long, we can use the pattern to know how many blocks + // to look back. block_end = buffer_end - kAes128BlockSize * pattern_descriptor.skip; } diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp index 112a2e6b..e18fcbea 100644 --- a/libwvdrmengine/cdm/core/src/initialization_data.cpp +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -14,10 +14,6 @@ namespace { const char kKeyFormatVersionsSeparator = '/'; -const char kColon = ':'; -const char kDoubleQuote = '\"'; -const char kLeftBracket = '['; -const char kRightBracket = ']'; const std::string kBase64String = "base64,"; const uint32_t kFourCcCbc1 = 0x63626331; const uint32_t kFourCcCbcs = 0x63626373; diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 3a0d2cd8..a4f31774 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -36,7 +36,6 @@ const uint32_t kFourCcCbcs = 0x63626373; const uint32_t kFourCcLittleEndianCbc1 = 0x31636263; const uint32_t kFourCcLittleEndianCbcs = 0x73636263; const uint32_t kFourCcCenc = 0x63656e63; -const uint32_t kFourCcCens = 0x63656e73; } // namespace @@ -49,17 +48,41 @@ using video_widevine::ClientIdentification_NameValue; using video_widevine::DrmDeviceCertificate; using video_widevine::EncryptedClientIdentification; using video_widevine::License; -using video_widevine::License_KeyContainer; using video_widevine::LicenseError; using video_widevine::LicenseIdentification; using video_widevine::LicenseRequest; using video_widevine::LicenseRequest_ContentIdentification; using video_widevine::LicenseRequest_ContentIdentification_CencDeprecated; -using video_widevine::LicenseRequest_ContentIdentification_WebmDeprecated; using video_widevine::LicenseRequest_ContentIdentification_ExistingLicense; +using video_widevine::LicenseRequest_ContentIdentification_WebmDeprecated; +using video_widevine::License_KeyContainer; using video_widevine::SignedDrmDeviceCertificate; using video_widevine::SignedMessage; +static std::vector ExtractSubSessionKeys(const License& license) { + std::vector key_array; + + // Extract sub session key(s) + for (int i = 0; i < license.key_size(); ++i) { + CryptoKey key; + switch (license.key(i).type()) { + case License_KeyContainer::SUB_SESSION: + key.set_key_data(license.key(i).key()); + key.set_key_data_iv(license.key(i).iv()); + key.set_key_id(license.key(i).id()); + key.set_track_label(license.key(i).track_label()); + key_array.push_back(key); + break; + + default: + // Ignore all but SUB_SESSION key types. + break; + } + } + + return key_array; +} + static std::vector ExtractContentKeys(const License& license) { std::vector key_array; @@ -88,6 +111,7 @@ static std::vector ExtractContentKeys(const License& license) { if (license.has_protection_scheme()) { four_cc = license.protection_scheme(); } + key.set_track_label(license.key(i).track_label()); switch (four_cc) { // b/30713238: Android N assumed that the "protection scheme" Four // CC code, after being extracted from the protobuf, was host byte @@ -116,11 +140,28 @@ static std::vector ExtractContentKeys(const License& license) { } break; default: - // Ignore SIGNING key types as they are not content related + // Ignore SIGNING and SUB_SESSION key types as they are not content + // related. break; } } + std::vector sub_session_keys = ExtractSubSessionKeys(license); + // Match the track label from the key arrays and add sub_license_key_id to + // the content key array. + LOGV("Received %d subsession keys", sub_session_keys.size()); + if (!sub_session_keys.empty()) { + for (size_t i = 0; i < key_array.size(); ++i) { + if (key_array[i].track_label().empty()) continue; + for (size_t x = 0; x < sub_session_keys.size(); ++x) { + if (sub_session_keys[x].track_label() == key_array[i].track_label()) { + key_array[i].set_sub_session_key_id(sub_session_keys[x].key_id()); + key_array[i].set_sub_session_key(sub_session_keys[x].key_data()); + } + } + } + } + return key_array; } @@ -222,8 +263,8 @@ CdmResponseType CdmLicense::PrepareKeyRequest( status = PrepareClientId(app_parameters, &license_request); if (NO_ERROR != status) return status; - status = PrepareContentId(init_data, license_type, request_id, - &license_request); + status = + PrepareContentId(init_data, license_type, request_id, &license_request); if (NO_ERROR != status) return status; license_request.set_type(LicenseRequest::NEW); @@ -238,6 +279,37 @@ CdmResponseType CdmLicense::PrepareKeyRequest( } license_request.set_key_control_nonce(nonce); LOGD("PrepareKeyRequest: nonce=%u", nonce); + + // Prepare the request for any embedded keys that may exist in the + // initialization data. + std::vector embedded_key_data = + init_data.ExtractEmbeddedKeys(); + for (size_t i = 0; i < embedded_key_data.size(); ++i) { + bool exists = false; + if (!crypto_session_->GenerateSubSessionNonce( + embedded_key_data[i].sub_session_key_id(), &exists, &nonce)) { + if (exists) { + return LICENSE_REQUEST_NONCE_GENERATION_ERROR; + } + } + SignedMessage signed_sub_license; + License_KeyContainer keyc; + + // Parse the sub license for this track to extract the label. + if (!signed_sub_license.ParseFromString(embedded_key_data[i].key_msg()) || + !keyc.ParseFromString(signed_sub_license.msg()) || + keyc.track_label().empty()) { + return LICENSE_REQUEST_INVALID_SUBLICENSE; + } + + LicenseRequest::SubSessionData* sub_session_data = + license_request.add_sub_session_data(); + sub_session_data->set_sub_session_key_id( + embedded_key_data[i].sub_session_key_id()); + sub_session_data->set_nonce(nonce); + sub_session_data->set_track_label(keyc.track_label()); + } + license_request.set_protocol_version(video_widevine::VERSION_2_1); // License request is complete. Serialize it. @@ -333,7 +405,8 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest( } // TODO(rfrias): Refactor to avoid needing to call CdmSession - if (cdm_session) { + if (cdm_session && + cdm_session->get_usage_support_type() == kUsageEntrySupport) { CdmResponseType status = cdm_session->UpdateUsageEntryInformation(); if (NO_ERROR != status) return status; } @@ -446,7 +519,6 @@ CdmResponseType CdmLicense::HandleKeyResponse( LOGE("CdmLicense::HandleKeyResponse: no session keys present"); return SESSION_KEYS_NOT_FOUND; } - if (!crypto_session_->GenerateDerivedKeys(key_request_, signed_response.session_key())) return GENERATE_DERIVED_KEYS_ERROR; @@ -595,6 +667,48 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse( } } +CdmResponseType CdmLicense::HandleSubLicense( + const InitializationData& init_data) { + std::vector subkeys = + init_data.ExtractEmbeddedKeys(); + std::set loaded_keys; + // Build a license with the rotated keys. + License license; + for (size_t i = 0; i < subkeys.size(); ++i) { + SignedMessage sm; + if (!sm.ParseFromString(subkeys[i].key_msg())) { + return LICENSE_REQUEST_INVALID_SUBLICENSE; + } + License_KeyContainer keyc; + if (!keyc.ParseFromString(sm.msg())) { + return LICENSE_REQUEST_INVALID_SUBLICENSE; + } + std::vector keys; + keys.resize(1); + keys[0].set_key_id(keyc.id()); + keys[0].set_key_data(keyc.key()); + keys[0].set_key_data_iv(keyc.iv()); + keys[0].set_key_control(keyc.key_control().key_control_block()); + keys[0].set_key_control_iv(keyc.key_control().iv()); + keys[0].set_track_label(keyc.track_label()); + //TODO: passing empty cipher_mode and srm_req params - OK? + CdmResponseType result = crypto_session_->LoadKeys( + sm.msg(), sm.signature(), std::string(), std::string(), keys, + std::string(), std::string()); + if (result != KEY_ADDED) { + LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d", + result); + return result; + } + loaded_keys.insert(keyc.id()); + *license.add_key() = keyc; + } + loaded_keys_.swap(loaded_keys); + policy_engine_->UpdateLicenseKeys(license); + + return KEY_MESSAGE; +} + bool CdmLicense::RestoreOfflineLicense( const CdmKeyMessage& license_request, const CdmKeyResponse& license_response, @@ -642,7 +756,8 @@ bool CdmLicense::RestoreOfflineLicense( } if (!provider_session_token_.empty()) { - if (cdm_session) { + if (cdm_session && + cdm_session->get_usage_support_type() == kUsageEntrySupport) { CdmResponseType status = cdm_session->UpdateUsageEntryInformation(); if (NO_ERROR != status) return false; } @@ -1067,8 +1182,7 @@ CdmResponseType CdmLicense::PrepareContentId( template bool CdmLicense::SetTypeAndId(CdmLicenseType license_type, - const std::string& request_id, - T* content_id) { + const std::string& request_id, T* content_id) { switch (license_type) { case kLicenseTypeOffline: content_id->set_license_type(video_widevine::OFFLINE); diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index 1d503d2b..84673380 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -18,7 +18,6 @@ namespace { const int kCdmPolicyTimerDurationSeconds = 1; const int kClockSkewDelta = 5; // seconds -const int64_t kHdcpCheckInterval = 10; } // namespace @@ -169,6 +168,14 @@ void PolicyEngine::SetLicense(const License& license) { UpdateLicense(license); } +void PolicyEngine::UpdateLicenseKeys(const video_widevine::License& license) { + // Use the current policy and set the new keys. + video_widevine::License loadable = license; + loadable.mutable_policy()->CopyFrom(policy_); + license_keys_->SetFromLicense(loadable); + NotifyKeysChange(kKeyStatusUsable); +} + void PolicyEngine::SetLicenseForRelease(const License& license) { license_id_.Clear(); license_id_.CopyFrom(license.id()); diff --git a/libwvdrmengine/cdm/core/src/privacy_crypto_openssl.cpp b/libwvdrmengine/cdm/core/src/privacy_crypto_openssl.cpp index d68f53aa..09206f4a 100644 --- a/libwvdrmengine/cdm/core/src/privacy_crypto_openssl.cpp +++ b/libwvdrmengine/cdm/core/src/privacy_crypto_openssl.cpp @@ -88,38 +88,60 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, return false; } - EVP_CIPHER_CTX ctx; - if (EVP_EncryptInit(&ctx, EVP_aes_128_cbc(), +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX ctx_struct; + EVP_CIPHER_CTX* evp_cipher_ctx = &ctx_struct; +#else + EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new(); +#endif + if (EVP_EncryptInit(evp_cipher_ctx, EVP_aes_128_cbc(), reinterpret_cast(&key_[0]), reinterpret_cast(&(*iv)[0])) == 0) { LOGE("AesCbcKey::Encrypt: AES CBC setup failure: %s", ERR_error_string(ERR_get_error(), NULL)); - EVP_CIPHER_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif return false; } out->resize(in.size() + AES_BLOCK_SIZE); int out_length = out->size(); if (EVP_EncryptUpdate( - &ctx, reinterpret_cast(&(*out)[0]), &out_length, + evp_cipher_ctx, reinterpret_cast(&(*out)[0]), &out_length, reinterpret_cast(const_cast(in.data())), in.size()) == 0) { LOGE("AesCbcKey::Encrypt: encryption failure: %s", ERR_error_string(ERR_get_error(), NULL)); - EVP_CIPHER_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif return false; } int padding = 0; - if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast(&(*out)[out_length]), + if (EVP_EncryptFinal_ex(evp_cipher_ctx, + reinterpret_cast(&(*out)[out_length]), &padding) == 0) { LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s", ERR_error_string(ERR_get_error(), NULL)); - EVP_CIPHER_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif return false; } - EVP_CIPHER_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif out->resize(out_length + padding); return true; } @@ -195,11 +217,16 @@ static int LogOpenSSLError(const char* msg, size_t /* len */, void* /* ctx */) { static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message, const std::string &signature) { - EVP_MD_CTX ctx; - EVP_MD_CTX_init(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX ctx_struct; + EVP_MD_CTX* evp_md_ctx = &ctx_struct; + EVP_MD_CTX_init(evp_md_ctx); +#else + EVP_MD_CTX* evp_md_ctx = EVP_MD_CTX_new(); +#endif EVP_PKEY_CTX *pctx = NULL; - if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */, + if (EVP_DigestVerifyInit(evp_md_ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */, pkey) != 1) { LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); goto err; @@ -221,13 +248,13 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message, goto err; } - if (EVP_DigestVerifyUpdate(&ctx, message.data(), message.size()) != 1) { + if (EVP_DigestVerifyUpdate(evp_md_ctx, message.data(), message.size()) != 1) { LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); goto err; } if (EVP_DigestVerifyFinal( - &ctx, const_cast( + evp_md_ctx, const_cast( reinterpret_cast(signature.data())), signature.size()) != 1) { LOGE( @@ -236,12 +263,20 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message, goto err; } - EVP_MD_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX_cleanup(evp_md_ctx); +#else + EVP_MD_CTX_free(evp_md_ctx); +#endif return true; err: ERR_print_errors_cb(LogOpenSSLError, NULL); - EVP_MD_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX_cleanup(evp_md_ctx); +#else + EVP_MD_CTX_free(evp_md_ctx); +#endif return false; } diff --git a/libwvdrmengine/cdm/core/src/service_certificate.cpp b/libwvdrmengine/cdm/core/src/service_certificate.cpp index 3f331076..c6c9ec01 100644 --- a/libwvdrmengine/cdm/core/src/service_certificate.cpp +++ b/libwvdrmengine/cdm/core/src/service_certificate.cpp @@ -200,14 +200,22 @@ CdmResponseType ServiceCertificate::VerifySignedMessage( return NO_ERROR; } -CdmResponseType ServiceCertificate::EncryptClientId( - CryptoSession* crypto_session, const ClientIdentification* clear_client_id, - EncryptedClientIdentification* encrypted_client_id) { +CdmResponseType ServiceCertificate::EncryptRsaOaep(const std::string& plaintext, + std::string* ciphertext) { if (!public_key_) { LOGE("Service certificate not set."); return DEVICE_CERTIFICATE_ERROR_4; } + if (!public_key_->Encrypt(plaintext, ciphertext)) + return CLIENT_ID_RSA_ENCRYPT_ERROR; + + return NO_ERROR; +} + +CdmResponseType ServiceCertificate::EncryptClientId( + CryptoSession* crypto_session, const ClientIdentification* clear_client_id, + EncryptedClientIdentification* encrypted_client_id) { encrypted_client_id->set_provider_id(provider_id_); encrypted_client_id->set_service_certificate_serial_number(serial_number_); @@ -227,8 +235,9 @@ CdmResponseType ServiceCertificate::EncryptClientId( if (!aes.Init(key)) return CLIENT_ID_AES_INIT_ERROR; if (!aes.Encrypt(id, &enc_id, &iv)) return CLIENT_ID_AES_ENCRYPT_ERROR; - if (!public_key_->Encrypt(key, &enc_key)) - return CLIENT_ID_RSA_ENCRYPT_ERROR; + CdmResponseType encrypt_result = EncryptRsaOaep(key, &enc_key); + if (encrypt_result != NO_ERROR) + return encrypt_result; encrypted_client_id->set_encrypted_client_id_iv(iv); encrypted_client_id->set_encrypted_privacy_key(enc_key); diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index cc1d84c4..c88d4a10 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -225,7 +225,7 @@ class WvCdmEnginePreProvTestStaging : public WvCdmEnginePreProvTest { static void SetUpTestCase() { // NOTE: Select server configuration - CommonSetup(kContentProtectionStagingLicense); + CommonSetup(kContentProtectionStagingServer); } }; @@ -237,7 +237,7 @@ class WvCdmEnginePreProvTestProd : public WvCdmEnginePreProvTest { static void SetUpTestCase() { // NOTE: Select server configuration - CommonSetup(kContentProtectionProdLicense); + CommonSetup(kContentProtectionProductionServer); } }; @@ -249,32 +249,20 @@ class WvCdmEnginePreProvTestUat : public WvCdmEnginePreProvTest { static void SetUpTestCase() { // NOTE: Select server configuration - CommonSetup(kContentProtectionUatLicense); + CommonSetup(kContentProtectionUatServer); } }; -class WvCdmEnginePreProvTestStagingProv30 : public WvCdmEnginePreProvTest { +class WvCdmEnginePreProvTestUatBinary : public WvCdmEnginePreProvTest { public: - WvCdmEnginePreProvTestStagingProv30() {} + WvCdmEnginePreProvTestUatBinary() {} - virtual ~WvCdmEnginePreProvTestStagingProv30() {} - - static void SetUpTestCase() { - // NOTE: Select server configuration - CommonSetup(kContentProtectionStagingPlusProv30); - } -}; - -class WvCdmEnginePreProvTestStagingProv30Binary : public WvCdmEnginePreProvTest { - public: - WvCdmEnginePreProvTestStagingProv30Binary() {} - - virtual ~WvCdmEnginePreProvTestStagingProv30Binary() {} + virtual ~WvCdmEnginePreProvTestUatBinary() {} static void SetUpTestCase() { // NOTE: Select server configuration // Override default setting of provisioning_messages_are_binary property - CommonSetup(kContentProtectionUatPlusProv30, true); + CommonSetup(kContentProtectionUatServer, true); } protected: @@ -340,25 +328,13 @@ class WvCdmEnginePreProvTestStagingProv30Binary : public WvCdmEnginePreProvTest }; -class WvCdmEnginePreProvTestUatProv30 : public WvCdmEnginePreProvTest { - public: - WvCdmEnginePreProvTestUatProv30() {} - - virtual ~WvCdmEnginePreProvTestUatProv30() {} - - static void SetUpTestCase() { - // NOTE: Select server configuration - CommonSetup(kContentProtectionStagingPlusProv30); - } -}; - class WvCdmEngineTest : public WvCdmEnginePreProvTest { public: WvCdmEngineTest() {} static void SetUpTestCase() { // NOTE: Select server configuration - CommonSetup(kContentProtectionStagingLicense); + CommonSetup(kContentProtectionStagingServer); } virtual void SetUp() { @@ -602,11 +578,7 @@ TEST_F(WvCdmEnginePreProvTestStaging, DISABLED_ProvisioningTest) { Provision(); } -TEST_F(WvCdmEnginePreProvTestStagingProv30, ProvisioningTest) { - Provision(); -} - -TEST_F(WvCdmEnginePreProvTestStagingProv30Binary, ProvisioningTest) { +TEST_F(WvCdmEnginePreProvTestUatBinary, DISABLED_ProvisioningTest) { Provision(); } diff --git a/libwvdrmengine/cdm/core/test/config_test_env.cpp b/libwvdrmengine/cdm/core/test/config_test_env.cpp index 3675a7f6..5c17bfe5 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.cpp +++ b/libwvdrmengine/cdm/core/test/config_test_env.cpp @@ -15,11 +15,104 @@ namespace { const std::string kWidevineKeySystem = "com.widevine.alpha"; -// For staging servers -// NOTE: This matches the service cert returned by the staging -// server. This is the one that the staging provisioning server uses. +// Content Protection license server (Production) data +const std::string kCpProductionLicenseServer = + "https://widevine-proxy.appspot.com/proxy"; +const std::string kCpProductionProvisioningServerUrl = + "https://www.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; +// NOTE: Provider ID = staging.google.com +const std::string kCpProductionServiceCertificate = + "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" + "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" + "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" + "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" + "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" + "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" + "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" + "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" + "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" + "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" + "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" + "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" + "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" + "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" + "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" + "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" + "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" + "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" + "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" + "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" + "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" + "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" + "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" + "250A4EB9C84AB3E6539F6B6FDF56899EA29914"; + +// Content Protection license server (UAT) data +const std::string kCpUatLicenseServer = + "https://proxy.uat.widevine.com/proxy"; +// TODO(rfrias): replace when b62880305 is addressed. For now use production URL +const std::string kCpUatProvisioningServerUrl = + "https://www.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; +// NOTE: Provider ID = staging.google.com +const std::string kCpUatServiceCertificate = + "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" + "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" + "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" + "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" + "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" + "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" + "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" + "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" + "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" + "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" + "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" + "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" + "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" + "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" + "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" + "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" + "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" + "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" + "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" + "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" + "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" + "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" + "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" + "250A4EB9C84AB3E6539F6B6FDF56899EA29914"; +const std::string kCpClientAuth = ""; +const std::string kCpKeyId = + "00000042" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000022" // pssh data size + // pssh data: + "08011a0d7769646576696e655f746573" // "streaming_clip1" + "74220f73747265616d696e675f636c69" + "7031"; +const std::string kCpOfflineKeyId = + "00000040" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000020" // pssh data size + // pssh data: + "08011a0d7769646576696e655f746573" // "offline_clip2" + "74220d6f66666c696e655f636c697032"; + +// Content Protection license server (staging) data +const std::string kCpStagingLicenseServer = + "https://proxy.staging.widevine.com/proxy"; +const std::string kCpStagingProvisioningServerUrl = + "https://staging-www.sandbox.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; // NOTE: Provider ID = license.widevine.com -const std::string kStagingServiceCertificate = +const std::string kCpStagingServiceCertificate = "0ac102080312101705b917cc1204868b06333a2f772a8c1882b482920522" "8e023082010a028201010099ed5b3b327dab5e24efc3b62a95b598520ad5" "bccb37503e0645b814d876b8df40510441ad8ce3adb11bb88c4e725a5e4a" @@ -44,95 +137,25 @@ const std::string kStagingServiceCertificate = "f9b4342cc8df543cb1a1182f7c5fff33f10490faca5b25360b76015e9c5a" "06ab8ee02f00d2e8d5986104aacc4dd475fd96ee9ce4e326f21b83c70585" "77b38732cddabc6a6bed13fb0d49d38a45eb87a5f4"; - -// NOTE: Provider ID = staging.google.com -const std::string kProdServiceCertificate = - "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" - "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" - "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" - "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" - "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" - "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" - "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" - "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" - "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" - "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" - "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" - "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" - "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" - "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" - "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" - "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" - "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" - "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" - "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" - "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" - "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" - "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" - "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" - "250A4EB9C84AB3E6539F6B6FDF56899EA29914"; - -// For UAT License servers -// NOTE: This matches the service cert returned by the UAT server. -// NOTE: Provider ID = staging.google.com -const std::string kUatServiceCertificate = - "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" - "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" - "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" - "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" - "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" - "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" - "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" - "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" - "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" - "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" - "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" - "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" - "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" - "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" - "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" - "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" - "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" - "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" - "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" - "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" - "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" - "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" - "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" - "250A4EB9C84AB3E6539F6B6FDF56899EA29914"; - -// Content Protection license server (Production) data -const std::string kCpProdLicenseServer = - "https://widevine-proxy.appspot.com/proxy"; - -// Content Protection license server (UAT) data -const std::string kCpUatLicenseServer = - "https://proxy.uat.widevine.com/proxy"; - -const std::string kCpClientAuth = ""; -const std::string kCpKeyId = - "00000042" // blob size +const CdmInitData kCpStagingSrmOuputProtectionRequired = + "0000003d" // blob size "70737368" // "pssh" "00000000" // flags "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id - "00000022" // pssh data size + "0000001d" // pssh data size // pssh data: "08011a0d7769646576696e655f746573" - "74220f73747265616d696e675f636c69" - "7031"; -const std::string kCpOfflineKeyId = - "00000040" // blob size + "74220a74656172735f73726d32"; // "tears_srm2" +const CdmInitData kCpStagingSrmOuputProtectionRequested = + "0000003d" // blob size "70737368" // "pssh" "00000000" // flags "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id - "00000020" // pssh data size + "0000001d" // pssh data size // pssh data: "08011a0d7769646576696e655f746573" - "74220d6f66666c696e655f636c697032"; - -// Content Protection license server (staging) data -const std::string kCpStagingLicenseServer = - "https://proxy.staging.widevine.com/proxy"; + "74220a74656172735f73726d32"; // "tears_srm1" +const CdmInitData kEmptyData; // Google Play license server data const std::string kGpLicenseServer = @@ -172,44 +195,18 @@ const std::string kWrongKeyId = "0901121094889920e8d6520098577df8" "f2dd5546"; -// URL of provisioning server (overrides value from GetProvisioningRequest()) -const std::string kProductionProvisioningServerUrl = - "https://www.googleapis.com/" - "certificateprovisioning/v1/devicecertificates/create" - "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; - -const std::string kStagingProvisioningServerUrl = - "https://staging-www.sandbox.googleapis.com/" - "certificateprovisioning/v1/devicecertificates/create" - "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; - const ConfigTestEnv::LicenseServerConfiguration license_servers[] = { - {kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId, - kGpOfflineKeyId, kStagingProvisioningServerUrl, ""}, - - {kContentProtectionProdLicense, kCpProdLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kProductionProvisioningServerUrl, kProdServiceCertificate}, - - {kContentProtectionUatLicense, kCpUatLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kProductionProvisioningServerUrl, kProdServiceCertificate}, - - {kContentProtectionStagingLicense, kCpStagingLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kStagingProvisioningServerUrl, kStagingServiceCertificate}, - - {kContentProtectionProdPlusProv30, kCpProdLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kStagingProvisioningServerUrl, kStagingServiceCertificate}, - - {kContentProtectionUatPlusProv30, kCpUatLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kStagingProvisioningServerUrl, kStagingServiceCertificate}, - - {kContentProtectionStagingPlusProv30, kCpStagingLicenseServer, - kStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kStagingProvisioningServerUrl, kStagingServiceCertificate}, + {kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId, + kGpOfflineKeyId, kCpProductionProvisioningServerUrl, ""}, + {kContentProtectionProductionServer, kCpProductionLicenseServer, + kCpProductionServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, + kCpProductionProvisioningServerUrl, kCpProductionServiceCertificate}, + {kContentProtectionUatServer, kCpUatLicenseServer, kCpUatServiceCertificate, + kCpClientAuth, kCpKeyId, kCpOfflineKeyId, + kCpUatProvisioningServerUrl, kCpUatServiceCertificate}, + {kContentProtectionStagingServer, kCpStagingLicenseServer, + kCpStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, + kCpStagingProvisioningServerUrl, kCpStagingServiceCertificate}, }; } // namespace @@ -254,4 +251,35 @@ void ConfigTestEnv::Init(ServerConfigurationId server_id) { wrong_key_id_ = kWrongKeyId; } +const CdmInitData ConfigTestEnv::GetInitData(ContentId content_id) { + switch (content_id) { + case kContentIdStreaming: + return wvcdm::a2bs_hex(kCpKeyId); + case kContentIdOffline: + return wvcdm::a2bs_hex(kCpOfflineKeyId); + case kContentIdStagingSrmOuputProtectionRequested: + return wvcdm::a2bs_hex(kCpStagingSrmOuputProtectionRequested); + case kContentIdStagingSrmOuputProtectionRequired: + return wvcdm::a2bs_hex(kCpStagingSrmOuputProtectionRequired); + default: + return kEmptyData; + } +} + +const std::string& ConfigTestEnv::GetLicenseServerUrl( + ServerConfigurationId server_configuration_id) { + switch (server_configuration_id) { + case kGooglePlayServer: + return kGpLicenseServer; + case kContentProtectionUatServer: + return kCpUatLicenseServer; + case kContentProtectionStagingServer: + return kCpStagingLicenseServer; + case kContentProtectionProductionServer: + return kCpProductionLicenseServer; + default: + return kEmptyData; + } +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/config_test_env.h b/libwvdrmengine/cdm/core/test/config_test_env.h index 1a3f26dc..6a27edc8 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.h +++ b/libwvdrmengine/cdm/core/test/config_test_env.h @@ -25,17 +25,24 @@ namespace wvcdm { typedef enum { kGooglePlayServer, // not tested recently - kContentProtectionProdLicense, - kContentProtectionUatLicense, - kContentProtectionStagingLicense, - kContentProtectionProdPlusProv30, - kContentProtectionUatPlusProv30, - kContentProtectionStagingPlusProv30, + kContentProtectionUatServer, + kContentProtectionStagingServer, + kContentProtectionProductionServer, } ServerConfigurationId; +// Identifies content used in tests. Specify Prod/Uat/Staging if content +// has been registered across license services. +enum ContentId { + kContentIdStreaming, + kContentIdOffline, + kContentIdStagingSrmOuputProtectionRequested, + kContentIdStagingSrmOuputProtectionRequired, +}; + // Configures default test environment. class ConfigTestEnv { public: + typedef struct { ServerConfigurationId id; std::string license_server_url; @@ -68,6 +75,10 @@ class ConfigTestEnv { } const KeyId& wrong_key_id() const { return wrong_key_id_; } + static const CdmInitData GetInitData(ContentId content_id); + static const std::string& GetLicenseServerUrl( + ServerConfigurationId server_configuration_id); + void set_key_id(KeyId& key_id) { key_id_.assign(key_id); } void set_key_system(CdmKeySystem& key_system) { key_system_.assign(key_system); @@ -75,6 +86,9 @@ class ConfigTestEnv { void set_license_server(std::string& license_server) { license_server_.assign(license_server); } + void set_provisioning_server(std::string& provisioning_server) { + provisioning_server_.assign(provisioning_server); + } private: void Init(ServerConfigurationId server_id); diff --git a/libwvdrmengine/cdm/core/test/http_socket.cpp b/libwvdrmengine/cdm/core/test/http_socket.cpp index f1d20daf..f37db03a 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.cpp +++ b/libwvdrmengine/cdm/core/test/http_socket.cpp @@ -2,6 +2,7 @@ #include "http_socket.h" +#include #include #include #include @@ -36,13 +37,14 @@ bool Tokenize(const std::string& source, const std::string& delim, } SSL_CTX* InitSslContext() { - const SSL_METHOD* method; - SSL_CTX* ctx; - OpenSSL_add_all_algorithms(); SSL_load_error_strings(); - method = TLSv1_2_client_method(); - ctx = SSL_CTX_new(method); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + const SSL_METHOD* method = TLSv1_2_client_method(); +#else + const SSL_METHOD* method = TLS_client_method(); +#endif + SSL_CTX* ctx = SSL_CTX_new(method); if (!ctx) LOGE("failed to create SSL context"); int ret = SSL_CTX_set_cipher_list( ctx, "ALL:!RC4-MD5:!RC4-SHA:!ECDHE-ECDSA-RC4-SHA:!ECDHE-RSA-RC4-SHA"); diff --git a/libwvdrmengine/cdm/core/test/license_unittest.cpp b/libwvdrmengine/cdm/core/test/license_unittest.cpp index 0b916487..06859523 100644 --- a/libwvdrmengine/cdm/core/test/license_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/license_unittest.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "clock.h" #include "crypto_session.h" #include "initialization_data.h" @@ -79,6 +81,38 @@ const std::string kLicenseRequestSignature = a2bs_hex( "9AE18B91516E0CDD0B581590DDDEA2A2527E2C9ABA273629B586A9D22D451A827E332CFC3E" "9BEDB6CF3D8713F9E11675DF1F5DB9038DBBECAB9D1683F8722CAF6E18EC8C04AEE5"); +const std::string kSubLicensePssh = a2bs_hex( + "000002317073736800000000edef8ba979d64acea3c827dcd51d21ed000002010801120d54" + "6573744b6579415544494f30120a546573744b6579534430120a546573744b65794844301a" + "0048e3dc959b0650025a9b010a101f10e4700b1a5b731c545fe2405cea1b12860112620a0d" + "546573744b6579415544494f3012102f4b661d1064b5ea82efcd3ef850f45b1a10a02c5cea" + "5182383c064c06abbc79bfa8200242240a1023acc9a0ef2bca66af2a1307cc9edeb21210e1" + "f1d352b4c6b1aad1fd78423db256946205415544494f1a20aaf4537f09332c502a88f43a18" + "a3e21ec28bbde675c5d87054fbca06f98e98015a95010a10c034cf6ae181b8d07f2e79142d" + "792bb3128001125c0a0a546573744b657953443012106d37a0a201afdef8a494f89a4b0772" + "4a1a10457db86b73bf87177a5cc61c0d04b690200242240a1001fe432d2a8afb7054ae76a3" + "9c2727e612108761b7e0ba354ee4132117a9de12abd3620253441a20e3f37529cb795b35a0" + "d186e4ce7187f08dda5f1df136ddb92eb0a65a899635005a95010a1021cdec9b2105c6b643" + "e71f68e5302c85128001125c0a0a546573744b6579484430121012a3e3afe1e23be2c3fc55" + "fddad877451a108f31ff0865f4d4fb41d96414297f7728200242240a1061e3cbca755b36c8" + "e7d6dda03af20e4f1210a2fa2fc5d10c9c84ddc5511446ce77e9620248441a20a945699aef" + "49355b0214b636edb7670bbe350c58b69cd52f207953b380a52df2"); + +const std::string kSubSessionKeyID1 = + a2bs_hex("1f10e4700b1a5b731c545fe2405cea1b"); + +const std::string kSubSessionKeyID2 = + a2bs_hex("c034cf6ae181b8d07f2e79142d792bb3"); + +const std::string kSubSessionKeyID3 = + a2bs_hex("21cdec9b2105c6b643e71f68e5302c85"); + +const CryptoSession::SupportedCertificateTypes kDefaultSupportedCertTypes = { + true, + true, + true + }; + class MockCryptoSession : public CryptoSession { public: MockCryptoSession() : CryptoSession(NULL) { } @@ -90,6 +124,9 @@ class MockCryptoSession : public CryptoSession { MOCK_METHOD1(GetApiVersion, bool(uint32_t*)); MOCK_METHOD1(GenerateNonce, bool(uint32_t*)); MOCK_METHOD3(PrepareRequest, bool(const std::string&, bool, std::string*)); + MOCK_METHOD3(GenerateSubSessionNonce, + bool(const std::string& sub_session_key_id, bool* exists, + uint32_t* nonce)); }; class MockPolicyEngine : public PolicyEngine { @@ -129,12 +166,17 @@ using ::testing::UnorderedElementsAre; class CdmLicenseTest : public ::testing::Test { protected: + CdmLicenseTest(const std::string& pssh = (kCencInitDataHdr + kCencPssh)) + : pssh_(pssh) {} virtual void SetUp() { clock_ = new MockClock(); crypto_session_ = new MockCryptoSession(); - init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT, - kCencInitDataHdr + kCencPssh); + init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT, pssh_); policy_engine_ = new MockPolicyEngine(crypto_session_); + + ON_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())) + .WillByDefault( + DoAll(SetArgPointee<0>(kDefaultSupportedCertTypes), Return(true))); } virtual void TearDown() { @@ -145,7 +187,7 @@ class CdmLicenseTest : public ::testing::Test { if (clock_) delete clock_; } - void CreateCdmLicense() { + virtual void CreateCdmLicense() { cdm_license_ = new CdmLicense(kCdmSessionId, clock_); clock_ = NULL; } @@ -156,6 +198,12 @@ class CdmLicenseTest : public ::testing::Test { MockInitializationData* init_data_; MockPolicyEngine* policy_engine_; ServiceCertificate service_cert_; + std::string pssh_; +}; + +class SubLicenseTest : public CdmLicenseTest { + protected: + SubLicenseTest() : CdmLicenseTest(kSubLicensePssh) {} }; TEST_F(CdmLicenseTest, InitSuccess) { @@ -169,8 +217,8 @@ TEST_F(CdmLicenseTest, InitSuccess) { TEST_F(CdmLicenseTest, InitFail_EmptyToken) { CreateCdmLicense(); - EXPECT_FALSE(cdm_license_->Init(&service_cert_, "", kClientTokenDrmCert, - "", crypto_session_, policy_engine_)); + EXPECT_FALSE(cdm_license_->Init(&service_cert_, "", kClientTokenDrmCert, "", + crypto_session_, policy_engine_)); } TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) { @@ -191,19 +239,14 @@ TEST_F(CdmLicenseTest, InitWithNullServiceCert) { EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); CreateCdmLicense(); - EXPECT_TRUE(cdm_license_->Init(NULL, kToken, kClientTokenDrmCert, - "", crypto_session_, policy_engine_)); + EXPECT_TRUE(cdm_license_->Init(NULL, kToken, kClientTokenDrmCert, "", + crypto_session_, policy_engine_)); } TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { bool usage_information_support = true; CryptoSession::HdcpCapability current_hdcp_version = HDCP_NO_DIGITAL_OUTPUT; CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1; - CryptoSession::SupportedCertificateTypes device_supported_certs = { - true, - true, - true - }; uint32_t crypto_session_api_version = 9; EXPECT_CALL(*crypto_session_, IsOpen()) @@ -216,8 +259,7 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(current_hdcp_version), SetArgPointee<1>(max_hdcp_version), Return(true))); - EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())) - .WillOnce(DoAll(SetArgPointee<0>(device_supported_certs), Return(true))); + EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())); EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())) .WillOnce( DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true))); @@ -319,4 +361,89 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { EXPECT_EQ(kNonce, license_request.key_control_nonce()); } +TEST_F(SubLicenseTest, VerifySubSessionData) { + bool usage_information_support = true; + CryptoSession::HdcpCapability current_hdcp_version = HDCP_NO_DIGITAL_OUTPUT; + CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1; + uint32_t crypto_session_api_version = 9; + + EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); + EXPECT_CALL(*crypto_session_, GenerateRequestId(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kCryptoRequestId), Return(true))); + EXPECT_CALL(*crypto_session_, UsageInformationSupport(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(usage_information_support), Return(true))); + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(current_hdcp_version), + SetArgPointee<1>(max_hdcp_version), Return(true))); + EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true))); + EXPECT_CALL(*clock_, GetCurrentTime()).WillOnce(Return(kLicenseStartTime)); + EXPECT_CALL(*crypto_session_, GenerateNonce(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kNonce), Return(true))); + EXPECT_CALL(*crypto_session_, PrepareRequest(_, Eq(false), NotNull())) + .WillOnce( + DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true))); + EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())); + + // SubLicense session data calls. + // TODO(jfore): These calls are being invoked twice each. This should not + // present a functional problem, but we should investigate why. + EXPECT_CALL(*crypto_session_, + GenerateSubSessionNonce(kSubSessionKeyID1, NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<1>(true), SetArgPointee<2>(0), Return(true))); + EXPECT_CALL(*crypto_session_, + GenerateSubSessionNonce(kSubSessionKeyID2, NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<1>(true), SetArgPointee<2>(1), Return(true))); + EXPECT_CALL(*crypto_session_, + GenerateSubSessionNonce(kSubSessionKeyID3, NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<1>(true), SetArgPointee<2>(2), Return(true))); + + CreateCdmLicense(); + // TODO(gmorgan) fix below - no default service certificate + //service_cert_.Init(kDefaultServiceCertificate); + EXPECT_TRUE(cdm_license_->Init( + &service_cert_, kToken, kClientTokenDrmCert, kEmptyString, + crypto_session_, policy_engine_)); + CdmAppParameterMap app_parameters; + CdmKeyMessage signed_request; + Properties::set_use_certificates_as_identification(true); + std::string server_url; + EXPECT_EQ(cdm_license_->PrepareKeyRequest(*init_data_, kLicenseTypeStreaming, + app_parameters, &signed_request, + &server_url), + KEY_MESSAGE); + EXPECT_TRUE(!signed_request.empty()); + + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(signed_request)); + LicenseRequest license_request; + EXPECT_TRUE(license_request.ParseFromString(signed_message.msg())); + EXPECT_EQ(3, license_request.sub_session_data().size()); + + for (int i = 0; i < license_request.sub_session_data().size(); ++i) { + const video_widevine::LicenseRequest_SubSessionData& sl = + license_request.sub_session_data(i); + EXPECT_EQ(static_cast(i), sl.nonce()); + switch (i) { + case 0: + EXPECT_EQ(kSubSessionKeyID1, sl.sub_session_key_id()); + EXPECT_EQ("AUDIO", sl.track_label()); + break; + case 1: + EXPECT_EQ(kSubSessionKeyID2, sl.sub_session_key_id()); + EXPECT_EQ("SD", sl.track_label()); + break; + case 3: + EXPECT_EQ(kSubSessionKeyID3, sl.sub_session_key_id()); + EXPECT_EQ("HD", sl.track_label()); + break; + } + } +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index d15ca05c..39892ff4 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -60,6 +60,10 @@ class HdcpOnlyMockCryptoSession : public CryptoSession { CryptoSession(NULL) {} MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*)); + bool DoRealGetHdcpCapabilities(HdcpCapability* current, + HdcpCapability* max) { + return CryptoSession::GetHdcpCapabilities(current, max); + } }; class MockCdmEventListener : public WvCdmEventListener { @@ -85,6 +89,7 @@ using video_widevine::OFFLINE; using ::testing::_; using ::testing::AtLeast; using ::testing::InSequence; +using ::testing::Invoke; using ::testing::MockFunction; using ::testing::Pair; using ::testing::Return; @@ -125,6 +130,12 @@ class PolicyEngineTest : public ::testing::Test { policy->set_renewal_delay_seconds(0); policy->set_renewal_retry_interval_seconds(kLicenseRenewalRetryInterval); policy->set_renew_with_usage(false); + + ON_CALL(crypto_session_, GetHdcpCapabilities(_, _)) + .WillByDefault( + Invoke( + &crypto_session_, + &HdcpOnlyMockCryptoSession::DoRealGetHdcpCapabilities)); } void InjectMockClock() { @@ -152,7 +163,7 @@ class PolicyEngineTest : public ::testing::Test { expected_has_new_usable_key)); } - StrictMock crypto_session_; + NiceMock crypto_session_; StrictMock mock_event_listener_; MockClock* mock_clock_; scoped_ptr policy_engine_; diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index 5293f26c..0a794482 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -608,6 +608,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE: *os << "USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE"; break; + case LICENSE_REQUEST_INVALID_SUBLICENSE: + *os << "LICENSE_REQUEST_INVALID_SUBLICENSE"; + break; default: *os << "Unknown CdmResponseType"; break; diff --git a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp index fed2f314..5be41123 100644 --- a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp @@ -250,7 +250,8 @@ TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveLicenses) { EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull())) .WillOnce( DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true)); + // TODO: Why not being called? + //EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true)); EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, kEmptyUsageEntryInfoVector)) .Times(2) diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index dcf8797a..472e418b 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -11,6 +11,7 @@ #include "cdm_identifier.h" #include "file_store.h" #include "lock.h" +#include "metrics.pb.h" #include "timer.h" #include "wv_cdm_types.h" @@ -125,7 +126,8 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { // Validate a passed-in service certificate virtual bool IsValidServiceCertificate(const std::string& certificate); - // Retrieve the serialized metrics from the CDM. + // Retrieve the serialized metrics from CdmEngine and CdmSession instances + // that have been closed. virtual void GetSerializedMetrics(std::string* serialized_metrics); private: @@ -142,12 +144,17 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { // Finds the CdmEngine instance for the given session id, returning NULL if // not found. CdmEngine* GetCdmForSessionId(const std::string& session_id); + // Closes CdmEngine instances that don't have any open sessions. Also stores + // metrics data for closed CdmEngine instances. + // Callers must acquire the cdms_lock_ before calling this method. + void CloseCdmsWithoutSessions(); uint32_t GenerateSessionSharingId(); // timer related methods to drive policy decisions void EnablePolicyTimer(); - void DisablePolicyTimer(bool force); + void DisablePolicyTimer(); // Disable if all cdm engines are closed. + void ForceDisablePolicyTimer(); // Force disable the policy timer. void OnTimerEvent(); static Lock session_sharing_id_generation_lock_; @@ -162,6 +169,9 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { // This contains weak pointers to the CDM instances contained in |cdms_|. std::map cdm_by_session_id_; + // The metrics for cdm engines and sessions that have been closed. + drm_metrics::MetricsGroup metrics_; + CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule); }; diff --git a/libwvdrmengine/cdm/metrics/include/metrics_collections.h b/libwvdrmengine/cdm/metrics/include/metrics_collections.h index ae4cdc85..be161e2c 100644 --- a/libwvdrmengine/cdm/metrics/include/metrics_collections.h +++ b/libwvdrmengine/cdm/metrics/include/metrics_collections.h @@ -141,7 +141,7 @@ class CryptoMetrics { EventMetric oemcrypto_rewrap_device_rsa_key_; EventMetric oemcrypto_rewrap_device_rsa_key_30_; EventMetric oemcrypto_security_level_; - EventMetric oemcrypto_security_patch_level_; + EventMetric oemcrypto_security_patch_level_; EventMetric oemcrypto_select_key_; EventMetric oemcrypto_supports_usage_table_; EventMetric oemcrypto_update_usage_table_; diff --git a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp index 93b35e05..34b9722c 100644 --- a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp +++ b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp @@ -4,6 +4,7 @@ #include +#include "log.h" #include "metrics.pb.h" using drm_metrics::MetricsGroup; @@ -438,6 +439,10 @@ EngineMetrics::EngineMetrics() : EngineMetrics::~EngineMetrics() { AutoLock kock(session_metrics_lock_); std::vector::iterator i; + if (!session_metrics_list_.empty()) { + LOGV("EngineMetrics::~EngineMetrics. Session count: %d", + session_metrics_list_.size()); + } for (i = session_metrics_list_.begin(); i != session_metrics_list_.end(); i++) { delete *i; diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 11b70625..0ed64e44 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -24,7 +24,7 @@ Lock WvContentDecryptionModule::session_sharing_id_generation_lock_; WvContentDecryptionModule::WvContentDecryptionModule() {} WvContentDecryptionModule::~WvContentDecryptionModule() { - DisablePolicyTimer(true); + ForceDisablePolicyTimer(); } bool WvContentDecryptionModule::IsSupported(const std::string& init_data_type) { @@ -72,6 +72,7 @@ CdmResponseType WvContentDecryptionModule::OpenSession( CdmResponseType WvContentDecryptionModule::CloseSession( const CdmSessionId& session_id) { + LOGV("WvContentDecryptionModule::CloseSession. id: %s", session_id.c_str()); CdmEngine* cdm_engine = GetCdmForSessionId(session_id); // TODO(rfrias): Avoid reusing the error codes from CdmEngine. if (!cdm_engine) return SESSION_NOT_FOUND_1; @@ -85,7 +86,9 @@ CdmResponseType WvContentDecryptionModule::CloseSession( if (sts == NO_ERROR) { cdm_by_session_id_.erase(session_id); } - DisablePolicyTimer(false); + + DisablePolicyTimer(); + return sts; } @@ -404,21 +407,10 @@ bool WvContentDecryptionModule::IsValidServiceCertificate( void WvContentDecryptionModule::GetSerializedMetrics( std::string* serialized_metrics) { - drm_metrics::MetricsGroup metric_group; - { - AutoLock auto_lock(cdms_lock_); - for (auto it = cdms_.begin(); it != cdms_.end(); it++) { - metrics::EngineMetrics* engine_metrics = - it->second.cdm_engine->GetMetrics(); - if (engine_metrics) { - // Serialize the metrics from the engine and any completed sessions. - // Clear the metrics from any completed sessions. - engine_metrics->Serialize( - metric_group.add_metric_sub_group(), true, true); - } - } - } - metric_group.SerializeToString(serialized_metrics); + AutoLock auto_lock(cdms_lock_); + CloseCdmsWithoutSessions(); + metrics_.SerializeToString(serialized_metrics); + metrics_.Clear(); } WvContentDecryptionModule::CdmInfo::CdmInfo() @@ -449,30 +441,66 @@ CdmEngine* WvContentDecryptionModule::GetCdmForSessionId( return it->second; } +// This method requires that the caller first acquire cdms_lock_. +void WvContentDecryptionModule::CloseCdmsWithoutSessions() { + for (auto it = cdms_.begin(); it != cdms_.end();) { + if (it->second.cdm_engine->SessionSize() != 0) { + ++it; + } else { + // Retrieve the metrics from the engine and any completed + // sessions. Clear the metrics from any completed sessions. + metrics::EngineMetrics* engine_metrics = + it->second.cdm_engine->GetMetrics(); + // engine_metrics should never be null. + if (engine_metrics != NULL) { + engine_metrics->Serialize( + metrics_.add_metric_sub_group(), + false, // Report complete AND incomplete sessions. + true); // Clear session metrics after reporting. + } else { + // Engine metrics should never be null. + LOGI("WvContentDecryptionModule::CloseCdmsWithoutSessions." + "engine_metrics was unexpectedly NULL."); + } + + // The CDM is no longer used for this identifier, delete it. + it = cdms_.erase(it); + } + } +} + void WvContentDecryptionModule::EnablePolicyTimer() { AutoLock auto_lock(policy_timer_lock_); if (!policy_timer_.IsRunning()) policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds); } -void WvContentDecryptionModule::DisablePolicyTimer(bool force) { - bool has_sessions = false; +void WvContentDecryptionModule::DisablePolicyTimer() { + bool cdms_is_empty = false; { AutoLock auto_lock(cdms_lock_); - for (auto it = cdms_.begin(); it != cdms_.end();) { - if (it->second.cdm_engine->SessionSize() != 0) { - has_sessions = true; - ++it; - } else { - // The CDM is no longer used for this identifier, delete it. - it = cdms_.erase(it); - } - } + CloseCdmsWithoutSessions(); + cdms_is_empty = cdms_.empty(); } AutoLock auto_lock(policy_timer_lock_); - if ((!has_sessions || force) && policy_timer_.IsRunning()) + if(cdms_is_empty) { + if (policy_timer_.IsRunning()) { + policy_timer_.Stop(); + } + } +} + +void WvContentDecryptionModule::ForceDisablePolicyTimer() { + { + AutoLock auto_lock(cdms_lock_); + CloseCdmsWithoutSessions(); + } + + AutoLock auto_lock(policy_timer_lock_); + if (policy_timer_.IsRunning()) { policy_timer_.Stop(); + } } void WvContentDecryptionModule::OnTimerEvent() { diff --git a/libwvdrmengine/cdm/test/Android.mk b/libwvdrmengine/cdm/test/Android.mk index 4dcc72c3..d27b48b7 100644 --- a/libwvdrmengine/cdm/test/Android.mk +++ b/libwvdrmengine/cdm/test/Android.mk @@ -11,6 +11,10 @@ test_name := buffer_reader_test test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk +test_name := cdm_feature_test +test_src_dir := . +include $(LOCAL_PATH)/unit-test.mk + test_name := cdm_engine_test test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk @@ -75,6 +79,10 @@ test_name := usage_table_header_unittest test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk +test_name := wv_cdm_metrics_test +test_src_dir := . +include $(LOCAL_PATH)/unit-test.mk + test_name := distribution_test test_src_dir := ../metrics/test include $(LOCAL_PATH)/unit-test.mk diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index f29714a5..adf9af0b 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -48,7 +48,7 @@ wvcdm::CdmKeySystem g_key_system; std::string g_license_server; wvcdm::KeyId g_wrong_key_id; wvcdm::ServerConfigurationId g_license_server_id = - wvcdm::kContentProtectionUatLicense; + wvcdm::kContentProtectionUatServer; // TODO(rfrias): refactor to print out the decryption test names struct SubSampleInfo { @@ -1591,9 +1591,9 @@ int main(int argc, char** argv) { if (!license_id.compare("gp")) { g_license_server_id = wvcdm::kGooglePlayServer; } else if (!license_id.compare("cp")) { - g_license_server_id = wvcdm::kContentProtectionUatLicense; + g_license_server_id = wvcdm::kContentProtectionUatServer; } else if (!license_id.compare("st")) { - g_license_server_id = wvcdm::kContentProtectionStagingLicense; + g_license_server_id = wvcdm::kContentProtectionStagingServer; } else { std::cout << "Invalid license server id" << optarg << std::endl; show_usage = true; diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 187dd20c..b2e68d82 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -66,8 +66,8 @@ wvcdm::KeyId g_key_id; wvcdm::CdmKeySystem g_key_system; std::string g_license_server; wvcdm::KeyId g_wrong_key_id; -wvcdm::ServerConfigurationId g_license_server_id = - wvcdm::kContentProtectionUatLicense; +wvcdm::ServerConfigurationId g_server_configuration_id = + wvcdm::kContentProtectionUatServer; std::string g_service_certificate; // TODO(rfrias): refactor to print out the decryption test names @@ -991,7 +991,7 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { protected: void GetOfflineConfiguration(std::string* key_id, std::string* client_auth) { - ConfigTestEnv config(g_license_server_id, false); + ConfigTestEnv config(g_server_configuration_id, false); if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0) key_id->assign(wvcdm::a2bs_hex(config.key_id())); else @@ -3902,11 +3902,12 @@ int main(int argc, char** argv) { case 'i': { std::string license_id(optarg); if (!license_id.compare("gp")) { - g_license_server_id = wvcdm::kGooglePlayServer; + g_server_configuration_id = wvcdm::kGooglePlayServer; } else if (!license_id.compare("cp")) { - g_license_server_id = wvcdm::kContentProtectionUatLicense; + g_server_configuration_id = wvcdm::kContentProtectionUatServer; } else if (!license_id.compare("st")) { - g_license_server_id = wvcdm::kContentProtectionStagingLicense; + g_server_configuration_id = + wvcdm::kContentProtectionStagingServer; } else { std::cout << "Invalid license server id" << optarg << std::endl; show_usage = true; @@ -3940,7 +3941,7 @@ int main(int argc, char** argv) { return 0; } - g_config = new wvcdm::ConfigTestEnv(g_license_server_id); + g_config = new wvcdm::ConfigTestEnv(g_server_configuration_id); g_client_auth.assign(g_config->client_auth()); g_key_system.assign(g_config->key_system()); g_wrong_key_id.assign(g_config->wrong_key_id()); diff --git a/libwvdrmengine/cdm/test/wv_cdm_metrics_test.cpp b/libwvdrmengine/cdm/test/wv_cdm_metrics_test.cpp index ab9bc760..a411808e 100644 --- a/libwvdrmengine/cdm/test/wv_cdm_metrics_test.cpp +++ b/libwvdrmengine/cdm/test/wv_cdm_metrics_test.cpp @@ -13,7 +13,7 @@ using ::testing::Eq; using ::testing::StrEq; -using ::testing::Ge; +using ::testing::Gt; using ::testing::Test; using wvcdm::CdmResponseType; @@ -53,17 +53,13 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) { ASSERT_TRUE(metrics.ParseFromString(serialized_metrics)); EXPECT_THAT(metrics.metric_size(), Eq(0)); ASSERT_THAT(metrics.metric_sub_group_size(), Eq(1)); - ASSERT_THAT(metrics.metric_sub_group(0).metric_size(), Ge(6)); + ASSERT_THAT(metrics.metric_sub_group(0).metric_size(), Gt(0)); EXPECT_THAT(metrics.metric_sub_group(0).metric_sub_group_size(), Eq(0)); - EXPECT_THAT(metrics.metric_sub_group(0).metric(0).name(), - StrEq("/drm/widevine/oemcrypto/initialization_mode")); - // Can't check the initialization mode value. Different devices will have - // different values. EXPECT_THAT( - metrics.metric_sub_group(0).metric(5).name(), + metrics.metric_sub_group(0).metric(0).name(), StrEq("/drm/widevine/cdm_engine/" "get_provisioning_request/time/count{error:0}")); - EXPECT_THAT(metrics.metric_sub_group(0).metric(5).value().int_value(), Eq(1)); + EXPECT_THAT(metrics.metric_sub_group(0).metric(0).value().int_value(), Eq(1)); } @@ -87,14 +83,13 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineAndSessionMetrics) { // The outer container will never have metrics. EXPECT_THAT(metrics.metric_size(), Eq(0)); ASSERT_THAT(metrics.metric_sub_group_size(), Eq(1)); - ASSERT_THAT(metrics.metric_sub_group(0).metric_size(), Ge(6)); + ASSERT_THAT(metrics.metric_sub_group(0).metric_size(), Gt(0)); - // Validate engine-level metrics. - EXPECT_THAT(metrics.metric_sub_group(0).metric(0).name(), - StrEq("/drm/widevine/oemcrypto/initialization_mode")); - EXPECT_THAT(metrics.metric_sub_group(0).metric(5).name(), - StrEq("/drm/widevine/cdm_engine/open_session/count{error:7}")); - EXPECT_THAT(metrics.metric_sub_group(0).metric(5).value().int_value(), Eq(1)); + // Validate an engine-level metric. + EXPECT_THAT( + metrics.metric_sub_group(0).metric(0).name(), + StrEq("/drm/widevine/cdm_engine/open_session/time/count{error:7}")); + EXPECT_THAT(metrics.metric_sub_group(0).metric(0).value().int_value(), Eq(1)); // Validate a session-level metric. EXPECT_THAT(metrics.metric_sub_group(0).metric_sub_group_size(), Eq(1)); @@ -106,7 +101,7 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineAndSessionMetrics) { TEST_F(WvContentDecryptionModuleMetricsTest, MultipleEngineMetric) { CdmSessionId session_id; wvcdm::CdmKeySystem key_system("com.widevine"); - CdmIdentifier identifier = { "foo", "bar", "baz" }; + CdmIdentifier identifier = { "foo", "bar" }; // Openning the session will fail with NEEDS_PROVISIONING error. But it will // still create some session-level stats. @@ -133,23 +128,17 @@ TEST_F(WvContentDecryptionModuleMetricsTest, MultipleEngineMetric) { for (int i = 0; i < metrics.metric_sub_group_size(); i++) { // Validate the engine-level metric. - ASSERT_THAT(metrics.metric_sub_group(i).metric_size(), Ge(6)); - EXPECT_THAT(metrics.metric_sub_group(i).metric(0).name(), - StrEq("/drm/widevine/oemcrypto/initialization_mode")); - EXPECT_THAT(metrics.metric_sub_group(i).metric(5).name(), - StrEq("/drm/widevine/cdm_engine/open_session/count{error:7}")); - EXPECT_THAT(metrics.metric_sub_group(i).metric(5).value().int_value(), - Eq(1)); - + ASSERT_THAT(metrics.metric_sub_group(i).metric_size(), Gt(0)); + EXPECT_THAT( + metrics.metric_sub_group(i).metric(0).name(), + StrEq("/drm/widevine/cdm_engine/open_session/time/count{error:7}")); + EXPECT_THAT(metrics.metric_sub_group(i).metric(0).value().int_value(), Eq(1)); // Validate a session-level metric. EXPECT_THAT(metrics.metric_sub_group(i).metric_sub_group_size(), Eq(1)); EXPECT_THAT( metrics.metric_sub_group(i).metric_sub_group(0).metric(0).name(), StrEq("/drm/widevine/cdm_session/session_id")); } - - // Verify that the second metrics app package name is set. - EXPECT_THAT(metrics.metric_sub_group(1).app_package_name(), StrEq("baz")); } } diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index cc21f70b..354362f7 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -278,10 +278,11 @@ enum { kUsageStoreEntryRetrieveInvalidStorageType = ERROR_DRM_VENDOR_MIN + 266, kReleaseAllUsageInfoError6 = ERROR_DRM_VENDOR_MIN + 267, kReleaseAllUsageInfoError7 = ERROR_DRM_VENDOR_MIN + 268, + kLicenseRequestInvalidSublicense = ERROR_DRM_VENDOR_MIN + 269, // This should always follow the last error code. // The offset value should be updated each time a new error code is added. - kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 268, + kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 269, // Used by crypto test mode kErrorTestMode = ERROR_DRM_VENDOR_MAX, diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index 71f0ce57..acab1f4b 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -535,6 +535,8 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) { return kReleaseAllUsageInfoError6; case wvcdm::RELEASE_ALL_USAGE_INFO_ERROR_7: return kReleaseAllUsageInfoError7; + case wvcdm::LICENSE_REQUEST_INVALID_SUBLICENSE: + return kLicenseRequestInvalidSublicense; case wvcdm::UNUSED_1: case wvcdm::UNUSED_2: diff --git a/libwvdrmengine/include_hidl/mapErrors-inl.h b/libwvdrmengine/include_hidl/mapErrors-inl.h index b1b7dc34..7bbbad4f 100644 --- a/libwvdrmengine/include_hidl/mapErrors-inl.h +++ b/libwvdrmengine/include_hidl/mapErrors-inl.h @@ -197,6 +197,7 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) { case wvcdm::WEBM_INIT_DATA_UNAVAILABLE: case wvcdm::PREPARE_WEBM_CONTENT_ID_FAILED: case wvcdm::UNSUPPORTED_INIT_DATA_FORMAT: + case wvcdm::LICENSE_REQUEST_INVALID_SUBLICENSE: case wvcdm::LICENSE_REQUEST_NONCE_GENERATION_ERROR: case wvcdm::LICENSE_REQUEST_SIGNING_ERROR: case wvcdm::EMPTY_LICENSE_REQUEST: diff --git a/libwvdrmengine/mediacrypto/Android.mk b/libwvdrmengine/mediacrypto/Android.mk index bf68f649..8f83f7c1 100644 --- a/libwvdrmengine/mediacrypto/Android.mk +++ b/libwvdrmengine/mediacrypto/Android.mk @@ -22,7 +22,8 @@ LOCAL_HEADER_LIBRARIES := \ libutils_headers LOCAL_STATIC_LIBRARIES := \ - libcrypto_static \ + libcdm_protos \ + libcrypto \ LOCAL_SHARED_LIBRARIES := \ liblog @@ -59,7 +60,8 @@ LOCAL_HEADER_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libcdm_protos \ - libcrypto + libcrypto \ + libwidevinehidl_utils \ LOCAL_SHARED_LIBRARIES := \ android.hardware.drm@1.0 \ diff --git a/libwvdrmengine/mediacrypto/test/Android.mk b/libwvdrmengine/mediacrypto/test/Android.mk index bc50cb6a..1fba8755 100644 --- a/libwvdrmengine/mediacrypto/test/Android.mk +++ b/libwvdrmengine/mediacrypto/test/Android.mk @@ -21,7 +21,7 @@ LOCAL_STATIC_LIBRARIES := \ libcdm \ libcdm_protos \ libcdm_utils \ - libcrypto_static \ + libcrypto \ libjsmn \ libgmock \ libgmock_main \ @@ -79,7 +79,7 @@ LOCAL_STATIC_LIBRARIES := \ libcdm \ libcdm_protos \ libcdm_utils \ - libcrypto_static \ + libcrypto \ libjsmn \ libgmock \ libgmock_main \ diff --git a/libwvdrmengine/mediadrm/Android.mk b/libwvdrmengine/mediadrm/Android.mk index 05583428..30141745 100644 --- a/libwvdrmengine/mediadrm/Android.mk +++ b/libwvdrmengine/mediadrm/Android.mk @@ -25,6 +25,9 @@ LOCAL_HEADER_LIBRARIES := \ LOCAL_SHARED_LIBRARIES := \ liblog +LOCAL_STATIC_LIBRARIES := \ + libcdm_protos + LOCAL_MODULE := libwvdrmdrmplugin LOCAL_PROPRIETARY_MODULE := true @@ -56,7 +59,9 @@ LOCAL_C_INCLUDES := \ LOCAL_HEADER_LIBRARIES := \ libutils_headers \ -LOCAL_STATIC_LIBRARIES := libcrypto_static +LOCAL_STATIC_LIBRARIES := \ + libcdm_protos \ + libcrypto LOCAL_SHARED_LIBRARIES := \ android.hardware.drm@1.0 \ diff --git a/libwvdrmengine/mediadrm/test/Android.mk b/libwvdrmengine/mediadrm/test/Android.mk index f6d62730..874698d9 100644 --- a/libwvdrmengine/mediadrm/test/Android.mk +++ b/libwvdrmengine/mediadrm/test/Android.mk @@ -22,7 +22,7 @@ LOCAL_STATIC_LIBRARIES := \ libcdm \ libcdm_protos \ libcdm_utils \ - libcrypto_static \ + libcrypto \ libjsmn \ libgmock \ libgmock_main \ @@ -80,7 +80,7 @@ LOCAL_STATIC_LIBRARIES := \ libcdm \ libcdm_protos \ libcdm_utils \ - libcrypto_static \ + libcrypto \ libjsmn \ libgmock \ libgmock_main \ diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index 93ba3388..1d0d8985 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -1080,9 +1080,6 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) { static const std::string openSessions = "42"; static const std::string maxSessions = "54"; static const std::string oemCryptoApiVersion = "13"; - std::string serializedMetrics( - kSerializedMetrics, kSerializedMetrics + sizeof(kSerializedMetrics)); - static const std::string currentSRMVersion = "1"; static const std::string cdmVersion = "Infinity Minus 1"; std::string serializedMetrics( diff --git a/libwvdrmengine/mediadrm/test/legacy_src/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/legacy_src/WVDrmPlugin_test.cpp index cc368547..c496291b 100644 --- a/libwvdrmengine/mediadrm/test/legacy_src/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/legacy_src/WVDrmPlugin_test.cpp @@ -848,9 +848,6 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) { static const string openSessions = "15"; static const string maxSessions = "18"; static const string oemCryptoApiVersion = "10"; - string serializedMetrics(kSerializedMetrics, - kSerializedMetrics + sizeof(kSerializedMetrics)); - static const string currentSRMVersion = "1"; static const string cdmVersion = "Infinity Minus 1"; string serializedMetrics(kSerializedMetrics, diff --git a/libwvdrmengine/oemcrypto/mock/Android.mk b/libwvdrmengine/oemcrypto/mock/Android.mk index 9a3e43bb..79208ea6 100644 --- a/libwvdrmengine/oemcrypto/mock/Android.mk +++ b/libwvdrmengine/oemcrypto/mock/Android.mk @@ -38,7 +38,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libcdm_utils \ - libcrypto_static \ + libcrypto \ # Proprietary modules are put in vendor/lib instead of /system/lib. LOCAL_PROPRIETARY_MODULE := true diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp index 72225ab0..8721e3f3 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp @@ -1295,12 +1295,22 @@ OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8, int out_len = 0; while (remaining) { - EVP_CIPHER_CTX ctx; - EVP_CIPHER_CTX_init(&ctx); - EVP_CIPHER_CTX_set_padding(&ctx, 0); - if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_ctr(), NULL, key_u8, aes_iv_u8)) { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX ctx_struct; + EVP_CIPHER_CTX* evp_cipher_ctx = &ctx_struct; + EVP_CIPHER_CTX_init(evp_cipher_ctx); +#else + EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new(); +#endif + EVP_CIPHER_CTX_set_padding(evp_cipher_ctx, 0); + if (!EVP_DecryptInit_ex(evp_cipher_ctx, EVP_aes_128_ctr(), NULL, + key_u8, aes_iv_u8)) { LOGE("[DecryptCTR(): EVP_INIT ERROR]"); - EVP_CIPHER_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif return OEMCrypto_ERROR_DECRYPT_FAILED; } @@ -1317,23 +1327,36 @@ OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8, decrypt_length = remaining; } - if (!EVP_DecryptUpdate(&ctx, &clear_data[l], &out_len, &cipher_data[l], - decrypt_length)) { + if (!EVP_DecryptUpdate(evp_cipher_ctx, &clear_data[l], &out_len, + &cipher_data[l], decrypt_length)) { LOGE("[DecryptCTR(): EVP_UPDATE_ERROR]"); - EVP_CIPHER_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif return OEMCrypto_ERROR_DECRYPT_FAILED; } l += decrypt_length; remaining = cipher_data_length - l; int final; - if (!EVP_DecryptFinal_ex( - &ctx, &clear_data[cipher_data_length - remaining], & final)) { + if (!EVP_DecryptFinal_ex(evp_cipher_ctx, + &clear_data[cipher_data_length - remaining], + &final)) { LOGE("[DecryptCTR(): EVP_FINAL_ERROR]"); - EVP_CIPHER_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif return OEMCrypto_ERROR_DECRYPT_FAILED; } - EVP_CIPHER_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif // If remaining is not zero, reset the iv before the second pass. if (remaining) { diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index c43bc68b..3de2db0a 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -40,6 +40,17 @@ void PrintTo(const vector& value, ostream* os) { } } // namespace std +namespace { +int GetRandBytes(unsigned char* buf, int num) { + // returns 1 on success, -1 if not supported, or 0 if other failure. +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + return RAND_pseudo_bytes(buf, num); +#else + return RAND_bytes(buf, num); +#endif +} +} // namespace + namespace wvoec { // Increment counter for AES-CTR. The CENC spec specifies we increment only @@ -293,18 +304,18 @@ void Session::SetKeyId(int index, const string& key_id) { void Session::FillSimpleMessage(uint32_t duration, uint32_t control, uint32_t nonce, const std::string& pst) { EXPECT_EQ( - 1, RAND_pseudo_bytes(license_.mac_key_iv, sizeof(license_.mac_key_iv))); - EXPECT_EQ(1, RAND_pseudo_bytes(license_.mac_keys, sizeof(license_.mac_keys))); + 1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv))); + EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys))); for (unsigned int i = 0; i < num_keys_; i++) { memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength); license_.keys[i].key_id_length = kDefaultKeyIdLength; memset(license_.keys[i].key_id, i, license_.keys[i].key_id_length); - EXPECT_EQ(1, RAND_pseudo_bytes(license_.keys[i].key_data, + EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_data, sizeof(license_.keys[i].key_data))); license_.keys[i].key_data_length = wvcdm::KEY_SIZE; - EXPECT_EQ(1, RAND_pseudo_bytes(license_.keys[i].key_iv, + EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_iv, sizeof(license_.keys[i].key_iv))); - EXPECT_EQ(1, RAND_pseudo_bytes(license_.keys[i].control_iv, + EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv, sizeof(license_.keys[i].control_iv))); if (global_features.api_version == 13) { // For version 13, we require OEMCrypto to handle kc13 for all licenses. @@ -508,9 +519,9 @@ void Session::TestDecryptCTR(bool select_key_first, vector unencryptedData(256); for (size_t i = 0; i < unencryptedData.size(); i++) unencryptedData[i] = i % 256; - EXPECT_EQ(1, RAND_pseudo_bytes(&unencryptedData[0], unencryptedData.size())); + EXPECT_EQ(1, GetRandBytes(&unencryptedData[0], unencryptedData.size())); vector encryptionIv(wvcdm::KEY_IV_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], wvcdm::KEY_IV_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], wvcdm::KEY_IV_SIZE)); vector encryptedData(unencryptedData.size()); EncryptCTR(unencryptedData, license_.keys[key_index].key_data, &encryptionIv[0], &encryptedData); @@ -622,11 +633,23 @@ void Session::LoadOEMCert(bool verify_cert) { X509_STORE_CTX_init(store_ctx.get(), store.get(), x509_cert, NULL); // TODO(fredgc): Verify cert is signed by Google. int result = X509_verify_cert(store_ctx.get()); - ASSERT_GE(0, result) << " OEM Cert not valid. " - << X509_verify_cert_error_string(store_ctx->error); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + ASSERT_GE(0, result) << " OEM Cert not valid. " << + X509_verify_cert_error_string(store_ctx->error); +#else + ASSERT_GE(0, result) << " OEM Cert not valid. " << + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get())); +#endif if (result == 0) { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) printf("Cert not verified: %s.\n", X509_verify_cert_error_string(store_ctx->error)); +#else + printf("Cert not verified: %s.\n", + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get()))); +#endif } } } @@ -650,7 +673,7 @@ void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted, memcpy(message.rsa_key, rsa_key.data(), rsa_key.size()); message.rsa_key_length = rsa_key.size(); } - EXPECT_EQ(1, RAND_pseudo_bytes(message.rsa_key_iv, wvcdm::KEY_IV_SIZE)); + EXPECT_EQ(1, GetRandBytes(message.rsa_key_iv, wvcdm::KEY_IV_SIZE)); message.nonce = nonce_; EncryptProvisioningMessage(&message, encrypted, *encryption_key); @@ -744,38 +767,43 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length) { - EVP_MD_CTX ctx; - EVP_MD_CTX_init(&ctx); - EVP_PKEY_CTX* pctx = NULL; +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX md_ctx_struct; + EVP_MD_CTX* md_ctx = &md_ctx_struct; + EVP_MD_CTX_init(md_ctx); +#else + EVP_MD_CTX* md_ctx = EVP_MD_CTX_new(); +#endif + EVP_PKEY_CTX* pkey_ctx = NULL; - if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */, + if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, EVP_sha1(), NULL /* no ENGINE */, pkey) != 1) { LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); goto err; } - if (EVP_PKEY_CTX_set_signature_md(pctx, + if (EVP_PKEY_CTX_set_signature_md(pkey_ctx, const_cast(EVP_sha1())) != 1) { LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature"); goto err; } - if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1) { + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) { LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature"); goto err; } - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, SHA_DIGEST_LENGTH) != 1) { + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, SHA_DIGEST_LENGTH) != 1) { LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature"); goto err; } - if (EVP_DigestVerifyUpdate(&ctx, message, message_length) != 1) { + if (EVP_DigestVerifyUpdate(md_ctx, message, message_length) != 1) { LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); goto err; } - if (EVP_DigestVerifyFinal(&ctx, const_cast(signature), + if (EVP_DigestVerifyFinal(md_ctx, const_cast(signature), signature_length) != 1) { LOGE( "EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad " @@ -783,12 +811,20 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, goto err; } - EVP_MD_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX_cleanup(md_ctx); +#else + EVP_MD_CTX_free(md_ctx); +#endif return true; err: dump_openssl_error(); - EVP_MD_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX_cleanup(md_ctx); +#else + EVP_MD_CTX_free(md_ctx); +#endif return false; } diff --git a/libwvdrmengine/oemcrypto/test/oec_test_data.h b/libwvdrmengine/oemcrypto/test/oec_test_data.h index 518dd192..eae4a60d 100644 --- a/libwvdrmengine/oemcrypto/test/oec_test_data.h +++ b/libwvdrmengine/oemcrypto/test/oec_test_data.h @@ -20,7 +20,7 @@ namespace wvoec { // The first keybox, kTestKeybox, with deviceID "TestKey01" is used for most of // the tests. It should be loaded by OEMCrypto when OEMCrypto_LoadTestKeybox // is called. -const wvoec_mock::WidevineKeybox kTestKeybox = { +static const wvoec_mock::WidevineKeybox kTestKeybox = { // Sample keybox used for test vectors { // deviceID @@ -52,7 +52,7 @@ const wvoec_mock::WidevineKeybox kTestKeybox = { } }; -static wvoec_mock::WidevineKeybox kValidKeybox02 = { +static const wvoec_mock::WidevineKeybox kValidKeybox02 = { // Sample keybox used for test vectors { // deviceID @@ -84,7 +84,7 @@ static wvoec_mock::WidevineKeybox kValidKeybox02 = { } }; -static wvoec_mock::WidevineKeybox kValidKeybox03 = { +static const wvoec_mock::WidevineKeybox kValidKeybox03 = { // Sample keybox used for test vectors { // deviceID diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 54179b29..dc1ae558 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -57,6 +57,17 @@ void PrintTo(const tuple bad_srm(42); - RAND_pseudo_bytes(&bad_srm[0], bad_srm.size()); + GetRandBytes(&bad_srm[0], bad_srm.size()); EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadSRM(&bad_srm[0], bad_srm.size())); } @@ -386,7 +397,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) { TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { const int kDataSize = 256; vector input_buffer(kDataSize); - RAND_pseudo_bytes(&input_buffer[0], input_buffer.size()); + GetRandBytes(&input_buffer[0], input_buffer.size()); vector output_buffer(kDataSize); OEMCrypto_DestBufferDesc dest_buffer; dest_buffer.type = OEMCrypto_BufferType_Clear; @@ -420,7 +431,7 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { TEST_F(OEMCryptoClientTest, ClearCopyTestLargeBufferAPI10) { vector input_buffer(kMaxDecryptSize); - RAND_pseudo_bytes(&input_buffer[0], input_buffer.size()); + GetRandBytes(&input_buffer[0], input_buffer.size()); vector output_buffer(kMaxDecryptSize); OEMCrypto_DestBufferDesc dest_buffer; dest_buffer.type = OEMCrypto_BufferType_Clear; @@ -537,7 +548,7 @@ TEST_F(OEMCryptoProv30Test, OEMCertSignature) { OEMCryptoResult sts; // Sign a Message vector data(500); - RAND_pseudo_bytes(&data[0], data.size()); + GetRandBytes(&data[0], data.size()); size_t signature_length = 0; vector signature(1); @@ -565,7 +576,7 @@ TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { OEMCryptoResult sts; // Sign a Message vector data(500); - RAND_pseudo_bytes(&data[0], data.size()); + GetRandBytes(&data[0], data.size()); size_t signature_length = 0; vector signature(1); @@ -591,7 +602,7 @@ TEST_F(OEMCryptoProv30Test, OEMCertSignatureLargeBuffer) { OEMCryptoResult sts; // Sign a Message vector data(kMaxMessageSize); - RAND_pseudo_bytes(&data[0], data.size()); + GetRandBytes(&data[0], data.size()); size_t signature_length = 0; vector signature(1); @@ -1884,8 +1895,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1902,8 +1913,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1916,8 +1927,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1935,8 +1946,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1956,7 +1967,7 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, EvenOffset) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); // CTR Mode is self-inverse -- i.e. We can pick the encrypted data and // compute the unencrypted data. By picking the encrypted data to be all 0, // it is easier to re-encrypt the data and debug problems. Similarly, we @@ -1984,8 +1995,8 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, OddOffset) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -2008,7 +2019,7 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); encryptionIv = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); @@ -2028,8 +2039,8 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, PartialBlock) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -2049,8 +2060,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptLargeBuffer) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -2063,8 +2074,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -2080,7 +2091,7 @@ TEST_F(OEMCryptoSessionTests, DecryptUnencryptedNoKey) { vector in_buffer(256); for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256; vector encryptionIv(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); // Describe the output vector out_buffer(in_buffer.size()); OEMCrypto_DestBufferDesc destBuffer; @@ -2589,7 +2600,7 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); const size_t size = 50; vector licenseRequest(size); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], licenseRequest.size(), NULL, @@ -2670,7 +2681,7 @@ TEST_F(OEMCryptoUsesCertificate, RSASignature) { OEMCryptoResult sts; // Sign a Message vector licenseRequest(500); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 0; uint8_t signature[500]; @@ -2699,7 +2710,7 @@ TEST_F(OEMCryptoUsesCertificate, RSASignatureLargeBuffer) { OEMCryptoResult sts; // Sign a Message vector licenseRequest(kMaxMessageSize); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 0; uint8_t signature[500]; @@ -2757,7 +2768,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { // Sign a Message vector licenseRequest(size); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 256; vector signature(signature_length); sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], @@ -2788,7 +2799,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector licenseRequest(size); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], licenseRequest.size(), NULL, diff --git a/libwvdrmengine/run_all_unit_tests.sh b/libwvdrmengine/run_all_unit_tests.sh index 824bc8eb..13a2ae2a 100755 --- a/libwvdrmengine/run_all_unit_tests.sh +++ b/libwvdrmengine/run_all_unit_tests.sh @@ -75,6 +75,9 @@ adb_shell_run request_license_test # cdm_extended_duration_test takes >30 minutes to run. # adb_shell_run cdm_extended_duration_test +# cdm_feature_test to be run with modified/mock oemcrypto +# adb_shell_run cdm_feature_test + # Additional tests adb_shell_run base64_test adb_shell_run buffer_reader_test @@ -98,6 +101,7 @@ adb_shell_run policy_engine_unittest adb_shell_run service_certificate_unittest adb_shell_run timer_unittest adb_shell_run usage_table_header_unittest +adb_shell_run wv_cdm_metrics_test # Run the non-Treble test on non-Treble devices if adb shell ls /vendor/lib/mediadrm/libwvdrmengine.so &> /dev/null || diff --git a/libwvdrmengine/test/unit/Android.mk b/libwvdrmengine/test/unit/Android.mk index a4f3e0b8..fced9690 100644 --- a/libwvdrmengine/test/unit/Android.mk +++ b/libwvdrmengine/test/unit/Android.mk @@ -18,7 +18,7 @@ LOCAL_C_INCLUDES := \ vendor/widevine/libwvdrmengine/oemcrypto/include \ LOCAL_STATIC_LIBRARIES := \ - libcrypto_static \ + libcrypto \ libgtest \ libgtest_main \ @@ -64,7 +64,7 @@ LOCAL_C_INCLUDES := \ vendor/widevine/libwvdrmengine/oemcrypto/include \ LOCAL_STATIC_LIBRARIES := \ - libcrypto_static \ + libcrypto \ libgtest \ libgtest_main \ diff --git a/libwvdrmengine/vts/vendor_module/Android.mk b/libwvdrmengine/vts/vendor_module/Android.mk index 782ca00a..2812e80a 100644 --- a/libwvdrmengine/vts/vendor_module/Android.mk +++ b/libwvdrmengine/vts/vendor_module/Android.mk @@ -24,7 +24,7 @@ LOCAL_STATIC_LIBRARIES := \ libgtest \ libcdm \ libcdm_utils \ - libcrypto_static + libcrypto LOCAL_SHARED_LIBRARIES := \ libcutils \