Allow Apps to Voluntarily Downgrade to L3 Crypto
This merges the following changes from the Widevine CDM repository: 564f4cc Add CdmClientPropertySet to CDM Adds an interface to the CDM that allows it to query its client for certain properties. In this case, this includes the ability to specify what security level is desired, as well as support for service ceritifcate privacy mode. 9cfbd3e Force Level 3 fallback Adds support for voluntarily invoking L3 crypto to the OEMCrypto wrapper. 95d12c1 Add pointer to CdmClientPropertySet class to OpenSession. Adds support for storing the property set on a session-by-session basis and choosing the appropriate crypto level. 17de442 Add Settable Properties for Clank to Android Adds support for setting the aforementioned properties to the DrmEngine bbe704d Fixes to force fallback to level three security Corrections to invoke provisioning, OEMCrypto API with configured security level rather than the default. Unit tests were also revised. Note that some parts of this are also support for the ability to use a service certificate-based privacy mode. The remaining code for supporting this mode is still forthcoming. Bug: 10109249 Change-Id: I2755e4dea1de3e8a56cff237360298f7b7f1bddc
This commit is contained in:
@@ -92,7 +92,6 @@ LOCAL_C_INCLUDES := \
|
|||||||
LOCAL_STATIC_LIBRARIES := \
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
libcdm \
|
libcdm \
|
||||||
libcdm_utils \
|
libcdm_utils \
|
||||||
libwvwrapper \
|
|
||||||
libwvlevel3 \
|
libwvlevel3 \
|
||||||
libprotobuf-cpp-2.3.0-lite \
|
libprotobuf-cpp-2.3.0-lite \
|
||||||
libwvdrmcryptoplugin \
|
libwvdrmcryptoplugin \
|
||||||
@@ -121,7 +120,6 @@ include $(BUILD_SHARED_LIBRARY)
|
|||||||
|
|
||||||
include vendor/widevine/libwvdrmengine/cdm/Android.mk
|
include vendor/widevine/libwvdrmengine/cdm/Android.mk
|
||||||
include vendor/widevine/libwvdrmengine/level3/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/mediacrypto/Android.mk
|
||||||
include vendor/widevine/libwvdrmengine/mediadrm/Android.mk
|
include vendor/widevine/libwvdrmengine/mediadrm/Android.mk
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ LOCAL_C_INCLUDES := \
|
|||||||
|
|
||||||
LOCAL_C_INCLUDES += \
|
LOCAL_C_INCLUDES += \
|
||||||
external/openssl/include \
|
external/openssl/include \
|
||||||
external/protobuf/src \
|
external/protobuf/src
|
||||||
../oemcrypto/include
|
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := libcdm_protos
|
LOCAL_STATIC_LIBRARIES := libcdm_protos
|
||||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers)
|
LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers)
|
||||||
@@ -30,7 +29,9 @@ LOCAL_SRC_FILES := \
|
|||||||
$(CORE_SRC_DIR)/crypto_session.cpp \
|
$(CORE_SRC_DIR)/crypto_session.cpp \
|
||||||
$(CORE_SRC_DIR)/device_files.cpp \
|
$(CORE_SRC_DIR)/device_files.cpp \
|
||||||
$(CORE_SRC_DIR)/license.cpp \
|
$(CORE_SRC_DIR)/license.cpp \
|
||||||
|
$(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \
|
||||||
$(CORE_SRC_DIR)/policy_engine.cpp \
|
$(CORE_SRC_DIR)/policy_engine.cpp \
|
||||||
|
$(CORE_SRC_DIR)/privacy_crypto.cpp \
|
||||||
$(SRC_DIR)/wv_content_decryption_module.cpp
|
$(SRC_DIR)/wv_content_decryption_module.cpp
|
||||||
|
|
||||||
LOCAL_MODULE := libcdm
|
LOCAL_MODULE := libcdm
|
||||||
|
|||||||
23
libwvdrmengine/cdm/core/include/cdm_client_property_set.h
Normal file
23
libwvdrmengine/cdm/core/include/cdm_client_property_set.h
Normal file
@@ -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 <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
|
||||||
|
class CdmClientPropertySet {
|
||||||
|
public:
|
||||||
|
virtual ~CdmClientPropertySet() {}
|
||||||
|
|
||||||
|
virtual std::string security_level() const = 0;
|
||||||
|
virtual bool use_privacy_mode() const = 0;
|
||||||
|
virtual std::vector<uint8_t> service_certificate() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace wvcdm
|
||||||
|
|
||||||
|
#endif // CDM_BASE_CDM_CLIENT_PROPERTY_SET_H_
|
||||||
@@ -4,11 +4,13 @@
|
|||||||
#define CDM_BASE_CDM_ENGINE_H_
|
#define CDM_BASE_CDM_ENGINE_H_
|
||||||
|
|
||||||
#include "certificate_provisioning.h"
|
#include "certificate_provisioning.h"
|
||||||
|
#include "oemcrypto_adapter.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
|
class CdmClientPropertySet;
|
||||||
class CdmSession;
|
class CdmSession;
|
||||||
class CryptoEngine;
|
class CryptoEngine;
|
||||||
class WvCdmEventListener;
|
class WvCdmEventListener;
|
||||||
@@ -22,8 +24,10 @@ class CdmEngine : public TimerHandler {
|
|||||||
virtual ~CdmEngine();
|
virtual ~CdmEngine();
|
||||||
|
|
||||||
// Session related methods
|
// Session related methods
|
||||||
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
virtual CdmResponseType OpenSession(
|
||||||
CdmSessionId* session_id);
|
const CdmKeySystem& key_system,
|
||||||
|
const CdmClientPropertySet* property_set,
|
||||||
|
CdmSessionId* session_id);
|
||||||
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
||||||
|
|
||||||
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id);
|
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id);
|
||||||
@@ -116,9 +120,11 @@ class CdmEngine : public TimerHandler {
|
|||||||
|
|
||||||
// instance variables
|
// instance variables
|
||||||
CdmSessionMap sessions_;
|
CdmSessionMap sessions_;
|
||||||
CertificateProvisioning cert_provisioning_;
|
|
||||||
CdmReleaseKeySetMap release_key_sets_;
|
CdmReleaseKeySetMap release_key_sets_;
|
||||||
|
|
||||||
|
CertificateProvisioning cert_provisioning_;
|
||||||
|
SecurityLevel cert_provisioning_requested_security_level_;
|
||||||
|
|
||||||
// policy timer
|
// policy timer
|
||||||
Timer policy_timer_;
|
Timer policy_timer_;
|
||||||
|
|
||||||
|
|||||||
@@ -8,23 +8,20 @@
|
|||||||
#include "crypto_session.h"
|
#include "crypto_session.h"
|
||||||
#include "device_files.h"
|
#include "device_files.h"
|
||||||
#include "license.h"
|
#include "license.h"
|
||||||
|
#include "oemcrypto_adapter.h"
|
||||||
#include "policy_engine.h"
|
#include "policy_engine.h"
|
||||||
#include "scoped_ptr.h"
|
#include "scoped_ptr.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
|
class CdmClientPropertySet;
|
||||||
class WvCdmEventListener;
|
class WvCdmEventListener;
|
||||||
|
|
||||||
class CdmSession {
|
class CdmSession {
|
||||||
public:
|
public:
|
||||||
CdmSession()
|
explicit CdmSession(const CdmClientPropertySet* cdm_client_property_set);
|
||||||
: session_id_(GenerateSessionId()),
|
~CdmSession();
|
||||||
crypto_session_(NULL),
|
|
||||||
license_received_(false),
|
|
||||||
reinitialize_session_(false),
|
|
||||||
license_type_(kLicenseTypeStreaming) {}
|
|
||||||
~CdmSession() {}
|
|
||||||
|
|
||||||
CdmResponseType Init();
|
CdmResponseType Init();
|
||||||
|
|
||||||
@@ -87,13 +84,14 @@ class CdmSession {
|
|||||||
void OnTimerEvent();
|
void OnTimerEvent();
|
||||||
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||||
|
|
||||||
|
SecurityLevel GetRequestedSecurityLevel();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Generate unique ID for each new session.
|
// Generate unique ID for each new session.
|
||||||
CdmSessionId GenerateSessionId();
|
CdmSessionId GenerateSessionId();
|
||||||
bool GenerateKeySetId(CdmKeySetId* key_set_id);
|
bool GenerateKeySetId(CdmKeySetId* key_set_id);
|
||||||
|
|
||||||
bool LoadDeviceCertificate(std::string* cert, std::string* wrapped_key);
|
|
||||||
bool StoreLicense(DeviceFiles::LicenseState state);
|
bool StoreLicense(DeviceFiles::LicenseState state);
|
||||||
|
|
||||||
// instance variables
|
// instance variables
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#define CDM_BASE_CERTIFICATE_PROVISIONING_H_
|
#define CDM_BASE_CERTIFICATE_PROVISIONING_H_
|
||||||
|
|
||||||
#include "crypto_session.h"
|
#include "crypto_session.h"
|
||||||
|
#include "oemcrypto_adapter.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
@@ -16,7 +17,8 @@ class CertificateProvisioning {
|
|||||||
~CertificateProvisioning() {};
|
~CertificateProvisioning() {};
|
||||||
|
|
||||||
// Provisioning related methods
|
// Provisioning related methods
|
||||||
CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request,
|
CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level,
|
||||||
|
CdmProvisioningRequest* request,
|
||||||
std::string* default_url);
|
std::string* default_url);
|
||||||
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response);
|
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response);
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "lock.h"
|
#include "lock.h"
|
||||||
|
#include "oemcrypto_adapter.h"
|
||||||
#include "OEMCryptoCENC.h"
|
#include "OEMCryptoCENC.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
@@ -29,7 +30,8 @@ class CryptoSession {
|
|||||||
bool GetSystemId(uint32_t* system_id);
|
bool GetSystemId(uint32_t* system_id);
|
||||||
bool GetProvisioningId(std::string* provisioning_id);
|
bool GetProvisioningId(std::string* provisioning_id);
|
||||||
|
|
||||||
CdmResponseType Open();
|
CdmResponseType Open() { return Open(kLevelDefault); }
|
||||||
|
CdmResponseType Open(SecurityLevel requested_security_level);
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
bool IsOpen() { return open_; }
|
bool IsOpen() { return open_; }
|
||||||
@@ -64,7 +66,7 @@ class CryptoSession {
|
|||||||
bool SelectKey(const std::string& key_id);
|
bool SelectKey(const std::string& key_id);
|
||||||
CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
|
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:
|
private:
|
||||||
void Init();
|
void Init();
|
||||||
@@ -88,7 +90,7 @@ class CryptoSession {
|
|||||||
|
|
||||||
OEMCryptoBufferType destination_buffer_type_;
|
OEMCryptoBufferType destination_buffer_type_;
|
||||||
bool is_destination_buffer_type_valid_;
|
bool is_destination_buffer_type_valid_;
|
||||||
CdmSecurityLevel security_level_;
|
SecurityLevel requested_security_level_;
|
||||||
|
|
||||||
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
|
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class DeviceFiles {
|
|||||||
CdmKeyResponse* key_renewal_response,
|
CdmKeyResponse* key_renewal_response,
|
||||||
std::string* release_server_url);
|
std::string* release_server_url);
|
||||||
virtual bool DeleteLicense(const std::string& key_set_id);
|
virtual bool DeleteLicense(const std::string& key_set_id);
|
||||||
|
virtual bool DeleteAllFiles();
|
||||||
virtual bool DeleteAllLicenses();
|
virtual bool DeleteAllLicenses();
|
||||||
virtual bool LicenseExists(const std::string& key_set_id);
|
virtual bool LicenseExists(const std::string& key_set_id);
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ class PolicyEngine;
|
|||||||
class CdmLicense {
|
class CdmLicense {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CdmLicense();
|
CdmLicense()
|
||||||
~CdmLicense();
|
: session_(NULL),
|
||||||
|
initialized_(false),
|
||||||
|
service_certificate_response_pending_(false) {};
|
||||||
|
~CdmLicense() {};
|
||||||
|
|
||||||
bool Init(const std::string& token, CryptoSession* session,
|
bool Init(const std::string& token, CryptoSession* session,
|
||||||
PolicyEngine* policy_engine);
|
PolicyEngine* policy_engine);
|
||||||
@@ -28,28 +31,37 @@ class CdmLicense {
|
|||||||
bool PrepareKeyRequest(const CdmInitData& pssh_data,
|
bool PrepareKeyRequest(const CdmInitData& pssh_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
const CdmAppParameterMap& app_parameters,
|
const CdmAppParameterMap& app_parameters,
|
||||||
|
const CdmSessionId& session_id,
|
||||||
CdmKeyMessage* signed_request,
|
CdmKeyMessage* signed_request,
|
||||||
std::string* server_url);
|
std::string* server_url);
|
||||||
bool PrepareKeyUpdateRequest(bool is_renewal,
|
bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request,
|
||||||
CdmKeyMessage* signed_request,
|
|
||||||
std::string* server_url);
|
std::string* server_url);
|
||||||
CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
|
CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
|
||||||
CdmResponseType HandleKeyUpdateResponse(
|
CdmResponseType HandleKeyUpdateResponse(
|
||||||
bool is_renewal,
|
bool is_renewal, const CdmKeyResponse& license_response);
|
||||||
const CdmKeyResponse& license_response);
|
|
||||||
|
|
||||||
bool RestoreOfflineLicense(CdmKeyMessage& license_request,
|
bool RestoreOfflineLicense(CdmKeyMessage& license_request,
|
||||||
CdmKeyResponse& license_response,
|
CdmKeyResponse& license_response,
|
||||||
CdmKeyResponse& license_renewal_response);
|
CdmKeyResponse& license_renewal_response);
|
||||||
|
bool HasInitData() { return !init_data_.empty(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CdmResponseType HandleKeyErrorResponse(
|
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||||
const video_widevine_server::sdk::SignedMessage& signed_message);
|
std::string* server_url);
|
||||||
|
CdmResponseType HandleServiceCertificateResponse(
|
||||||
|
const CdmKeyResponse& service_certificate_response);
|
||||||
|
|
||||||
|
CdmResponseType HandleKeyErrorResponse(
|
||||||
|
const video_widevine_server::sdk::SignedMessage& signed_message);
|
||||||
|
|
||||||
CryptoSession* session_;
|
CryptoSession* session_;
|
||||||
PolicyEngine* policy_engine_;
|
PolicyEngine* policy_engine_;
|
||||||
std::string server_url_;
|
std::string server_url_;
|
||||||
std::string token_;
|
std::string token_;
|
||||||
|
std::string service_certificate_;
|
||||||
|
std::string init_data_;
|
||||||
|
bool initialized_;
|
||||||
|
bool service_certificate_response_pending_;
|
||||||
|
|
||||||
// Used for certificate based licensing
|
// Used for certificate based licensing
|
||||||
CdmKeyMessage key_request_;
|
CdmKeyMessage key_request_;
|
||||||
|
|||||||
39
libwvdrmengine/cdm/core/include/oemcrypto_adapter.h
Normal file
39
libwvdrmengine/cdm/core/include/oemcrypto_adapter.h
Normal file
@@ -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_
|
||||||
77
libwvdrmengine/cdm/core/include/privacy_crypto.h
Normal file
77
libwvdrmengine/cdm/core/include/privacy_crypto.h
Normal file
@@ -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 <string>
|
||||||
|
|
||||||
|
#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_
|
||||||
@@ -6,11 +6,16 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "cdm_client_property_set.h"
|
||||||
#include "lock.h"
|
#include "lock.h"
|
||||||
|
#include "scoped_ptr.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
|
typedef std::map<CdmSessionId, const CdmClientPropertySet*>
|
||||||
|
CdmClientPropertySetMap;
|
||||||
|
|
||||||
// This class saves information about features and properties enabled
|
// This class saves information about features and properties enabled
|
||||||
// for a given platform. At initialization it initializes properties from
|
// for a given platform. At initialization it initializes properties from
|
||||||
// property_configuration.h. That file specifies features selected for each
|
// property_configuration.h. That file specifies features selected for each
|
||||||
@@ -40,6 +45,9 @@ class Properties {
|
|||||||
static inline bool extract_pssh_data() {
|
static inline bool extract_pssh_data() {
|
||||||
return 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 GetCompanyName(std::string* company_name);
|
||||||
static bool GetModelName(std::string* model_name);
|
static bool GetModelName(std::string* model_name);
|
||||||
static bool GetArchitectureName(std::string* arch_name);
|
static bool GetArchitectureName(std::string* arch_name);
|
||||||
@@ -49,8 +57,20 @@ class Properties {
|
|||||||
static bool GetDeviceFilesBasePath(CdmSecurityLevel security_level,
|
static bool GetDeviceFilesBasePath(CdmSecurityLevel security_level,
|
||||||
std::string* base_path);
|
std::string* base_path);
|
||||||
static bool GetFactoryKeyboxPath(std::string* keybox);
|
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<uint8_t> 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:
|
private:
|
||||||
|
static const CdmClientPropertySet* GetCdmClientPropertySet(
|
||||||
|
const CdmSessionId& session_id);
|
||||||
static void set_begin_license_usage_when_received(bool flag) {
|
static void set_begin_license_usage_when_received(bool flag) {
|
||||||
begin_license_usage_when_received_ = flag;
|
begin_license_usage_when_received_ = flag;
|
||||||
}
|
}
|
||||||
@@ -72,6 +92,9 @@ class Properties {
|
|||||||
static void set_extract_pssh_data(bool flag) {
|
static void set_extract_pssh_data(bool flag) {
|
||||||
extract_pssh_data_ = 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 begin_license_usage_when_received_;
|
||||||
static bool require_explicit_renew_request_;
|
static bool require_explicit_renew_request_;
|
||||||
@@ -80,6 +103,8 @@ class Properties {
|
|||||||
static bool oem_crypto_use_userspace_buffers_;
|
static bool oem_crypto_use_userspace_buffers_;
|
||||||
static bool use_certificates_as_identification_;
|
static bool use_certificates_as_identification_;
|
||||||
static bool extract_pssh_data_;
|
static bool extract_pssh_data_;
|
||||||
|
static bool decrypt_with_empty_session_support_;
|
||||||
|
static scoped_ptr<CdmClientPropertySetMap> session_property_set_;
|
||||||
|
|
||||||
CORE_DISALLOW_COPY_AND_ASSIGN(Properties);
|
CORE_DISALLOW_COPY_AND_ASSIGN(Properties);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ const int kCdmPolicyTimerDurationSeconds = 1;
|
|||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
CdmEngine::CdmEngine() {
|
CdmEngine::CdmEngine()
|
||||||
|
: cert_provisioning_requested_security_level_(kLevelDefault) {
|
||||||
Properties::Init();
|
Properties::Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ CdmEngine::~CdmEngine() {
|
|||||||
|
|
||||||
CdmResponseType CdmEngine::OpenSession(
|
CdmResponseType CdmEngine::OpenSession(
|
||||||
const CdmKeySystem& key_system,
|
const CdmKeySystem& key_system,
|
||||||
|
const CdmClientPropertySet* property_set,
|
||||||
CdmSessionId* session_id) {
|
CdmSessionId* session_id) {
|
||||||
LOGI("CdmEngine::OpenSession");
|
LOGI("CdmEngine::OpenSession");
|
||||||
|
|
||||||
@@ -49,8 +51,7 @@ CdmResponseType CdmEngine::OpenSession(
|
|||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_ptr<CdmSession> new_session(new CdmSession());
|
scoped_ptr<CdmSession> new_session(new CdmSession(property_set));
|
||||||
|
|
||||||
if (new_session->session_id().empty()) {
|
if (new_session->session_id().empty()) {
|
||||||
LOGE("CdmEngine::OpenSession: failure to generate session ID");
|
LOGE("CdmEngine::OpenSession: failure to generate session ID");
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
@@ -58,10 +59,13 @@ CdmResponseType CdmEngine::OpenSession(
|
|||||||
|
|
||||||
CdmResponseType sts = new_session->Init();
|
CdmResponseType sts = new_session->Init();
|
||||||
if (sts != NO_ERROR) {
|
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;
|
return sts;
|
||||||
}
|
}
|
||||||
|
|
||||||
*session_id = new_session->session_id();
|
*session_id = new_session->session_id();
|
||||||
sessions_[*session_id] = new_session.release();
|
sessions_[*session_id] = new_session.release();
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
@@ -76,7 +80,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CdmSessionId session_id;
|
CdmSessionId session_id;
|
||||||
CdmResponseType sts = OpenSession(KEY_SYSTEM, &session_id);
|
CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id);
|
||||||
|
|
||||||
if (sts != NO_ERROR)
|
if (sts != NO_ERROR)
|
||||||
return sts;
|
return sts;
|
||||||
@@ -178,6 +182,10 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
|||||||
server_url);
|
server_url);
|
||||||
|
|
||||||
if (KEY_MESSAGE != sts) {
|
if (KEY_MESSAGE != sts) {
|
||||||
|
if (sts == NEED_PROVISIONING) {
|
||||||
|
cert_provisioning_requested_security_level_ =
|
||||||
|
iter->second->GetRequestedSecurityLevel();
|
||||||
|
}
|
||||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
|
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||||
"sts = %d", (int)sts);
|
"sts = %d", (int)sts);
|
||||||
return sts;
|
return sts;
|
||||||
@@ -262,7 +270,13 @@ CdmResponseType CdmEngine::RestoreKey(
|
|||||||
return UNKNOWN_ERROR;
|
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) {
|
CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) {
|
||||||
@@ -425,7 +439,10 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
|
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
|
||||||
return UNKNOWN_ERROR;
|
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;
|
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()) {
|
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;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,33 @@ namespace wvcdm {
|
|||||||
|
|
||||||
typedef std::set<WvCdmEventListener*>::iterator CdmEventListenerIter;
|
typedef std::set<WvCdmEventListener*>::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() {
|
CdmResponseType CdmSession::Init() {
|
||||||
scoped_ptr<CryptoSession> session(new CryptoSession());
|
scoped_ptr<CryptoSession> session(new CryptoSession());
|
||||||
CdmResponseType sts = session->Open();
|
|
||||||
|
CdmResponseType sts = session->Open(GetRequestedSecurityLevel());
|
||||||
if (NO_ERROR != sts) return sts;
|
if (NO_ERROR != sts) return sts;
|
||||||
|
|
||||||
std::string token;
|
std::string token;
|
||||||
if (Properties::use_certificates_as_identification()) {
|
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 {
|
} else {
|
||||||
if (!session->GetToken(&token)) return UNKNOWN_ERROR;
|
if (!session->GetToken(&token)) return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
@@ -129,7 +148,7 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
|||||||
? UNKNOWN_ERROR
|
? UNKNOWN_ERROR
|
||||||
: GenerateRenewalRequest(key_request, server_url);
|
: GenerateRenewalRequest(key_request, server_url);
|
||||||
} else {
|
} else {
|
||||||
if (init_data.empty()) {
|
if (init_data.empty() && !license_parser_.HasInitData()) {
|
||||||
LOGW("CdmSession::GenerateKeyRequest: init data absent");
|
LOGW("CdmSession::GenerateKeyRequest: init data absent");
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
@@ -148,8 +167,9 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!license_parser_.PrepareKeyRequest(
|
if (!license_parser_.PrepareKeyRequest(pssh_data, license_type,
|
||||||
pssh_data, license_type, app_parameters, key_request, server_url)) {
|
app_parameters, session_id_,
|
||||||
|
key_request, server_url)) {
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,8 +330,21 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CdmSession::IsKeyValid(const KeyId& key_id) {
|
bool CdmSession::IsKeyValid(const KeyId& key_id) {
|
||||||
// TODO(gmorgan): lookup key and determine if valid.
|
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
||||||
// return (session_keys_.find(key_id) != session_keys_.end());
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +370,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
while (key_set_id->empty()) {
|
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;
|
return false;
|
||||||
|
|
||||||
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
|
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
|
||||||
@@ -350,16 +383,6 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
|
|||||||
return true;
|
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) {
|
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||||
File file;
|
File file;
|
||||||
DeviceFiles handle;
|
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
|
} // namespace wvcdm
|
||||||
|
|||||||
@@ -53,11 +53,12 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
|||||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||||
*/
|
*/
|
||||||
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||||
|
SecurityLevel requested_security_level,
|
||||||
CdmProvisioningRequest* request,
|
CdmProvisioningRequest* request,
|
||||||
std::string* default_url) {
|
std::string* default_url) {
|
||||||
default_url->assign(kDefaultProvisioningServerUrl);
|
default_url->assign(kDefaultProvisioningServerUrl);
|
||||||
|
|
||||||
CdmResponseType sts = crypto_session_.Open();
|
CdmResponseType sts = crypto_session_.Open(requested_security_level);
|
||||||
if (NO_ERROR != sts) {
|
if (NO_ERROR != sts) {
|
||||||
LOGE("GetProvisioningRequest: fails to create a crypto session");
|
LOGE("GetProvisioningRequest: fails to create a crypto session");
|
||||||
return sts;
|
return sts;
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
#include "crypto_key.h"
|
#include "crypto_key.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
// TODO(gmorgan,jtinker): decide if OEMCryptoCENC is needed here.
|
|
||||||
#include "OEMCryptoCENC.h"
|
|
||||||
#include "properties.h"
|
#include "properties.h"
|
||||||
#include "string_conversions.h"
|
#include "string_conversions.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
@@ -37,7 +35,7 @@ int CryptoSession::session_count_ = 0;
|
|||||||
CryptoSession::CryptoSession()
|
CryptoSession::CryptoSession()
|
||||||
: open_(false),
|
: open_(false),
|
||||||
is_destination_buffer_type_valid_(false),
|
is_destination_buffer_type_valid_(false),
|
||||||
security_level_(kSecurityLevelUninitialized) {
|
requested_security_level_(kLevelDefault) {
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +77,7 @@ bool CryptoSession::ValidateKeybox() {
|
|||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
OEMCryptoResult result = OEMCrypto_IsKeyboxValid();
|
OEMCryptoResult result = OEMCrypto_IsKeyboxValid(requested_security_level_);
|
||||||
return (OEMCrypto_SUCCESS == result);
|
return (OEMCrypto_SUCCESS == result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +93,8 @@ bool CryptoSession::GetToken(std::string* token) {
|
|||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
|
OEMCryptoResult sts =
|
||||||
|
OEMCrypto_GetKeyData(buf, &bufSize, requested_security_level_);
|
||||||
if (OEMCrypto_SUCCESS != sts) {
|
if (OEMCrypto_SUCCESS != sts) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -110,16 +109,8 @@ CdmSecurityLevel CryptoSession::GetSecurityLevel() {
|
|||||||
return kSecurityLevelUninitialized;
|
return kSecurityLevelUninitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (security_level_) {
|
std::string security_level =
|
||||||
case kSecurityLevelL1:
|
OEMCrypto_SecurityLevel(requested_security_level_);
|
||||||
case kSecurityLevelL2:
|
|
||||||
case kSecurityLevelL3:
|
|
||||||
return security_level_;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string security_level = OEMCrypto_SecurityLevel();
|
|
||||||
|
|
||||||
if ((security_level.size() != 2) || (security_level.at(0) != 'L')) {
|
if ((security_level.size() != 2) || (security_level.at(0) != 'L')) {
|
||||||
return kSecurityLevelUnknown;
|
return kSecurityLevelUnknown;
|
||||||
@@ -127,20 +118,16 @@ CdmSecurityLevel CryptoSession::GetSecurityLevel() {
|
|||||||
|
|
||||||
switch (security_level.at(1)) {
|
switch (security_level.at(1)) {
|
||||||
case '1':
|
case '1':
|
||||||
security_level_ = kSecurityLevelL1;
|
return kSecurityLevelL1;
|
||||||
break;
|
|
||||||
case '2':
|
case '2':
|
||||||
security_level_ = kSecurityLevelL2;
|
return kSecurityLevelL2;
|
||||||
break;
|
|
||||||
case '3':
|
case '3':
|
||||||
security_level_ = kSecurityLevelL3;
|
return kSecurityLevelL3;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
security_level_ = kSecurityLevelUnknown;
|
return kSecurityLevelUnknown;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return security_level_;
|
return kSecurityLevelUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
|
bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
|
||||||
@@ -159,7 +146,8 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
|
|||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return false;
|
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) {
|
if (OEMCrypto_SUCCESS != sts) {
|
||||||
return false;
|
return false;
|
||||||
@@ -183,7 +171,8 @@ bool CryptoSession::GetSystemId(uint32_t* system_id) {
|
|||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size);
|
OEMCryptoResult sts =
|
||||||
|
OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_);
|
||||||
|
|
||||||
if (OEMCrypto_SUCCESS != sts) {
|
if (OEMCrypto_SUCCESS != sts) {
|
||||||
return false;
|
return false;
|
||||||
@@ -211,7 +200,8 @@ bool CryptoSession::GetProvisioningId(std::string* provisioning_id) {
|
|||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size);
|
OEMCryptoResult sts =
|
||||||
|
OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_);
|
||||||
|
|
||||||
if (OEMCrypto_SUCCESS != sts) {
|
if (OEMCrypto_SUCCESS != sts) {
|
||||||
return false;
|
return false;
|
||||||
@@ -221,14 +211,15 @@ bool CryptoSession::GetProvisioningId(std::string* provisioning_id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType CryptoSession::Open() {
|
CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
|
||||||
LOGV("CryptoSession::Open: Lock");
|
LOGV("CryptoSession::Open: Lock");
|
||||||
AutoLock auto_lock(crypto_lock_);
|
AutoLock auto_lock(crypto_lock_);
|
||||||
if (!initialized_) return false;
|
if (!initialized_) return UNKNOWN_ERROR;
|
||||||
if (open_) return true;
|
if (open_) return NO_ERROR;
|
||||||
|
|
||||||
OEMCrypto_SESSION sid;
|
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) {
|
if (OEMCrypto_SUCCESS == sts) {
|
||||||
oec_session_id_ = static_cast<CryptoSessionId>(sid);
|
oec_session_id_ = static_cast<CryptoSessionId>(sid);
|
||||||
LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_);
|
LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_);
|
||||||
@@ -680,7 +671,11 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
|||||||
return true;
|
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);
|
OEMCryptoResult sts = OEMCrypto_GetRandom(random_data, data_length);
|
||||||
|
|
||||||
if (sts != OEMCrypto_SUCCESS) {
|
if (sts != OEMCrypto_SUCCESS) {
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
|||||||
|
|
||||||
std::string path;
|
std::string path;
|
||||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &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;
|
return false;
|
||||||
}
|
}
|
||||||
path.append(key_set_id);
|
path.append(key_set_id);
|
||||||
@@ -296,13 +296,13 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
|||||||
|
|
||||||
bool DeviceFiles::DeleteAllLicenses() {
|
bool DeviceFiles::DeleteAllLicenses() {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
LOGW("DeviceFiles::DeleteLicense: not initialized");
|
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string path;
|
std::string path;
|
||||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &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;
|
return false;
|
||||||
}
|
}
|
||||||
path.append(kWildcard);
|
path.append(kWildcard);
|
||||||
@@ -311,6 +311,21 @@ bool DeviceFiles::DeleteAllLicenses() {
|
|||||||
return file_->Remove(path);
|
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) {
|
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
LOGW("DeviceFiles::LicenseExists: not initialized");
|
LOGW("DeviceFiles::LicenseExists: not initialized");
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "policy_engine.h"
|
#include "policy_engine.h"
|
||||||
#include "properties.h"
|
#include "properties.h"
|
||||||
|
#include "privacy_crypto.h"
|
||||||
#include "string_conversions.h"
|
#include "string_conversions.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
|
|
||||||
@@ -20,6 +21,41 @@ std::string kDeviceNameKey = "device_name";
|
|||||||
std::string kProductNameKey = "product_name";
|
std::string kProductNameKey = "product_name";
|
||||||
std::string kBuildInfoKey = "build_info";
|
std::string kBuildInfoKey = "build_info";
|
||||||
std::string kDeviceIdKey = "device_id";
|
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 {
|
namespace wvcdm {
|
||||||
@@ -27,6 +63,8 @@ namespace wvcdm {
|
|||||||
// Protobuf generated classes.
|
// Protobuf generated classes.
|
||||||
using video_widevine_server::sdk::ClientIdentification;
|
using video_widevine_server::sdk::ClientIdentification;
|
||||||
using video_widevine_server::sdk::ClientIdentification_NameValue;
|
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;
|
||||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
|
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
|
||||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC;
|
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;
|
||||||
using video_widevine_server::sdk::License_KeyContainer;
|
using video_widevine_server::sdk::License_KeyContainer;
|
||||||
using video_widevine_server::sdk::LicenseError;
|
using video_widevine_server::sdk::LicenseError;
|
||||||
|
using video_widevine_server::sdk::SignedDeviceCertificate;
|
||||||
using video_widevine_server::sdk::SignedMessage;
|
using video_widevine_server::sdk::SignedMessage;
|
||||||
|
|
||||||
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||||
@@ -81,36 +120,65 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
|||||||
return key_array;
|
return key_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmLicense::CdmLicense() : session_(NULL) {}
|
|
||||||
|
|
||||||
CdmLicense::~CdmLicense() {}
|
|
||||||
|
|
||||||
bool CdmLicense::Init(const std::string& token, CryptoSession* session,
|
bool CdmLicense::Init(const std::string& token, CryptoSession* session,
|
||||||
PolicyEngine* policy_engine) {
|
PolicyEngine* policy_engine) {
|
||||||
if (token.size() == 0) return false;
|
if (token.size() == 0) {
|
||||||
if (session == NULL || !session->IsOpen()) return false;
|
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;
|
token_ = token;
|
||||||
session_ = session;
|
session_ = session;
|
||||||
policy_engine_ = policy_engine;
|
policy_engine_ = policy_engine;
|
||||||
|
initialized_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
const CdmAppParameterMap& app_parameters,
|
const CdmAppParameterMap& app_parameters,
|
||||||
|
const CdmSessionId& session_id,
|
||||||
CdmKeyMessage* signed_request,
|
CdmKeyMessage* signed_request,
|
||||||
std::string* server_url) {
|
std::string* server_url) {
|
||||||
if (!session_ || token_.empty()) {
|
if (!initialized_) {
|
||||||
|
LOGE("CdmLicense::PrepareKeyRequest: not initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (init_data.empty()) {
|
if (init_data.empty() && init_data_.empty()) {
|
||||||
LOGE("CdmLicense::PrepareKeyRequest : No init data provided;");
|
LOGE("CdmLicense::PrepareKeyRequest: empty init data provided");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (session_id.empty()) {
|
||||||
|
LOGE("CdmLicense::PrepareKeyRequest: empty session id provided");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!signed_request) {
|
if (!signed_request) {
|
||||||
LOGE("CdmLicense::PrepareKeyRequest : No signed request provided.");
|
LOGE("CdmLicense::PrepareKeyRequest: no signed request provided");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!server_url) {
|
||||||
|
LOGE("CdmLicense::PrepareKeyRequest: no server url provided");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id);
|
||||||
|
std::vector<uint8_t> 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?
|
// TODO(gmorgan): Request ID owned by session?
|
||||||
std::string request_id;
|
std::string request_id;
|
||||||
@@ -170,13 +238,71 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
|||||||
client_info->set_value(value);
|
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<uint8_t*>(&key[0]))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!session_->GetRandom(iv.size(), reinterpret_cast<uint8_t*>(&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
|
// Content Identification may be a cenc_id, a webm_id or a license_id
|
||||||
LicenseRequest_ContentIdentification* content_id =
|
LicenseRequest_ContentIdentification* content_id =
|
||||||
license_request.mutable_content_id();
|
license_request.mutable_content_id();
|
||||||
|
|
||||||
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
|
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
|
||||||
content_id->mutable_cenc_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) {
|
switch (license_type) {
|
||||||
case kLicenseTypeOffline:
|
case kLicenseTypeOffline:
|
||||||
@@ -187,7 +313,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
|
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
|
||||||
license_type);
|
license_type);
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -205,7 +331,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
|||||||
if (!session_->GenerateNonce(&nonce)) {
|
if (!session_->GenerateNonce(&nonce)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
license_request.set_key_control_nonce(UintToString(nonce));
|
license_request.set_key_control_nonce(nonce);
|
||||||
LOGD("PrepareKeyRequest: nonce=%u", nonce);
|
LOGD("PrepareKeyRequest: nonce=%u", nonce);
|
||||||
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
|
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,
|
bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
|
||||||
CdmKeyMessage* signed_request,
|
CdmKeyMessage* signed_request,
|
||||||
std::string* server_url) {
|
std::string* server_url) {
|
||||||
if (!session_) {
|
if (!initialized_) {
|
||||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: Invalid crypto session");
|
LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!signed_request) {
|
if (!signed_request) {
|
||||||
@@ -274,7 +400,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
|
|||||||
if (!session_->GenerateNonce(&nonce)) {
|
if (!session_->GenerateNonce(&nonce)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
license_request.set_key_control_nonce(UintToString(nonce));
|
license_request.set_key_control_nonce(nonce);
|
||||||
LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce);
|
LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce);
|
||||||
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
|
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
|
||||||
|
|
||||||
@@ -289,7 +415,8 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (license_request_signature.empty()) {
|
if (license_request_signature.empty()) {
|
||||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: empty license request"
|
LOGE(
|
||||||
|
"CdmLicense::PrepareKeyUpdateRequest: empty license request"
|
||||||
" signature");
|
" signature");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -307,28 +434,46 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
|
|||||||
|
|
||||||
CdmResponseType CdmLicense::HandleKeyResponse(
|
CdmResponseType CdmLicense::HandleKeyResponse(
|
||||||
const CdmKeyResponse& license_response) {
|
const CdmKeyResponse& license_response) {
|
||||||
if (!session_) {
|
if (!initialized_) {
|
||||||
|
LOGE("CdmLicense::HandleKeyResponse: not initialized");
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
if (license_response.empty()) {
|
if (license_response.empty()) {
|
||||||
LOGE("CdmLicense::HandleKeyResponse : Empty license response.");
|
LOGE("CdmLicense::HandleKeyResponse: empty license response");
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (service_certificate_response_pending_)
|
||||||
|
return CdmLicense::HandleServiceCertificateResponse(license_response);
|
||||||
|
|
||||||
SignedMessage signed_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) {
|
if (signed_response.type() == SignedMessage::ERROR) {
|
||||||
return HandleKeyErrorResponse(signed_response);
|
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;
|
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 (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_,
|
if (!session_->GenerateDerivedKeys(key_request_,
|
||||||
signed_response.session_key()))
|
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) {
|
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;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,9 +523,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||||
bool is_renewal,
|
bool is_renewal, const CdmKeyResponse& license_response) {
|
||||||
const CdmKeyResponse& license_response) {
|
if (!initialized_) {
|
||||||
if (!session_) {
|
LOGE("CdmLicense::HandleKeyUpdateResponse: not initialized");
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
if (license_response.empty()) {
|
if (license_response.empty()) {
|
||||||
@@ -401,7 +550,8 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
|||||||
|
|
||||||
License license;
|
License license;
|
||||||
if (!license.ParseFromString(signed_response.msg())) {
|
if (!license.ParseFromString(signed_response.msg())) {
|
||||||
LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse license"
|
LOGE(
|
||||||
|
"CdmLicense::HandleKeyUpdateResponse: Unable to parse license"
|
||||||
" from signed message");
|
" from signed message");
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
@@ -423,15 +573,12 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
|||||||
// merge from Eureka)
|
// merge from Eureka)
|
||||||
policy_engine_->UpdateLicense(license);
|
policy_engine_->UpdateLicense(license);
|
||||||
|
|
||||||
if (!is_renewal)
|
if (!is_renewal) return KEY_ADDED;
|
||||||
return KEY_ADDED;
|
|
||||||
|
|
||||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||||
|
|
||||||
if (session_->RefreshKeys(signed_response.msg(),
|
if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(),
|
||||||
signed_response.signature(),
|
key_array.size(), &key_array[0])) {
|
||||||
key_array.size(),
|
|
||||||
&key_array[0])) {
|
|
||||||
return KEY_ADDED;
|
return KEY_ADDED;
|
||||||
} else {
|
} else {
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
@@ -439,13 +586,14 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CdmLicense::RestoreOfflineLicense(
|
bool CdmLicense::RestoreOfflineLicense(
|
||||||
CdmKeyMessage& license_request,
|
CdmKeyMessage& license_request, CdmKeyResponse& license_response,
|
||||||
CdmKeyResponse& license_response,
|
|
||||||
CdmKeyResponse& license_renewal_response) {
|
CdmKeyResponse& license_renewal_response) {
|
||||||
|
|
||||||
if (license_request.empty() || license_response.empty()) {
|
if (license_request.empty() || license_response.empty()) {
|
||||||
LOGE("CdmLicense::RestoreOfflineLicense: key_request or response empty: "
|
LOGE(
|
||||||
"%u %u", license_request.size(), license_response.size());
|
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
|
||||||
|
"%u %u",
|
||||||
|
license_request.size(), license_response.size());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,36 +604,139 @@ bool CdmLicense::RestoreOfflineLicense(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
|
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
|
||||||
LOGE("CdmLicense::RestoreOfflineLicense: license request type: expected = "
|
LOGE(
|
||||||
|
"CdmLicense::RestoreOfflineLicense: license request type: expected = "
|
||||||
"%d, actual = %d",
|
"%d, actual = %d",
|
||||||
SignedMessage::LICENSE_REQUEST,
|
SignedMessage::LICENSE_REQUEST, signed_request.type());
|
||||||
signed_request.type());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Properties::use_certificates_as_identification()) {
|
if (Properties::use_certificates_as_identification()) {
|
||||||
key_request_ = signed_request.msg();
|
key_request_ = signed_request.msg();
|
||||||
}
|
} else {
|
||||||
else {
|
if (!session_->GenerateDerivedKeys(signed_request.msg())) return false;
|
||||||
if (!session_->GenerateDerivedKeys(signed_request.msg()))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType sts = HandleKeyResponse(license_response);
|
CdmResponseType sts = HandleKeyResponse(license_response);
|
||||||
|
|
||||||
if (sts != KEY_ADDED)
|
if (sts != KEY_ADDED) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!license_renewal_response.empty()) {
|
if (!license_renewal_response.empty()) {
|
||||||
sts = HandleKeyUpdateResponse(true, license_renewal_response);
|
sts = HandleKeyUpdateResponse(true, license_renewal_response);
|
||||||
|
|
||||||
if (sts != KEY_ADDED)
|
if (sts != KEY_ADDED) return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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<uint8_t> 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(
|
CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||||
const SignedMessage& signed_message) {
|
const SignedMessage& signed_message) {
|
||||||
|
|
||||||
@@ -496,14 +747,14 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (license_error.error_code()) {
|
switch (license_error.error_code()) {
|
||||||
case LicenseError::INVALID_CREDENTIALS:
|
case LicenseError::INVALID_DEVICE_CERTIFICATE:
|
||||||
return NEED_PROVISIONING;
|
return NEED_PROVISIONING;
|
||||||
case LicenseError::REVOKED_CREDENTIALS:
|
case LicenseError::REVOKED_DEVICE_CERTIFICATE:
|
||||||
return DEVICE_REVOKED;
|
return DEVICE_REVOKED;
|
||||||
case LicenseError::SERVICE_UNAVAILABLE:
|
case LicenseError::SERVICE_UNAVAILABLE:
|
||||||
default:
|
default:
|
||||||
LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d",
|
LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d",
|
||||||
license_error.error_code());
|
license_error.error_code());
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,17 @@
|
|||||||
// inline to avoid having to hardcode the import path. This is a temporary
|
// 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.
|
// 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";
|
syntax = "proto2";
|
||||||
|
|
||||||
package video_widevine_server.sdk;
|
package video_widevine_server.sdk;
|
||||||
@@ -116,7 +127,8 @@ message License {
|
|||||||
// |key_control| is documented here:
|
// |key_control| is documented here:
|
||||||
// https://docs.google.com/a/google.com/document/d/17eDxzzGpPc2qSm7zW68_5ensuxbHErYCvD3IxSKETRo/edit#
|
// https://docs.google.com/a/google.com/document/d/17eDxzzGpPc2qSm7zW68_5ensuxbHErYCvD3IxSKETRo/edit#
|
||||||
// If present, the key control must be communicated to the secure
|
// 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 key_control_block = 1;
|
||||||
optional bytes iv = 2;
|
optional bytes iv = 2;
|
||||||
}
|
}
|
||||||
@@ -140,6 +152,16 @@ message License {
|
|||||||
}
|
}
|
||||||
optional CGMS cgms_flags = 2 [default = CGMS_NONE];
|
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 id = 1;
|
||||||
optional bytes iv = 2;
|
optional bytes iv = 2;
|
||||||
optional bytes key = 3;
|
optional bytes key = 3;
|
||||||
@@ -148,6 +170,7 @@ message License {
|
|||||||
optional OutputProtection required_protection = 6;
|
optional OutputProtection required_protection = 6;
|
||||||
optional OutputProtection requested_protection = 7;
|
optional OutputProtection requested_protection = 7;
|
||||||
optional KeyControl key_control = 8;
|
optional KeyControl key_control = 8;
|
||||||
|
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional LicenseIdentification id = 1;
|
optional LicenseIdentification id = 1;
|
||||||
@@ -194,20 +217,32 @@ message LicenseRequest {
|
|||||||
|
|
||||||
// The client_id provides information authenticating the calling device. It
|
// The client_id provides information authenticating the calling device. It
|
||||||
// contains the Widevine keybox token that was installed on the device at the
|
// 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 ClientIdentification client_id = 1;
|
||||||
optional ContentIdentification content_id = 2;
|
optional ContentIdentification content_id = 2;
|
||||||
optional RequestType type = 3;
|
optional RequestType type = 3;
|
||||||
optional int64 request_time = 4;
|
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];
|
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 {
|
message LicenseError {
|
||||||
enum Error {
|
enum Error {
|
||||||
INVALID_CREDENTIALS = 1;
|
// The device credentials are invalid. The device must re-provision.
|
||||||
REVOKED_CREDENTIALS = 2;
|
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;
|
SERVICE_UNAVAILABLE = 3;
|
||||||
}
|
}
|
||||||
optional Error error_code = 1;
|
optional Error error_code = 1;
|
||||||
@@ -218,6 +253,8 @@ message SignedMessage {
|
|||||||
LICENSE_REQUEST = 1;
|
LICENSE_REQUEST = 1;
|
||||||
LICENSE = 2;
|
LICENSE = 2;
|
||||||
ERROR = 3;
|
ERROR = 3;
|
||||||
|
SERVICE_CERTIFICATE_REQUEST = 4;
|
||||||
|
SERVICE_CERTIFICATE = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional MessageType type = 1;
|
optional MessageType type = 1;
|
||||||
@@ -230,7 +267,10 @@ message SignedMessage {
|
|||||||
message SessionInit {
|
message SessionInit {
|
||||||
optional bytes session_id = 1;
|
optional bytes session_id = 1;
|
||||||
optional bytes purchase_id = 2;
|
optional bytes purchase_id = 2;
|
||||||
|
// master_signing_key should be 128 bits in length.
|
||||||
optional bytes master_signing_key = 3;
|
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 bytes signing_key = 4;
|
||||||
optional int64 license_start_time = 5;
|
optional int64 license_start_time = 5;
|
||||||
}
|
}
|
||||||
@@ -267,7 +307,7 @@ message ProvisioningResponse {
|
|||||||
optional bytes device_rsa_key = 1;
|
optional bytes device_rsa_key = 1;
|
||||||
// Initialization vector used to encrypt device_rsa_key. Required.
|
// Initialization vector used to encrypt device_rsa_key. Required.
|
||||||
optional bytes device_rsa_key_iv = 2;
|
optional bytes device_rsa_key_iv = 2;
|
||||||
// Serialized DeviceCertificate. Required.
|
// Serialized SignedDeviceCertificate. Required.
|
||||||
optional bytes device_certificate = 3;
|
optional bytes device_certificate = 3;
|
||||||
// Nonce value matching nonce in ProvisioningRequest. Required.
|
// Nonce value matching nonce in ProvisioningRequest. Required.
|
||||||
optional bytes nonce = 4;
|
optional bytes nonce = 4;
|
||||||
@@ -288,10 +328,13 @@ message SignedProvisioningMessage {
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
// Author: tinskip@google.com (Thomas Inskip)
|
// 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:
|
// Description:
|
||||||
// ClientIdentification message used by provisioning and license protocols.
|
// ClientIdentification messages used by provisioning and license protocols.
|
||||||
|
|
||||||
option java_outer_classname = "ClientIdentificationProtos";
|
|
||||||
|
|
||||||
// ClientIdentification message used to authenticate the client device.
|
// ClientIdentification message used to authenticate the client device.
|
||||||
message ClientIdentification {
|
message ClientIdentification {
|
||||||
@@ -312,3 +355,138 @@ message ClientIdentification {
|
|||||||
// Optional client information name/value pairs.
|
// Optional client information name/value pairs.
|
||||||
repeated NameValue client_info = 3;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
665
libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp
Normal file
665
libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp
Normal file
@@ -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 <dlfcn.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#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<OEMCrypto_SESSION, LevelSession>::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<char*>(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<OEMCrypto_SESSION, LevelSession> 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
|
||||||
51
libwvdrmengine/cdm/core/src/oemcrypto_adapter_static.cpp
Normal file
51
libwvdrmengine/cdm/core/src/oemcrypto_adapter_static.cpp
Normal file
@@ -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
|
||||||
201
libwvdrmengine/cdm/core/src/privacy_crypto.cpp
Normal file
201
libwvdrmengine/cdm/core/src/privacy_crypto.cpp
Normal file
@@ -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<const uint8_t*>(&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<const uint8_t*>(&in[0]),
|
||||||
|
reinterpret_cast<uint8_t*>(&out[0]), in.size(), &key_,
|
||||||
|
reinterpret_cast<uint8_t*>(&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<char*>(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<int>(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<unsigned char*>(
|
||||||
|
reinterpret_cast<const unsigned char*>(clear_message.data())),
|
||||||
|
reinterpret_cast<unsigned char*>(&(*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<int>(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<unsigned char*>(
|
||||||
|
reinterpret_cast<const unsigned char*>(signature.data())),
|
||||||
|
reinterpret_cast<unsigned char*>(&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<const unsigned char*>(message.data()), message.size(),
|
||||||
|
reinterpret_cast<unsigned char*>(&message_digest[0]));
|
||||||
|
|
||||||
|
// Verify PSS padding.
|
||||||
|
if (RSA_verify_PKCS1_PSS(
|
||||||
|
key_, reinterpret_cast<const unsigned char*>(message_digest.data()),
|
||||||
|
EVP_sha1(),
|
||||||
|
reinterpret_cast<const unsigned char*>(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
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
#include "properties.h"
|
#include "log.h"
|
||||||
#include "properties_configuration.h"
|
#include "properties_configuration.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
bool Properties::begin_license_usage_when_received_;
|
bool Properties::begin_license_usage_when_received_;
|
||||||
bool Properties::require_explicit_renew_request_;
|
bool Properties::require_explicit_renew_request_;
|
||||||
bool Properties::oem_crypto_use_secure_buffers_;
|
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::oem_crypto_use_userspace_buffers_;
|
||||||
bool Properties::use_certificates_as_identification_;
|
bool Properties::use_certificates_as_identification_;
|
||||||
bool Properties::extract_pssh_data_;
|
bool Properties::extract_pssh_data_;
|
||||||
|
bool Properties::decrypt_with_empty_session_support_;
|
||||||
|
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
|
||||||
|
|
||||||
void Properties::Init() {
|
void Properties::Init() {
|
||||||
begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived;
|
begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived;
|
||||||
@@ -23,6 +24,73 @@ void Properties::Init() {
|
|||||||
use_certificates_as_identification_ =
|
use_certificates_as_identification_ =
|
||||||
kPropertyUseCertificatesAsIdentification;
|
kPropertyUseCertificatesAsIdentification;
|
||||||
extract_pssh_data_ = kExtractPsshData;
|
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<CdmClientPropertySetMap::iterator, bool> result =
|
||||||
|
session_property_set_->insert(
|
||||||
|
std::pair<const CdmSessionId,
|
||||||
|
const CdmClientPropertySet*>(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<uint8_t> 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<uint8_t>();
|
||||||
|
}
|
||||||
|
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
|
} // namespace wvcdm
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class WvCdmEngineTest : public testing::Test {
|
|||||||
public:
|
public:
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
cdm_engine_.reset(new CdmEngine());
|
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() {
|
virtual void TearDown() {
|
||||||
|
|||||||
@@ -78,9 +78,11 @@ TEST_F(LicenseTest, DISABLED_PrepareKeyRequest) {
|
|||||||
std::string signed_request;
|
std::string signed_request;
|
||||||
CdmAppParameterMap app_parameters;
|
CdmAppParameterMap app_parameters;
|
||||||
std::string server_url;
|
std::string server_url;
|
||||||
|
CdmSessionId session_id;
|
||||||
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
||||||
kLicenseTypeStreaming,
|
kLicenseTypeStreaming,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
|
session_id,
|
||||||
&signed_request,
|
&signed_request,
|
||||||
&server_url);
|
&server_url);
|
||||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||||
@@ -90,10 +92,12 @@ TEST_F(LicenseTest, DISABLED_PrepareKeyRequest) {
|
|||||||
TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
|
TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
|
||||||
std::string signed_request;
|
std::string signed_request;
|
||||||
CdmAppParameterMap app_parameters;
|
CdmAppParameterMap app_parameters;
|
||||||
|
CdmSessionId session_id;
|
||||||
std::string server_url;
|
std::string server_url;
|
||||||
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
||||||
kLicenseTypeStreaming,
|
kLicenseTypeStreaming,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
|
session_id,
|
||||||
&signed_request,
|
&signed_request,
|
||||||
&server_url);
|
&server_url);
|
||||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||||
@@ -104,10 +108,12 @@ TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
|
|||||||
TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) {
|
TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) {
|
||||||
std::string signed_request;
|
std::string signed_request;
|
||||||
CdmAppParameterMap app_parameters;
|
CdmAppParameterMap app_parameters;
|
||||||
|
CdmSessionId session_id;
|
||||||
std::string server_url;
|
std::string server_url;
|
||||||
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
||||||
kLicenseTypeStreaming,
|
kLicenseTypeStreaming,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
|
session_id,
|
||||||
&signed_request,
|
&signed_request,
|
||||||
&server_url);
|
&server_url);
|
||||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ void UrlRequest::AppendChunkToUpload(const std::string& data) {
|
|||||||
|
|
||||||
// buffer to store length of chunk
|
// buffer to store length of chunk
|
||||||
memset(buffer_, 0, kHttpBufferSize);
|
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
|
request_.append(buffer_); // appends size of chunk
|
||||||
LOGD("...\r\n%s", request_.c_str());
|
LOGD("...\r\n%s", request_.c_str());
|
||||||
request_.append(data);
|
request_.append(data);
|
||||||
@@ -61,7 +61,7 @@ void UrlRequest::ConcatenateChunkedResponse(const std::string http_response,
|
|||||||
// processes chunked encoding
|
// processes chunked encoding
|
||||||
size_t chunk_size = 0;
|
size_t chunk_size = 0;
|
||||||
size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size();
|
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()) {
|
if (chunk_size > http_response.size()) {
|
||||||
// precaution, in case we misread chunk size
|
// precaution, in case we misread chunk size
|
||||||
LOGE("invalid chunk size %u", 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
|
// Search for next chunk
|
||||||
chunk_size_pos = chunk_pos + chunk_size + kCrLf.size();
|
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()) {
|
if (chunk_size > http_response.size()) {
|
||||||
// precaution, in case we misread chunk size
|
// precaution, in case we misread chunk size
|
||||||
LOGE("invalid chunk size %u", chunk_size);
|
LOGE("invalid chunk size %u", chunk_size);
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ const bool kPropertyUseCertificatesAsIdentification = true;
|
|||||||
// once all platforms support it (b/9465346)
|
// once all platforms support it (b/9465346)
|
||||||
const bool kExtractPsshData = true;
|
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
|
} // namespace wvcdm
|
||||||
|
|
||||||
#endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_
|
#endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
|
class CdmClientPropertySet;
|
||||||
class CdmEngine;
|
class CdmEngine;
|
||||||
class WvCdmEventListener;
|
class WvCdmEventListener;
|
||||||
|
|
||||||
@@ -18,8 +19,10 @@ class WvContentDecryptionModule {
|
|||||||
virtual ~WvContentDecryptionModule();
|
virtual ~WvContentDecryptionModule();
|
||||||
|
|
||||||
// Session related methods
|
// Session related methods
|
||||||
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
virtual CdmResponseType OpenSession(
|
||||||
CdmSessionId* session_id);
|
const CdmKeySystem& key_system,
|
||||||
|
const CdmClientPropertySet* property_set,
|
||||||
|
CdmSessionId* session_id);
|
||||||
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
||||||
|
|
||||||
// Construct a valid license request.
|
// Construct a valid license request.
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const char kBasePathPrefix[] = "/data/mediadrm/IDM";
|
const char kBasePathPrefix[] = "/data/mediadrm/IDM";
|
||||||
const char kL1Dir[] = "/L1";
|
const char kL1Dir[] = "/L1/";
|
||||||
const char kL2Dir[] = "/L2";
|
const char kL2Dir[] = "/L2/";
|
||||||
const char kL3Dir[] = "/L3";
|
const char kL3Dir[] = "/L3/";
|
||||||
const char kFactoryKeyboxPath[] = "/factory/wv.keys";
|
const char kFactoryKeyboxPath[] = "/factory/wv.keys";
|
||||||
|
|
||||||
bool GetAndroidProperty(const char* key, std::string* value) {
|
bool GetAndroidProperty(const char* key, std::string* value) {
|
||||||
@@ -118,4 +118,13 @@ bool Properties::GetFactoryKeyboxPath(std::string* keybox) {
|
|||||||
return true;
|
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
|
} // namespace wvcdm
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ WvContentDecryptionModule::WvContentDecryptionModule()
|
|||||||
WvContentDecryptionModule::~WvContentDecryptionModule() {}
|
WvContentDecryptionModule::~WvContentDecryptionModule() {}
|
||||||
|
|
||||||
CdmResponseType WvContentDecryptionModule::OpenSession(
|
CdmResponseType WvContentDecryptionModule::OpenSession(
|
||||||
const CdmKeySystem& key_system, CdmSessionId* session_id) {
|
const CdmKeySystem& key_system,
|
||||||
return cdm_engine_->OpenSession(key_system, session_id);
|
const CdmClientPropertySet* property_set,
|
||||||
|
CdmSessionId* session_id) {
|
||||||
|
return cdm_engine_->OpenSession(key_system, property_set, session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType WvContentDecryptionModule::CloseSession(
|
CdmResponseType WvContentDecryptionModule::CloseSession(
|
||||||
|
|||||||
@@ -6,9 +6,13 @@
|
|||||||
|
|
||||||
#include "config_test_env.h"
|
#include "config_test_env.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "device_files.h"
|
||||||
|
#include "file_store.h"
|
||||||
#include "license_request.h"
|
#include "license_request.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "OEMCryptoCENC.h"
|
#include "OEMCryptoCENC.h"
|
||||||
|
#include "oemcrypto_adapter.h"
|
||||||
|
#include "properties.h"
|
||||||
#include "string_conversions.h"
|
#include "string_conversions.h"
|
||||||
#include "url_request.h"
|
#include "url_request.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
@@ -186,6 +190,38 @@ SubSampleInfo partial_offset_single_encrypted_sub_sample = {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
||||||
|
public:
|
||||||
|
TestWvCdmClientPropertySet()
|
||||||
|
: service_certificate_(std::vector<uint8_t>()),
|
||||||
|
use_privacy_mode_(false) {}
|
||||||
|
virtual ~TestWvCdmClientPropertySet() {}
|
||||||
|
|
||||||
|
virtual std::string security_level() const { return security_level_; }
|
||||||
|
virtual std::vector<uint8_t> 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<uint8_t>& 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<uint8_t> service_certificate_;
|
||||||
|
bool use_privacy_mode_;
|
||||||
|
};
|
||||||
|
|
||||||
class TestWvCdmEventListener : public WvCdmEventListener {
|
class TestWvCdmEventListener : public WvCdmEventListener {
|
||||||
public:
|
public:
|
||||||
@@ -337,7 +373,7 @@ class WvCdmDecryptionTest
|
|||||||
public ::testing::WithParamInterface<SubSampleInfo*> {};
|
public ::testing::WithParamInterface<SubSampleInfo*> {};
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
||||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||||
std::string provisioning_server_url;
|
std::string provisioning_server_url;
|
||||||
|
|
||||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||||
@@ -352,7 +388,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
|
||||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||||
std::string provisioning_server_url;
|
std::string provisioning_server_url;
|
||||||
|
|
||||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||||
@@ -376,15 +412,108 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
|
|||||||
decryptor_.CloseSession(session_id_);
|
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<int>(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<uint8_t> 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) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||||
GetKeyRequestResponse(g_license_server, g_client_auth, 200);
|
GetKeyRequestResponse(g_license_server, g_client_auth, 200);
|
||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) {
|
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);
|
std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id);
|
||||||
GenerateKeyRequest(g_key_system, wrong_message, kLicenseTypeStreaming);
|
GenerateKeyRequest(g_key_system, wrong_message, kLicenseTypeStreaming);
|
||||||
@@ -393,21 +522,21 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, AddSteamingKeyTest) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
|
|
||||||
@@ -416,13 +545,13 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
|
|||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
|
|
||||||
session_id_.clear();
|
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));
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
|
||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
|
|
||||||
@@ -432,7 +561,7 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
|
|||||||
|
|
||||||
session_id_.clear();
|
session_id_.clear();
|
||||||
key_set_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));
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
|
||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
|
|
||||||
@@ -444,7 +573,7 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
|
|
||||||
@@ -454,7 +583,7 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
|
|||||||
|
|
||||||
session_id_.clear();
|
session_id_.clear();
|
||||||
key_set_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_;
|
CdmSessionId restore_session_id = session_id_;
|
||||||
TestWvCdmEventListener listener;
|
TestWvCdmEventListener listener;
|
||||||
EXPECT_TRUE(decryptor_.AttachEventListener(restore_session_id, &listener));
|
EXPECT_TRUE(decryptor_.AttachEventListener(restore_session_id, &listener));
|
||||||
@@ -474,7 +603,7 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
|
|
||||||
@@ -484,7 +613,7 @@ TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
|
|
||||||
@@ -494,7 +623,7 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
|
|
||||||
@@ -564,7 +693,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
|
|
||||||
@@ -585,7 +714,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) {
|
|||||||
|
|
||||||
TEST_P(WvCdmDecryptionTest, DecryptionTest) {
|
TEST_P(WvCdmDecryptionTest, DecryptionTest) {
|
||||||
SubSampleInfo* data = GetParam();
|
SubSampleInfo* data = GetParam();
|
||||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||||
if (data->retrieve_key) {
|
if (data->retrieve_key) {
|
||||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
@@ -616,7 +745,7 @@ INSTANTIATE_TEST_CASE_P(
|
|||||||
&partial_single_encrypted_sub_sample));
|
&partial_single_encrypted_sub_sample));
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
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) {
|
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);
|
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||||
CdmKeySetId key_set_id = key_set_id_;
|
CdmKeySetId key_set_id = key_set_id_;
|
||||||
@@ -677,7 +806,7 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_RestoreOfflineLicenseDecryptionTest) {
|
|||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
|
|
||||||
session_id_.clear();
|
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));
|
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
|
||||||
/*
|
/*
|
||||||
// key 1, encrypted, 256b
|
// key 1, encrypted, 256b
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libgmock \
|
libgmock \
|
||||||
libgtest \
|
libgtest \
|
||||||
libgtest_main \
|
libgtest_main \
|
||||||
libwvwrapper \
|
|
||||||
libwvlevel3 \
|
libwvlevel3 \
|
||||||
libcdm_utils \
|
libcdm_utils \
|
||||||
libprotobuf-cpp-2.3.0-lite
|
libprotobuf-cpp-2.3.0-lite
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ enum {
|
|||||||
kErrorCDMGeneric = ERROR_DRM_VENDOR_MIN + 1,
|
kErrorCDMGeneric = ERROR_DRM_VENDOR_MIN + 1,
|
||||||
kErrorUnsupportedCrypto = ERROR_DRM_VENDOR_MIN + 2,
|
kErrorUnsupportedCrypto = ERROR_DRM_VENDOR_MIN + 2,
|
||||||
kErrorExpectedUnencrypted = ERROR_DRM_VENDOR_MIN + 3,
|
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
|
// Used by crypto test mode
|
||||||
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -23,7 +23,6 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libgmock \
|
libgmock \
|
||||||
libgmock_main \
|
libgmock_main \
|
||||||
libgtest \
|
libgtest \
|
||||||
libwvwrapper \
|
|
||||||
libwvlevel3 \
|
libwvlevel3 \
|
||||||
libprotobuf-cpp-2.3.0-lite \
|
libprotobuf-cpp-2.3.0-lite \
|
||||||
libwvdrmcryptoplugin \
|
libwvdrmcryptoplugin \
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "cdm_client_property_set.h"
|
||||||
#include "media/drm/DrmAPI.h"
|
#include "media/drm/DrmAPI.h"
|
||||||
#include "media/stagefright/foundation/ABase.h"
|
#include "media/stagefright/foundation/ABase.h"
|
||||||
#include "media/stagefright/foundation/AString.h"
|
#include "media/stagefright/foundation/AString.h"
|
||||||
@@ -157,6 +158,45 @@ class WVDrmPlugin : public android::DrmPlugin,
|
|||||||
OEMCrypto_Algorithm mMacAlgorithm;
|
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<uint8_t>& serviceCertificate) {
|
||||||
|
mServiceCertificate = serviceCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::vector<uint8_t> service_certificate() const {
|
||||||
|
return mServiceCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DISALLOW_EVIL_CONSTRUCTORS(WVClientPropertySet);
|
||||||
|
|
||||||
|
std::string mSecurityLevel;
|
||||||
|
bool mUsePrivacyMode;
|
||||||
|
std::vector<uint8_t> mServiceCertificate;
|
||||||
|
} mPropertySet;
|
||||||
|
|
||||||
WvContentDecryptionModule* mCDM;
|
WvContentDecryptionModule* mCDM;
|
||||||
WVGenericCryptoInterface* mCrypto;
|
WVGenericCryptoInterface* mCrypto;
|
||||||
map<CdmSessionId, CryptoSession> mCryptoSessions;
|
map<CdmSessionId, CryptoSession> mCryptoSessions;
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ using namespace android;
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace wvcdm;
|
using namespace wvcdm;
|
||||||
|
|
||||||
|
static const char* const kResetSecurityLevel = "";
|
||||||
|
static const char* const kEnable = "enable";
|
||||||
|
static const char* const kDisable = "disable";
|
||||||
|
|
||||||
WVDrmPlugin::WVDrmPlugin(WvContentDecryptionModule* cdm,
|
WVDrmPlugin::WVDrmPlugin(WvContentDecryptionModule* cdm,
|
||||||
WVGenericCryptoInterface* crypto)
|
WVGenericCryptoInterface* crypto)
|
||||||
: mCDM(cdm), mCrypto(crypto) {}
|
: mCDM(cdm), mCrypto(crypto) {}
|
||||||
@@ -51,10 +55,11 @@ WVDrmPlugin::~WVDrmPlugin() {
|
|||||||
|
|
||||||
status_t WVDrmPlugin::openSession(Vector<uint8_t>& sessionId) {
|
status_t WVDrmPlugin::openSession(Vector<uint8_t>& sessionId) {
|
||||||
CdmSessionId cdmSessionId;
|
CdmSessionId cdmSessionId;
|
||||||
CdmResponseType res = mCDM->OpenSession("com.widevine", &cdmSessionId);
|
CdmResponseType res = mCDM->OpenSession("com.widevine", &mPropertySet,
|
||||||
|
&cdmSessionId);
|
||||||
|
|
||||||
if (!isCdmResponseTypeSuccess(res)) {
|
if (!isCdmResponseTypeSuccess(res)) {
|
||||||
return mapAndNotifyOfCdmResponseType(sessionId, res);
|
return mapCdmResponseType(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
@@ -347,16 +352,22 @@ status_t WVDrmPlugin::getPropertyString(const String8& name,
|
|||||||
} else if (name == "algorithms") {
|
} else if (name == "algorithms") {
|
||||||
value = "AES/CBC/NoPadding,HmacSHA256";
|
value = "AES/CBC/NoPadding,HmacSHA256";
|
||||||
} else if (name == "securityLevel") {
|
} else if (name == "securityLevel") {
|
||||||
CdmQueryMap status;
|
string requestedLevel = mPropertySet.security_level();
|
||||||
CdmResponseType res = mCDM->QueryStatus(&status);
|
|
||||||
if (!isCdmResponseTypeSuccess(res)) {
|
if (requestedLevel.length() > 0) {
|
||||||
ALOGE("Error querying CDM status: %u", res);
|
value = requestedLevel.c_str();
|
||||||
return mapCdmResponseType(res);
|
} else {
|
||||||
} else if (!status.count(QUERY_KEY_SECURITY_LEVEL)) {
|
CdmQueryMap status;
|
||||||
ALOGE("CDM did not report a security level");
|
CdmResponseType res = mCDM->QueryStatus(&status);
|
||||||
return kErrorCDMGeneric;
|
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") {
|
} else if (name == "systemId") {
|
||||||
CdmQueryMap status;
|
CdmQueryMap status;
|
||||||
CdmResponseType res = mCDM->QueryStatus(&status);
|
CdmResponseType res = mCDM->QueryStatus(&status);
|
||||||
@@ -368,8 +379,14 @@ status_t WVDrmPlugin::getPropertyString(const String8& name,
|
|||||||
return kErrorCDMGeneric;
|
return kErrorCDMGeneric;
|
||||||
}
|
}
|
||||||
value = status[QUERY_KEY_SYSTEM_ID].c_str();
|
value = status[QUERY_KEY_SYSTEM_ID].c_str();
|
||||||
|
} else if (name == "privacyMode") {
|
||||||
|
if (mPropertySet.use_privacy_mode()) {
|
||||||
|
value = kEnable;
|
||||||
|
} else {
|
||||||
|
value = kDisable;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ALOGE("App requested unknown property %s", name.string());
|
ALOGE("App requested unknown string property %s", name.string());
|
||||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,8 +431,12 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name,
|
|||||||
value.clear();
|
value.clear();
|
||||||
value.appendArray(reinterpret_cast<const uint8_t*>(uniqueId.data()),
|
value.appendArray(reinterpret_cast<const uint8_t*>(uniqueId.data()),
|
||||||
uniqueId.size());
|
uniqueId.size());
|
||||||
|
} else if (name == "serviceCertificate") {
|
||||||
|
vector<uint8_t> cert = mPropertySet.service_certificate();
|
||||||
|
value.clear();
|
||||||
|
value.appendArray(&cert[0], cert.size());
|
||||||
} else {
|
} 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;
|
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,12 +445,48 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name,
|
|||||||
|
|
||||||
status_t WVDrmPlugin::setPropertyString(const String8& name,
|
status_t WVDrmPlugin::setPropertyString(const String8& name,
|
||||||
const String8& value) {
|
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,
|
status_t WVDrmPlugin::setPropertyByteArray(const String8& name,
|
||||||
const Vector<uint8_t>& value) {
|
const Vector<uint8_t>& value) {
|
||||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
if (name == "serviceCertificate") {
|
||||||
|
vector<uint8_t> 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<uint8_t>& sessionId,
|
status_t WVDrmPlugin::setCipherAlgorithm(const Vector<uint8_t>& sessionId,
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libgmock \
|
libgmock \
|
||||||
libgmock_main \
|
libgmock_main \
|
||||||
libgtest \
|
libgtest \
|
||||||
libwvwrapper \
|
|
||||||
libwvlevel3 \
|
libwvlevel3 \
|
||||||
libprotobuf-cpp-2.3.0-lite \
|
libprotobuf-cpp-2.3.0-lite \
|
||||||
libwvdrmdrmplugin \
|
libwvdrmdrmplugin \
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "cdm_client_property_set.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "media/stagefright/foundation/ABase.h"
|
#include "media/stagefright/foundation/ABase.h"
|
||||||
@@ -23,7 +24,8 @@ using namespace wvdrm;
|
|||||||
|
|
||||||
class MockCDM : public WvContentDecryptionModule {
|
class MockCDM : public WvContentDecryptionModule {
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD2(OpenSession, CdmResponseType(const CdmKeySystem&,
|
MOCK_METHOD3(OpenSession, CdmResponseType(const CdmKeySystem&,
|
||||||
|
const CdmClientPropertySet*,
|
||||||
CdmSessionId*));
|
CdmSessionId*));
|
||||||
|
|
||||||
MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&));
|
MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&));
|
||||||
@@ -133,8 +135,8 @@ TEST_F(WVDrmPluginTest, OpensSessions) {
|
|||||||
StrictMock<MockCrypto> crypto;
|
StrictMock<MockCrypto> crypto;
|
||||||
WVDrmPlugin plugin(&cdm, &crypto);
|
WVDrmPlugin plugin(&cdm, &crypto);
|
||||||
|
|
||||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<1>(cdmSessionId),
|
.WillOnce(DoAll(SetArgPointee<2>(cdmSessionId),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
// Provide expected behavior when plugin requests session control info
|
// Provide expected behavior when plugin requests session control info
|
||||||
@@ -633,9 +635,9 @@ TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) {
|
|||||||
bool match;
|
bool match;
|
||||||
|
|
||||||
// Provide expected behavior to support session creation
|
// Provide expected behavior to support session creation
|
||||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
|
||||||
.Times(AtLeast(1))
|
.Times(AtLeast(1))
|
||||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||||
@@ -719,9 +721,9 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Provide expected behavior to support session creation
|
// Provide expected behavior to support session creation
|
||||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
|
||||||
.Times(AtLeast(1))
|
.Times(AtLeast(1))
|
||||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||||
@@ -786,9 +788,9 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Provide expected behavior to support session creation
|
// Provide expected behavior to support session creation
|
||||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
|
||||||
.Times(AtLeast(1))
|
.Times(AtLeast(1))
|
||||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||||
@@ -855,9 +857,9 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Provide expected behavior to support session creation
|
// Provide expected behavior to support session creation
|
||||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
|
||||||
.Times(AtLeast(1))
|
.Times(AtLeast(1))
|
||||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||||
@@ -934,9 +936,9 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Provide expected behavior to support session creation
|
// Provide expected behavior to support session creation
|
||||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
|
||||||
.Times(AtLeast(1))
|
.Times(AtLeast(1))
|
||||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||||
@@ -977,9 +979,9 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) {
|
|||||||
.Times(1);
|
.Times(1);
|
||||||
|
|
||||||
// Provide expected behavior to support session creation
|
// Provide expected behavior to support session creation
|
||||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
|
||||||
.Times(AtLeast(1))
|
.Times(AtLeast(1))
|
||||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||||
@@ -1014,10 +1016,10 @@ TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) {
|
|||||||
CdmSessionId cdmSessionId1(sessionIdRaw1, sessionIdRaw1 + kSessionIdSize);
|
CdmSessionId cdmSessionId1(sessionIdRaw1, sessionIdRaw1 + kSessionIdSize);
|
||||||
CdmSessionId cdmSessionId2(sessionIdRaw2, sessionIdRaw2 + kSessionIdSize);
|
CdmSessionId cdmSessionId2(sessionIdRaw2, sessionIdRaw2 + kSessionIdSize);
|
||||||
|
|
||||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<1>(cdmSessionId1),
|
.WillOnce(DoAll(SetArgPointee<2>(cdmSessionId1),
|
||||||
Return(wvcdm::NO_ERROR)))
|
Return(wvcdm::NO_ERROR)))
|
||||||
.WillOnce(DoAll(SetArgPointee<1>(cdmSessionId2),
|
.WillOnce(DoAll(SetArgPointee<2>(cdmSessionId2),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId1, _))
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId1, _))
|
||||||
@@ -1071,9 +1073,9 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Provide expected behavior to support session creation
|
// Provide expected behavior to support session creation
|
||||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
|
||||||
.Times(AtLeast(1))
|
.Times(AtLeast(1))
|
||||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||||
@@ -1099,3 +1101,217 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) {
|
|||||||
plugin.onEvent(cdmSessionId, LICENSE_EXPIRED_EVENT);
|
plugin.onEvent(cdmSessionId, LICENSE_EXPIRED_EVENT);
|
||||||
plugin.onEvent(cdmSessionId, LICENSE_RENEWAL_NEEDED_EVENT);
|
plugin.onEvent(cdmSessionId, LICENSE_RENEWAL_NEEDED_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) {
|
||||||
|
StrictMock<MockCDM> cdm;
|
||||||
|
StrictMock<MockCrypto> 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<MockCDM> cdm;
|
||||||
|
StrictMock<MockCrypto> 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<MockCDM> cdm;
|
||||||
|
StrictMock<MockCrypto> 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<MockCDM> cdm;
|
||||||
|
StrictMock<MockCrypto> 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<uint8_t> privacyCert;
|
||||||
|
privacyCert.appendArray(privacyCertRaw, kPrivacyCertSize);
|
||||||
|
Vector<uint8_t> 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());
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -130,6 +130,8 @@ OEMCryptoResult Level3_DeriveKeysFromSessionKey(OEMCrypto_SESSION session,
|
|||||||
size_t mac_key_context_length,
|
size_t mac_key_context_length,
|
||||||
const uint8_t *enc_key_context,
|
const uint8_t *enc_key_context,
|
||||||
size_t enc_key_context_length);
|
size_t enc_key_context_length);
|
||||||
|
uint32_t Level3_APIVersion();
|
||||||
|
const char* Level3_SecurityLevel();
|
||||||
OEMCryptoResult Level3_Generic_Encrypt(OEMCrypto_SESSION session,
|
OEMCryptoResult Level3_Generic_Encrypt(OEMCrypto_SESSION session,
|
||||||
const uint8_t* in_buffer,
|
const uint8_t* in_buffer,
|
||||||
size_t buffer_length,
|
size_t buffer_length,
|
||||||
|
|||||||
@@ -1,647 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* mock implementation of OEMCrypto APIs
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#include "OEMCryptoCENC.h"
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#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<char *>(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
|
|
||||||
@@ -22,9 +22,9 @@ LOCAL_C_INCLUDES += \
|
|||||||
vendor/widevine/libwvdrmengine/third_party/stringencoders/src \
|
vendor/widevine/libwvdrmengine/third_party/stringencoders/src \
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := \
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
|
libcdm \
|
||||||
libgtest \
|
libgtest \
|
||||||
libgtest_main \
|
libgtest_main \
|
||||||
libwvwrapper \
|
|
||||||
libwvlevel3 \
|
libwvlevel3 \
|
||||||
libcdm_utils \
|
libcdm_utils \
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user