diff --git a/libwvdrmengine/Android.mk b/libwvdrmengine/Android.mk index fbc58966..4f0a45f8 100644 --- a/libwvdrmengine/Android.mk +++ b/libwvdrmengine/Android.mk @@ -92,7 +92,6 @@ LOCAL_C_INCLUDES := \ LOCAL_STATIC_LIBRARIES := \ libcdm \ libcdm_utils \ - libwvwrapper \ libwvlevel3 \ libprotobuf-cpp-2.3.0-lite \ libwvdrmcryptoplugin \ @@ -121,7 +120,6 @@ include $(BUILD_SHARED_LIBRARY) include vendor/widevine/libwvdrmengine/cdm/Android.mk include vendor/widevine/libwvdrmengine/level3/Android.mk -include vendor/widevine/libwvdrmengine/oemcrypto/Android.mk include vendor/widevine/libwvdrmengine/mediacrypto/Android.mk include vendor/widevine/libwvdrmengine/mediadrm/Android.mk diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index 879ed747..237366e9 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -14,8 +14,7 @@ LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES += \ external/openssl/include \ - external/protobuf/src \ - ../oemcrypto/include + external/protobuf/src LOCAL_STATIC_LIBRARIES := libcdm_protos LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers) @@ -30,7 +29,9 @@ LOCAL_SRC_FILES := \ $(CORE_SRC_DIR)/crypto_session.cpp \ $(CORE_SRC_DIR)/device_files.cpp \ $(CORE_SRC_DIR)/license.cpp \ + $(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \ $(CORE_SRC_DIR)/policy_engine.cpp \ + $(CORE_SRC_DIR)/privacy_crypto.cpp \ $(SRC_DIR)/wv_content_decryption_module.cpp LOCAL_MODULE := libcdm diff --git a/libwvdrmengine/cdm/core/include/cdm_client_property_set.h b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h new file mode 100644 index 00000000..7945859f --- /dev/null +++ b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h @@ -0,0 +1,23 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_CDM_CLIENT_PROPERTY_SET_H_ +#define CDM_BASE_CDM_CLIENT_PROPERTY_SET_H_ + +#include +#include +#include + +namespace wvcdm { + +class CdmClientPropertySet { + public: + virtual ~CdmClientPropertySet() {} + + virtual std::string security_level() const = 0; + virtual bool use_privacy_mode() const = 0; + virtual std::vector service_certificate() const = 0; +}; + +} // namespace wvcdm + +#endif // CDM_BASE_CDM_CLIENT_PROPERTY_SET_H_ diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 4319d7bb..c7814751 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -4,11 +4,13 @@ #define CDM_BASE_CDM_ENGINE_H_ #include "certificate_provisioning.h" +#include "oemcrypto_adapter.h" #include "timer.h" #include "wv_cdm_types.h" namespace wvcdm { +class CdmClientPropertySet; class CdmSession; class CryptoEngine; class WvCdmEventListener; @@ -22,8 +24,10 @@ class CdmEngine : public TimerHandler { virtual ~CdmEngine(); // Session related methods - virtual CdmResponseType OpenSession(const CdmKeySystem& key_system, - CdmSessionId* session_id); + virtual CdmResponseType OpenSession( + const CdmKeySystem& key_system, + const CdmClientPropertySet* property_set, + CdmSessionId* session_id); virtual CdmResponseType CloseSession(const CdmSessionId& session_id); virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); @@ -116,9 +120,11 @@ class CdmEngine : public TimerHandler { // instance variables CdmSessionMap sessions_; - CertificateProvisioning cert_provisioning_; CdmReleaseKeySetMap release_key_sets_; + CertificateProvisioning cert_provisioning_; + SecurityLevel cert_provisioning_requested_security_level_; + // policy timer Timer policy_timer_; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index aa4cc57e..fe8f4ada 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -8,23 +8,20 @@ #include "crypto_session.h" #include "device_files.h" #include "license.h" +#include "oemcrypto_adapter.h" #include "policy_engine.h" #include "scoped_ptr.h" #include "wv_cdm_types.h" namespace wvcdm { +class CdmClientPropertySet; class WvCdmEventListener; class CdmSession { public: - CdmSession() - : session_id_(GenerateSessionId()), - crypto_session_(NULL), - license_received_(false), - reinitialize_session_(false), - license_type_(kLicenseTypeStreaming) {} - ~CdmSession() {} + explicit CdmSession(const CdmClientPropertySet* cdm_client_property_set); + ~CdmSession(); CdmResponseType Init(); @@ -87,13 +84,14 @@ class CdmSession { void OnTimerEvent(); void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); + SecurityLevel GetRequestedSecurityLevel(); + private: // Generate unique ID for each new session. CdmSessionId GenerateSessionId(); bool GenerateKeySetId(CdmKeySetId* key_set_id); - bool LoadDeviceCertificate(std::string* cert, std::string* wrapped_key); bool StoreLicense(DeviceFiles::LicenseState state); // instance variables diff --git a/libwvdrmengine/cdm/core/include/certificate_provisioning.h b/libwvdrmengine/cdm/core/include/certificate_provisioning.h index 33984e6a..9e80a197 100644 --- a/libwvdrmengine/cdm/core/include/certificate_provisioning.h +++ b/libwvdrmengine/cdm/core/include/certificate_provisioning.h @@ -4,6 +4,7 @@ #define CDM_BASE_CERTIFICATE_PROVISIONING_H_ #include "crypto_session.h" +#include "oemcrypto_adapter.h" #include "wv_cdm_types.h" namespace wvcdm { @@ -16,7 +17,8 @@ class CertificateProvisioning { ~CertificateProvisioning() {}; // Provisioning related methods - CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request, + CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level, + CdmProvisioningRequest* request, std::string* default_url); CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response); diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index bf75cd5d..588ea58a 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -9,6 +9,7 @@ #include #include "lock.h" +#include "oemcrypto_adapter.h" #include "OEMCryptoCENC.h" #include "wv_cdm_types.h" @@ -29,7 +30,8 @@ class CryptoSession { bool GetSystemId(uint32_t* system_id); bool GetProvisioningId(std::string* provisioning_id); - CdmResponseType Open(); + CdmResponseType Open() { return Open(kLevelDefault); } + CdmResponseType Open(SecurityLevel requested_security_level); void Close(); bool IsOpen() { return open_; } @@ -64,7 +66,7 @@ class CryptoSession { bool SelectKey(const std::string& key_id); CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); - bool GetRandom(uint8_t* random_data, size_t data_length); + bool GetRandom(size_t data_length, uint8_t* random_data); private: void Init(); @@ -88,7 +90,7 @@ class CryptoSession { OEMCryptoBufferType destination_buffer_type_; bool is_destination_buffer_type_valid_; - CdmSecurityLevel security_level_; + SecurityLevel requested_security_level_; CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession); }; diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 692da784..86c55c0e 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -45,6 +45,7 @@ class DeviceFiles { CdmKeyResponse* key_renewal_response, std::string* release_server_url); virtual bool DeleteLicense(const std::string& key_set_id); + virtual bool DeleteAllFiles(); virtual bool DeleteAllLicenses(); virtual bool LicenseExists(const std::string& key_set_id); diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index cee5219b..b68be566 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -19,8 +19,11 @@ class PolicyEngine; class CdmLicense { public: - CdmLicense(); - ~CdmLicense(); + CdmLicense() + : session_(NULL), + initialized_(false), + service_certificate_response_pending_(false) {}; + ~CdmLicense() {}; bool Init(const std::string& token, CryptoSession* session, PolicyEngine* policy_engine); @@ -28,28 +31,37 @@ class CdmLicense { bool PrepareKeyRequest(const CdmInitData& pssh_data, const CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, + const CdmSessionId& session_id, CdmKeyMessage* signed_request, std::string* server_url); - bool PrepareKeyUpdateRequest(bool is_renewal, - CdmKeyMessage* signed_request, + bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request, std::string* server_url); CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response); CdmResponseType HandleKeyUpdateResponse( - bool is_renewal, - const CdmKeyResponse& license_response); + bool is_renewal, const CdmKeyResponse& license_response); bool RestoreOfflineLicense(CdmKeyMessage& license_request, CdmKeyResponse& license_response, CdmKeyResponse& license_renewal_response); + bool HasInitData() { return !init_data_.empty(); } -private: - CdmResponseType HandleKeyErrorResponse( - const video_widevine_server::sdk::SignedMessage& signed_message); + private: + bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request, + std::string* server_url); + CdmResponseType HandleServiceCertificateResponse( + const CdmKeyResponse& service_certificate_response); + + CdmResponseType HandleKeyErrorResponse( + const video_widevine_server::sdk::SignedMessage& signed_message); CryptoSession* session_; PolicyEngine* policy_engine_; std::string server_url_; std::string token_; + std::string service_certificate_; + std::string init_data_; + bool initialized_; + bool service_certificate_response_pending_; // Used for certificate based licensing CdmKeyMessage key_request_; diff --git a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h new file mode 100644 index 00000000..4de39aa4 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h @@ -0,0 +1,39 @@ +/********************************************************************* + * oemcrypto_adapter.h + * + * (c) Copyright 2013 Google, Inc. + * + * This interface allows CDM to open a Level 3 session instead of + * letting the wrapper function choose between level 1 or level 3. + * + *********************************************************************/ + +#ifndef OEMCRYPTO_ADAPTER_H_ +#define OEMCRYPTO_ADAPTER_H_ + +#include "OEMCryptoCENC.h" + +namespace wvcdm { + +enum SecurityLevel { + kLevelDefault, + kLevel3 +}; + +/* This attempts to open a session at the desired security level. + If one level is not available, the other will be used instead. */ +OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, + SecurityLevel level); +OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level); +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength, + SecurityLevel level); +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength, + SecurityLevel level); +OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, + size_t keyBoxLength, + SecurityLevel level); +uint32_t OEMCrypto_APIVersion(SecurityLevel level); +const char* OEMCrypto_SecurityLevel(SecurityLevel level); +} // namespace wvcdm + +#endif // OEMCRYPTO_ADAPTER_H_ diff --git a/libwvdrmengine/cdm/core/include/privacy_crypto.h b/libwvdrmengine/cdm/core/include/privacy_crypto.h new file mode 100644 index 00000000..31d2c1bd --- /dev/null +++ b/libwvdrmengine/cdm/core/include/privacy_crypto.h @@ -0,0 +1,77 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// RsaPublicKey based on //depot/google3/video/widevine/common/rsa_key.h by +// tinskip@google.com. +// +// Description: +// Declaration of classes representing AES and RSA public keys used +// for signature verification and encryption. +// +// AES encryption details: +// Algorithm: AES-CBC +// +// RSA signature details: +// Algorithm: RSASSA-PSS +// Hash algorithm: SHA1 +// Mask generation function: mgf1SHA1 +// Salt length: 20 bytes +// Trailer field: 0xbc +// +// RSA encryption details: +// Algorithm: RSA-OAEP +// Mask generation function: mgf1SHA1 +// Label (encoding paramter): empty string + +#ifndef CDM_BASE_PRIVACY_CRYPTO_H_ +#define CDM_BASE_PRIVACY_CRYPTO_H_ + +#include + +#include "openssl/aes.h" +#include "openssl/rsa.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +class AesCbcKey { + public: + AesCbcKey() : initialized_(false) {}; + ~AesCbcKey() {}; + + bool Init(const std::string& key); + bool Encrypt(const std::string& in, std::string* out, std::string* iv); + + private: + AES_KEY key_; + bool initialized_; + + CORE_DISALLOW_COPY_AND_ASSIGN(AesCbcKey); +}; + +class RsaPublicKey { + public: + RsaPublicKey() : key_(NULL) {} + ~RsaPublicKey(); + + // Initializes an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey + bool Init(const std::string& serialized_key); + + // Encrypt a message using RSA-OAEP. Caller retains ownership of all + // parameters. Returns true if successful, false otherwise. + bool Encrypt(const std::string& clear_message, + std::string* encrypted_message); + + // Verify RSSASSA-PSS signature. Caller retains ownership of all parameters. + // Returns true if validation succeeds, false otherwise. + bool VerifySignature(const std::string& message, + const std::string& signature); + + private: + RSA* key_; + + CORE_DISALLOW_COPY_AND_ASSIGN(RsaPublicKey); +}; + +} // namespace wvcdm + +#endif // CDM_BASE_PRIVACY_CRYPTO_H_ diff --git a/libwvdrmengine/cdm/core/include/properties.h b/libwvdrmengine/cdm/core/include/properties.h index f5b3fd26..49fed6a8 100644 --- a/libwvdrmengine/cdm/core/include/properties.h +++ b/libwvdrmengine/cdm/core/include/properties.h @@ -6,11 +6,16 @@ #include #include +#include "cdm_client_property_set.h" #include "lock.h" +#include "scoped_ptr.h" #include "wv_cdm_types.h" namespace wvcdm { +typedef std::map + CdmClientPropertySetMap; + // This class saves information about features and properties enabled // for a given platform. At initialization it initializes properties from // property_configuration.h. That file specifies features selected for each @@ -40,6 +45,9 @@ class Properties { static inline bool extract_pssh_data() { return extract_pssh_data_; } + static inline bool decrypt_with_empty_session_support() { + return decrypt_with_empty_session_support_; + } static bool GetCompanyName(std::string* company_name); static bool GetModelName(std::string* model_name); static bool GetArchitectureName(std::string* arch_name); @@ -49,8 +57,20 @@ class Properties { static bool GetDeviceFilesBasePath(CdmSecurityLevel security_level, std::string* base_path); static bool GetFactoryKeyboxPath(std::string* keybox); + static bool GetOEMCryptoPath(std::string* library_name); + static const std::string GetSecurityLevel(const CdmSessionId& session_id); + static const std::vector GetServiceCertificate( + const CdmSessionId& session_id); + static bool UsePrivacyMode(const CdmSessionId& session_id); + + static bool AddSessionPropertySet( + const CdmSessionId& session_id, + const CdmClientPropertySet* property_set); + static bool RemoveSessionPropertySet(const CdmSessionId& session_id); private: + static const CdmClientPropertySet* GetCdmClientPropertySet( + const CdmSessionId& session_id); static void set_begin_license_usage_when_received(bool flag) { begin_license_usage_when_received_ = flag; } @@ -72,6 +92,9 @@ class Properties { static void set_extract_pssh_data(bool flag) { extract_pssh_data_ = flag; } + static void set_decrypt_with_empty_session_support(bool flag) { + decrypt_with_empty_session_support_ = flag; + } static bool begin_license_usage_when_received_; static bool require_explicit_renew_request_; @@ -80,6 +103,8 @@ class Properties { static bool oem_crypto_use_userspace_buffers_; static bool use_certificates_as_identification_; static bool extract_pssh_data_; + static bool decrypt_with_empty_session_support_; + static scoped_ptr session_property_set_; CORE_DISALLOW_COPY_AND_ASSIGN(Properties); }; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index fc7ec340..0089b969 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -21,7 +21,8 @@ const int kCdmPolicyTimerDurationSeconds = 1; namespace wvcdm { -CdmEngine::CdmEngine() { +CdmEngine::CdmEngine() + : cert_provisioning_requested_security_level_(kLevelDefault) { Properties::Init(); } @@ -36,6 +37,7 @@ CdmEngine::~CdmEngine() { CdmResponseType CdmEngine::OpenSession( const CdmKeySystem& key_system, + const CdmClientPropertySet* property_set, CdmSessionId* session_id) { LOGI("CdmEngine::OpenSession"); @@ -49,8 +51,7 @@ CdmResponseType CdmEngine::OpenSession( return KEY_ERROR; } - scoped_ptr new_session(new CdmSession()); - + scoped_ptr new_session(new CdmSession(property_set)); if (new_session->session_id().empty()) { LOGE("CdmEngine::OpenSession: failure to generate session ID"); return UNKNOWN_ERROR; @@ -58,10 +59,13 @@ CdmResponseType CdmEngine::OpenSession( CdmResponseType sts = new_session->Init(); if (sts != NO_ERROR) { - LOGE("CdmEngine::OpenSession: bad session init"); + if (sts == NEED_PROVISIONING) { + cert_provisioning_requested_security_level_ = + new_session->GetRequestedSecurityLevel(); + } + LOGE("CdmEngine::OpenSession: bad session init: %u", sts); return sts; } - *session_id = new_session->session_id(); sessions_[*session_id] = new_session.release(); return NO_ERROR; @@ -76,7 +80,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) { } CdmSessionId session_id; - CdmResponseType sts = OpenSession(KEY_SYSTEM, &session_id); + CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id); if (sts != NO_ERROR) return sts; @@ -178,6 +182,10 @@ CdmResponseType CdmEngine::GenerateKeyRequest( server_url); if (KEY_MESSAGE != sts) { + if (sts == NEED_PROVISIONING) { + cert_provisioning_requested_security_level_ = + iter->second->GetRequestedSecurityLevel(); + } LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, " "sts = %d", (int)sts); return sts; @@ -262,7 +270,13 @@ CdmResponseType CdmEngine::RestoreKey( return UNKNOWN_ERROR; } - return iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline); + CdmResponseType sts = + iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline); + if (sts == NEED_PROVISIONING) { + cert_provisioning_requested_security_level_ = + iter->second->GetRequestedSecurityLevel(); + } + return sts; } CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) { @@ -425,7 +439,10 @@ CdmResponseType CdmEngine::GetProvisioningRequest( LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters"); return UNKNOWN_ERROR; } - return cert_provisioning_.GetProvisioningRequest(request, default_url); + return cert_provisioning_.GetProvisioningRequest( + cert_provisioning_requested_security_level_, + request, + default_url); } /* @@ -479,9 +496,19 @@ CdmResponseType CdmEngine::Decrypt( return KEY_ERROR; } - CdmSessionMap::iterator iter = sessions_.find(session_id); + CdmSessionMap::iterator iter; + if (session_id.empty()) { + if (!Properties::decrypt_with_empty_session_support()) return KEY_ERROR; + + // Loop through the sessions to find the session containing the key_id. + for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) { + if (iter->second->IsKeyValid(*parameters.key_id)) break; + } + } else { + iter = sessions_.find(session_id); + } if (iter == sessions_.end()) { - LOGW("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str()); + LOGE("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str()); return KEY_ERROR; } diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index a69e40b8..ded038d8 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -26,14 +26,33 @@ namespace wvcdm { typedef std::set::iterator CdmEventListenerIter; +CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set) + : session_id_(GenerateSessionId()), + crypto_session_(NULL), + license_received_(false), + reinitialize_session_(false), + license_type_(kLicenseTypeStreaming) { + if (cdm_client_property_set) { + Properties::AddSessionPropertySet(session_id_, cdm_client_property_set); + } +} + +CdmSession::~CdmSession() { Properties::RemoveSessionPropertySet(session_id_); } + CdmResponseType CdmSession::Init() { scoped_ptr session(new CryptoSession()); - CdmResponseType sts = session->Open(); + + CdmResponseType sts = session->Open(GetRequestedSecurityLevel()); if (NO_ERROR != sts) return sts; std::string token; if (Properties::use_certificates_as_identification()) { - if (!LoadDeviceCertificate(&token, &wrapped_key_)) return NEED_PROVISIONING; + File file; + DeviceFiles handle; + if (!handle.Init(&file, session.get()->GetSecurityLevel()) || + !handle.RetrieveCertificate(&token, &wrapped_key_)) { + return NEED_PROVISIONING; + } } else { if (!session->GetToken(&token)) return UNKNOWN_ERROR; } @@ -129,7 +148,7 @@ CdmResponseType CdmSession::GenerateKeyRequest( ? UNKNOWN_ERROR : GenerateRenewalRequest(key_request, server_url); } else { - if (init_data.empty()) { + if (init_data.empty() && !license_parser_.HasInitData()) { LOGW("CdmSession::GenerateKeyRequest: init data absent"); return KEY_ERROR; } @@ -148,8 +167,9 @@ CdmResponseType CdmSession::GenerateKeyRequest( } } - if (!license_parser_.PrepareKeyRequest( - pssh_data, license_type, app_parameters, key_request, server_url)) { + if (!license_parser_.PrepareKeyRequest(pssh_data, license_type, + app_parameters, session_id_, + key_request, server_url)) { return KEY_ERROR; } @@ -310,8 +330,21 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { } bool CdmSession::IsKeyValid(const KeyId& key_id) { - // TODO(gmorgan): lookup key and determine if valid. - // return (session_keys_.find(key_id) != session_keys_.end()); + if (crypto_session_.get() == NULL || !crypto_session_->IsOpen()) + return false; + + if (key_id_ != key_id) { + // OEMCrypto does not provide a way to query the existence/validity of a + // key. SelectKey can be used to check whether a key is valid, but there + // is also a side effect - the key is selected for decryption, which might + // be undesirable and it posts restriction on the use of IsKeyValid API. + // TODO(kqyang, gmorgan): consider adding a function in OEMCrypto to check + // if a key is valid. + if (!crypto_session_->SelectKey(key_id)) { + return false; + } + key_id_ = key_id; + } return true; } @@ -337,7 +370,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) { return false; while (key_set_id->empty()) { - if (!crypto_session_->GetRandom(&random_data[0], random_data.size())) + if (!crypto_session_->GetRandom(random_data.size(), &random_data[0])) return false; *key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data); @@ -350,16 +383,6 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) { return true; } -bool CdmSession::LoadDeviceCertificate(std::string* certificate, - std::string* wrapped_key) { - File file; - DeviceFiles handle; - if (!handle.Init(&file, crypto_session_->GetSecurityLevel())) - return false; - - return handle.RetrieveCertificate(certificate, wrapped_key); -} - bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) { File file; DeviceFiles handle; @@ -404,4 +427,13 @@ void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { } } +SecurityLevel CdmSession::GetRequestedSecurityLevel() { + if (Properties::GetSecurityLevel(session_id_) + .compare(QUERY_VALUE_SECURITY_LEVEL_L3) == 0) { + return kLevel3; + } + + return kLevelDefault; +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index 1bdfaa28..36fee47b 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -53,11 +53,12 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString( * Returns NO_ERROR for success and UNKNOWN_ERROR if fails. */ CdmResponseType CertificateProvisioning::GetProvisioningRequest( + SecurityLevel requested_security_level, CdmProvisioningRequest* request, std::string* default_url) { default_url->assign(kDefaultProvisioningServerUrl); - CdmResponseType sts = crypto_session_.Open(); + CdmResponseType sts = crypto_session_.Open(requested_security_level); if (NO_ERROR != sts) { LOGE("GetProvisioningRequest: fails to create a crypto session"); return sts; diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 13be31cd..934d0e80 100755 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -10,8 +10,6 @@ #include "crypto_key.h" #include "log.h" -// TODO(gmorgan,jtinker): decide if OEMCryptoCENC is needed here. -#include "OEMCryptoCENC.h" #include "properties.h" #include "string_conversions.h" #include "wv_cdm_constants.h" @@ -37,7 +35,7 @@ int CryptoSession::session_count_ = 0; CryptoSession::CryptoSession() : open_(false), is_destination_buffer_type_valid_(false), - security_level_(kSecurityLevelUninitialized) { + requested_security_level_(kLevelDefault) { Init(); } @@ -79,7 +77,7 @@ bool CryptoSession::ValidateKeybox() { if (!initialized_) { return false; } - OEMCryptoResult result = OEMCrypto_IsKeyboxValid(); + OEMCryptoResult result = OEMCrypto_IsKeyboxValid(requested_security_level_); return (OEMCrypto_SUCCESS == result); } @@ -95,7 +93,8 @@ bool CryptoSession::GetToken(std::string* token) { if (!initialized_) { return false; } - OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize); + OEMCryptoResult sts = + OEMCrypto_GetKeyData(buf, &bufSize, requested_security_level_); if (OEMCrypto_SUCCESS != sts) { return false; } @@ -110,16 +109,8 @@ CdmSecurityLevel CryptoSession::GetSecurityLevel() { return kSecurityLevelUninitialized; } - switch (security_level_) { - case kSecurityLevelL1: - case kSecurityLevelL2: - case kSecurityLevelL3: - return security_level_; - default: - break; - } - - std::string security_level = OEMCrypto_SecurityLevel(); + std::string security_level = + OEMCrypto_SecurityLevel(requested_security_level_); if ((security_level.size() != 2) || (security_level.at(0) != 'L')) { return kSecurityLevelUnknown; @@ -127,20 +118,16 @@ CdmSecurityLevel CryptoSession::GetSecurityLevel() { switch (security_level.at(1)) { case '1': - security_level_ = kSecurityLevelL1; - break; + return kSecurityLevelL1; case '2': - security_level_ = kSecurityLevelL2; - break; + return kSecurityLevelL2; case '3': - security_level_ = kSecurityLevelL3; - break; + return kSecurityLevelL3; default: - security_level_ = kSecurityLevelUnknown; - break; + return kSecurityLevelUnknown; } - return security_level_; + return kSecurityLevelUnknown; } bool CryptoSession::GetDeviceUniqueId(std::string* device_id) { @@ -159,7 +146,8 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) { if (!initialized_) { return false; } - OEMCryptoResult sts = OEMCrypto_GetDeviceID(&id[0], &id_length); + OEMCryptoResult sts = + OEMCrypto_GetDeviceID(&id[0], &id_length, requested_security_level_); if (OEMCrypto_SUCCESS != sts) { return false; @@ -183,7 +171,8 @@ bool CryptoSession::GetSystemId(uint32_t* system_id) { if (!initialized_) { return false; } - OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size); + OEMCryptoResult sts = + OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_); if (OEMCrypto_SUCCESS != sts) { return false; @@ -211,7 +200,8 @@ bool CryptoSession::GetProvisioningId(std::string* provisioning_id) { if (!initialized_) { return false; } - OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size); + OEMCryptoResult sts = + OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_); if (OEMCrypto_SUCCESS != sts) { return false; @@ -221,14 +211,15 @@ bool CryptoSession::GetProvisioningId(std::string* provisioning_id) { return true; } -CdmResponseType CryptoSession::Open() { +CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { LOGV("CryptoSession::Open: Lock"); AutoLock auto_lock(crypto_lock_); - if (!initialized_) return false; - if (open_) return true; + if (!initialized_) return UNKNOWN_ERROR; + if (open_) return NO_ERROR; OEMCrypto_SESSION sid; - OEMCryptoResult sts = OEMCrypto_OpenSession(&sid); + requested_security_level_ = requested_security_level; + OEMCryptoResult sts = OEMCrypto_OpenSession(&sid, requested_security_level); if (OEMCrypto_SUCCESS == sts) { oec_session_id_ = static_cast(sid); LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_); @@ -680,7 +671,11 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, return true; } -bool CryptoSession::GetRandom(uint8_t* random_data, size_t data_length) { +bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) { + if (random_data == NULL) { + LOGE("CryptoSession::GetRandom: random data destination not provided"); + return false; + } OEMCryptoResult sts = OEMCrypto_GetRandom(random_data, data_length); if (sts != OEMCrypto_SUCCESS) { diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index f861c602..1baf8d20 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -285,7 +285,7 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) { std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { - LOGW("DeviceFiles::StoreFile: Unable to get base path"); + LOGW("DeviceFiles::DeleteLicense: Unable to get base path"); return false; } path.append(key_set_id); @@ -296,13 +296,13 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) { bool DeviceFiles::DeleteAllLicenses() { if (!initialized_) { - LOGW("DeviceFiles::DeleteLicense: not initialized"); + LOGW("DeviceFiles::DeleteAllLicenses: not initialized"); return false; } std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { - LOGW("DeviceFiles::StoreFile: Unable to get base path"); + LOGW("DeviceFiles::DeleteAllLicenses: Unable to get base path"); return false; } path.append(kWildcard); @@ -311,6 +311,21 @@ bool DeviceFiles::DeleteAllLicenses() { return file_->Remove(path); } +bool DeviceFiles::DeleteAllFiles() { + if (!initialized_) { + LOGW("DeviceFiles::DeleteAllFiles: not initialized"); + return false; + } + + std::string path; + if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { + LOGW("DeviceFiles::DeleteAllFiles: Unable to get base path"); + return false; + } + + return file_->Remove(path); +} + bool DeviceFiles::LicenseExists(const std::string& key_set_id) { if (!initialized_) { LOGW("DeviceFiles::LicenseExists: not initialized"); diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index d8b01d84..47f546f6 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -9,6 +9,7 @@ #include "log.h" #include "policy_engine.h" #include "properties.h" +#include "privacy_crypto.h" #include "string_conversions.h" #include "wv_cdm_constants.h" @@ -20,6 +21,41 @@ std::string kDeviceNameKey = "device_name"; std::string kProductNameKey = "product_name"; std::string kBuildInfoKey = "build_info"; std::string kDeviceIdKey = "device_id"; +const unsigned char kServiceCertificateCAPublicKey[] = { + 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5, 0x62, 0x07, + 0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, 0x76, 0xbe, + 0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f, 0xe9, 0x61, + 0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, 0xe4, 0xdd, + 0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b, 0x83, 0xbe, + 0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, 0xc2, 0x65, + 0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01, 0xb5, 0x11, + 0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, 0xb4, 0x7a, + 0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14, 0xf1, 0x22, + 0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, 0xa0, 0x0a, + 0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6, 0x0e, 0x85, + 0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, 0xf2, 0x59, + 0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31, 0xa0, 0xee, + 0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, 0xae, 0xb2, + 0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90, 0x97, 0x2c, + 0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, 0x94, 0x53, + 0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f, 0x07, 0x53, + 0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, 0xea, 0x1e, + 0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17, 0xe8, 0xc9, + 0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, 0xb7, 0xf5, + 0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf, 0x39, 0xf3, + 0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, 0x9c, 0xee, + 0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42, 0x72, 0x85, + 0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, 0xa6, 0x82, + 0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4, 0x3a, 0x3b, + 0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, 0xe9, 0x4c, + 0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72, 0x71, 0x04, + 0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, 0x8f, 0xe1, + 0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04, 0xb5, 0xd7, + 0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, 0x38, 0xff, + 0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33, 0x61, 0x53, + 0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, 0xd5, 0xf0, + 0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02, 0x03, 0x01, + 0x00, 0x01}; } namespace wvcdm { @@ -27,6 +63,8 @@ namespace wvcdm { // Protobuf generated classes. using video_widevine_server::sdk::ClientIdentification; using video_widevine_server::sdk::ClientIdentification_NameValue; +using video_widevine_server::sdk::DeviceCertificate; +using video_widevine_server::sdk::EncryptedClientIdentification; using video_widevine_server::sdk::LicenseRequest; using video_widevine_server::sdk::LicenseRequest_ContentIdentification; using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC; @@ -35,6 +73,7 @@ using video_widevine_server::sdk:: using video_widevine_server::sdk::License; using video_widevine_server::sdk::License_KeyContainer; using video_widevine_server::sdk::LicenseError; +using video_widevine_server::sdk::SignedDeviceCertificate; using video_widevine_server::sdk::SignedMessage; static std::vector ExtractContentKeys(const License& license) { @@ -81,36 +120,65 @@ static std::vector ExtractContentKeys(const License& license) { return key_array; } -CdmLicense::CdmLicense() : session_(NULL) {} - -CdmLicense::~CdmLicense() {} - bool CdmLicense::Init(const std::string& token, CryptoSession* session, PolicyEngine* policy_engine) { - if (token.size() == 0) return false; - if (session == NULL || !session->IsOpen()) return false; + if (token.size() == 0) { + LOGE("CdmLicense::Init: empty token provided"); + return false; + } + if (session == NULL || !session->IsOpen()) { + LOGE("CdmLicense::Init: crypto session not provided or not open"); + return false; + } + if (policy_engine == NULL) { + LOGE("CdmLicense::Init: no policy engine provided"); + return false; + } token_ = token; session_ = session; policy_engine_ = policy_engine; + initialized_ = true; return true; } bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, const CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, + const CdmSessionId& session_id, CdmKeyMessage* signed_request, std::string* server_url) { - if (!session_ || token_.empty()) { + if (!initialized_) { + LOGE("CdmLicense::PrepareKeyRequest: not initialized"); return false; } - if (init_data.empty()) { - LOGE("CdmLicense::PrepareKeyRequest : No init data provided;"); + if (init_data.empty() && init_data_.empty()) { + LOGE("CdmLicense::PrepareKeyRequest: empty init data provided"); + return false; + } + if (session_id.empty()) { + LOGE("CdmLicense::PrepareKeyRequest: empty session id provided"); return false; } if (!signed_request) { - LOGE("CdmLicense::PrepareKeyRequest : No signed request provided."); + LOGE("CdmLicense::PrepareKeyRequest: no signed request provided"); return false; } + if (!server_url) { + LOGE("CdmLicense::PrepareKeyRequest: no server url provided"); + return false; + } + + bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id); + std::vector cert = Properties::GetServiceCertificate(session_id); + std::string serialized_service_certificate(cert.begin(), cert.end()); + + if (serialized_service_certificate.empty()) + serialized_service_certificate = service_certificate_; + + if (privacy_mode_enabled && serialized_service_certificate.empty()) { + init_data_ = init_data; + return PrepareServiceCertificateRequest(signed_request, server_url); + } // TODO(gmorgan): Request ID owned by session? std::string request_id; @@ -170,13 +238,71 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, client_info->set_value(value); } + if (privacy_mode_enabled) { + EncryptedClientIdentification* encrypted_client_id = + license_request.mutable_encrypted_client_id(); + DeviceCertificate service_certificate; + + if (!service_certificate.ParseFromString(serialized_service_certificate)) { + LOGE( + "CdmLicense::PrepareKeyRequest: unable to parse retrieved " + "service certificate"); + return false; + } + + if (service_certificate.type() != + video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) { + LOGE( + "CdmLicense::PrepareKeyRequest: retrieved certificate not of type" + " service, %d", + service_certificate.type()); + return false; + } + encrypted_client_id->set_service_id(service_certificate.service_id()); + encrypted_client_id->set_service_certificate_serial_number( + service_certificate.serial_number()); + + std::string iv(KEY_IV_SIZE, 0); + std::string key(KEY_SIZE, 0); + + if (!session_->GetRandom(key.size(), reinterpret_cast(&key[0]))) { + return false; + } + if (!session_->GetRandom(iv.size(), reinterpret_cast(&iv[0]))) { + return false; + } + std::string id, enc_id, enc_key; + client_id->SerializeToString(&id); + + AesCbcKey aes; + if (!aes.Init(key)) return false; + if (!aes.Encrypt(id, &enc_id, &iv)) return false; + + RsaPublicKey rsa; + if (!rsa.Init(service_certificate.public_key())) return false; + if (!rsa.Encrypt(key, &enc_key)) return false; + + encrypted_client_id->set_encrypted_client_id_iv(iv); + encrypted_client_id->set_encrypted_privacy_key(enc_key); + encrypted_client_id->set_encrypted_client_id(enc_id); + license_request.clear_client_id(); + } + // Content Identification may be a cenc_id, a webm_id or a license_id LicenseRequest_ContentIdentification* content_id = license_request.mutable_content_id(); LicenseRequest_ContentIdentification_CENC* cenc_content_id = content_id->mutable_cenc_id(); - cenc_content_id->add_pssh(init_data); + + if (!init_data.empty()) { + cenc_content_id->add_pssh(init_data); + } else if (privacy_mode_enabled && !init_data_.empty()) { + cenc_content_id->add_pssh(init_data_); + } else { + LOGD("CdmLicense::PrepareKeyRequest: init data not available"); + return false; + } switch (license_type) { case kLicenseTypeOffline: @@ -187,7 +313,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, break; default: LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d", - license_type); + license_type); return false; break; } @@ -205,7 +331,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, if (!session_->GenerateNonce(&nonce)) { return false; } - license_request.set_key_control_nonce(UintToString(nonce)); + license_request.set_key_control_nonce(nonce); LOGD("PrepareKeyRequest: nonce=%u", nonce); license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1); @@ -245,8 +371,8 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request, std::string* server_url) { - if (!session_) { - LOGE("CdmLicense::PrepareKeyUpdateRequest: Invalid crypto session"); + if (!initialized_) { + LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized"); return false; } if (!signed_request) { @@ -274,7 +400,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal, if (!session_->GenerateNonce(&nonce)) { return false; } - license_request.set_key_control_nonce(UintToString(nonce)); + license_request.set_key_control_nonce(nonce); LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce); license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1); @@ -289,7 +415,8 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal, return false; if (license_request_signature.empty()) { - LOGE("CdmLicense::PrepareKeyUpdateRequest: empty license request" + LOGE( + "CdmLicense::PrepareKeyUpdateRequest: empty license request" " signature"); return false; } @@ -307,28 +434,46 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal, CdmResponseType CdmLicense::HandleKeyResponse( const CdmKeyResponse& license_response) { - if (!session_) { + if (!initialized_) { + LOGE("CdmLicense::HandleKeyResponse: not initialized"); return KEY_ERROR; } if (license_response.empty()) { - LOGE("CdmLicense::HandleKeyResponse : Empty license response."); + LOGE("CdmLicense::HandleKeyResponse: empty license response"); return KEY_ERROR; } + if (service_certificate_response_pending_) + return CdmLicense::HandleServiceCertificateResponse(license_response); + SignedMessage signed_response; - if (!signed_response.ParseFromString(license_response)) return KEY_ERROR; + if (!signed_response.ParseFromString(license_response)) { + LOGE( + "CdmLicense::HandleKeyResponse: unable to parse signed license" + " response"); + return KEY_ERROR; + } if (signed_response.type() == SignedMessage::ERROR) { return HandleKeyErrorResponse(signed_response); } - if (!signed_response.has_signature()) return KEY_ERROR; + if (!signed_response.has_signature()) { + LOGE("CdmLicense::HandleKeyResponse: license response is not signed"); + return KEY_ERROR; + } License license; - if (!license.ParseFromString(signed_response.msg())) return KEY_ERROR; + if (!license.ParseFromString(signed_response.msg())) { + LOGE("CdmLicense::HandleKeyResponse: unable to parse license response"); + return KEY_ERROR; + } if (Properties::use_certificates_as_identification()) { - if (!signed_response.has_session_key()) return KEY_ERROR; + if (!signed_response.has_session_key()) { + LOGE("CdmLicense::HandleKeyResponse: no session keys present"); + return KEY_ERROR; + } if (!session_->GenerateDerivedKeys(key_request_, signed_response.session_key())) @@ -349,6 +494,10 @@ CdmResponseType CdmLicense::HandleKeyResponse( } if (mac_key_iv.size() != KEY_IV_SIZE || mac_key.size() != MAC_KEY_SIZE) { + LOGE( + "CdmLicense::HandleKeyResponse: mac key/iv size error" + "(key/iv size expected: %d/%d, actual: %d/%d", + MAC_KEY_SIZE, KEY_IV_SIZE, mac_key.size(), mac_key_iv.size()); return KEY_ERROR; } } @@ -374,9 +523,9 @@ CdmResponseType CdmLicense::HandleKeyResponse( } CdmResponseType CdmLicense::HandleKeyUpdateResponse( - bool is_renewal, - const CdmKeyResponse& license_response) { - if (!session_) { + bool is_renewal, const CdmKeyResponse& license_response) { + if (!initialized_) { + LOGE("CdmLicense::HandleKeyUpdateResponse: not initialized"); return KEY_ERROR; } if (license_response.empty()) { @@ -401,7 +550,8 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse( License license; if (!license.ParseFromString(signed_response.msg())) { - LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse license" + LOGE( + "CdmLicense::HandleKeyUpdateResponse: Unable to parse license" " from signed message"); return KEY_ERROR; } @@ -423,15 +573,12 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse( // merge from Eureka) policy_engine_->UpdateLicense(license); - if (!is_renewal) - return KEY_ADDED; + if (!is_renewal) return KEY_ADDED; std::vector key_array = ExtractContentKeys(license); - if (session_->RefreshKeys(signed_response.msg(), - signed_response.signature(), - key_array.size(), - &key_array[0])) { + if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(), + key_array.size(), &key_array[0])) { return KEY_ADDED; } else { return KEY_ERROR; @@ -439,13 +586,14 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse( } bool CdmLicense::RestoreOfflineLicense( - CdmKeyMessage& license_request, - CdmKeyResponse& license_response, + CdmKeyMessage& license_request, CdmKeyResponse& license_response, CdmKeyResponse& license_renewal_response) { if (license_request.empty() || license_response.empty()) { - LOGE("CdmLicense::RestoreOfflineLicense: key_request or response empty: " - "%u %u", license_request.size(), license_response.size()); + LOGE( + "CdmLicense::RestoreOfflineLicense: key_request or response empty: " + "%u %u", + license_request.size(), license_response.size()); return false; } @@ -456,36 +604,139 @@ bool CdmLicense::RestoreOfflineLicense( } if (signed_request.type() != SignedMessage::LICENSE_REQUEST) { - LOGE("CdmLicense::RestoreOfflineLicense: license request type: expected = " + LOGE( + "CdmLicense::RestoreOfflineLicense: license request type: expected = " "%d, actual = %d", - SignedMessage::LICENSE_REQUEST, - signed_request.type()); + SignedMessage::LICENSE_REQUEST, signed_request.type()); return false; } if (Properties::use_certificates_as_identification()) { key_request_ = signed_request.msg(); - } - else { - if (!session_->GenerateDerivedKeys(signed_request.msg())) - return false; + } else { + if (!session_->GenerateDerivedKeys(signed_request.msg())) return false; } CdmResponseType sts = HandleKeyResponse(license_response); - if (sts != KEY_ADDED) - return false; + if (sts != KEY_ADDED) return false; if (!license_renewal_response.empty()) { sts = HandleKeyUpdateResponse(true, license_renewal_response); - if (sts != KEY_ADDED) - return false; + if (sts != KEY_ADDED) return false; } return true; } +bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request, + std::string* server_url) { + if (!initialized_) { + LOGE("CdmLicense::PrepareServiceCertificateRequest: not initialized"); + return false; + } + if (!signed_request) { + LOGE( + "CdmLicense::PrepareServiceCertificateRequest: no signed request" + " provided"); + return false; + } + if (!server_url) { + LOGE( + "CdmLicense::PrepareServiceCertificateRequest: no server url" + " provided"); + return false; + } + SignedMessage signed_message; + signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST); + signed_message.SerializeToString(signed_request); + *server_url = server_url_; + service_certificate_response_pending_ = true; + + return true; +} + +CdmResponseType CdmLicense::HandleServiceCertificateResponse( + const CdmKeyResponse& service_certificate_response) { + if (!initialized_) { + LOGE("CdmLicense::HandleServiceCertificateResponse: not initialized"); + return KEY_ERROR; + } + if (service_certificate_response.empty()) { + LOGE( + "CdmLicense::HandleServiceCertificateResponse: empty service " + "certificate response"); + return KEY_ERROR; + } + + SignedMessage signed_response; + if (!signed_response.ParseFromString(service_certificate_response)) + return KEY_ERROR; + + switch (signed_response.type()) { + case SignedMessage::ERROR: + return HandleKeyErrorResponse(signed_response); + case SignedMessage::SERVICE_CERTIFICATE: + break; // expected message type + default: + LOGE( + "CdmLicense::HandleServiceCertificateResponse: unexpected signed" + "message type: %d", + signed_response.type()); + return KEY_ERROR; + } + + SignedDeviceCertificate signed_service_certificate; + if (!signed_service_certificate.ParseFromString(signed_response.msg())) { + LOGE( + "CdmLicense::HandleServiceCertificateResponse: unable to parse" + "signed device certificate"); + return KEY_ERROR; + } + + RsaPublicKey root_ca_key; + std::vector ca_public_key( + &kServiceCertificateCAPublicKey[0], + &kServiceCertificateCAPublicKey[sizeof(kServiceCertificateCAPublicKey)]); + if (!root_ca_key.Init(b2a_hex(ca_public_key))) { + LOGE( + "CdmLicense::HandleServiceCertificateResponse: public key" + "initialization failed"); + return KEY_ERROR; + } + + if (!root_ca_key.VerifySignature( + signed_service_certificate.device_certificate(), + signed_service_certificate.signature())) { + LOGE( + "CdmLicense::HandleServiceCertificateResponse: service " + "certificate verification failed"); + return KEY_ERROR; + } + + DeviceCertificate service_certificate; + if (!service_certificate.ParseFromString( + signed_service_certificate.device_certificate())) { + LOGE( + "CdmLicense::HandleServiceCertificateResponse: unable to parse " + "retrieved service certificate"); + return KEY_ERROR; + } + + if (service_certificate.type() != + video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) { + LOGE( + "CdmLicense::HandleServiceCertificateResponse: certificate not of type" + " service, %d", + service_certificate.type()); + return KEY_ERROR; + } + + service_certificate_ = signed_service_certificate.device_certificate(); + return NEED_KEY; +} + CdmResponseType CdmLicense::HandleKeyErrorResponse( const SignedMessage& signed_message) { @@ -496,14 +747,14 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse( } switch (license_error.error_code()) { - case LicenseError::INVALID_CREDENTIALS: + case LicenseError::INVALID_DEVICE_CERTIFICATE: return NEED_PROVISIONING; - case LicenseError::REVOKED_CREDENTIALS: + case LicenseError::REVOKED_DEVICE_CERTIFICATE: return DEVICE_REVOKED; case LicenseError::SERVICE_UNAVAILABLE: default: LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d", - license_error.error_code()); + license_error.error_code()); return KEY_ERROR; } } diff --git a/libwvdrmengine/cdm/core/src/license_protocol.proto b/libwvdrmengine/cdm/core/src/license_protocol.proto index 9aed6359..9f0d4157 100644 --- a/libwvdrmengine/cdm/core/src/license_protocol.proto +++ b/libwvdrmengine/cdm/core/src/license_protocol.proto @@ -8,6 +8,17 @@ // inline to avoid having to hardcode the import path. This is a temporary // workaround for not getting proto_path to work in Android build envionment. // +// Origin: +// This file is derived from the authoritative source file at +// https://cs.corp.google.com/#google3/video/widevine/server/sdk/ +// license_protocol.proto +// +// Description: +// Definitions of the protocol buffer messages used in the Widevine license +// exchange protocol, which is described in the document +// https://docs.google.com/a/google.com/document/d/ +// 1cng6cDnchbDQDymLEd5MxMc_laS3EDv6IsoW3IzpgwQ + syntax = "proto2"; package video_widevine_server.sdk; @@ -116,7 +127,8 @@ message License { // |key_control| is documented here: // https://docs.google.com/a/google.com/document/d/17eDxzzGpPc2qSm7zW68_5ensuxbHErYCvD3IxSKETRo/edit# // If present, the key control must be communicated to the secure - // environment prior to any usage. + // environment prior to any usage. This message is automatically generated + // by the Widevine License Server SDK. optional bytes key_control_block = 1; optional bytes iv = 2; } @@ -140,6 +152,16 @@ message License { } optional CGMS cgms_flags = 2 [default = CGMS_NONE]; } + + message OperatorSessionKeyPermissions { + // Permissions/key usage flags for operator service keys + // (type = OPERATOR_SESSION). + optional bool allow_encrypt = 1 [default = false]; + optional bool allow_decrypt = 2 [default = false]; + optional bool allow_sign = 3 [default = false]; + optional bool allow_signature_verify = 4 [default = false]; + } + optional bytes id = 1; optional bytes iv = 2; optional bytes key = 3; @@ -148,6 +170,7 @@ message License { optional OutputProtection required_protection = 6; optional OutputProtection requested_protection = 7; optional KeyControl key_control = 8; + optional OperatorSessionKeyPermissions operator_session_key_permissions = 9; } optional LicenseIdentification id = 1; @@ -194,20 +217,32 @@ message LicenseRequest { // The client_id provides information authenticating the calling device. It // contains the Widevine keybox token that was installed on the device at the - // factory. This field is required for a valid license request. + // factory. This field or encrypted_client_id below is required for a valid + // license request, but both should never be present in the same request. optional ClientIdentification client_id = 1; optional ContentIdentification content_id = 2; optional RequestType type = 3; optional int64 request_time = 4; - optional bytes key_control_nonce = 5; + // Old-style decimal-encoded string key control nonce. + optional bytes key_control_nonce_deprecated = 5; optional ProtocolVersion protocol_version = 6 [default = VERSION_2_0]; + // New-style uint32 key control nonce, please use instead of + // key_control_nonce_deprecated. + optional uint32 key_control_nonce = 7; + // Encrypted ClientIdentification message, used for privacy purposes. + optional EncryptedClientIdentification encrypted_client_id = 8; } message LicenseError { enum Error { - INVALID_CREDENTIALS = 1; - REVOKED_CREDENTIALS = 2; + // The device credentials are invalid. The device must re-provision. + INVALID_DEVICE_CERTIFICATE = 1; + // The device credentials have been revoked. Re-provisioning is not + // possible. + REVOKED_DEVICE_CERTIFICATE = 2; + // The service is currently unavailable due to the backend being down + // or similar circumstances. SERVICE_UNAVAILABLE = 3; } optional Error error_code = 1; @@ -218,6 +253,8 @@ message SignedMessage { LICENSE_REQUEST = 1; LICENSE = 2; ERROR = 3; + SERVICE_CERTIFICATE_REQUEST = 4; + SERVICE_CERTIFICATE = 5; } optional MessageType type = 1; @@ -230,7 +267,10 @@ message SignedMessage { message SessionInit { optional bytes session_id = 1; optional bytes purchase_id = 2; + // master_signing_key should be 128 bits in length. optional bytes master_signing_key = 3; + // signing_key should be 512 bits in length to be split into two + // (server || client) HMAC-SHA256 keys. optional bytes signing_key = 4; optional int64 license_start_time = 5; } @@ -267,7 +307,7 @@ message ProvisioningResponse { optional bytes device_rsa_key = 1; // Initialization vector used to encrypt device_rsa_key. Required. optional bytes device_rsa_key_iv = 2; - // Serialized DeviceCertificate. Required. + // Serialized SignedDeviceCertificate. Required. optional bytes device_certificate = 3; // Nonce value matching nonce in ProvisioningRequest. Required. optional bytes nonce = 4; @@ -288,10 +328,13 @@ message SignedProvisioningMessage { // Copyright 2013 Google Inc. All Rights Reserved. // Author: tinskip@google.com (Thomas Inskip) // +// Origin: +// This file is derived from the authoritative source file at +// https://cs.corp.google.com/#google3/video/widevine/server/sdk/ +// license_protocol.proto +// // Description: -// ClientIdentification message used by provisioning and license protocols. - -option java_outer_classname = "ClientIdentificationProtos"; +// ClientIdentification messages used by provisioning and license protocols. // ClientIdentification message used to authenticate the client device. message ClientIdentification { @@ -312,3 +355,138 @@ message ClientIdentification { // Optional client information name/value pairs. repeated NameValue client_info = 3; } + +// EncryptedClientIdentification message used to hold ClientIdentification +// messages encrypted for privacy purposes. +message EncryptedClientIdentification { + // Service ID for which the ClientIdentifcation is encrypted (owner of service + // certificate). + optional string service_id = 1; + // Serial number for the service certificate for which ClientIdentification is + // encrypted. + optional string service_certificate_serial_number = 2; + // Serialized ClientIdentification message, encrypted with the privacy key using + // AES-128-CBC with PKCS#5 padding. + optional bytes encrypted_client_id = 3; + // Initialization vector needed to decrypt encrypted_client_id. + optional bytes encrypted_client_id_iv = 4; + // AES-128 privacy key, encrytped with the service public public key using + // RSA-OAEP. + optional bytes encrypted_privacy_key = 5; +}; + +// ---------------------------------------------------------------------------- +// device_certificate.proto +// ---------------------------------------------------------------------------- +// Copyright 2013 Google Inc. All Rights Reserved. +// Author: tinskip@google.com (Thomas Inskip) +// +// Description: +// Device certificate and certificate status list format definitions. + +// Certificate definition for user devices, intermediate, service, and root +// certificates. +message DeviceCertificate { + enum CertificateType { + ROOT = 0; + INTERMEDIATE = 1; + USER_DEVICE = 2; + SERVICE = 3; + } + + // Type of certificate. Required. + optional CertificateType type = 1; + // 128-bit globally unique serial number of certificate. + // Value is 0 for root certificate. Required. + optional bytes serial_number = 2; + // POSIX time, in seconds, when the certificate was created. Required. + optional uint32 creation_time_seconds = 3; + // Device public key. PKCS#1 ASN.1 DER-encoded. Required. + optional bytes public_key = 4; + // Widevine system ID for the device. Required for intermediate and + // user device certificates. + optional uint32 system_id = 5; + // True if the certificate corresponds to a test (non production) device or + // service. Optional. + optional bool test_device = 6 [default = false]; + // Service identifier (web origin) for the service which owns the certificate. + // Required for service certificates. + optional string service_id = 7; +} + +// DeviceCertificate signed with intermediate or root certificate private key. +message SignedDeviceCertificate { + // Serialized DeviceCertificate. Required. + optional bytes device_certificate = 1; + // Signature of device_certificate. Signed with root or intermediate + // certificate private key using RSASSA-PSS. Required. + optional bytes signature = 2; + // Intermediate signing certificate. Present only for user device + // certificates. All others signed with root certificate private key. + optional SignedDeviceCertificate signer = 3; +} + +// Contains device model information for a provisioned device. +message ProvisionedDeviceInfo { + enum WvSecurityLevel { + // Defined in Widevine Security Integration Guide for DASH on Android: + // https://docs.google.com/a/google.com/document/d/1Zum-fcJeoIw6KG1kDP_KepIE5h9gAZg0PaMtemBvk9c/edit#heading=h.1t3h5sf + LEVEL_UNSPECIFIED = 0; + LEVEL_1 = 1; + LEVEL_2 = 2; + LEVEL_3 = 3; + } + + // Widevine system ID for the device. Mandatory. + optional uint32 system_id = 1; + // Name of system-on-a-chip. Optional. + optional string soc = 2; + // Name of manufacturer. Optional. + optional string manufacturer = 3; + // Manufacturer's model name. Matches "brand" in device metadata. Optional. + optional string model = 4; + // Type of device (Phone, Tablet, TV, etc). + optional string device_type = 5; + // Device model year. Optional. + optional uint32 model_year = 6; + // Widevine-defined security level. Optional. + optional WvSecurityLevel security_level = 7 [default = LEVEL_UNSPECIFIED]; + // True if the certificate corresponds to a test (non production) device. + // Optional. + optional bool test_device = 8 [default = false]; +} + +// Contains the status of the root or an intermediate DeviceCertificate. +message DeviceCertificateStatus { + enum CertificateStatus { + VALID = 0; + REVOKED = 1; + }; + + // Serial number of the DeviceCertificate to which this message refers. + // Required. + optional bytes serial_number = 1; + // Status of the certificate. Optional. + optional CertificateStatus status = 2 [default = VALID]; + // Device model information about the device to which the certificate + // corresponds. Required. + optional ProvisionedDeviceInfo device_info = 4; +} + +// List of DeviceCertificateStatus. Used to propagate certificate revocation and +// update list. +message DeviceCertificateStatusList { + // POSIX time, in seconds, when the list was created. Required. + optional uint32 creation_time_seconds = 1; + // DeviceCertificateStatus for each certifificate. + repeated DeviceCertificateStatus certificate_status = 2; +} + +// Signed CertificateStatusList +message SignedCertificateStatusList { + // Serialized DeviceCertificateStatusList. Required. + optional bytes certificate_status_list = 1; + // Signature of certificate_status_list. Signed with root certificate private + // key using RSASSA-PSS. Required. + optional bytes signature = 2; +} diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp new file mode 100644 index 00000000..dc1cfb3e --- /dev/null +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -0,0 +1,665 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Wrapper of OEMCrypto APIs for platforms that support both Levels 1 and 3. + * This should be used when liboemcrypto.so is dynamically loaded at run + * time and not linked with the CDM code at compile time. + * An implementation should compile either oemcrypto_adapter_dynamic.cpp or + * oemcrypto_adapter_static.cpp, but not both. + * + ******************************************************************************/ + +#include "oemcrypto_adapter.h" + +#include +#include +#include +#include +#include +#include + +#include "level3.h" +#include "log.h" +#include "file_store.h" +#include "properties.h" + +using namespace wvoec_level3; + +namespace wvcdm { + +typedef OEMCryptoResult (*L1_Initialize_t)(void); +typedef OEMCryptoResult (*L1_Terminate_t)(void); +typedef OEMCryptoResult (*L1_OpenSession_t)(OEMCrypto_SESSION* session); +typedef OEMCryptoResult (*L1_CloseSession_t)(OEMCrypto_SESSION session); +typedef OEMCryptoResult (*L1_GenerateDerivedKeys_t)( + OEMCrypto_SESSION session, const uint8_t* mac_key_context, + uint32_t mac_key_context_length, const uint8_t* enc_key_context, + uint32_t enc_key_context_length); +typedef OEMCryptoResult (*L1_GenerateNonce_t)(OEMCrypto_SESSION session, + uint32_t* nonce); +typedef OEMCryptoResult (*L1_GenerateSignature_t)(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); +typedef OEMCryptoResult (*L1_LoadKeys_t)( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys, + const OEMCrypto_KeyObject* key_array); +typedef OEMCryptoResult (*L1_RefreshKeys_t)( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array); +typedef OEMCryptoResult (*L1_SelectKey_t)(const OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_length); +typedef OEMCryptoResult (*L1_DecryptCTR_t)( + OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length, + bool is_encrypted, const uint8_t* iv, size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags); +typedef OEMCryptoResult (*L1_InstallKeybox_t)(const uint8_t* keybox, + size_t keyBoxLength); +typedef OEMCryptoResult (*L1_IsKeyboxValid_t)(void); +typedef OEMCryptoResult (*L1_GetDeviceID_t)(uint8_t* deviceID, + size_t* idLength); +typedef OEMCryptoResult (*L1_GetKeyData_t)(uint8_t* keyData, + size_t* keyDataLength); +typedef OEMCryptoResult (*L1_GetRandom_t)(uint8_t* randomData, + size_t dataLength); +typedef OEMCryptoResult (*L1_WrapKeybox_t)(const uint8_t* keybox, + size_t keyBoxLength, + uint8_t* wrappedKeybox, + size_t* wrappedKeyBoxLength, + const uint8_t* transportKey, + size_t transportKeyLength); +typedef OEMCryptoResult (*L1_RewrapDeviceRSAKey_t)( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, const uint32_t* nonce, + const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key, + size_t* wrapped_rsa_key_length); +typedef OEMCryptoResult (*L1_LoadDeviceRSAKey_t)(OEMCrypto_SESSION session, + const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length); +typedef OEMCryptoResult (*L1_GenerateRSASignature_t)(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); +typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)( + OEMCrypto_SESSION session, const uint8_t* enc_session_key, + size_t enc_session_key_length, const uint8_t* mac_key_context, + size_t mac_key_context_length, const uint8_t* enc_key_context, + size_t enc_key_context_length); +typedef OEMCryptoResult (*L1_Generic_Encrypt_t)( + OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, + const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer); +typedef OEMCryptoResult (*L1_Generic_Decrypt_t)( + OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, + const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer); + +typedef OEMCryptoResult (*L1_Generic_Sign_t)(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length); + +typedef OEMCryptoResult (*L1_Generic_Verify_t)(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length); +typedef uint32_t (*L1_APIVersion_t)(); +typedef const char* (*L1_SecurityLevel_t)(); + +struct FunctionPointers { + L1_Initialize_t Initialize; + L1_Terminate_t Terminate; + L1_OpenSession_t OpenSession; + L1_CloseSession_t CloseSession; + L1_GenerateDerivedKeys_t GenerateDerivedKeys; + L1_GenerateNonce_t GenerateNonce; + L1_GenerateSignature_t GenerateSignature; + L1_LoadKeys_t LoadKeys; + L1_RefreshKeys_t RefreshKeys; + L1_SelectKey_t SelectKey; + L1_DecryptCTR_t DecryptCTR; + L1_InstallKeybox_t InstallKeybox; + L1_IsKeyboxValid_t IsKeyboxValid; + L1_GetDeviceID_t GetDeviceID; + L1_GetKeyData_t GetKeyData; + L1_GetRandom_t GetRandom; + L1_WrapKeybox_t WrapKeybox; + L1_RewrapDeviceRSAKey_t RewrapDeviceRSAKey; + L1_LoadDeviceRSAKey_t LoadDeviceRSAKey; + L1_GenerateRSASignature_t GenerateRSASignature; + L1_DeriveKeysFromSessionKey_t DeriveKeysFromSessionKey; + L1_APIVersion_t APIVersion; + L1_SecurityLevel_t SecurityLevel; + L1_Generic_Encrypt_t Generic_Encrypt; + L1_Generic_Decrypt_t Generic_Decrypt; + L1_Generic_Sign_t Generic_Sign; + L1_Generic_Verify_t Generic_Verify; +}; + +struct LevelSession { + FunctionPointers* fcn; + OEMCrypto_SESSION session; + LevelSession() : fcn(0), session(0) {}; +}; + +#define QUOTE_DEFINE(A) #A +#define QUOTE(A) QUOTE_DEFINE(A) +#define LOOKUP(Name, Function) \ + level1_.Name = \ + (L1_##Name##_t)dlsym(level1_library_, QUOTE(Function)); \ + if (!level1_.Name) { \ + LOGW("Could not load L1 %s. Falling Back to L3.", \ + QUOTE(OEMCrypto_##Name)); \ + return false; \ + } + +class Adapter { + public: + typedef std::map::iterator map_iterator; + + Adapter() : level1_valid_(false), level1_library_(NULL) {} + + ~Adapter() { + for (map_iterator i = session_map_.begin(); i != session_map_.end(); i++) { + if (i->second.fcn) i->second.fcn->CloseSession(i->second.session); + } + session_map_.clear(); + } + + OEMCryptoResult Initialize() { + LoadLevel3(); + OEMCryptoResult result = Level3_Initialize(); + std::string library_name; + if (!wvcdm::Properties::GetOEMCryptoPath(&library_name)) { + LOGW("L1 library not specified. Falling Back to L3"); + return result; + } + level1_library_ = dlopen(library_name.c_str(), RTLD_NOW); + if (level1_library_ == NULL) { + LOGW("Could not load %s. Falling Back to L3. %s", library_name.c_str(), + dlerror()); + return result; + } + if (LoadLevel1()) { + LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1."); + } else { + dlclose(level1_library_); + level1_library_ = NULL; + level1_valid_ = false; + } + return result; + } + + bool LoadLevel1() { + level1_valid_ = true; + LOOKUP(Initialize, OEMCrypto_Initialize); + LOOKUP(Terminate, OEMCrypto_Terminate); + LOOKUP(OpenSession, OEMCrypto_OpenSession); + LOOKUP(CloseSession, OEMCrypto_CloseSession); + LOOKUP(GenerateDerivedKeys, OEMCrypto_GenerateDerivedKeys); + LOOKUP(GenerateNonce, OEMCrypto_GenerateNonce); + LOOKUP(GenerateSignature, OEMCrypto_GenerateSignature); + LOOKUP(LoadKeys, OEMCrypto_LoadKeys); + LOOKUP(RefreshKeys, OEMCrypto_RefreshKeys); + LOOKUP(SelectKey, OEMCrypto_SelectKey); + LOOKUP(DecryptCTR, OEMCrypto_DecryptCTR); + LOOKUP(InstallKeybox, OEMCrypto_InstallKeybox); + LOOKUP(IsKeyboxValid, OEMCrypto_IsKeyboxValid); + LOOKUP(GetDeviceID, OEMCrypto_GetDeviceID); + LOOKUP(GetKeyData, OEMCrypto_GetKeyData); + LOOKUP(GetRandom, OEMCrypto_GetRandom); + LOOKUP(WrapKeybox, OEMCrypto_WrapKeybox); + LOOKUP(RewrapDeviceRSAKey, OEMCrypto_RewrapDeviceRSAKey); + LOOKUP(LoadDeviceRSAKey, OEMCrypto_LoadDeviceRSAKey); + LOOKUP(GenerateRSASignature, OEMCrypto_GenerateRSASignature); + LOOKUP(DeriveKeysFromSessionKey, OEMCrypto_DeriveKeysFromSessionKey); + LOOKUP(APIVersion, OEMCrypto_APIVersion); + LOOKUP(SecurityLevel, OEMCrypto_SecurityLevel); + LOOKUP(Generic_Decrypt, OEMCrypto_Generic_Decrypt); + LOOKUP(Generic_Encrypt, OEMCrypto_Generic_Encrypt); + LOOKUP(Generic_Sign, OEMCrypto_Generic_Sign); + LOOKUP(Generic_Verify, OEMCrypto_Generic_Verify); + if (!level1_valid_) { + return false; + } + OEMCryptoResult st = level1_.Initialize(); + if (st != OEMCrypto_SUCCESS) { + LOGW("Could not initialize L1. Falling Back to L3."); + return false; + } + uint32_t level1_version = level1_.APIVersion(); + if (level1_version != oec_latest_version) { + LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.", + level1_version, oec_latest_version); + return false; + } + if (OEMCrypto_SUCCESS == level1_.IsKeyboxValid()) { + return true; + } + wvcdm::File file; + std::string filename; + if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) { + LOGW("Bad Level 1 Keybox. Falling Back to L3."); + return false; + } + ssize_t size = file.FileSize(filename); + if (size <= 0 || !file.Open(filename, file.kBinary | file.kReadOnly)) { + LOGW("Could not open %s. Falling Back to L3.", filename.c_str()); + return false; + } + uint8_t keybox[size]; + ssize_t size_read = file.Read(reinterpret_cast(keybox), size); + if (level1_.InstallKeybox(keybox, size) != OEMCrypto_SUCCESS) { + LOGE("Could NOT install keybox from %s. Falling Back to L3.", + filename.c_str()); + false; + } + LOGI("Installed keybox from %s", filename.c_str()); + return true; + } + + void LoadLevel3() { + level3_.Initialize = Level3_Initialize; + level3_.Terminate = Level3_Terminate; + level3_.OpenSession = Level3_OpenSession; + level3_.CloseSession = Level3_CloseSession; + level3_.GenerateDerivedKeys = Level3_GenerateDerivedKeys; + level3_.GenerateNonce = Level3_GenerateNonce; + level3_.GenerateSignature = Level3_GenerateSignature; + level3_.LoadKeys = Level3_LoadKeys; + level3_.RefreshKeys = Level3_RefreshKeys; + level3_.SelectKey = Level3_SelectKey; + level3_.DecryptCTR = Level3_DecryptCTR; + level3_.InstallKeybox = Level3_InstallKeybox; + level3_.IsKeyboxValid = Level3_IsKeyboxValid; + level3_.GetDeviceID = Level3_GetDeviceID; + level3_.GetKeyData = Level3_GetKeyData; + level3_.GetRandom = Level3_GetRandom; + level3_.WrapKeybox = Level3_WrapKeybox; + level3_.RewrapDeviceRSAKey = Level3_RewrapDeviceRSAKey; + level3_.LoadDeviceRSAKey = Level3_LoadDeviceRSAKey; + level3_.GenerateRSASignature = Level3_GenerateRSASignature; + level3_.DeriveKeysFromSessionKey = Level3_DeriveKeysFromSessionKey; + level3_.APIVersion = Level3_APIVersion; + level3_.SecurityLevel = Level3_SecurityLevel; + level3_.Generic_Decrypt = Level3_Generic_Decrypt; + level3_.Generic_Encrypt = Level3_Generic_Encrypt; + level3_.Generic_Sign = Level3_Generic_Sign; + level3_.Generic_Verify = Level3_Generic_Verify; + } + + OEMCryptoResult Terminate() { + OEMCryptoResult result = Level3_Terminate(); + if (level1_valid_) { + result = level1_.Terminate(); + dlclose(level1_library_); + level1_library_ = NULL; + } + return result; + } + + const FunctionPointers* get(SecurityLevel level) { + if (level1_valid_ && level == kLevelDefault) return &level1_; + return &level3_; + } + + LevelSession get(OEMCrypto_SESSION session) { + map_iterator pair = session_map_.find(session); + if (pair == session_map_.end()) { + return LevelSession(); + } + return pair->second; + } + + OEMCryptoResult OpenSession(OEMCrypto_SESSION* session, SecurityLevel level) { + LevelSession new_session; + OEMCryptoResult result; + if (level == kLevelDefault && level1_valid_) { + new_session.fcn = &level1_; + result = level1_.OpenSession(&new_session.session); + *session = new_session.session; + } else { + new_session.fcn = &level3_; + result = level3_.OpenSession(&new_session.session); + *session = new_session.session + kLevel3Offset; + } + if (result == OEMCrypto_SUCCESS) { + // Make sure session is not already in my list of sessions. + while (session_map_.find(*session) != session_map_.end()) { + (*session)++; + } + session_map_[*session] = new_session; + } + return result; + } + + OEMCryptoResult CloseSession(OEMCrypto_SESSION session) { + map_iterator pair = session_map_.find(session); + if (pair == session_map_.end()) { + return OEMCrypto_ERROR_INVALID_SESSION; + } + OEMCryptoResult result = + pair->second.fcn->CloseSession(pair->second.session); + session_map_.erase(pair); + return result; + } + + private: + bool level1_valid_; + void* level1_library_; + struct FunctionPointers level1_; + struct FunctionPointers level3_; + std::map session_map_; + // This is just for debugging the map between session ids. + // If we add this to the level 3 session id, then the external session + // id will match the internal session id in the last two digits. + static const OEMCrypto_SESSION kLevel3Offset = 25600; +}; +static Adapter* kAdapter = 0; + +extern "C" OEMCryptoResult OEMCrypto_Initialize(void) { + if (kAdapter) { + delete kAdapter; + } + kAdapter = new Adapter(); + return kAdapter->Initialize(); +} + +extern "C" OEMCryptoResult OEMCrypto_Terminate(void) { + OEMCryptoResult result = OEMCrypto_SUCCESS; + if (kAdapter) { + result = kAdapter->Terminate(); + delete kAdapter; + } + kAdapter = NULL; + return result; +} + +extern "C" OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) { + return OEMCrypto_OpenSession(session, kLevelDefault); +} + +OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, + SecurityLevel level) { + if (!kAdapter) return OEMCrypto_ERROR_OPEN_SESSION_FAILED; + return kAdapter->OpenSession(session, level); +} + +extern "C" OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { + if (!kAdapter) return OEMCrypto_ERROR_CLOSE_SESSION_FAILED; + return kAdapter->CloseSession(session); +} + +extern "C" OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, + uint32_t* nonce) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->GenerateNonce(pair.session, nonce); +} + +extern "C" OEMCryptoResult OEMCrypto_GenerateDerivedKeys( + OEMCrypto_SESSION session, const uint8_t* mac_key_context, + uint32_t mac_key_context_length, const uint8_t* enc_key_context, + uint32_t enc_key_context_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->GenerateDerivedKeys(pair.session, mac_key_context, + mac_key_context_length, enc_key_context, + enc_key_context_length); +} + +extern "C" OEMCryptoResult OEMCrypto_GenerateSignature( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->GenerateSignature(pair.session, message, message_length, + signature, signature_length); +} + +extern "C" OEMCryptoResult OEMCrypto_LoadKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys, + const OEMCrypto_KeyObject* key_array) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->LoadKeys(pair.session, message, message_length, signature, + signature_length, enc_mac_key_iv, enc_mac_key, + num_keys, key_array); +} + +extern "C" OEMCryptoResult OEMCrypto_RefreshKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->RefreshKeys(pair.session, message, message_length, signature, + signature_length, num_keys, key_array); +} + +extern "C" OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->SelectKey(pair.session, key_id, key_id_length); +} + +extern "C" OEMCryptoResult OEMCrypto_DecryptCTR( + OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length, + bool is_encrypted, const uint8_t* iv, size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->DecryptCTR(pair.session, data_addr, data_length, + is_encrypted, iv, offset, out_buffer, + subsample_flags); +} + +extern "C" OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, + size_t keyBoxLength) { + return OEMCrypto_InstallKeybox(keybox, keyBoxLength, kLevelDefault); +} + +OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, + size_t keyBoxLength, + SecurityLevel level) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = kAdapter->get(level); + if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return fcn->InstallKeybox(keybox, keyBoxLength); +} + +extern "C" OEMCryptoResult OEMCrypto_IsKeyboxValid() { + return OEMCrypto_IsKeyboxValid(kLevelDefault); +} + +OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = kAdapter->get(level); + if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return fcn->IsKeyboxValid(); +} + +extern "C" OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, + size_t* idLength) { + return OEMCrypto_GetDeviceID(deviceID, idLength, kLevelDefault); +} + +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength, + SecurityLevel level) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = kAdapter->get(level); + if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return fcn->GetDeviceID(deviceID, idLength); +} + +extern "C" OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, + size_t* keyDataLength) { + return OEMCrypto_GetKeyData(keyData, keyDataLength, kLevelDefault); +} + +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength, + SecurityLevel level) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = kAdapter->get(level); + if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return fcn->GetKeyData(keyData, keyDataLength); +} + +extern "C" OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, + size_t dataLength) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = kAdapter->get(kLevelDefault); + if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return fcn->GetRandom(randomData, dataLength); +} + +extern "C" OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox, + size_t keyBoxLength, + uint8_t* wrappedKeybox, + size_t* wrappedKeyBoxLength, + const uint8_t* transportKey, + size_t transportKeyLength) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = kAdapter->get(kLevelDefault); + if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return fcn->WrapKeybox(keybox, keyBoxLength, wrappedKeybox, + wrappedKeyBoxLength, transportKey, transportKeyLength); +} + +extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, const uint32_t* nonce, + const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key, + size_t* wrapped_rsa_key_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->RewrapDeviceRSAKey( + pair.session, message, message_length, signature, signature_length, nonce, + enc_rsa_key, enc_rsa_key_length, enc_rsa_key_iv, wrapped_rsa_key, + wrapped_rsa_key_length); +} + +extern "C" OEMCryptoResult OEMCrypto_LoadDeviceRSAKey( + OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn + ->LoadDeviceRSAKey(pair.session, wrapped_rsa_key, wrapped_rsa_key_length); +} + +extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->GenerateRSASignature(pair.session, message, message_length, + signature, signature_length); +} + +extern "C" OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( + OEMCrypto_SESSION session, const uint8_t* enc_session_key, + size_t enc_session_key_length, const uint8_t* mac_key_context, + size_t mac_key_context_length, const uint8_t* enc_key_context, + size_t enc_key_context_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->DeriveKeysFromSessionKey( + pair.session, enc_session_key, enc_session_key_length, mac_key_context, + mac_key_context_length, enc_key_context, enc_key_context_length); +} + +extern "C" uint32_t OEMCrypto_APIVersion() { + return OEMCrypto_APIVersion(kLevelDefault); +} + +uint32_t OEMCrypto_APIVersion(SecurityLevel level) { + if (!kAdapter) return 0; + const FunctionPointers* fcn = kAdapter->get(level); + if (!fcn) return 0; + return fcn->APIVersion(); +} + +extern "C" const char* OEMCrypto_SecurityLevel() { + return OEMCrypto_SecurityLevel(kLevelDefault); +} + +const char* OEMCrypto_SecurityLevel(SecurityLevel level) { + if (!kAdapter) return ""; + const FunctionPointers* fcn = kAdapter->get(level); + if (!fcn) return ""; + return fcn->SecurityLevel(); +} + +extern "C" OEMCryptoResult OEMCrypto_Generic_Encrypt( + OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, + const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { + + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->Generic_Encrypt(pair.session, in_buffer, buffer_length, iv, + algorithm, out_buffer); +} + +extern "C" OEMCryptoResult OEMCrypto_Generic_Decrypt( + OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, + const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->Generic_Decrypt(pair.session, in_buffer, buffer_length, iv, + algorithm, out_buffer); +} + +extern "C" OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->Generic_Sign(pair.session, in_buffer, buffer_length, + algorithm, signature, signature_length); +} + +extern "C" OEMCryptoResult OEMCrypto_Generic_Verify( + OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, + OEMCrypto_Algorithm algorithm, const uint8_t* signature, + size_t signature_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + return pair.fcn->Generic_Verify(pair.session, in_buffer, buffer_length, + algorithm, signature, signature_length); +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_static.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_static.cpp new file mode 100644 index 00000000..d3858715 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_static.cpp @@ -0,0 +1,51 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Wrapper of OEMCrypto APIs for platforms that support Level 1 only. + * This should be used when liboemcrypto.so is linked with the CDM code at + * compile time. + * An implementation should compile either oemcrypto_adapter_dynamic.cpp or + * oemcrypto_adapter_static.cpp, but not both. + * + ******************************************************************************/ + +#include "OEMCryptoCENC.h" +#include "oemcrypto_adapter.h" + +namespace wvcdm { + +OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, + SecurityLevel level) { + return ::OEMCrypto_OpenSession(session); +} + +OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) { + return ::OEMCrypto_IsKeyboxValid(); +} + +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength, + SecurityLevel level) { + return ::OEMCrypto_GetDeviceID(deviceID, idLength); +} + +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength, + SecurityLevel level) { + return ::OEMCrypto_GetKeyData(keyData, keyDataLength); +} + +OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, + size_t keyBoxLength, + SecurityLevel level) { + return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength); +} + +uint32_t OEMCrypto_APIVersion(SecurityLevel level) { + return ::OEMCrypto_APIVersion(); +} + +const char* OEMCrypto_SecurityLevel(SecurityLevel level) { + return ::OEMCrypto_SecurityLevel(); +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/privacy_crypto.cpp b/libwvdrmengine/cdm/core/src/privacy_crypto.cpp new file mode 100644 index 00000000..20fdbdc3 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/privacy_crypto.cpp @@ -0,0 +1,201 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Original code at //depot/google3/video/widevine/common/rsa_key.cc by +// tinskip@google.com. Modified for core CDM usage. +// +// Description: +// Definition of classes representing RSA public keys used +// for signature verification and encryption and decryption. +// + +#include "privacy_crypto.h" + +#include "log.h" +#include "openssl/bio.h" +#include "openssl/err.h" +#include "openssl/evp.h" +#include "openssl/pem.h" +#include "openssl/rsa.h" +#include "openssl/sha.h" + +namespace { +const int kPssSaltLength = 20; +const int kRsaPkcs1OaepPaddingLength = 41; +const int kBitsInAByte = 8; +} // namespace + +namespace wvcdm { + +bool AesCbcKey::Init(const std::string& key) { + if (key.empty()) { + LOGE("AesCbcKey::Init: no key provided"); + return false; + } + if (key.size() != AES_BLOCK_SIZE) { + LOGE("AesCbcKey::Init: unexpected key size: %d", key.size()); + return false; + } + if (AES_set_encrypt_key(reinterpret_cast(&key[0]), + key.size() * kBitsInAByte, &key_) != 0) { + LOGE("AesCbcKey::Init: AES CBC key setup failure: %s", + ERR_error_string(ERR_get_error(), NULL)); + return false; + } + initialized_ = true; + return true; +} + +bool AesCbcKey::Encrypt(const std::string& in, std::string* out, + std::string* iv) { + if (in.empty()) { + LOGE("AesCbcKey::Encrypt: no cleartext provided"); + return false; + } + if (in.size() % AES_BLOCK_SIZE) { + LOGE("AesCbcKey::Encrypt: cleartext not a block multiple: %d", in.size()); + return false; + } + if (iv == NULL) { + LOGE("AesCbcKey::Encrypt: initialization vector destination not provided"); + return false; + } + if (iv->size() != AES_BLOCK_SIZE) { + LOGE("AesCbcKey::Encrypt: invalid iv size: %d", iv->size()); + return false; + } + if (out == NULL) { + LOGE("AesCbcKey::Encrypt: crypttext destination not provided"); + return false; + } + if (!initialized_) { + LOGE("AesCbcKey::Encrypt: AES key not initialized"); + return false; + } + + out->resize(in.size()); + + AES_cbc_encrypt(reinterpret_cast(&in[0]), + reinterpret_cast(&out[0]), in.size(), &key_, + reinterpret_cast(&iv[0]), AES_ENCRYPT); + return true; +} + +RsaPublicKey::~RsaPublicKey() { + if (key_ != NULL) { + RSA_free(key_); + } +} + +bool RsaPublicKey::Init(const std::string& serialized_key) { + + if (serialized_key.empty()) { + LOGE("RsaPublicKey::Init: no serialized key provided"); + return false; + } + + BIO* bio = BIO_new_mem_buf(const_cast(serialized_key.data()), + serialized_key.size()); + if (bio == NULL) { + LOGE("RsaPublicKey::Init: BIO_new_mem_buf returned NULL"); + return false; + } + key_ = d2i_RSAPublicKey_bio(bio, NULL); + BIO_free(bio); + + if (key_ == NULL) { + LOGE("RsaPublicKey::Init: RSA key deserialization failure"); + return false; + } + + return true; +} + +bool RsaPublicKey::Encrypt(const std::string& clear_message, + std::string* encrypted_message) { + if (clear_message.empty()) { + LOGE("RsaPublicKey::Encrypt: message to be encrypted is empty"); + return false; + } + if (encrypted_message == NULL) { + LOGE("RsaPublicKey::Encrypt: no encrypt message buffer provided"); + return false; + } + if (key_ == NULL) { + LOGE("RsaPublicKey::Encrypt: RSA key not initialized"); + return false; + } + + int rsa_size = RSA_size(key_); + if (static_cast(clear_message.size()) > + rsa_size - kRsaPkcs1OaepPaddingLength) { + LOGE("RsaPublicKey::Encrypt: message too large to be encrypted (actual %d", + " max allowed %d)", clear_message.size(), + rsa_size - kRsaPkcs1OaepPaddingLength); + return false; + } + encrypted_message->assign(rsa_size, 0); + if (RSA_public_encrypt( + clear_message.size(), + const_cast( + reinterpret_cast(clear_message.data())), + reinterpret_cast(&(*encrypted_message)[0]), key_, + RSA_PKCS1_OAEP_PADDING) != rsa_size) { + LOGE("RsaPublicKey::Encrypt: encrypt failure: %s", + ERR_error_string(ERR_get_error(), NULL)); + return false; + } + return true; +} + +bool RsaPublicKey::VerifySignature(const std::string& message, + const std::string& signature) { + if (key_ == NULL) { + LOGE("RsaPublicKey::VerifySignature: RSA key not initialized"); + return false; + } + if (message.empty()) { + LOGE("RsaPublicKey::VerifySignature: signed message is empty"); + return false; + } + + int rsa_size = RSA_size(key_); + if (static_cast(signature.size()) != rsa_size) { + LOGE( + "RsaPublicKey::VerifySignature: message signature is of the wrong " + "size (expected %d, actual %d)", + rsa_size, signature.size()); + return false; + } + // Decrypt the signature. + std::string padded_digest(signature.size(), 0); + if (RSA_public_decrypt( + signature.size(), + const_cast( + reinterpret_cast(signature.data())), + reinterpret_cast(&padded_digest[0]), key_, + RSA_NO_PADDING) != rsa_size) { + LOGE("RsaPublicKey::VerifySignature: RSA public decrypt failure: %s", + ERR_error_string(ERR_get_error(), NULL)); + return false; + } + + // Hash the message using SHA1. + std::string message_digest(SHA_DIGEST_LENGTH, 0); + SHA1(reinterpret_cast(message.data()), message.size(), + reinterpret_cast(&message_digest[0])); + + // Verify PSS padding. + if (RSA_verify_PKCS1_PSS( + key_, reinterpret_cast(message_digest.data()), + EVP_sha1(), + reinterpret_cast(padded_digest.data()), + kPssSaltLength) == 0) { + LOGE("RsaPublicKey::VerifySignature: RSA verify failure: %s", + ERR_error_string(ERR_get_error(), NULL)); + return false; + } + + return true; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/properties.cpp b/libwvdrmengine/cdm/core/src/properties.cpp index de641453..ffc56411 100644 --- a/libwvdrmengine/cdm/core/src/properties.cpp +++ b/libwvdrmengine/cdm/core/src/properties.cpp @@ -1,11 +1,10 @@ // Copyright 2013 Google Inc. All Rights Reserved. -#include "properties.h" +#include "log.h" #include "properties_configuration.h" #include "wv_cdm_constants.h" namespace wvcdm { - bool Properties::begin_license_usage_when_received_; bool Properties::require_explicit_renew_request_; bool Properties::oem_crypto_use_secure_buffers_; @@ -13,6 +12,8 @@ bool Properties::oem_crypto_use_fifo_; bool Properties::oem_crypto_use_userspace_buffers_; bool Properties::use_certificates_as_identification_; bool Properties::extract_pssh_data_; +bool Properties::decrypt_with_empty_session_support_; +scoped_ptr Properties::session_property_set_; void Properties::Init() { begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived; @@ -23,6 +24,73 @@ void Properties::Init() { use_certificates_as_identification_ = kPropertyUseCertificatesAsIdentification; extract_pssh_data_ = kExtractPsshData; + decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport; + session_property_set_.reset(new CdmClientPropertySetMap()); } +bool Properties::AddSessionPropertySet( + const CdmSessionId& session_id, + const CdmClientPropertySet* property_set) { + if (NULL == session_property_set_.get()) { + return false; + } + std::pair result = + session_property_set_->insert( + std::pair(session_id, property_set)); + return result.second; +} + +bool Properties::RemoveSessionPropertySet(const CdmSessionId& session_id) { + if (NULL == session_property_set_.get()) { + return false; + } + return (1 == session_property_set_->erase(session_id)); +} + +const CdmClientPropertySet* Properties::GetCdmClientPropertySet( + const CdmSessionId& session_id) { + if (NULL != session_property_set_.get()) { + CdmClientPropertySetMap::const_iterator it = + session_property_set_->find(session_id); + if (it != session_property_set_->end()) { + return it->second; + } + } + return NULL; +} + +const std::string Properties::GetSecurityLevel(const CdmSessionId& session_id) { + const CdmClientPropertySet* property_set = + GetCdmClientPropertySet(session_id); + if (NULL == property_set) { + LOGE("Properties::GetSecurityLevel: cannot find property set for %s", + session_id.c_str()); + return ""; + } + return property_set->security_level(); +} + +const std::vector Properties::GetServiceCertificate( + const CdmSessionId& session_id) { + const CdmClientPropertySet* property_set = + GetCdmClientPropertySet(session_id); + if (NULL == property_set) { + LOGE("Properties::GetServiceCertificate: cannot find property set for %s", + session_id.c_str()); + return std::vector(); + } + return property_set->service_certificate(); +} + +bool Properties::UsePrivacyMode(const CdmSessionId& session_id) { + const CdmClientPropertySet* property_set = + GetCdmClientPropertySet(session_id); + if (NULL == property_set) { + LOGE("Properties::UsePrivacyMode: cannot find property set for %s", + session_id.c_str()); + return false; + } + return property_set->use_privacy_mode(); +} } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index 1ae66e11..6baf4877 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -88,7 +88,7 @@ class WvCdmEngineTest : public testing::Test { public: virtual void SetUp() { cdm_engine_.reset(new CdmEngine()); - cdm_engine_->OpenSession(g_key_system, &session_id_); + cdm_engine_->OpenSession(g_key_system, NULL, &session_id_); } virtual void TearDown() { diff --git a/libwvdrmengine/cdm/core/test/license_unittest.cpp b/libwvdrmengine/cdm/core/test/license_unittest.cpp index 9cc6c862..a8f71549 100644 --- a/libwvdrmengine/cdm/core/test/license_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/license_unittest.cpp @@ -78,9 +78,11 @@ TEST_F(LicenseTest, DISABLED_PrepareKeyRequest) { std::string signed_request; CdmAppParameterMap app_parameters; std::string server_url; + CdmSessionId session_id; license_.PrepareKeyRequest(a2bs_hex(kInitData), kLicenseTypeStreaming, app_parameters, + session_id, &signed_request, &server_url); EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest)); @@ -90,10 +92,12 @@ TEST_F(LicenseTest, DISABLED_PrepareKeyRequest) { TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) { std::string signed_request; CdmAppParameterMap app_parameters; + CdmSessionId session_id; std::string server_url; license_.PrepareKeyRequest(a2bs_hex(kInitData), kLicenseTypeStreaming, app_parameters, + session_id, &signed_request, &server_url); EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest)); @@ -104,10 +108,12 @@ TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) { TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) { std::string signed_request; CdmAppParameterMap app_parameters; + CdmSessionId session_id; std::string server_url; license_.PrepareKeyRequest(a2bs_hex(kInitData), kLicenseTypeStreaming, app_parameters, + session_id, &signed_request, &server_url); EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest)); diff --git a/libwvdrmengine/cdm/core/test/url_request.cpp b/libwvdrmengine/cdm/core/test/url_request.cpp index 2bcc70c2..b845d2ea 100644 --- a/libwvdrmengine/cdm/core/test/url_request.cpp +++ b/libwvdrmengine/cdm/core/test/url_request.cpp @@ -41,7 +41,7 @@ void UrlRequest::AppendChunkToUpload(const std::string& data) { // buffer to store length of chunk memset(buffer_, 0, kHttpBufferSize); - snprintf(buffer_, kHttpBufferSize, "%x\r\n", data.size()); + snprintf(buffer_, kHttpBufferSize, "%zx\r\n", data.size()); request_.append(buffer_); // appends size of chunk LOGD("...\r\n%s", request_.c_str()); request_.append(data); @@ -61,7 +61,7 @@ void UrlRequest::ConcatenateChunkedResponse(const std::string http_response, // processes chunked encoding size_t chunk_size = 0; size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size(); - sscanf(&http_response[chunk_size_pos], "%x", &chunk_size); + sscanf(&http_response[chunk_size_pos], "%zx", &chunk_size); if (chunk_size > http_response.size()) { // precaution, in case we misread chunk size LOGE("invalid chunk size %u", chunk_size); @@ -85,7 +85,7 @@ void UrlRequest::ConcatenateChunkedResponse(const std::string http_response, // Search for next chunk chunk_size_pos = chunk_pos + chunk_size + kCrLf.size(); - sscanf(&http_response[chunk_size_pos], "%x", &chunk_size); + sscanf(&http_response[chunk_size_pos], "%zx", &chunk_size); if (chunk_size > http_response.size()) { // precaution, in case we misread chunk size LOGE("invalid chunk size %u", chunk_size); diff --git a/libwvdrmengine/cdm/include/properties_configuration.h b/libwvdrmengine/cdm/include/properties_configuration.h index cd4087c9..58af5936 100644 --- a/libwvdrmengine/cdm/include/properties_configuration.h +++ b/libwvdrmengine/cdm/include/properties_configuration.h @@ -31,6 +31,10 @@ const bool kPropertyUseCertificatesAsIdentification = true; // once all platforms support it (b/9465346) const bool kExtractPsshData = true; +// If true, session_id parameter to CdmEngine::Decrypt can be empty; the +// function will try to find out the session_id from the key_id. +const bool kDecryptWithEmptySessionSupport = false; + } // namespace wvcdm #endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_ diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 9efd74b6..d89c4c98 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -9,6 +9,7 @@ namespace wvcdm { +class CdmClientPropertySet; class CdmEngine; class WvCdmEventListener; @@ -18,8 +19,10 @@ class WvContentDecryptionModule { virtual ~WvContentDecryptionModule(); // Session related methods - virtual CdmResponseType OpenSession(const CdmKeySystem& key_system, - CdmSessionId* session_id); + virtual CdmResponseType OpenSession( + const CdmKeySystem& key_system, + const CdmClientPropertySet* property_set, + CdmSessionId* session_id); virtual CdmResponseType CloseSession(const CdmSessionId& session_id); // Construct a valid license request. diff --git a/libwvdrmengine/cdm/src/properties_android.cpp b/libwvdrmengine/cdm/src/properties_android.cpp index 40a34e77..e8d2c10a 100644 --- a/libwvdrmengine/cdm/src/properties_android.cpp +++ b/libwvdrmengine/cdm/src/properties_android.cpp @@ -12,9 +12,9 @@ namespace { const char kBasePathPrefix[] = "/data/mediadrm/IDM"; -const char kL1Dir[] = "/L1"; -const char kL2Dir[] = "/L2"; -const char kL3Dir[] = "/L3"; +const char kL1Dir[] = "/L1/"; +const char kL2Dir[] = "/L2/"; +const char kL3Dir[] = "/L3/"; const char kFactoryKeyboxPath[] = "/factory/wv.keys"; bool GetAndroidProperty(const char* key, std::string* value) { @@ -118,4 +118,13 @@ bool Properties::GetFactoryKeyboxPath(std::string* keybox) { return true; } +bool Properties::GetOEMCryptoPath(std::string* library_name) { + if (!library_name) { + LOGW("Properties::GetOEMCryptoPath: Invalid parameter"); + return false; + } + *library_name = "liboemcrypto.so"; + return true; +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 537f62a5..ae8a1ada 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -17,8 +17,10 @@ WvContentDecryptionModule::WvContentDecryptionModule() WvContentDecryptionModule::~WvContentDecryptionModule() {} CdmResponseType WvContentDecryptionModule::OpenSession( - const CdmKeySystem& key_system, CdmSessionId* session_id) { - return cdm_engine_->OpenSession(key_system, session_id); + const CdmKeySystem& key_system, + const CdmClientPropertySet* property_set, + CdmSessionId* session_id) { + return cdm_engine_->OpenSession(key_system, property_set, session_id); } CdmResponseType WvContentDecryptionModule::CloseSession( diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index af10b093..530314b4 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -6,9 +6,13 @@ #include "config_test_env.h" #include "gtest/gtest.h" +#include "device_files.h" +#include "file_store.h" #include "license_request.h" #include "log.h" #include "OEMCryptoCENC.h" +#include "oemcrypto_adapter.h" +#include "properties.h" #include "string_conversions.h" #include "url_request.h" #include "wv_cdm_constants.h" @@ -186,6 +190,38 @@ SubSampleInfo partial_offset_single_encrypted_sub_sample = { } // namespace namespace wvcdm { +class TestWvCdmClientPropertySet : public CdmClientPropertySet { + public: + TestWvCdmClientPropertySet() + : service_certificate_(std::vector()), + use_privacy_mode_(false) {} + virtual ~TestWvCdmClientPropertySet() {} + + virtual std::string security_level() const { return security_level_; } + virtual std::vector service_certificate() const { + return service_certificate_; + } + virtual bool use_privacy_mode() const { return use_privacy_mode_; } + + void set_security_level(const std::string& security_level) { + if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) || + !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)) { + security_level_ = security_level; + } + } + void set_service_certificate( + const std::vector& service_certificate) { + service_certificate_ = service_certificate; + } + void set_use_privacy_mode(bool use_privacy_mode) { + use_privacy_mode_ = use_privacy_mode; + } + + private: + std::string security_level_; + std::vector service_certificate_; + bool use_privacy_mode_; +}; class TestWvCdmEventListener : public WvCdmEventListener { public: @@ -337,7 +373,7 @@ class WvCdmDecryptionTest public ::testing::WithParamInterface {}; TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); std::string provisioning_server_url; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( @@ -352,7 +388,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { } TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); std::string provisioning_server_url; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( @@ -376,15 +412,108 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) { decryptor_.CloseSession(session_id_); } +TEST_F(WvCdmRequestLicenseTest, PropertySetTest) { + TestWvCdmClientPropertySet property_set_L1; + TestWvCdmClientPropertySet property_set_L3; + TestWvCdmClientPropertySet property_set_Ln; + CdmSessionId session_id_L1; + CdmSessionId session_id_L3; + CdmSessionId session_id_Ln; + + property_set_L1.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L1); + property_set_L1.set_use_privacy_mode(true); + decryptor_.OpenSession(g_key_system, &property_set_L1, &session_id_L1); + property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); + property_set_L3.set_use_privacy_mode(false); + decryptor_.OpenSession(g_key_system, &property_set_L3, &session_id_L3); + property_set_Ln.set_security_level(""); + decryptor_.OpenSession(g_key_system, &property_set_Ln, &session_id_Ln); + + std::string security_level = Properties::GetSecurityLevel(session_id_L1); + EXPECT_TRUE(!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) || + !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)); + EXPECT_TRUE(Properties::UsePrivacyMode(session_id_L1)); + EXPECT_EQ(Properties::GetSecurityLevel(session_id_L3), + QUERY_VALUE_SECURITY_LEVEL_L3); + EXPECT_FALSE(Properties::UsePrivacyMode(session_id_L3)); + security_level = Properties::GetSecurityLevel(session_id_Ln); + EXPECT_TRUE(security_level.empty() || + !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)); + decryptor_.CloseSession(session_id_L1); + decryptor_.CloseSession(session_id_L3); + decryptor_.CloseSession(session_id_Ln); +} + +TEST_F(WvCdmRequestLicenseTest, ForceL3Test) { + TestWvCdmClientPropertySet property_set; + property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); + + File file; + DeviceFiles handle; + EXPECT_TRUE(handle.Init(&file, kSecurityLevelL3)); + EXPECT_TRUE(handle.DeleteAllFiles()); + + EXPECT_EQ(NEED_PROVISIONING, + decryptor_.OpenSession(g_key_system, &property_set, &session_id_)); + std::string provisioning_server_url; + EXPECT_EQ(NO_ERROR, + decryptor_.GetProvisioningRequest(&key_msg_, + &provisioning_server_url)); + EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url()); + std::string response = + GetCertRequestResponse(config_.provisioning_test_server_url(), 200); + EXPECT_NE(0, static_cast(response.size())); + EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response)); + + EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set, + &session_id_)); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + decryptor_.CloseSession(session_id_); +} + +TEST_F(WvCdmRequestLicenseTest, UsePrivacyModeTest) { + TestWvCdmClientPropertySet property_set; + + property_set.set_use_privacy_mode(true); + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + + if (property_set.service_certificate().empty()) { + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); + std::string resp = GetKeyRequestResponse(g_license_server, + g_client_auth, 200); + EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_), + wvcdm::NEED_KEY); + std::vector service_certificate(key_msg_.begin(), key_msg_.end()); + property_set.set_service_certificate(service_certificate); + } + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + decryptor_.CloseSession(session_id_); + + // property has service certificate set from previous request + EXPECT_FALSE(property_set.service_certificate().empty()); + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + decryptor_.CloseSession(session_id_); + + property_set.set_use_privacy_mode(false); + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + decryptor_.CloseSession(session_id_); +} + TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GetKeyRequestResponse(g_license_server, g_client_auth, 200); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id); GenerateKeyRequest(g_key_system, wrong_message, kLicenseTypeStreaming); @@ -393,21 +522,21 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) { } TEST_F(WvCdmRequestLicenseTest, AddSteamingKeyTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -416,13 +545,13 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { decryptor_.CloseSession(session_id_); session_id_.clear(); - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -432,7 +561,7 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { session_id_.clear(); key_set_id_.clear(); - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); decryptor_.CloseSession(session_id_); @@ -444,7 +573,7 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { } TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -454,7 +583,7 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { session_id_.clear(); key_set_id_.clear(); - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); CdmSessionId restore_session_id = session_id_; TestWvCdmEventListener listener; EXPECT_TRUE(decryptor_.AttachEventListener(restore_session_id, &listener)); @@ -474,7 +603,7 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { } TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -484,7 +613,7 @@ TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) { } TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -494,7 +623,7 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) { } TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -564,7 +693,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) { } TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -585,7 +714,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) { TEST_P(WvCdmDecryptionTest, DecryptionTest) { SubSampleInfo* data = GetParam(); - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); if (data->retrieve_key) { GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -616,7 +745,7 @@ INSTANTIATE_TEST_CASE_P( &partial_single_encrypted_sub_sample)); TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -669,7 +798,7 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) { } TEST_F(WvCdmRequestLicenseTest, DISABLED_RestoreOfflineLicenseDecryptionTest) { - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); CdmKeySetId key_set_id = key_set_id_; @@ -677,7 +806,7 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_RestoreOfflineLicenseDecryptionTest) { decryptor_.CloseSession(session_id_); session_id_.clear(); - decryptor_.OpenSession(g_key_system, &session_id_); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); /* // key 1, encrypted, 256b diff --git a/libwvdrmengine/cdm/test/unit-test.mk b/libwvdrmengine/cdm/test/unit-test.mk index 3accf210..af13a7ac 100644 --- a/libwvdrmengine/cdm/test/unit-test.mk +++ b/libwvdrmengine/cdm/test/unit-test.mk @@ -38,7 +38,6 @@ LOCAL_STATIC_LIBRARIES := \ libgmock \ libgtest \ libgtest_main \ - libwvwrapper \ libwvlevel3 \ libcdm_utils \ libprotobuf-cpp-2.3.0-lite diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index 3b9b7613..fad78f6c 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -17,7 +17,8 @@ enum { kErrorCDMGeneric = ERROR_DRM_VENDOR_MIN + 1, kErrorUnsupportedCrypto = ERROR_DRM_VENDOR_MIN + 2, kErrorExpectedUnencrypted = ERROR_DRM_VENDOR_MIN + 3, - kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 3, + kErrorSessionIsOpen = ERROR_DRM_VENDOR_MIN + 4, + kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 4, // Used by crypto test mode kErrorTestMode = ERROR_DRM_VENDOR_MAX, diff --git a/libwvdrmengine/level3/arm/libwvlevel3.a b/libwvdrmengine/level3/arm/libwvlevel3.a index bf429314..dee658cf 100644 Binary files a/libwvdrmengine/level3/arm/libwvlevel3.a and b/libwvdrmengine/level3/arm/libwvlevel3.a differ diff --git a/libwvdrmengine/level3/mips/libwvlevel3.a b/libwvdrmengine/level3/mips/libwvlevel3.a index efdb74a9..208dc60b 100644 Binary files a/libwvdrmengine/level3/mips/libwvlevel3.a and b/libwvdrmengine/level3/mips/libwvlevel3.a differ diff --git a/libwvdrmengine/level3/x86/libwvlevel3.a b/libwvdrmengine/level3/x86/libwvlevel3.a index adcd7847..08bd2854 100644 Binary files a/libwvdrmengine/level3/x86/libwvlevel3.a and b/libwvdrmengine/level3/x86/libwvlevel3.a differ diff --git a/libwvdrmengine/mediacrypto/test/Android.mk b/libwvdrmengine/mediacrypto/test/Android.mk index 0b13ba9f..a45bc2db 100644 --- a/libwvdrmengine/mediacrypto/test/Android.mk +++ b/libwvdrmengine/mediacrypto/test/Android.mk @@ -23,7 +23,6 @@ LOCAL_STATIC_LIBRARIES := \ libgmock \ libgmock_main \ libgtest \ - libwvwrapper \ libwvlevel3 \ libprotobuf-cpp-2.3.0-lite \ libwvdrmcryptoplugin \ diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index 060ff918..292d6153 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -8,6 +8,7 @@ #include #include +#include "cdm_client_property_set.h" #include "media/drm/DrmAPI.h" #include "media/stagefright/foundation/ABase.h" #include "media/stagefright/foundation/AString.h" @@ -157,6 +158,45 @@ class WVDrmPlugin : public android::DrmPlugin, OEMCrypto_Algorithm mMacAlgorithm; }; + class WVClientPropertySet : public wvcdm::CdmClientPropertySet { + public: + WVClientPropertySet() + : mUsePrivacyMode(false) {} + + virtual ~WVClientPropertySet() {} + + void set_security_level(const std::string& securityLevel) { + mSecurityLevel = securityLevel; + } + + virtual std::string security_level() const { + return mSecurityLevel; + } + + void set_use_privacy_mode(bool usePrivacyMode) { + mUsePrivacyMode = usePrivacyMode; + } + + virtual bool use_privacy_mode() const { + return mUsePrivacyMode; + } + + void set_service_certificate(const std::vector& serviceCertificate) { + mServiceCertificate = serviceCertificate; + } + + virtual std::vector service_certificate() const { + return mServiceCertificate; + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(WVClientPropertySet); + + std::string mSecurityLevel; + bool mUsePrivacyMode; + std::vector mServiceCertificate; + } mPropertySet; + WvContentDecryptionModule* mCDM; WVGenericCryptoInterface* mCrypto; map mCryptoSessions; diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index 35606e36..a1785254 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -25,6 +25,10 @@ using namespace android; using namespace std; using namespace wvcdm; +static const char* const kResetSecurityLevel = ""; +static const char* const kEnable = "enable"; +static const char* const kDisable = "disable"; + WVDrmPlugin::WVDrmPlugin(WvContentDecryptionModule* cdm, WVGenericCryptoInterface* crypto) : mCDM(cdm), mCrypto(crypto) {} @@ -51,10 +55,11 @@ WVDrmPlugin::~WVDrmPlugin() { status_t WVDrmPlugin::openSession(Vector& sessionId) { CdmSessionId cdmSessionId; - CdmResponseType res = mCDM->OpenSession("com.widevine", &cdmSessionId); + CdmResponseType res = mCDM->OpenSession("com.widevine", &mPropertySet, + &cdmSessionId); if (!isCdmResponseTypeSuccess(res)) { - return mapAndNotifyOfCdmResponseType(sessionId, res); + return mapCdmResponseType(res); } bool success = false; @@ -347,16 +352,22 @@ status_t WVDrmPlugin::getPropertyString(const String8& name, } else if (name == "algorithms") { value = "AES/CBC/NoPadding,HmacSHA256"; } else if (name == "securityLevel") { - CdmQueryMap status; - CdmResponseType res = mCDM->QueryStatus(&status); - if (!isCdmResponseTypeSuccess(res)) { - ALOGE("Error querying CDM status: %u", res); - return mapCdmResponseType(res); - } else if (!status.count(QUERY_KEY_SECURITY_LEVEL)) { - ALOGE("CDM did not report a security level"); - return kErrorCDMGeneric; + string requestedLevel = mPropertySet.security_level(); + + if (requestedLevel.length() > 0) { + value = requestedLevel.c_str(); + } else { + CdmQueryMap status; + CdmResponseType res = mCDM->QueryStatus(&status); + if (!isCdmResponseTypeSuccess(res)) { + ALOGE("Error querying CDM status: %u", res); + return mapCdmResponseType(res); + } else if (!status.count(QUERY_KEY_SECURITY_LEVEL)) { + ALOGE("CDM did not report a security level"); + return kErrorCDMGeneric; + } + value = status[QUERY_KEY_SECURITY_LEVEL].c_str(); } - value = status[QUERY_KEY_SECURITY_LEVEL].c_str(); } else if (name == "systemId") { CdmQueryMap status; CdmResponseType res = mCDM->QueryStatus(&status); @@ -368,8 +379,14 @@ status_t WVDrmPlugin::getPropertyString(const String8& name, return kErrorCDMGeneric; } value = status[QUERY_KEY_SYSTEM_ID].c_str(); + } else if (name == "privacyMode") { + if (mPropertySet.use_privacy_mode()) { + value = kEnable; + } else { + value = kDisable; + } } else { - ALOGE("App requested unknown property %s", name.string()); + ALOGE("App requested unknown string property %s", name.string()); return android::ERROR_DRM_CANNOT_HANDLE; } @@ -414,8 +431,12 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name, value.clear(); value.appendArray(reinterpret_cast(uniqueId.data()), uniqueId.size()); + } else if (name == "serviceCertificate") { + vector cert = mPropertySet.service_certificate(); + value.clear(); + value.appendArray(&cert[0], cert.size()); } else { - ALOGE("App requested unknown property %s", name.string()); + ALOGE("App requested unknown byte array property %s", name.string()); return android::ERROR_DRM_CANNOT_HANDLE; } @@ -424,12 +445,48 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name, status_t WVDrmPlugin::setPropertyString(const String8& name, const String8& value) { - return android::ERROR_DRM_CANNOT_HANDLE; + if (name == "securityLevel") { + if (mCryptoSessions.size() == 0) { + if (value == QUERY_VALUE_SECURITY_LEVEL_L3.c_str()) { + mPropertySet.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); + } else if (value == kResetSecurityLevel) { + mPropertySet.set_security_level(""); + } else { + ALOGE("App requested invalid security level %s", value.string()); + return android::BAD_VALUE; + } + } else { + ALOGE("App tried to change security level while sessions are open."); + return kErrorSessionIsOpen; + } + } else if (name == "privacyMode") { + if (value == kEnable) { + mPropertySet.set_use_privacy_mode(true); + } else if (value == kDisable) { + mPropertySet.set_use_privacy_mode(false); + } else { + ALOGE("App requested unknown privacy mode %s", value.string()); + return android::BAD_VALUE; + } + } else { + ALOGE("App set unknown string property %s", name.string()); + return android::ERROR_DRM_CANNOT_HANDLE; + } + + return android::OK; } status_t WVDrmPlugin::setPropertyByteArray(const String8& name, const Vector& value) { - return android::ERROR_DRM_CANNOT_HANDLE; + if (name == "serviceCertificate") { + vector cert(value.begin(), value.end()); + mPropertySet.set_service_certificate(cert); + } else { + ALOGE("App set unknown byte array property %s", name.string()); + return android::ERROR_DRM_CANNOT_HANDLE; + } + + return android::OK; } status_t WVDrmPlugin::setCipherAlgorithm(const Vector& sessionId, diff --git a/libwvdrmengine/mediadrm/test/Android.mk b/libwvdrmengine/mediadrm/test/Android.mk index 76baf4e5..1956ab83 100644 --- a/libwvdrmengine/mediadrm/test/Android.mk +++ b/libwvdrmengine/mediadrm/test/Android.mk @@ -23,7 +23,6 @@ LOCAL_STATIC_LIBRARIES := \ libgmock \ libgmock_main \ libgtest \ - libwvwrapper \ libwvlevel3 \ libprotobuf-cpp-2.3.0-lite \ libwvdrmdrmplugin \ diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index fdaf8c14..6e5aa2f4 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -6,6 +6,7 @@ #include #include +#include "cdm_client_property_set.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "media/stagefright/foundation/ABase.h" @@ -23,7 +24,8 @@ using namespace wvdrm; class MockCDM : public WvContentDecryptionModule { public: - MOCK_METHOD2(OpenSession, CdmResponseType(const CdmKeySystem&, + MOCK_METHOD3(OpenSession, CdmResponseType(const CdmKeySystem&, + const CdmClientPropertySet*, CdmSessionId*)); MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&)); @@ -133,8 +135,8 @@ TEST_F(WVDrmPluginTest, OpensSessions) { StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); - EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) - .WillOnce(DoAll(SetArgPointee<1>(cdmSessionId), + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _)) + .WillOnce(DoAll(SetArgPointee<2>(cdmSessionId), Return(wvcdm::NO_ERROR))); // Provide expected behavior when plugin requests session control info @@ -633,9 +635,9 @@ TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) { bool match; // Provide expected behavior to support session creation - EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _)) .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) @@ -719,9 +721,9 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) { } // Provide expected behavior to support session creation - EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _)) .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) @@ -786,9 +788,9 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) { } // Provide expected behavior to support session creation - EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _)) .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) @@ -855,9 +857,9 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) { } // Provide expected behavior to support session creation - EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _)) .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) @@ -934,9 +936,9 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) { } // Provide expected behavior to support session creation - EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _)) .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) @@ -977,9 +979,9 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) { .Times(1); // Provide expected behavior to support session creation - EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _)) .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) @@ -1014,10 +1016,10 @@ TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) { CdmSessionId cdmSessionId1(sessionIdRaw1, sessionIdRaw1 + kSessionIdSize); CdmSessionId cdmSessionId2(sessionIdRaw2, sessionIdRaw2 + kSessionIdSize); - EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) - .WillOnce(DoAll(SetArgPointee<1>(cdmSessionId1), + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _)) + .WillOnce(DoAll(SetArgPointee<2>(cdmSessionId1), Return(wvcdm::NO_ERROR))) - .WillOnce(DoAll(SetArgPointee<1>(cdmSessionId2), + .WillOnce(DoAll(SetArgPointee<2>(cdmSessionId2), Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId1, _)) @@ -1071,9 +1073,9 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) { } // Provide expected behavior to support session creation - EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _)) .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) @@ -1099,3 +1101,217 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) { plugin.onEvent(cdmSessionId, LICENSE_EXPIRED_EVENT); plugin.onEvent(cdmSessionId, LICENSE_RENEWAL_NEEDED_EVENT); } + +TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + const CdmClientPropertySet* propertySet = NULL; + + // Provide expected mock behavior + { + // Provide expected behavior in response to OpenSession and store the + // property set + EXPECT_CALL(cdm, OpenSession(_, _, _)) + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), + SaveArg<1>(&propertySet), + Return(wvcdm::NO_ERROR))); + + // Provide expected behavior when plugin requests session control info + EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) + .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); + + // Let gMock know these calls will happen but we aren't interested in them. + EXPECT_CALL(cdm, AttachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, DetachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + } + + plugin.openSession(sessionId); + + ASSERT_THAT(propertySet, NotNull()); + EXPECT_STREQ("", propertySet->security_level().c_str()); + EXPECT_FALSE(propertySet->use_privacy_mode()); + EXPECT_EQ(0u, propertySet->service_certificate().size()); +} + +TEST_F(WVDrmPluginTest, CanSetSecurityLevel) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + const CdmClientPropertySet* propertySet = NULL; + + // Provide expected mock behavior + { + // Provide expected behavior in response to OpenSession and store the + // property set + EXPECT_CALL(cdm, OpenSession(_, _, _)) + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), + SaveArg<1>(&propertySet), + Return(wvcdm::NO_ERROR))); + + // Provide expected behavior when plugin requests session control info + EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) + .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); + + // Let gMock know these calls will happen but we aren't interested in them. + EXPECT_CALL(cdm, AttachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, DetachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + } + + status_t res; + + // Test forcing L3 + res = plugin.setPropertyString(String8("securityLevel"), String8("L3")); + ASSERT_EQ(OK, res); + + plugin.openSession(sessionId); + ASSERT_THAT(propertySet, NotNull()); + EXPECT_STREQ("L3", propertySet->security_level().c_str()); + plugin.closeSession(sessionId); + + // Test forcing L1 (Should Fail) + res = plugin.setPropertyString(String8("securityLevel"), String8("L1")); + ASSERT_NE(OK, res); + + // Test un-forcing a level + res = plugin.setPropertyString(String8("securityLevel"), String8("")); + ASSERT_EQ(OK, res); + + plugin.openSession(sessionId); + ASSERT_THAT(propertySet, NotNull()); + EXPECT_STREQ("", propertySet->security_level().c_str()); + plugin.closeSession(sessionId); + + // Test nonsense (Should Fail) + res = plugin.setPropertyString(String8("securityLevel"), String8("nonsense")); + ASSERT_NE(OK, res); + + // Test attempting to force a level with a session open (Should Fail) + plugin.openSession(sessionId); + res = plugin.setPropertyString(String8("securityLevel"), String8("L3")); + ASSERT_NE(OK, res); +} + +TEST_F(WVDrmPluginTest, CanSetPrivacyMode) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + const CdmClientPropertySet* propertySet = NULL; + + // Provide expected mock behavior + { + // Provide expected behavior in response to OpenSession and store the + // property set + EXPECT_CALL(cdm, OpenSession(_, _, _)) + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), + SaveArg<1>(&propertySet), + Return(wvcdm::NO_ERROR))); + + // Provide expected behavior when plugin requests session control info + EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) + .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); + + // Let gMock know these calls will happen but we aren't interested in them. + EXPECT_CALL(cdm, AttachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, DetachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + } + + plugin.openSession(sessionId); + ASSERT_THAT(propertySet, NotNull()); + + status_t res; + + // Test turning on privacy mode + res = plugin.setPropertyString(String8("privacyMode"), String8("enable")); + ASSERT_EQ(OK, res); + EXPECT_TRUE(propertySet->use_privacy_mode()); + + // Test turning off privacy mode + res = plugin.setPropertyString(String8("privacyMode"), String8("disable")); + ASSERT_EQ(OK, res); + EXPECT_FALSE(propertySet->use_privacy_mode()); + + // Test nonsense (Should Fail) + res = plugin.setPropertyString(String8("privacyMode"), String8("nonsense")); + ASSERT_NE(OK, res); +} + +TEST_F(WVDrmPluginTest, CanSetServiceCertificate) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + const CdmClientPropertySet* propertySet = NULL; + + static const size_t kPrivacyCertSize = 256; + uint8_t privacyCertRaw[kPrivacyCertSize]; + + FILE* fp = fopen("/dev/urandom", "r"); + fread(privacyCertRaw, sizeof(uint8_t), kPrivacyCertSize, fp); + fclose(fp); + + Vector privacyCert; + privacyCert.appendArray(privacyCertRaw, kPrivacyCertSize); + Vector emptyVector; + + // Provide expected mock behavior + { + // Provide expected behavior in response to OpenSession and store the + // property set + EXPECT_CALL(cdm, OpenSession(_, _, _)) + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), + SaveArg<1>(&propertySet), + Return(wvcdm::NO_ERROR))); + + // Provide expected behavior when plugin requests session control info + EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) + .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); + + // Let gMock know these calls will happen but we aren't interested in them. + EXPECT_CALL(cdm, AttachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, DetachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + } + + plugin.openSession(sessionId); + ASSERT_THAT(propertySet, NotNull()); + + status_t res; + + // Test setting a certificate + res = plugin.setPropertyByteArray(String8("serviceCertificate"), privacyCert); + ASSERT_EQ(OK, res); + EXPECT_THAT(propertySet->service_certificate(), + ElementsAreArray(privacyCertRaw, kPrivacyCertSize)); + + // Test clearing a certificate + res = plugin.setPropertyByteArray(String8("serviceCertificate"), emptyVector); + ASSERT_EQ(OK, res); + EXPECT_EQ(0u, propertySet->service_certificate().size()); +} diff --git a/libwvdrmengine/oemcrypto/Android.mk b/libwvdrmengine/oemcrypto/Android.mk deleted file mode 100644 index 0c4f26e3..00000000 --- a/libwvdrmengine/oemcrypto/Android.mk +++ /dev/null @@ -1,34 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_C_INCLUDES := \ - vendor/widevine/libwvdrmengine/cdm/core/include \ - bionic \ - external/openssh \ - external/openssl/include \ - external/openssl/include/openssl \ - external/stlport/stlport \ - vendor/widevine/libwvdrmengine/oemcrypto/include \ - -LOCAL_SHARED_LIBRARIES := \ - libcrypto \ - libcutils \ - libdl \ - liblog \ - libstlport \ - libutils \ - libz \ - -LOCAL_STATIC_LIBRARIES := \ - libwvlevel3 \ - -LOCAL_MODULE := libwvwrapper - -# TODO(fredgc): remove mock reference when library is complete. -REL_MOCK_SOURCE := ../oemcrypto/mock/src - -LOCAL_SRC_FILES := \ - src/wrapper.cpp \ - -include $(BUILD_STATIC_LIBRARY) diff --git a/libwvdrmengine/oemcrypto/include/level3.h b/libwvdrmengine/oemcrypto/include/level3.h index 1a453f4d..b8e81d92 100644 --- a/libwvdrmengine/oemcrypto/include/level3.h +++ b/libwvdrmengine/oemcrypto/include/level3.h @@ -130,6 +130,8 @@ OEMCryptoResult Level3_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, size_t mac_key_context_length, const uint8_t *enc_key_context, size_t enc_key_context_length); +uint32_t Level3_APIVersion(); +const char* Level3_SecurityLevel(); OEMCryptoResult Level3_Generic_Encrypt(OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, diff --git a/libwvdrmengine/oemcrypto/src/wrapper.cpp b/libwvdrmengine/oemcrypto/src/wrapper.cpp deleted file mode 100644 index b2396f37..00000000 --- a/libwvdrmengine/oemcrypto/src/wrapper.cpp +++ /dev/null @@ -1,647 +0,0 @@ -/******************************************************************************* - * - * Copyright 2013 Google Inc. All Rights Reserved. - * - * mock implementation of OEMCrypto APIs - * - ******************************************************************************/ - -#include "OEMCryptoCENC.h" - -#include -#include -#include -#include -#include - -#include "level3.h" -#include "log.h" -#include "file_store.h" -#include "properties.h" - -using namespace wvoec_level3; - -namespace { -typedef OEMCryptoResult (*L1_Initialize_t)(void); -typedef OEMCryptoResult (*L1_Terminate_t)(void); -typedef OEMCryptoResult (*L1_OpenSession_t)(OEMCrypto_SESSION *session); -typedef OEMCryptoResult (*L1_CloseSession_t)(OEMCrypto_SESSION session); -typedef OEMCryptoResult (*L1_GenerateDerivedKeys_t)(OEMCrypto_SESSION session, - const uint8_t *mac_key_context, - uint32_t mac_key_context_length, - const uint8_t *enc_key_context, - uint32_t enc_key_context_length); -typedef OEMCryptoResult (*L1_GenerateNonce_t)(OEMCrypto_SESSION session, - uint32_t* nonce); -typedef OEMCryptoResult (*L1_GenerateSignature_t)(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - uint8_t* signature, - size_t* signature_length); -typedef OEMCryptoResult (*L1_LoadKeys_t)(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - const uint8_t* signature, - size_t signature_length, - const uint8_t* enc_mac_key_iv, - const uint8_t* enc_mac_key, - size_t num_keys, - const OEMCrypto_KeyObject* key_array); -typedef OEMCryptoResult (*L1_RefreshKeys_t)(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - const uint8_t* signature, - size_t signature_length, - size_t num_keys, - const OEMCrypto_KeyRefreshObject* key_array); -typedef OEMCryptoResult (*L1_SelectKey_t)(const OEMCrypto_SESSION session, - const uint8_t* key_id, - size_t key_id_length); -typedef OEMCryptoResult (*L1_DecryptCTR_t)(OEMCrypto_SESSION session, - const uint8_t *data_addr, - size_t data_length, - bool is_encrypted, - const uint8_t *iv, - size_t offset, - const OEMCrypto_DestBufferDesc* out_buffer, - uint8_t subsample_flags); -typedef OEMCryptoResult (*L1_InstallKeybox_t)(const uint8_t *keybox, - size_t keyBoxLength); -typedef OEMCryptoResult (*L1_IsKeyboxValid_t)(void); -typedef OEMCryptoResult (*L1_GetDeviceID_t)(uint8_t* deviceID, - size_t *idLength); -typedef OEMCryptoResult (*L1_GetKeyData_t)(uint8_t* keyData, - size_t *keyDataLength); -typedef OEMCryptoResult (*L1_GetRandom_t)(uint8_t* randomData, - size_t dataLength); -typedef OEMCryptoResult (*L1_WrapKeybox_t)(const uint8_t *keybox, - size_t keyBoxLength, - uint8_t *wrappedKeybox, - size_t *wrappedKeyBoxLength, - const uint8_t *transportKey, - size_t transportKeyLength); -typedef OEMCryptoResult (*L1_RewrapDeviceRSAKey_t)(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - const uint8_t* signature, - size_t signature_length, - const uint32_t *nonce, - const uint8_t* enc_rsa_key, - size_t enc_rsa_key_length, - const uint8_t* enc_rsa_key_iv, - uint8_t* wrapped_rsa_key, - size_t *wrapped_rsa_key_length); -typedef OEMCryptoResult (*L1_LoadDeviceRSAKey_t)(OEMCrypto_SESSION session, - const uint8_t* wrapped_rsa_key, - size_t wrapped_rsa_key_length); -typedef OEMCryptoResult (*L1_GenerateRSASignature_t)(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - uint8_t* signature, - size_t *signature_length); -typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)(OEMCrypto_SESSION session, - const uint8_t* enc_session_key, - size_t enc_session_key_length, - const uint8_t *mac_key_context, - size_t mac_key_context_length, - const uint8_t *enc_key_context, - size_t enc_key_context_length); -typedef OEMCryptoResult (*L1_Generic_Encrypt_t)(OEMCrypto_SESSION session, - const uint8_t* in_buffer, - size_t buffer_length, - const uint8_t* iv, - OEMCrypto_Algorithm algorithm, - uint8_t* out_buffer); -typedef OEMCryptoResult (*L1_Generic_Decrypt_t)(OEMCrypto_SESSION session, - const uint8_t* in_buffer, - size_t buffer_length, - const uint8_t* iv, - OEMCrypto_Algorithm algorithm, - uint8_t* out_buffer); - -typedef OEMCryptoResult (*L1_Generic_Sign_t)(OEMCrypto_SESSION session, - const uint8_t* in_buffer, - size_t buffer_length, - OEMCrypto_Algorithm algorithm, - uint8_t* signature, - size_t* signature_length); - -typedef OEMCryptoResult (*L1_Generic_Verify_t)(OEMCrypto_SESSION session, - const uint8_t* in_buffer, - size_t buffer_length, - OEMCrypto_Algorithm algorithm, - const uint8_t* signature, - size_t signature_length); -typedef uint8_t (*L1_APIVersion_t)(); -typedef const char* (*L1_SecurityLevel_t)(); - -struct FunctionPointers { - void* library; - L1_Initialize_t OEMCrypto_Initialize; - L1_Terminate_t OEMCrypto_Terminate; - L1_OpenSession_t OEMCrypto_OpenSession; - L1_CloseSession_t OEMCrypto_CloseSession; - L1_GenerateDerivedKeys_t OEMCrypto_GenerateDerivedKeys; - L1_GenerateNonce_t OEMCrypto_GenerateNonce; - L1_GenerateSignature_t OEMCrypto_GenerateSignature; - L1_LoadKeys_t OEMCrypto_LoadKeys; - L1_RefreshKeys_t OEMCrypto_RefreshKeys; - L1_SelectKey_t OEMCrypto_SelectKey; - L1_DecryptCTR_t OEMCrypto_DecryptCTR; - L1_InstallKeybox_t OEMCrypto_InstallKeybox; - L1_IsKeyboxValid_t OEMCrypto_IsKeyboxValid; - L1_GetDeviceID_t OEMCrypto_GetDeviceID; - L1_GetKeyData_t OEMCrypto_GetKeyData; - L1_GetRandom_t OEMCrypto_GetRandom; - L1_WrapKeybox_t OEMCrypto_WrapKeybox; - L1_RewrapDeviceRSAKey_t OEMCrypto_RewrapDeviceRSAKey; - L1_LoadDeviceRSAKey_t OEMCrypto_LoadDeviceRSAKey; - L1_GenerateRSASignature_t OEMCrypto_GenerateRSASignature; - L1_DeriveKeysFromSessionKey_t OEMCrypto_DeriveKeysFromSessionKey; - L1_APIVersion_t OEMCrypto_APIVersion; - L1_SecurityLevel_t OEMCrypto_SecurityLevel; - L1_Generic_Encrypt_t OEMCrypto_Generic_Encrypt; - L1_Generic_Decrypt_t OEMCrypto_Generic_Decrypt; - L1_Generic_Sign_t OEMCrypto_Generic_Sign; - L1_Generic_Verify_t OEMCrypto_Generic_Verify; -}; -static struct FunctionPointers level1; - -#define QUOTE_DEFINE(A) #A -#define QUOTE(A) QUOTE_DEFINE(A) -#define LOOKUP(Type, Name) \ - level1.Name = (Type)dlsym(level1.library, QUOTE(Name)); \ - if (!level1.Name) { \ - dll_valid = false; \ - } - -extern "C" -OEMCryptoResult OEMCrypto_Initialize(void) { - // LOGD("First, I will try to load Level 1"); - level1.library = dlopen("liboemcrypto.so", RTLD_NOW); - if (level1.library == NULL) { - LOGW("Could not load liboemcrypto.so. Falling Back to L3. %s", dlerror()); - return Level3_Initialize(); - } - bool dll_valid = true; - LOOKUP(L1_Initialize_t, OEMCrypto_Initialize); - LOOKUP(L1_Terminate_t, OEMCrypto_Terminate); - LOOKUP(L1_OpenSession_t, OEMCrypto_OpenSession); - LOOKUP(L1_CloseSession_t, OEMCrypto_CloseSession); - LOOKUP(L1_GenerateDerivedKeys_t, OEMCrypto_GenerateDerivedKeys); - LOOKUP(L1_GenerateNonce_t, OEMCrypto_GenerateNonce); - LOOKUP(L1_GenerateSignature_t, OEMCrypto_GenerateSignature); - LOOKUP(L1_LoadKeys_t, OEMCrypto_LoadKeys); - LOOKUP(L1_RefreshKeys_t, OEMCrypto_RefreshKeys); - LOOKUP(L1_SelectKey_t, OEMCrypto_SelectKey); - LOOKUP(L1_DecryptCTR_t, OEMCrypto_DecryptCTR); - LOOKUP(L1_InstallKeybox_t, OEMCrypto_InstallKeybox); - LOOKUP(L1_IsKeyboxValid_t, OEMCrypto_IsKeyboxValid); - LOOKUP(L1_GetDeviceID_t, OEMCrypto_GetDeviceID); - LOOKUP(L1_GetKeyData_t, OEMCrypto_GetKeyData); - LOOKUP(L1_GetRandom_t, OEMCrypto_GetRandom); - LOOKUP(L1_WrapKeybox_t, OEMCrypto_WrapKeybox); - LOOKUP(L1_RewrapDeviceRSAKey_t, OEMCrypto_RewrapDeviceRSAKey); - LOOKUP(L1_LoadDeviceRSAKey_t, OEMCrypto_LoadDeviceRSAKey); - LOOKUP(L1_GenerateRSASignature_t, OEMCrypto_GenerateRSASignature); - LOOKUP(L1_DeriveKeysFromSessionKey_t, OEMCrypto_DeriveKeysFromSessionKey); - LOOKUP(L1_APIVersion_t, OEMCrypto_APIVersion); - LOOKUP(L1_SecurityLevel_t, OEMCrypto_SecurityLevel); - LOOKUP(L1_Generic_Decrypt_t, OEMCrypto_Generic_Decrypt); - LOOKUP(L1_Generic_Encrypt_t, OEMCrypto_Generic_Encrypt); - LOOKUP(L1_Generic_Sign_t, OEMCrypto_Generic_Sign); - LOOKUP(L1_Generic_Verify_t, OEMCrypto_Generic_Verify); - if (!dll_valid) { - dlclose(level1.library); - level1.library = NULL; - LOGW("Could not load functions from liboemcrypto.so. Falling Back to L3."); - return Level3_Initialize(); - } - OEMCryptoResult st = level1.OEMCrypto_Initialize(); - if (st != OEMCrypto_SUCCESS) { - LOGW("Could not initialize liboemcrypto.so. Falling Back to L3."); - dlclose(level1.library); - level1.library = NULL; - return Level3_Initialize(); - } - if (level1.OEMCrypto_APIVersion) { - uint32_t level1_version = level1.OEMCrypto_APIVersion(); - if (level1_version != oec_latest_version) { - LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.", - level1_version, oec_latest_version); - dlclose(level1.library); - level1.library = NULL; - return Level3_Initialize(); - } - } - if (OEMCrypto_SUCCESS != OEMCrypto_IsKeyboxValid()) { - wvcdm::File file; - std::string filename; - if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) { - LOGW("Bad Level 1 Keybox. Falling Back to L3."); - dlclose(level1.library); - level1.library = NULL; - return Level3_Initialize(); - } - ssize_t size = file.FileSize(filename); - if( size <= 0 || !file.Open(filename, file.kBinary | file.kReadOnly) ) { - LOGW("Could not open %s. Falling Back to L3.", filename.c_str()); - dlclose(level1.library); - level1.library = NULL; - return Level3_Initialize(); - } - uint8_t keybox[size]; - ssize_t size_read = file.Read(reinterpret_cast(keybox), size); - if (level1.OEMCrypto_InstallKeybox(keybox, size) != OEMCrypto_SUCCESS) { - LOGE("Could NOT install keybox in /factory/wv.keys. Falling Back to L3."); - dlclose(level1.library); - level1.library = NULL; - return Level3_Initialize(); - } - LOGI("Installed keybox from %s", filename.c_str()); - } - LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1."); - return OEMCrypto_SUCCESS; -} - -extern "C" -OEMCryptoResult OEMCrypto_Terminate(void) { - if (level1.library) { - OEMCryptoResult st = level1.OEMCrypto_Terminate(); - dlclose(level1.library); - level1.library = NULL; - return st; - } - return Level3_Terminate(); -} - -extern "C" -OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) { - if (level1.library) { - return level1.OEMCrypto_OpenSession(session); - } - return Level3_OpenSession(session); -} - -extern "C" -OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { - if (level1.library) { - return level1.OEMCrypto_CloseSession(session); - } - return Level3_CloseSession(session); -} - -extern "C" -OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, - uint32_t* nonce) { - if (level1.library) { - return level1.OEMCrypto_GenerateNonce(session, nonce); - } - return Level3_GenerateNonce(session, nonce); -} - -extern "C" -OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, - const uint8_t* mac_key_context, - uint32_t mac_key_context_length, - const uint8_t* enc_key_context, - uint32_t enc_key_context_length) { - if (level1.library) { - return level1.OEMCrypto_GenerateDerivedKeys(session, mac_key_context, - mac_key_context_length, - enc_key_context, - enc_key_context_length); - } - return Level3_GenerateDerivedKeys(session, mac_key_context, - mac_key_context_length, - enc_key_context, - enc_key_context_length); -} - -extern "C" -OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - uint8_t* signature, - size_t* signature_length) { - if (level1.library) { - return level1.OEMCrypto_GenerateSignature(session, message, message_length, - signature, signature_length); - } - return Level3_GenerateSignature(session, message, message_length, - signature, signature_length); -} - - -extern "C" -OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - const uint8_t* signature, - size_t signature_length, - const uint8_t* enc_mac_key_iv, - const uint8_t* enc_mac_key, - size_t num_keys, - const OEMCrypto_KeyObject* key_array) { - if (level1.library) { - return level1.OEMCrypto_LoadKeys(session, message, message_length, signature, - signature_length, enc_mac_key_iv, enc_mac_key, - num_keys, key_array); - } - return Level3_LoadKeys(session, message, message_length, signature, - signature_length, enc_mac_key_iv, enc_mac_key, - num_keys, key_array); -} - -extern "C" -OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - const uint8_t* signature, - size_t signature_length, - size_t num_keys, - const OEMCrypto_KeyRefreshObject* key_array) { - if (level1.library) { - return level1.OEMCrypto_RefreshKeys(session, message, message_length, signature, - signature_length, num_keys, key_array); - } - return Level3_RefreshKeys(session, message, message_length, signature, - signature_length, num_keys, key_array); -} - -extern "C" -OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, - const uint8_t* key_id, - size_t key_id_length) { - if (level1.library) { - return level1.OEMCrypto_SelectKey(session, key_id, key_id_length); - } - return Level3_SelectKey(session, key_id, key_id_length); -} - -extern "C" -OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, - const uint8_t* data_addr, - size_t data_length, - bool is_encrypted, - const uint8_t* iv, - size_t offset, - const OEMCrypto_DestBufferDesc* out_buffer, - uint8_t subsample_flags) { - if (level1.library) { - return level1.OEMCrypto_DecryptCTR(session, data_addr, data_length, - is_encrypted, iv, offset, out_buffer, - subsample_flags); - } - return Level3_DecryptCTR(session, data_addr, data_length, - is_encrypted, iv, offset, out_buffer, subsample_flags); -} - -extern "C" -OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, - size_t keyBoxLength) { - if (level1.library) { - return level1.OEMCrypto_InstallKeybox(keybox, keyBoxLength); - } - return Level3_InstallKeybox(keybox, keyBoxLength); -} - -extern "C" -OEMCryptoResult OEMCrypto_IsKeyboxValid(void) { - if (level1.library) { - return level1.OEMCrypto_IsKeyboxValid(); - } - return Level3_IsKeyboxValid(); -} - -extern "C" -OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, - size_t* idLength) { - if (level1.library) { - return level1.OEMCrypto_GetDeviceID(deviceID, idLength); - } - return Level3_GetDeviceID(deviceID, idLength); -} - -extern "C" -OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, - size_t* keyDataLength) { - if (level1.library) { - return level1.OEMCrypto_GetKeyData(keyData, keyDataLength); - } - return Level3_GetKeyData(keyData, keyDataLength); -} - -extern "C" -OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) { - if (level1.library) { - return level1.OEMCrypto_GetRandom(randomData, dataLength); - } - return Level3_GetRandom(randomData, dataLength); -} - -extern "C" -OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox, - size_t keyBoxLength, - uint8_t* wrappedKeybox, - size_t* wrappedKeyBoxLength, - const uint8_t* transportKey, - size_t transportKeyLength) { - if (level1.library) { - return level1.OEMCrypto_WrapKeybox(keybox, keyBoxLength, wrappedKeybox, - wrappedKeyBoxLength, transportKey, - transportKeyLength); - } - return Level3_WrapKeybox(keybox, keyBoxLength, wrappedKeybox, - wrappedKeyBoxLength, transportKey, - transportKeyLength); -} - -extern "C" -OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - const uint8_t* signature, - size_t signature_length, - const uint32_t* nonce, - const uint8_t* enc_rsa_key, - size_t enc_rsa_key_length, - const uint8_t* enc_rsa_key_iv, - uint8_t* wrapped_rsa_key, - size_t* wrapped_rsa_key_length) { - if (level1.library) { - if (!level1.OEMCrypto_RewrapDeviceRSAKey) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - return level1.OEMCrypto_RewrapDeviceRSAKey(session, message, message_length, - signature, signature_length, nonce, - enc_rsa_key, enc_rsa_key_length, - enc_rsa_key_iv, wrapped_rsa_key, - wrapped_rsa_key_length); - } - return Level3_RewrapDeviceRSAKey(session, message, message_length, - signature, signature_length, nonce, - enc_rsa_key, enc_rsa_key_length, - enc_rsa_key_iv, wrapped_rsa_key, - wrapped_rsa_key_length); -} - -extern "C" -OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, - const uint8_t* wrapped_rsa_key, - size_t wrapped_rsa_key_length) { - if (level1.library) { - if (!level1.OEMCrypto_LoadDeviceRSAKey) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - return level1.OEMCrypto_LoadDeviceRSAKey(session, wrapped_rsa_key, - wrapped_rsa_key_length); - } - return Level3_LoadDeviceRSAKey(session, wrapped_rsa_key, - wrapped_rsa_key_length); -} - -extern "C" -OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, - const uint8_t* message, - size_t message_length, - uint8_t* signature, - size_t* signature_length) { - if (level1.library) { - if (!level1.OEMCrypto_GenerateRSASignature) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - return level1.OEMCrypto_GenerateRSASignature(session, message, message_length, - signature, signature_length); - } - return Level3_GenerateRSASignature(session, message, message_length, - signature, signature_length); -} - -extern "C" -OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, - const uint8_t* enc_session_key, - size_t enc_session_key_length, - const uint8_t* mac_key_context, - size_t mac_key_context_length, - const uint8_t* enc_key_context, - size_t enc_key_context_length) { - if (level1.library) { - if (!level1.OEMCrypto_DeriveKeysFromSessionKey) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - return level1.OEMCrypto_DeriveKeysFromSessionKey(session, enc_session_key, - enc_session_key_length, - mac_key_context, - mac_key_context_length, - enc_key_context, - enc_key_context_length); - } - return Level3_DeriveKeysFromSessionKey(session, enc_session_key, - enc_session_key_length, - mac_key_context, - mac_key_context_length, - enc_key_context, - enc_key_context_length); -} - -extern "C" -uint32_t OEMCrypto_APIVersion() { - if (level1.library) { - if (!level1.OEMCrypto_APIVersion) { - return 5; - } - return level1.OEMCrypto_APIVersion(); - } - return oec_latest_version; -} - -extern "C" -const char* OEMCrypto_SecurityLevel() { - if (level1.library) { - if (!level1.OEMCrypto_SecurityLevel) { - return "Unknown"; - } - return level1.OEMCrypto_SecurityLevel(); - } - return "L3"; -} - -extern "C" -OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, - const uint8_t* in_buffer, - size_t buffer_length, - const uint8_t* iv, - OEMCrypto_Algorithm algorithm, - uint8_t* out_buffer) { - - if (level1.library) { - if (!level1.OEMCrypto_Generic_Encrypt) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - return level1.OEMCrypto_Generic_Encrypt(session, in_buffer, buffer_length, - iv, algorithm, out_buffer); - } - return Level3_Generic_Encrypt(session, in_buffer, buffer_length, - iv, algorithm, out_buffer); -} - -extern "C" -OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, - const uint8_t* in_buffer, - size_t buffer_length, - const uint8_t* iv, - OEMCrypto_Algorithm algorithm, - uint8_t* out_buffer) { - if (level1.library) { - if (!level1.OEMCrypto_Generic_Decrypt) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - return level1.OEMCrypto_Generic_Decrypt(session, in_buffer, buffer_length, - iv, algorithm, out_buffer); - } - return Level3_Generic_Decrypt(session, in_buffer, buffer_length, - iv, algorithm, out_buffer); -} - -extern "C" -OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, - const uint8_t* in_buffer, - size_t buffer_length, - OEMCrypto_Algorithm algorithm, - uint8_t* signature, - size_t* signature_length) { - if (level1.library) { - if (!level1.OEMCrypto_Generic_Sign) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - return level1.OEMCrypto_Generic_Sign(session, in_buffer, buffer_length, - algorithm, signature, - signature_length); - } - return Level3_Generic_Sign(session, in_buffer, buffer_length, - algorithm, signature, - signature_length); -} - -extern "C" -OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, - const uint8_t* in_buffer, - size_t buffer_length, - OEMCrypto_Algorithm algorithm, - const uint8_t* signature, - size_t signature_length) { - if (level1.library) { - if (!level1.OEMCrypto_Generic_Verify) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - return level1.OEMCrypto_Generic_Verify(session, in_buffer, buffer_length, - algorithm, signature, - signature_length); - } - return Level3_Generic_Verify(session, in_buffer, buffer_length, - algorithm, signature, - signature_length); -} - -}; // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/test/Android.mk b/libwvdrmengine/oemcrypto/test/Android.mk index ab3a0762..71368e90 100644 --- a/libwvdrmengine/oemcrypto/test/Android.mk +++ b/libwvdrmengine/oemcrypto/test/Android.mk @@ -22,9 +22,9 @@ LOCAL_C_INCLUDES += \ vendor/widevine/libwvdrmengine/third_party/stringencoders/src \ LOCAL_STATIC_LIBRARIES := \ + libcdm \ libgtest \ libgtest_main \ - libwvwrapper \ libwvlevel3 \ libcdm_utils \