am f3ec8c19: Import updates to the Widevine CENC DRM Plugin
* commit 'f3ec8c19d60627bbee65e115280b93be8b2e0281': Import updates to the Widevine CENC DRM Plugin
This commit is contained in:
@@ -1,26 +1,22 @@
|
||||
# ----------------------------------------------------------------
|
||||
# -----------------------------------------------------------------------------
|
||||
# CDM top level makefile
|
||||
#
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
##########################################################
|
||||
# ----------------------------------------------------------------
|
||||
# Builds the protobuf static library and generate .pb.cc and .pb.h
|
||||
# license_protocol.pb.cc
|
||||
# license_protocol.pb.h
|
||||
# license_protocol.a
|
||||
# -----------------------------------------------------------------------------
|
||||
# Builds cdm_protos.a
|
||||
# Generates *.a, *.pb.h and *.pb.cc for *.proto files.
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := license_protocol_protos
|
||||
LOCAL_MODULE := cdm_protos
|
||||
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
bionic \
|
||||
external/stlport/stlport
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(call all-proto-files-under, cdm/core/src)
|
||||
LOCAL_SRC_FILES := $(call all-proto-files-under, cdm/core/src)
|
||||
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := \
|
||||
$(call local-intermediates-dir)/proto/$(LOCAL_PATH)/cdm/core/src
|
||||
@@ -31,8 +27,9 @@ include $(BUILD_STATIC_LIBRARY)
|
||||
# We can use cdm_proto_gen_headers later to establish the dependency.
|
||||
cdm_proto_gen_headers := $(proto_generated_headers)
|
||||
|
||||
|
||||
###########################################################
|
||||
# -----------------------------------------------------------------------------
|
||||
# Builds libwvdrmengine.so
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
@@ -40,7 +37,7 @@ LOCAL_SRC_FILES := \
|
||||
src/WVCreatePluginFactories.cpp \
|
||||
src/WVCryptoFactory.cpp \
|
||||
src/WVDrmFactory.cpp \
|
||||
src/WVUUID.cpp \
|
||||
src/WVUUID.cpp
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
bionic \
|
||||
@@ -55,19 +52,20 @@ LOCAL_C_INCLUDES := \
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libcdm \
|
||||
libl3crypto \
|
||||
libprotobuf-cpp-2.3.0-lite \
|
||||
libwvdrmcryptoplugin \
|
||||
libwvdrmdrmplugin \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcrypto \
|
||||
libdl \
|
||||
liblog \
|
||||
liboemcrypto \
|
||||
libstlport \
|
||||
libutils \
|
||||
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := \
|
||||
license_protocol_protos
|
||||
cdm_protos
|
||||
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers)
|
||||
|
||||
@@ -80,9 +78,9 @@ LOCAL_MODULE_TAGS := optional
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include vendor/widevine/libwvdrmengine/cdm/Android.mk
|
||||
include vendor/widevine/libwvdrmengine/level3/Android.mk
|
||||
include vendor/widevine/libwvdrmengine/mediacrypto/Android.mk
|
||||
include vendor/widevine/libwvdrmengine/mediadrm/Android.mk
|
||||
include vendor/widevine/libwvdrmengine/oemcrypto/mock/Android.mk
|
||||
|
||||
# clean up temp vars
|
||||
cdm_proto_gen_headers :=
|
||||
|
||||
@@ -15,7 +15,7 @@ LOCAL_C_INCLUDES += \
|
||||
external/protobuf/src \
|
||||
../oemcrypto/include
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := license_protocol_protos
|
||||
LOCAL_STATIC_LIBRARIES := cdm_protos
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers)
|
||||
|
||||
SRC_DIR := src
|
||||
@@ -28,6 +28,7 @@ LOCAL_SRC_FILES := \
|
||||
$(CORE_SRC_DIR)/crypto_session.cpp \
|
||||
$(CORE_SRC_DIR)/license.cpp \
|
||||
$(CORE_SRC_DIR)/policy_engine.cpp \
|
||||
$(CORE_SRC_DIR)/properties.cpp \
|
||||
$(CORE_SRC_DIR)/string_conversions.cpp \
|
||||
$(SRC_DIR)/clock.cpp \
|
||||
$(SRC_DIR)/lock.cpp \
|
||||
@@ -39,7 +40,3 @@ LOCAL_MODULE := libcdm
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
#########################################################
|
||||
# include the tests.
|
||||
include $(LOCAL_PATH)/test/Android.mk
|
||||
|
||||
@@ -30,7 +30,7 @@ class CdmEngine : public TimerHandler {
|
||||
const CdmKeySystem& key_system,
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmNameValueMap& app_parameters,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request);
|
||||
|
||||
// Accept license response and extract key info.
|
||||
@@ -59,10 +59,12 @@ class CdmEngine : public TimerHandler {
|
||||
const CdmInitData& init_data,
|
||||
const CdmKeyResponse& key_data);
|
||||
|
||||
// Query system information
|
||||
CdmResponseType QueryStatus(CdmQueryMap* info);
|
||||
|
||||
// Query license information
|
||||
CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmNameValueMap* key_info);
|
||||
|
||||
CdmQueryMap* key_info);
|
||||
|
||||
// Provisioning related methods
|
||||
CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request,
|
||||
@@ -83,7 +85,7 @@ class CdmEngine : public TimerHandler {
|
||||
size_t encrypted_size,
|
||||
const std::vector<uint8_t>& iv,
|
||||
size_t block_offset,
|
||||
void* decrypted_buffer);
|
||||
uint8_t* decrypted_buffer);
|
||||
|
||||
// Is the key known to any session?
|
||||
bool IsKeyValid(const KeyId& key_id);
|
||||
|
||||
@@ -42,11 +42,14 @@ class CdmSession {
|
||||
// CancelKeyRequest() - Cancel session.
|
||||
CdmResponseType CancelKeyRequest();
|
||||
|
||||
// Query license information
|
||||
CdmResponseType QueryKeyStatus(CdmQueryMap* key_info);
|
||||
|
||||
// Decrypt() - Accept encrypted buffer and return decrypted data.
|
||||
CdmResponseType Decrypt(const uint8_t* encrypted_buffer,
|
||||
size_t encrypted_size,
|
||||
size_t block_offset,
|
||||
const std::string& iv,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const KeyId& key_id,
|
||||
uint8_t* decrypted_buffer);
|
||||
|
||||
|
||||
@@ -10,9 +10,15 @@
|
||||
namespace wvcdm {
|
||||
|
||||
// Provides time related information. The implementation is platform dependent.
|
||||
class Clock {
|
||||
|
||||
// Provides the number of seconds since an epoch (00:00 hours, Jan 1, 1970 UTC)
|
||||
int64_t GetCurrentTime();
|
||||
public:
|
||||
Clock() {}
|
||||
virtual ~Clock() {}
|
||||
|
||||
// Provides the number of seconds since an epoch - 01/01/1970 00:00 UTC
|
||||
virtual int64_t GetCurrentTime();
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ class CryptoEngine {
|
||||
|
||||
bool GetToken(std::string* token);
|
||||
|
||||
CdmResponseType Query(CdmQueryMap* info);
|
||||
|
||||
private:
|
||||
|
||||
void DeleteInstance();
|
||||
|
||||
@@ -56,6 +56,11 @@ class CryptoSession {
|
||||
// Media data path
|
||||
bool SelectKey(const std::string& key_id);
|
||||
bool Decrypt(const InputDescriptor input, OutputDescriptor output);
|
||||
bool Decrypt(const uint8_t* encrypted_buffer,
|
||||
size_t encrypted_size,
|
||||
size_t block_offset,
|
||||
const std::vector<uint8_t>& iv,
|
||||
uint8_t* decrypted_buffer);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace wvcdm {
|
||||
using video_widevine_server::sdk::LicenseIdentification;
|
||||
|
||||
class CryptoSession;
|
||||
class PolicyEngine;
|
||||
|
||||
class CdmLicense {
|
||||
|
||||
@@ -19,7 +20,8 @@ class CdmLicense {
|
||||
CdmLicense();
|
||||
~CdmLicense();
|
||||
|
||||
bool Init(const std::string& token, CryptoSession* session);
|
||||
bool Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine);
|
||||
|
||||
bool PrepareKeyRequest(const CdmInitData& init_data,
|
||||
CdmKeyMessage* signed_request);
|
||||
@@ -31,6 +33,7 @@ private:
|
||||
|
||||
LicenseIdentification license_id_;
|
||||
CryptoSession* session_;
|
||||
PolicyEngine* policy_engine_;
|
||||
std::string token_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmLicense);
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class Clock;
|
||||
class PolicyEngineTest;
|
||||
|
||||
// This acts as an oracle that basically says "Yes(true) you may still decrypt
|
||||
// or no(false) you may not decrypt this data anymore."
|
||||
class PolicyEngine {
|
||||
@@ -17,8 +20,14 @@ class PolicyEngine {
|
||||
PolicyEngine();
|
||||
~PolicyEngine();
|
||||
|
||||
// |current_time| is used to check if license has to be renewed or expired.
|
||||
void OnTimerEvent(int64_t current_time, bool event_occurred, CdmEventType& event);
|
||||
// The value returned should be taken as a hint rather than an absolute
|
||||
// status. It is computed during the last call to either SetLicense/
|
||||
// UpdateLicense/OnTimerEvent/BeginDecryption and may be out of sync
|
||||
// depending on the amount of time elapsed. The current decryption
|
||||
// status is not calculated to avoid overhead in the decryption path.
|
||||
inline bool can_decrypt() { return can_decrypt_; }
|
||||
|
||||
void OnTimerEvent(bool& event_occurred, CdmEventType& event);
|
||||
|
||||
// SetLicense is used in handling the initial license response. It stores
|
||||
// an exact copy of the policy information stored in the license.
|
||||
@@ -26,6 +35,11 @@ class PolicyEngine {
|
||||
// permits playback.
|
||||
void SetLicense(const video_widevine_server::sdk::License& license);
|
||||
|
||||
// Call this on first decrypt to set the start of playback. This is
|
||||
// for cases where usage begins not when the license is received,
|
||||
// but at the start of playback
|
||||
void BeginDecryption(void);
|
||||
|
||||
// UpdateLicense is used in handling a license response for a renewal request.
|
||||
// The response may only contain any policy fields that have changed. In this
|
||||
// case an exact copy is not what we want to happen. We also will receive an
|
||||
@@ -33,6 +47,8 @@ class PolicyEngine {
|
||||
// kLicenseStateCanPlay if the license permits playback.
|
||||
void UpdateLicense(const video_widevine_server::sdk::License& license);
|
||||
|
||||
CdmResponseType Query(CdmQueryMap* key_info);
|
||||
|
||||
const video_widevine_server::sdk::LicenseIdentification& license_id() {
|
||||
return license_id_;
|
||||
}
|
||||
@@ -40,14 +56,17 @@ class PolicyEngine {
|
||||
private:
|
||||
typedef enum {
|
||||
kLicenseStateInitial,
|
||||
kLicenseStateInitialPendingUsage,
|
||||
kLicenseStateCanPlay,
|
||||
kLicenseStateCannotPlay,
|
||||
kLicenseStateNeedRenewal,
|
||||
kLicenseStateWaitingLicenseUpdate,
|
||||
kLicenseStateExpired
|
||||
} LicenseState;
|
||||
|
||||
void Init(Clock* clock);
|
||||
|
||||
bool IsLicenseDurationExpired(int64_t current_time);
|
||||
bool IsPlaybackDurationExpired(int64_t current_time);
|
||||
bool IsRenewalDelayExpired(int64_t current_time);
|
||||
bool IsRenewalRecoveryDurationExpired(int64_t current_time);
|
||||
bool IsRenewalRetryIntervalExpired(int64_t current_time);
|
||||
@@ -55,6 +74,7 @@ class PolicyEngine {
|
||||
void UpdateRenewalRequest(int64_t current_time);
|
||||
|
||||
LicenseState license_state_;
|
||||
bool can_decrypt_;
|
||||
|
||||
// This is the current policy information for this license. This gets updated
|
||||
// as license renewals occur.
|
||||
@@ -69,14 +89,30 @@ class PolicyEngine {
|
||||
// license request or renewal.
|
||||
int64_t license_start_time_;
|
||||
|
||||
// This is the time at which the license was received and playback was
|
||||
// started. These times are based off the local clock in case there is a
|
||||
// discrepency between local and server time.
|
||||
int64_t license_received_time_;
|
||||
int64_t playback_start_time_;
|
||||
|
||||
// This is used as a reference point for policy management. This value
|
||||
// represents an offset from license_start_time_. This is used to calculate
|
||||
// the time where renewal retries should occur.
|
||||
// represents an offset from license_received_time_. This is used to
|
||||
// calculate the time where renewal retries should occur.
|
||||
int64_t next_renewal_time_;
|
||||
int64_t policy_max_duration_seconds_;
|
||||
|
||||
bool properties_valid_;
|
||||
bool begin_license_usage_when_received_;
|
||||
|
||||
Clock* clock_;
|
||||
|
||||
// For testing
|
||||
friend class PolicyEngineTest;
|
||||
PolicyEngine(Clock* clock);
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(PolicyEngine);
|
||||
};
|
||||
|
||||
} // wvcdm
|
||||
|
||||
#endif // CDM_BASE_POLICY_ENGINE_H_
|
||||
|
||||
53
libwvdrmengine/cdm/core/include/properties.h
Normal file
53
libwvdrmengine/cdm/core/include/properties.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef CDM_BASE_PROPERTIES_H_
|
||||
#define CDM_BASE_PROPERTIES_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "lock.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef std::map<std::string, bool> CdmBooleanPropertiesMap;
|
||||
|
||||
struct CdmBooleanProperties {
|
||||
std::string name;
|
||||
bool value;
|
||||
};
|
||||
|
||||
// This class saves information about features and properties enabled
|
||||
// for a given platform. At initialization it reads in properties from
|
||||
// property_configuration.h. That file specifies features selected for each
|
||||
// platform. Core CDM can then query enabled features though the GetProperty
|
||||
// method and tailor its behaviour in a non-platform specific way.
|
||||
//
|
||||
// Additional features can be added at runtime as long as the key names do
|
||||
// not clash. Also, only boolean properties are supported at this time, though
|
||||
// it should be trivial to in support for other datatypes.
|
||||
class Properties {
|
||||
public:
|
||||
static Properties* GetInstance();
|
||||
|
||||
// value argument is only set if the property was found (true is returned)
|
||||
bool GetProperty(std::string& key, bool& value);
|
||||
|
||||
private:
|
||||
Properties();
|
||||
~Properties() {}
|
||||
|
||||
void SetProperty(std::string& key, bool value);
|
||||
|
||||
static Properties* instance_;
|
||||
static Lock properties_lock_;
|
||||
|
||||
CdmBooleanPropertiesMap boolean_properties_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(Properties);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // CDM_BASE_PROPERTIES_H_
|
||||
@@ -3,6 +3,8 @@
|
||||
#ifndef CDM_BASE_WV_CDM_CONSTANTS_H_
|
||||
#define CDM_BASE_WV_CDM_CONSTANTS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace wvcdm {
|
||||
static const size_t KEY_CONTROL_SIZE = 16;
|
||||
// TODO(kqyang): Key ID size is not fixed in spec, but conventionally we
|
||||
@@ -13,6 +15,38 @@ static const size_t KEY_IV_SIZE = 16;
|
||||
static const size_t KEY_PAD_SIZE = 16;
|
||||
static const size_t KEY_SIZE = 16;
|
||||
static const size_t MAC_KEY_SIZE = 32;
|
||||
|
||||
// define boolean property keys here
|
||||
// If false begin license usage on first playback
|
||||
static std::string kPropertyKeyBeginLicenseUsageWhenReceived =
|
||||
"WVBeginLicenseUsageWhenReceived";
|
||||
|
||||
// define query keys, values here
|
||||
static const std::string QUERY_KEY_LICENSE_TYPE = "LicenseType";
|
||||
// "Streaming", "Offline"
|
||||
static const std::string QUERY_KEY_PLAY_ALLOWED = "PlayAllowed";
|
||||
// "True", "False"
|
||||
static const std::string QUERY_KEY_PERSIST_ALLOWED = "PersistAllowed";
|
||||
// "True", "False"
|
||||
static const std::string QUERY_KEY_RENEW_ALLOWED = "RenewAllowed";
|
||||
// "True", "False"
|
||||
static const std::string QUERY_KEY_LICENSE_DURATION_REMAINING =
|
||||
"LicenseDurationRemaining"; // non-negative integer
|
||||
static const std::string QUERY_KEY_PLAYBACK_DURATION_REMAINING =
|
||||
"PlaybackDurationRemaining"; // non-negative integer
|
||||
static const std::string QUERY_KEY_RENEWAL_SERVER_URL = "RenewalServerUrl";
|
||||
// url
|
||||
static const std::string QUERY_KEY_SECURITY_LEVEL = "SecurityLevel";
|
||||
// "L1", "L3"
|
||||
static const std::string QUERY_VALUE_TRUE = "True";
|
||||
static const std::string QUERY_VALUE_FALSE = "False";
|
||||
static const std::string QUERY_VALUE_STREAMING = "Streaming";
|
||||
static const std::string QUERY_VALUE_OFFLINE = "Offline";
|
||||
static const std::string QUERY_VALUE_SECURITY_LEVEL_L1 = "L1";
|
||||
static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2";
|
||||
static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3";
|
||||
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // CDM_BASE_WV_CDM_CONSTANTS_H_
|
||||
|
||||
@@ -20,7 +20,8 @@ typedef std::string RequestId;
|
||||
typedef uint32_t CryptoResult;
|
||||
typedef uint32_t CryptoSessionId;
|
||||
typedef std::string CryptoKeyId;
|
||||
typedef std::map<std::string, std::string> CdmNameValueMap;
|
||||
typedef std::map<std::string, std::string> CdmAppParameterMap;
|
||||
typedef std::map<std::string, std::string> CdmQueryMap;
|
||||
typedef std::vector<std::string> CdmSecureStops;
|
||||
typedef std::vector<uint8_t> CdmSecureStopReleaseMessage;
|
||||
typedef std::string CdmProvisioningRequest;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "buffer_reader.h"
|
||||
#include "cdm_session.h"
|
||||
#include "crypto_engine.h"
|
||||
#include "log.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
@@ -70,16 +71,15 @@ CdmResponseType CdmEngine::OpenSession(
|
||||
CdmResponseType CdmEngine::CloseSession(CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::CloseSession");
|
||||
|
||||
CdmSession* cdm_session = sessions_[session_id];
|
||||
|
||||
if (!cdm_session) {
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
sessions_.erase(session_id);
|
||||
cdm_session->DestroySession();
|
||||
delete cdm_session;
|
||||
iter->second->DestroySession();
|
||||
delete iter->second;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -89,13 +89,12 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
const CdmKeySystem& key_system,
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmNameValueMap& app_parameters,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request) {
|
||||
LOGI("CdmEngine::GenerateKeyRequest");
|
||||
|
||||
CdmSession* session = sessions_[session_id];
|
||||
|
||||
if (!session) {
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
@@ -123,8 +122,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
key_request->clear();
|
||||
|
||||
// TODO(edwinwong, rfrias): need to pass in license type and app parameters
|
||||
CdmResponseType sts = session->GenerateKeyRequest(extracted_pssh,
|
||||
key_request);
|
||||
CdmResponseType sts = iter->second->GenerateKeyRequest(extracted_pssh,
|
||||
key_request);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d",
|
||||
@@ -146,9 +145,8 @@ CdmResponseType CdmEngine::AddKey(
|
||||
const CdmKeyResponse& key_data) {
|
||||
LOGI("CdmEngine::AddKey");
|
||||
|
||||
CdmSession* session = sessions_[session_id];
|
||||
|
||||
if (!session) {
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::AddKey: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
@@ -166,7 +164,7 @@ CdmResponseType CdmEngine::AddKey(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType sts = session->AddKey(key_data);
|
||||
CdmResponseType sts = iter->second->AddKey(key_data);
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts);
|
||||
}
|
||||
@@ -186,9 +184,8 @@ CdmResponseType CdmEngine::CancelKeyRequest(
|
||||
// active sessions. Sessions are currently not being destroyed here. We can
|
||||
// add this logic once the semantics of canceling the key is worked out.
|
||||
|
||||
CdmSession* session = sessions_[session_id];
|
||||
|
||||
if (!session) {
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::CancelKeyRequest: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
@@ -210,9 +207,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
CdmKeyMessage* key_request) {
|
||||
LOGI("CdmEngine::GenerateRenewalRequest");
|
||||
|
||||
CdmSession* session = sessions_[session_id];
|
||||
|
||||
if (!session) {
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
@@ -232,7 +228,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
|
||||
key_request->clear();
|
||||
|
||||
CdmResponseType sts = session->GenerateRenewalRequest(key_request);
|
||||
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: key request generation failed, sts=%d",
|
||||
@@ -251,9 +247,8 @@ CdmResponseType CdmEngine::RenewKey(
|
||||
const CdmKeyResponse& key_data) {
|
||||
LOGI("CdmEngine::RenewKey");
|
||||
|
||||
CdmSession* session = sessions_[session_id];
|
||||
|
||||
if (!session) {
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
@@ -271,7 +266,7 @@ CdmResponseType CdmEngine::RenewKey(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType sts = session->RenewKey(key_data);
|
||||
CdmResponseType sts = iter->second->RenewKey(key_data);
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", (int)sts);
|
||||
return sts;
|
||||
@@ -280,11 +275,25 @@ CdmResponseType CdmEngine::RenewKey(
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryStatus");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
if (crypto_engine) {
|
||||
return crypto_engine->Query(key_info);
|
||||
}
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyStatus(
|
||||
const CdmSessionId& session_id,
|
||||
CdmNameValueMap* key_info) {
|
||||
// TODO(edwinwong, rfrias): add implementation
|
||||
return NO_ERROR;
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryKeyStatus");
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
return iter->second->QueryKeyStatus(key_info);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
@@ -320,16 +329,18 @@ CdmResponseType CdmEngine::Decrypt(
|
||||
size_t encrypted_size,
|
||||
const std::vector<uint8_t>& iv,
|
||||
size_t block_offset,
|
||||
void* decrypted_buffer) {
|
||||
CdmSession* session = sessions_[session_id];
|
||||
|
||||
if (!session) {
|
||||
uint8_t* decrypted_buffer) {
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGW("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): Need to add implemenation and hook up
|
||||
// decryption though to oem_crypto
|
||||
if (NO_ERROR != iter->second->Decrypt(encrypted_buffer, encrypted_size,
|
||||
block_offset, iv, key_id,
|
||||
decrypted_buffer))
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ bool CdmSession::Init() {
|
||||
std::string token;
|
||||
if (!crypto_engine->GetToken(&token)) return false;
|
||||
|
||||
return license_parser_.Init(token, crypto_session_);
|
||||
return license_parser_.Init(token, crypto_session_, &policy_engine_);
|
||||
}
|
||||
|
||||
bool CdmSession::DestroySession() {
|
||||
@@ -67,6 +67,10 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
|
||||
return policy_engine_.Query(key_info);
|
||||
}
|
||||
|
||||
// CancelKeyRequest() - Cancel session.
|
||||
CdmResponseType CdmSession::CancelKeyRequest() {
|
||||
// TODO(gmorgan): cancel and clean up session
|
||||
@@ -78,10 +82,20 @@ CdmResponseType CdmSession::CancelKeyRequest() {
|
||||
CdmResponseType CdmSession::Decrypt(const uint8_t* encrypted_buffer,
|
||||
size_t encrypted_size,
|
||||
size_t block_offset,
|
||||
const std::string& iv,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const KeyId& key_id,
|
||||
uint8_t* decrypted_buffer) {
|
||||
return UNKNOWN_ERROR;
|
||||
if (!crypto_session_)
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
if (!crypto_session_->SelectKey(key_id))
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
if (!crypto_session_->Decrypt(encrypted_buffer, encrypted_size,
|
||||
block_offset, iv, decrypted_buffer))
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// License renewal
|
||||
@@ -131,7 +145,7 @@ void CdmSession::OnTimerEvent() {
|
||||
bool event_occurred = false;
|
||||
CdmEventType event;
|
||||
|
||||
policy_engine_.OnTimerEvent(GetCurrentTime(), event_occurred, event);
|
||||
policy_engine_.OnTimerEvent(event_occurred, event);
|
||||
|
||||
if (event_occurred) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
|
||||
43
libwvdrmengine/cdm/core/src/certificate_provisioning.proto
Normal file
43
libwvdrmengine/cdm/core/src/certificate_provisioning.proto
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
// Author: tinskip@google.com (Thomas Inskip)
|
||||
//
|
||||
// Description:
|
||||
// Public protocol buffer definitions for Widevine Device Certificate
|
||||
// Provisioning protocol.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package video_widevine_server.sdk;
|
||||
|
||||
import "vendor/widevine/libwvdrmengine/cdm/core/src/client_identification.proto";
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
// Provisioning request sent by client devices to provisioning service.
|
||||
message ProvisioningRequest {
|
||||
// Device root of trust and other client identification. Required.
|
||||
optional ClientIdentification client_id = 1;
|
||||
// Nonce value used to prevent replay attacks. Required.
|
||||
optional bytes nonce = 2;
|
||||
}
|
||||
|
||||
// Provisioning response sent by the provisioning server to client devices.
|
||||
message ProvisioningResponse {
|
||||
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
||||
// Required.
|
||||
optional bytes device_rsa_key = 1;
|
||||
// Initialization vector used to encrypt device_rsa_key. Required.
|
||||
optional bytes device_rsa_key_iv = 2;
|
||||
// Serialized DeviceCertificate. Required.
|
||||
optional bytes device_certificate = 3;
|
||||
// Nonce value matching nonce in ProvisioningRequest. Required.
|
||||
optional bytes nonce = 4;
|
||||
}
|
||||
|
||||
// Serialized ProvisioningRequest or ProvisioningResponse signed with
|
||||
// The message authentication key.
|
||||
message SignedProvisioningMessage {
|
||||
// Serialized ProvisioningRequest or ProvisioningResponse. Required.
|
||||
optional bytes message = 1;
|
||||
// HMAC-SHA256 signature of message. Required.
|
||||
optional bytes signature = 2;
|
||||
}
|
||||
30
libwvdrmengine/cdm/core/src/client_identification.proto
Normal file
30
libwvdrmengine/cdm/core/src/client_identification.proto
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
// Author: tinskip@google.com (Thomas Inskip)
|
||||
//
|
||||
// Description:
|
||||
// ClientIdentification message used by provisioning and license protocols.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package video_widevine_server.sdk;
|
||||
option java_outer_classname = "ClientIdentificationProtos";
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
// ClientIdentification message used to authenticate the client device.
|
||||
message ClientIdentification {
|
||||
enum TokenType {
|
||||
KEYBOX = 0;
|
||||
}
|
||||
|
||||
message NameValue {
|
||||
optional string name = 1;
|
||||
optional string value = 2;
|
||||
}
|
||||
|
||||
// Type of factory-provisioned device root of trust. Optional.
|
||||
optional TokenType type = 1 [default = KEYBOX];
|
||||
// Factory-provisioned device root of trust. Required.
|
||||
optional bytes token = 2;
|
||||
// Optional client information name/value pairs.
|
||||
repeated NameValue client_info = 3;
|
||||
}
|
||||
@@ -175,4 +175,10 @@ bool CryptoEngine::GetToken(std::string* token) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoEngine::Query(CdmQueryMap* key_info) {
|
||||
LOGV("CryptoEngine::GetToken: Query");
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = OEMCrypto_SecurityLevel();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
31
libwvdrmengine/cdm/core/src/crypto_session.cpp
Normal file → Executable file
31
libwvdrmengine/cdm/core/src/crypto_session.cpp
Normal file → Executable file
@@ -230,6 +230,7 @@ bool CryptoSession::LoadKeys(const std::string& message,
|
||||
ko->key_id_length = ki->key_id().length();
|
||||
ko->key_data_iv = msg + GetOffset(message, ki->key_data_iv());
|
||||
ko->key_data = msg + GetOffset(message, ki->key_data());
|
||||
ko->key_data_length = ki->key_data().length();
|
||||
if (ki->HasKeyControl()) {
|
||||
ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv());
|
||||
ko->key_control = msg + GetOffset(message, ki->key_control());
|
||||
@@ -324,6 +325,36 @@ bool CryptoSession::Decrypt(const InputDescriptor input,
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(jfore): Define InputDescriptor and OutputDecriptor and
|
||||
// remove this method. For now this is a level 3 decrypt.
|
||||
bool CryptoSession::Decrypt(const uint8_t* encrypted_buffer,
|
||||
size_t encrypted_size,
|
||||
size_t block_offset,
|
||||
const std::vector<uint8_t>& iv,
|
||||
uint8_t* decrypted_buffer) {
|
||||
LOGV("CryptoSession::Decrypt: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
// TODO(gmorgan): handle inputs and outputs to decrypt call
|
||||
const uint8_t* data_addr = NULL;
|
||||
uint32_t data_length = 0;
|
||||
bool is_encrypted = false;
|
||||
uint32_t offset = block_offset;
|
||||
OEMCrypto_DestBufferDesc out_buffer;
|
||||
|
||||
out_buffer.type = OEMCrypto_BufferType_Clear;
|
||||
out_buffer.buffer.clear.address = decrypted_buffer;
|
||||
out_buffer.buffer.clear.max_length = encrypted_size;
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCTR(oec_session_id_, encrypted_buffer,
|
||||
encrypted_size, true, &iv[0],
|
||||
offset, &out_buffer);
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GenerateNonce(uint32_t* nonce) {
|
||||
if (!nonce) {
|
||||
LOGE("input parameter is null");
|
||||
|
||||
117
libwvdrmengine/cdm/core/src/device_certificate.proto
Normal file
117
libwvdrmengine/cdm/core/src/device_certificate.proto
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
// Author: tinskip@google.com (Thomas Inskip)
|
||||
//
|
||||
// Description:
|
||||
// Device certificate and certificate status list format definitions.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package video_widevine_server.sdk;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
option java_outer_classname = "DeviceCertificateProtos";
|
||||
option java_package = "com.google.video.widevine.protos";
|
||||
|
||||
// Certificate definition for user devices, intermediate, and root certificates.
|
||||
message DeviceCertificate {
|
||||
enum CertificateType {
|
||||
ROOT = 0;
|
||||
INTERMEDIATE = 1;
|
||||
USER_DEVICE = 2;
|
||||
}
|
||||
|
||||
// Type of certificate. Required.
|
||||
optional CertificateType type = 1;
|
||||
// 128-bit globally unique serial number of certificate.
|
||||
// Value is 0 for root certificate. Required.
|
||||
optional bytes serial_number = 2;
|
||||
// POSIX time, in seconds, when the certificate was created. Required.
|
||||
optional uint32 creation_time_seconds = 3;
|
||||
// Device public key. PKCS#1 ASN.1 DER-encoded. Required.
|
||||
optional bytes public_key = 4;
|
||||
// Widevine system ID for the device. Required for intermediate and
|
||||
// user device certificates.
|
||||
optional uint32 system_id = 5;
|
||||
// True if the certificate corresponds to a test (non production) device.
|
||||
// Optional.
|
||||
optional bool test_device = 6 [default = false];
|
||||
}
|
||||
|
||||
// DeviceCertificate signed with intermediate or root certificate private key.
|
||||
message SignedDeviceCertificate {
|
||||
// Serialized DeviceCertificate. Required.
|
||||
optional bytes device_certificate = 1;
|
||||
// Signature of device_certificate. Signed with root or intermediate
|
||||
// certificate private key using RSASSA-PSS. Required.
|
||||
optional bytes signature = 2;
|
||||
// Intermediate signing certificate. Present only for user device
|
||||
// certificates. All others signed with root certificate private key.
|
||||
optional SignedDeviceCertificate signer = 3;
|
||||
}
|
||||
|
||||
// Contains device model information for a provisioned device.
|
||||
message ProvisionedDeviceInfo {
|
||||
enum WvSecurityLevel {
|
||||
// Defined in Widevine Security Integration Guide for DASH on Android:
|
||||
// https://docs.google.com/a/google.com/document/d/1Zum-fcJeoIw6KG1kDP_KepIE5h9gAZg0PaMtemBvk9c/edit#heading=h.1t3h5sf
|
||||
LEVEL_UNSPECIFIED = 0;
|
||||
LEVEL_1 = 1;
|
||||
LEVEL_2 = 2;
|
||||
LEVEL_3 = 3;
|
||||
}
|
||||
|
||||
// Widevine system ID for the device. Mandatory.
|
||||
optional uint32 system_id = 1;
|
||||
// Name of system-on-a-chip. Optional.
|
||||
optional string soc = 2;
|
||||
// Name of manufacturer. Optional.
|
||||
optional string manufacturer = 3;
|
||||
// Manufacturer's model name. Matches "brand" in device metadata. Optional.
|
||||
optional string model = 4;
|
||||
// Type of device (Phone, Tablet, TV, etc).
|
||||
optional string device_type = 5;
|
||||
// Device model year. Optional.
|
||||
optional uint32 model_year = 6;
|
||||
// Widevine-defined security level. Optional.
|
||||
optional WvSecurityLevel security_level = 7 [default = LEVEL_UNSPECIFIED];
|
||||
// True if the certificate corresponds to a test (non production) device.
|
||||
// Optional.
|
||||
optional bool test_device = 8 [default = false];
|
||||
}
|
||||
|
||||
// Contains the status of the root or an intermediate DeviceCertificate.
|
||||
message DeviceCertificateStatus {
|
||||
enum CertificateStatus {
|
||||
VALID = 0;
|
||||
REVOKED = 1;
|
||||
};
|
||||
|
||||
// Serial number of the DeviceCertificate to which this message refers.
|
||||
// Required.
|
||||
optional bytes serial_number = 1;
|
||||
// Status of the certificate. Optional.
|
||||
optional CertificateStatus status = 2 [default = VALID];
|
||||
// Current version of a valid certificate. Present only if status = VALID.
|
||||
optional uint32 current_certificate_version = 3;
|
||||
// Device model information about the device to which the certificate
|
||||
// corresponds. Required.
|
||||
optional ProvisionedDeviceInfo device_info = 4;
|
||||
}
|
||||
|
||||
// List of DeviceCertificateStatus. Used to propagate certificate revocation and
|
||||
// update list.
|
||||
message DeviceCertificateStatusList {
|
||||
// POSIX time, in seconds, when the list was created. Required.
|
||||
optional uint32 creation_time_seconds = 1;
|
||||
// DeviceCertificateStatus for each certifificate.
|
||||
repeated DeviceCertificateStatus certificate_status = 2;
|
||||
}
|
||||
|
||||
// Signed CertificateStatusList
|
||||
message SignedCertificateStatusList {
|
||||
// Serialized CertificateStatusList. Required.
|
||||
optional bytes certificate_status_list = 1;
|
||||
// Signature of certificate_status_list. Signed with root certificate private
|
||||
// key using RSASSA-PSS. Required.
|
||||
optional bytes signature = 2;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "log.h"
|
||||
#include "policy_engine.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
@@ -25,13 +26,16 @@ CdmLicense::CdmLicense(): session_(NULL) {}
|
||||
|
||||
CdmLicense::~CdmLicense() {}
|
||||
|
||||
bool CdmLicense::Init(const std::string& token, CryptoSession* session) {
|
||||
bool CdmLicense::Init(const std::string& token,
|
||||
CryptoSession* session,
|
||||
PolicyEngine* policy_engine) {
|
||||
if (token.size() == 0)
|
||||
return false;
|
||||
if (session == NULL || !session->IsValid() || !session->IsOpen())
|
||||
return false;
|
||||
token_ = token;
|
||||
session_ = session;
|
||||
policy_engine_ = policy_engine;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -239,9 +243,7 @@ bool CdmLicense::HandleKeyResponse(const CdmKeyResponse& license_response) {
|
||||
|
||||
if (num_keys == 0) return false;
|
||||
|
||||
// TODO(kqyang): move protocol buffer related stuff in policy
|
||||
// engine to this file.
|
||||
// policy_engine_.SetLicense(license);
|
||||
policy_engine_->SetLicense(license);
|
||||
|
||||
bool status = session_->LoadKeys(signed_response.msg(),
|
||||
signed_response.signature(),
|
||||
@@ -282,9 +284,7 @@ bool CdmLicense::HandleKeyRenewalResponse(
|
||||
//This is the normal case.
|
||||
license_id_.CopyFrom(license.id());
|
||||
|
||||
// TODO(kqyang): should we move protocol buffer related stuff in policy
|
||||
// engine to this file instead?
|
||||
// policy_engine_.UpdateLicense(license);
|
||||
policy_engine_->UpdateLicense(license);
|
||||
} else {
|
||||
// This isn't supposed to happen.
|
||||
// TODO(jfore): Handle wrap? We can miss responses and that should be
|
||||
|
||||
@@ -4,81 +4,106 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "properties_configuration.h"
|
||||
#include "string_conversions.h"
|
||||
#include "clock.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
PolicyEngine::PolicyEngine() :
|
||||
license_state_(kLicenseStateInitial),
|
||||
license_start_time_(0),
|
||||
next_renewal_time_(0),
|
||||
policy_max_duration_seconds_(0) {
|
||||
PolicyEngine::PolicyEngine() {
|
||||
Init(new Clock());
|
||||
}
|
||||
|
||||
PolicyEngine::PolicyEngine(Clock* clock) {
|
||||
Init(clock);
|
||||
}
|
||||
|
||||
PolicyEngine::~PolicyEngine() {
|
||||
if (clock_)
|
||||
delete clock_;
|
||||
}
|
||||
|
||||
void PolicyEngine::OnTimerEvent(int64_t current_time, bool event_occured, CdmEventType& event) {
|
||||
void PolicyEngine::Init(Clock* clock) {
|
||||
license_state_ = kLicenseStateInitial;
|
||||
can_decrypt_ = false;
|
||||
license_start_time_ = 0;
|
||||
license_received_time_ = 0;
|
||||
playback_start_time_ = 0;
|
||||
next_renewal_time_ = 0;
|
||||
policy_max_duration_seconds_ = 0;
|
||||
clock_ = clock;
|
||||
properties_valid_ = true;
|
||||
|
||||
if (!Properties::GetInstance()->GetProperty(
|
||||
kPropertyKeyBeginLicenseUsageWhenReceived,
|
||||
begin_license_usage_when_received_)) {
|
||||
LOGW("PolicyEngine::PolicyEngine: Unable to access property - begin license usage");
|
||||
properties_valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::OnTimerEvent(bool& event_occured, CdmEventType& event) {
|
||||
event_occured = false;
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
|
||||
// License expiration trumps all.
|
||||
if (IsLicenseDurationExpired(current_time) &&
|
||||
license_state_ != kLicenseStateExpired) {
|
||||
if ((IsLicenseDurationExpired(current_time) ||
|
||||
IsPlaybackDurationExpired(current_time)) &&
|
||||
license_state_ != kLicenseStateExpired) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
can_decrypt_ = false;
|
||||
event = LICENSE_EXPIRED_EVENT;
|
||||
event_occured = true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool renewal_needed = false;
|
||||
|
||||
// Test to determine if renewal should be attempted.
|
||||
switch (license_state_) {
|
||||
case kLicenseStateInitialPendingUsage:
|
||||
case kLicenseStateCanPlay: {
|
||||
if (IsRenewalDelayExpired(current_time)) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
UpdateRenewalRequest(current_time);
|
||||
event = LICENSE_RENEWAL_NEEDED_EVENT;
|
||||
event_occured = true;
|
||||
}
|
||||
return;
|
||||
if (IsRenewalDelayExpired(current_time))
|
||||
renewal_needed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case kLicenseStateNeedRenewal: {
|
||||
UpdateRenewalRequest(current_time);
|
||||
event = LICENSE_RENEWAL_NEEDED_EVENT;
|
||||
event_occured = true;
|
||||
return;
|
||||
renewal_needed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case kLicenseStateWaitingLicenseUpdate: {
|
||||
if (IsRenewalRetryIntervalExpired(current_time)) {
|
||||
UpdateRenewalRequest(current_time);
|
||||
event = LICENSE_RENEWAL_NEEDED_EVENT;
|
||||
event_occured = true;
|
||||
}
|
||||
return;
|
||||
if (IsRenewalRetryIntervalExpired(current_time))
|
||||
renewal_needed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case kLicenseStateInitial:
|
||||
case kLicenseStateExpired: {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
license_state_ = kLicenseStateCannotPlay;
|
||||
return;
|
||||
license_state_ = kLicenseStateExpired;
|
||||
can_decrypt_ = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (renewal_needed) {
|
||||
UpdateRenewalRequest(current_time);
|
||||
event = LICENSE_RENEWAL_NEEDED_EVENT;
|
||||
event_occured = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix up differences between Eureka and other platforms' use cases.
|
||||
// Eureka does not get calls to decrypt. license usage begins
|
||||
// when we receive the initial license. renew_with_usage will cause renewal to
|
||||
// occur on the first call to OnTimeEvent after PolicyEngine::SetLicense is
|
||||
// called.
|
||||
void PolicyEngine::SetLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
license_id_.Clear();
|
||||
@@ -89,89 +114,158 @@ void PolicyEngine::SetLicense(
|
||||
|
||||
void PolicyEngine::UpdateLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
if (!license.has_policy() || kLicenseStateExpired == license_state_)
|
||||
if (!license.has_policy() || kLicenseStateExpired == license_state_ ||
|
||||
!properties_valid_)
|
||||
return;
|
||||
|
||||
policy_.MergeFrom(license.policy());
|
||||
policy_max_duration_seconds_ = 0;
|
||||
|
||||
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
|
||||
// will be set to the minimum of the following policies :
|
||||
// rental_duration_seconds, playback_duration_seconds, and
|
||||
// license_duration_seconds. The value is used to determine
|
||||
// when the license expires.
|
||||
if (policy_.has_rental_duration_seconds())
|
||||
policy_max_duration_seconds_ = policy_.rental_duration_seconds();
|
||||
|
||||
if ((policy_.has_playback_duration_seconds() &&
|
||||
(policy_.playback_duration_seconds() < policy_max_duration_seconds_)) ||
|
||||
!policy_max_duration_seconds_) {
|
||||
policy_max_duration_seconds_ = policy_.playback_duration_seconds();
|
||||
}
|
||||
|
||||
if ((policy_.has_license_duration_seconds() &&
|
||||
(policy_.license_duration_seconds() < policy_max_duration_seconds_)) ||
|
||||
!policy_max_duration_seconds_) {
|
||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||
}
|
||||
|
||||
switch (license_state_) {
|
||||
case kLicenseStateInitial: {
|
||||
// Process initial license.
|
||||
license_start_time_ = license.license_start_time();
|
||||
if (policy_.can_play()) {
|
||||
license_state_ =
|
||||
policy_.renew_with_usage() ?
|
||||
kLicenseStateNeedRenewal : kLicenseStateCanPlay;
|
||||
} else {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
}
|
||||
next_renewal_time_ = license_start_time_
|
||||
+ policy_.renewal_delay_seconds();
|
||||
}
|
||||
break;
|
||||
|
||||
case kLicenseStateExpired:
|
||||
// Ignore policy updates.
|
||||
return;
|
||||
|
||||
default: {
|
||||
// Process license renewal.
|
||||
if (license.id().version() > license_id_.version()) {
|
||||
// This is the normal case.
|
||||
policy_.MergeFrom(license.policy());
|
||||
license_id_.CopyFrom(license.id());
|
||||
} else {
|
||||
// This isn't supposed to happen.
|
||||
// TODO(jfore): Handle wrap? We can miss responses and that should be
|
||||
// considered normal until retries are exhausted.
|
||||
policy_.set_can_play(false);
|
||||
case kLicenseStateInitial:
|
||||
case kLicenseStateInitialPendingUsage:
|
||||
case kLicenseStateCanPlay:
|
||||
case kLicenseStateNeedRenewal:
|
||||
case kLicenseStateWaitingLicenseUpdate:
|
||||
if (!policy_.can_play()) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
return;
|
||||
}
|
||||
|
||||
if (license.has_license_start_time()) {
|
||||
// license start has been updated. Transition back to the
|
||||
// normal kLicenseStateCanPlay state if playback is allowed by
|
||||
// the updated license.
|
||||
license_start_time_ = license.license_start_time();
|
||||
next_renewal_time_ = license_start_time_
|
||||
+ policy_.renewal_delay_seconds();
|
||||
license_state_ =
|
||||
policy_.can_play() ? kLicenseStateCanPlay : kLicenseStateExpired;
|
||||
} else {
|
||||
// license start was not updated. Continue sending renewel requests
|
||||
// at the specified retry rate. To perform this we transition directly
|
||||
// to kLicenseStateWaitingLicenseUpdate. While in this state
|
||||
// IsRenewalRetryIntervalExpired will always return false if retries are
|
||||
// not allowed. Note that next_renewal_time_ was updated when this
|
||||
// renewal was requested.
|
||||
license_state_ =
|
||||
policy_.can_play() ?
|
||||
kLicenseStateWaitingLicenseUpdate : kLicenseStateExpired;
|
||||
// some basic license validation
|
||||
if (license_state_ == kLicenseStateInitial) {
|
||||
// license start time needs to be present in the initial response
|
||||
if (!license.has_license_start_time())
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// TODO(edwingwong, rfrias): Check back with Thomas and see if
|
||||
// we need to enforce that all duration windows are absent if
|
||||
// license_start_time is not present. This is a TBD.
|
||||
|
||||
// if renewal, discard license if version has not been updated
|
||||
if (license.id().version() > license_id_.version())
|
||||
license_id_.CopyFrom(license.id());
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// Update time information
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
// TODO(edwingwong, rfrias): Check back with Thomas and see if
|
||||
// we need to enforce that all duration windows are absent if
|
||||
// license_start_time is not present. This is a TBD.
|
||||
if (license.has_license_start_time())
|
||||
license_start_time_ = license.license_start_time();
|
||||
license_received_time_ = current_time;
|
||||
next_renewal_time_ = current_time +
|
||||
policy_.renewal_delay_seconds();
|
||||
|
||||
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
|
||||
// will be set to the minimum of the following policies :
|
||||
// rental_duration_seconds and license_duration_seconds.
|
||||
// The value is used to determine when the license expires.
|
||||
policy_max_duration_seconds_ = 0;
|
||||
|
||||
if (policy_.has_rental_duration_seconds())
|
||||
policy_max_duration_seconds_ = policy_.rental_duration_seconds();
|
||||
|
||||
if ((policy_.license_duration_seconds() > 0) &&
|
||||
((policy_.license_duration_seconds() <
|
||||
policy_max_duration_seconds_) ||
|
||||
policy_max_duration_seconds_ == 0)) {
|
||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||
}
|
||||
|
||||
if (begin_license_usage_when_received_)
|
||||
playback_start_time_ = current_time;
|
||||
|
||||
// Update state
|
||||
if (begin_license_usage_when_received_) {
|
||||
if (policy_.renew_with_usage()) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (license_state_ == kLicenseStateInitial) {
|
||||
license_state_ = kLicenseStateInitialPendingUsage;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::BeginDecryption() {
|
||||
if ((playback_start_time_ == 0) &&
|
||||
(!begin_license_usage_when_received_)) {
|
||||
switch (license_state_) {
|
||||
case kLicenseStateInitialPendingUsage:
|
||||
case kLicenseStateNeedRenewal:
|
||||
case kLicenseStateWaitingLicenseUpdate:
|
||||
playback_start_time_ = clock_->GetCurrentTime();
|
||||
|
||||
if (policy_.renew_with_usage()) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
break;
|
||||
case kLicenseStateCanPlay:
|
||||
case kLicenseStateInitial:
|
||||
case kLicenseStateExpired:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
|
||||
std::stringstream ss;
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
|
||||
if (license_state_ == kLicenseStateInitial)
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
(*key_info)[QUERY_KEY_LICENSE_TYPE] =
|
||||
license_id_.type() == video_widevine_server::sdk::STREAMING ?
|
||||
QUERY_VALUE_STREAMING : QUERY_VALUE_OFFLINE;
|
||||
(*key_info)[QUERY_KEY_PLAY_ALLOWED] = policy_.can_play() ?
|
||||
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*key_info)[QUERY_KEY_PERSIST_ALLOWED] = policy_.can_persist() ?
|
||||
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*key_info)[QUERY_KEY_RENEW_ALLOWED] = policy_.can_renew() ?
|
||||
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
int64_t remaining_time = policy_max_duration_seconds_ +
|
||||
license_received_time_ - current_time;
|
||||
if (remaining_time < 0)
|
||||
remaining_time = 0;
|
||||
ss << remaining_time;
|
||||
(*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
|
||||
remaining_time = policy_.playback_duration_seconds() + playback_start_time_ -
|
||||
current_time;
|
||||
if (remaining_time < 0)
|
||||
remaining_time = 0;
|
||||
ss << remaining_time;
|
||||
(*key_info)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str();
|
||||
(*key_info)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
|
||||
license_state_ = kLicenseStateWaitingLicenseUpdate;
|
||||
next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds();
|
||||
@@ -182,30 +276,38 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
|
||||
// will always return false if the value is 0.
|
||||
bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) {
|
||||
return policy_max_duration_seconds_ &&
|
||||
license_start_time_ + policy_max_duration_seconds_ <=
|
||||
license_received_time_ + policy_max_duration_seconds_ <=
|
||||
current_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) {
|
||||
return (policy_.playback_duration_seconds() > 0) &&
|
||||
playback_start_time_ &&
|
||||
playback_start_time_ + policy_.playback_duration_seconds() <=
|
||||
current_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
|
||||
return (policy_.renewal_delay_seconds() > 0) &&
|
||||
license_start_time_ + policy_.renewal_delay_seconds() <=
|
||||
return policy_.can_renew() &&
|
||||
(policy_.renewal_delay_seconds() > 0) &&
|
||||
license_received_time_ + policy_.renewal_delay_seconds() <=
|
||||
current_time;
|
||||
}
|
||||
|
||||
// TODO(jfore): there is some gray around how this should be
|
||||
// implemented. It currently is not.
|
||||
// TODO(jfore, edwinwong, rfrias): This field is in flux and currently
|
||||
// not implemented. Will address after possible updates from Thomas.
|
||||
bool PolicyEngine::IsRenewalRecoveryDurationExpired(
|
||||
int64_t current_time) {
|
||||
return (policy_.renewal_recovery_duration_seconds() > 0) &&
|
||||
license_start_time_ + policy_.renewal_recovery_duration_seconds() <=
|
||||
license_received_time_ + policy_.renewal_recovery_duration_seconds() <=
|
||||
current_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalRetryIntervalExpired(
|
||||
int64_t current_time) {
|
||||
return (policy_.renewal_retry_interval_seconds() > 0) &&
|
||||
next_renewal_time_ <= current_time;
|
||||
return policy_.can_renew() &&
|
||||
(policy_.renewal_retry_interval_seconds() > 0) &&
|
||||
next_renewal_time_ <= current_time;
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
|
||||
|
||||
44
libwvdrmengine/cdm/core/src/properties.cpp
Normal file
44
libwvdrmengine/cdm/core/src/properties.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "properties.h"
|
||||
#include "properties_configuration.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
Properties* Properties::instance_ = NULL;
|
||||
Lock Properties::properties_lock_;
|
||||
|
||||
Properties::Properties() {
|
||||
// Read in static properties/features
|
||||
uint32_t size = sizeof(kCdmBooleanProperties)/sizeof(CdmBooleanProperties);
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
boolean_properties_[kCdmBooleanProperties[i].name] =
|
||||
kCdmBooleanProperties[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
Properties* Properties::GetInstance() {
|
||||
AutoLock auto_lock(properties_lock_);
|
||||
if (instance_ == NULL) {
|
||||
instance_ = new Properties();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
bool Properties::GetProperty(std::string& key, bool& value) {
|
||||
CdmBooleanPropertiesMap::iterator itr = boolean_properties_.find(key);
|
||||
|
||||
if (itr == boolean_properties_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = itr->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Properties::SetProperty(std::string& key, bool value) {
|
||||
boolean_properties_[key] = value;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -33,7 +33,7 @@ class WvCdmEngineTest : public testing::Test {
|
||||
protected:
|
||||
void GenerateKeyRequest(const std::string& key_system,
|
||||
const std::string& init_data) {
|
||||
wvcdm::CdmNameValueMap app_parameters;
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
EXPECT_EQ(cdm_engine_.GenerateKeyRequest(session_id_,
|
||||
true, // is_key_system_present
|
||||
key_system,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "crypto_session.h"
|
||||
#include "license.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "policy_engine.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
namespace {
|
||||
@@ -57,7 +58,7 @@ class LicenseTest : public ::testing::Test {
|
||||
EXPECT_TRUE(crypto_engine->GetToken(&token));
|
||||
|
||||
EXPECT_TRUE(session_->IsOpen());
|
||||
EXPECT_TRUE(license_.Init(token, session_));
|
||||
EXPECT_TRUE(license_.Init(token, session_, &policy_engine_));
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
@@ -67,11 +68,12 @@ class LicenseTest : public ::testing::Test {
|
||||
|
||||
CryptoSession* session_;
|
||||
CdmLicense license_;
|
||||
PolicyEngine policy_engine_;
|
||||
};
|
||||
|
||||
TEST(LicenseTestSession, InitNullSession) {
|
||||
CdmLicense license;
|
||||
EXPECT_FALSE(license.Init("Dummy", NULL));
|
||||
EXPECT_FALSE(license.Init("Dummy", NULL, NULL));
|
||||
}
|
||||
|
||||
TEST_F(LicenseTest, PrepareKeyRequest) {
|
||||
|
||||
737
libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp
Normal file
737
libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp
Normal file
@@ -0,0 +1,737 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "clock.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "license.h"
|
||||
#include "policy_engine.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
//protobuf generated classes.
|
||||
using video_widevine_server::sdk::License;
|
||||
using video_widevine_server::sdk::License_Policy;
|
||||
using video_widevine_server::sdk::LicenseIdentification;
|
||||
using video_widevine_server::sdk::STREAMING;
|
||||
using video_widevine_server::sdk::OFFLINE;
|
||||
|
||||
// gmock methods
|
||||
using ::testing::Return;
|
||||
using ::testing::AtLeast;
|
||||
|
||||
|
||||
class MockClock : public Clock {
|
||||
public:
|
||||
MOCK_METHOD0(GetCurrentTime, int64_t());
|
||||
};
|
||||
|
||||
class PolicyEngineTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
mock_clock_ = new MockClock();
|
||||
policy_engine_ = new PolicyEngine(mock_clock_);
|
||||
|
||||
license_start_time_ = 1413517500; // ~ 01/01/2013
|
||||
license_renewal_delay_ = 604200; // 7 days - 10 minutes
|
||||
license_renewal_retry_interval_ = 30;
|
||||
license_duration_ = 604800; // 7 days
|
||||
playback_duration_ = 86400; // 24 hours
|
||||
|
||||
license_.set_license_start_time(license_start_time_);
|
||||
|
||||
LicenseIdentification* id = license_.mutable_id();
|
||||
id->set_version(1);
|
||||
id->set_type(STREAMING);
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy = license_.mutable_policy();
|
||||
policy->set_can_play(true);
|
||||
policy->set_can_persist(true);
|
||||
policy->set_can_renew(true);
|
||||
policy->set_rental_duration_seconds(license_duration_);
|
||||
policy->set_playback_duration_seconds(playback_duration_);
|
||||
policy->set_license_duration_seconds(license_duration_);
|
||||
policy->set_renewal_recovery_duration_seconds(license_duration_ -
|
||||
license_renewal_delay_); // 10 minutes
|
||||
policy->set_renewal_server_url(
|
||||
"https://jmt17.google.com/video-dev/license/GetCencLicense");
|
||||
policy->set_renewal_delay_seconds(license_renewal_delay_);
|
||||
policy->set_renewal_retry_interval_seconds(
|
||||
license_renewal_retry_interval_);
|
||||
policy->set_renew_with_usage(false);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
}
|
||||
|
||||
MockClock* mock_clock_;
|
||||
PolicyEngine* policy_engine_;
|
||||
License license_;
|
||||
License_Policy* policy_;
|
||||
|
||||
int64_t license_start_time_;
|
||||
int64_t license_renewal_delay_;
|
||||
int64_t license_renewal_retry_interval_;
|
||||
int64_t license_duration_;
|
||||
int64_t playback_duration_;
|
||||
};
|
||||
|
||||
TEST_F(PolicyEngineTest, NoLicense) {
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackSuccess) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(3)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 10));
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(3)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 10));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_can_play(false);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): persist license verification test needed
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(4)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 3600))
|
||||
.WillOnce(Return(license_start_time_ + 3601));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_rental_duration_seconds(3600);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): tests needed when begin license usage when received
|
||||
// is enabled
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(4)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 10000))
|
||||
.WillOnce(Return(license_start_time_ + 13598))
|
||||
.WillOnce(Return(license_start_time_ + 13602));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_playback_duration_seconds(3600);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(4)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 3600))
|
||||
.WillOnce(Return(license_start_time_ + 3601));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_license_duration_seconds(3600);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(4)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 3600))
|
||||
.WillOnce(Return(license_start_time_ + 3601));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_rental_duration_seconds(0);
|
||||
policy->set_license_duration_seconds(3600);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(4)
|
||||
.WillOnce(Return(license_start_time_ + 10000))
|
||||
.WillOnce(Return(license_start_time_ + 10005))
|
||||
.WillOnce(Return(license_start_time_ + 13598))
|
||||
.WillOnce(Return(license_start_time_ + 13602));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_playback_duration_seconds(0);
|
||||
policy->set_license_duration_seconds(3600);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(4)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 3600))
|
||||
.WillOnce(Return(license_start_time_ + 3601));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_license_duration_seconds(0);
|
||||
policy->set_rental_duration_seconds(3600);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(4)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 604800))
|
||||
.WillOnce(Return(license_start_time_ + 604810));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_rental_duration_seconds(0);
|
||||
policy->set_playback_duration_seconds(0);
|
||||
policy->set_license_duration_seconds(0);
|
||||
policy->set_renewal_delay_seconds(604900);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
|
||||
// TODO(edwinwong, rfrias): renewal url test needed
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(5)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||
playback_duration_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ + 10));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_can_renew(false);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(6)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||
playback_duration_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 15))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ +
|
||||
license_renewal_retry_interval_ + 10));
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
license_.set_license_start_time(license_start_time_ +
|
||||
license_renewal_delay_ + 15);
|
||||
LicenseIdentification* id = license_.mutable_id();
|
||||
id->set_version(2);
|
||||
policy_engine_->UpdateLicense(license_);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(6)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||
playback_duration_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ + 10));
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
license_.set_license_start_time(license_start_time_ +
|
||||
license_renewal_delay_ + 15);
|
||||
policy_engine_->UpdateLicense(license_);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(10)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||
playback_duration_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ + 15));
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackOk_RenewedSuccessAfterExpiry) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(10)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||
playback_duration_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 55))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 67))
|
||||
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 200));
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
license_.set_license_start_time(license_start_time_ +
|
||||
license_renewal_delay_ + 55);
|
||||
LicenseIdentification* id = license_.mutable_id();
|
||||
id->set_version(2);
|
||||
policy_engine_->UpdateLicense(license_);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(6)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 10))
|
||||
.WillOnce(Return(license_start_time_ + 20))
|
||||
.WillOnce(Return(license_start_time_ + 40))
|
||||
.WillOnce(Return(license_start_time_ + 50));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_renew_with_usage(true);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_TRUE(event_occurred);
|
||||
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||
|
||||
license_.set_license_start_time(license_start_time_ + 30);
|
||||
policy->set_renew_with_usage(false);
|
||||
LicenseIdentification* id = license_.mutable_id();
|
||||
id->set_version(2);
|
||||
policy_engine_->UpdateLicense(license_);
|
||||
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(1)
|
||||
.WillOnce(Return(license_start_time_));
|
||||
|
||||
CdmQueryMap query_info;
|
||||
EXPECT_EQ(UNKNOWN_ERROR, policy_engine_->Query(&query_info));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, QuerySuccess) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(2)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 100));
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
CdmQueryMap query_info;
|
||||
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
|
||||
EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]);
|
||||
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]);
|
||||
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
|
||||
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]);
|
||||
|
||||
int64_t remaining_time;
|
||||
std::istringstream ss;
|
||||
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
|
||||
ss >> remaining_time;
|
||||
EXPECT_LT(0, remaining_time);
|
||||
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
|
||||
ss >> remaining_time;
|
||||
EXPECT_LT(0, remaining_time);
|
||||
|
||||
EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL],
|
||||
policy->renewal_server_url());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, QuerySuccess_Offline) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(4)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 10))
|
||||
.WillOnce(Return(license_start_time_ + 100));
|
||||
|
||||
LicenseIdentification* id = license_.mutable_id();
|
||||
id->set_type(OFFLINE);
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy->set_can_play(false);
|
||||
policy->set_can_persist(false);
|
||||
policy->set_can_renew(false);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
|
||||
CdmQueryMap query_info;
|
||||
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
|
||||
EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]);
|
||||
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PLAY_ALLOWED]);
|
||||
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
|
||||
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_RENEW_ALLOWED]);
|
||||
|
||||
int64_t remaining_time;
|
||||
std::istringstream ss;
|
||||
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
|
||||
ss >> remaining_time;
|
||||
EXPECT_EQ(0, remaining_time);
|
||||
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
|
||||
ss >> remaining_time;
|
||||
EXPECT_EQ(0, remaining_time);
|
||||
|
||||
EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL],
|
||||
policy->renewal_server_url());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.Times(4)
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + 5))
|
||||
.WillOnce(Return(license_start_time_ + 10))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ + 20));
|
||||
|
||||
LicenseIdentification* id = license_.mutable_id();
|
||||
id->set_type(OFFLINE);
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
bool event_occurred;
|
||||
CdmEventType event;
|
||||
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||
EXPECT_FALSE(event_occurred);
|
||||
|
||||
policy_engine_->BeginDecryption();
|
||||
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||
|
||||
CdmQueryMap query_info;
|
||||
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
|
||||
EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]);
|
||||
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]);
|
||||
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
|
||||
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]);
|
||||
|
||||
int64_t remaining_time;
|
||||
std::istringstream ss;
|
||||
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
|
||||
ss >> remaining_time;
|
||||
EXPECT_EQ(0, remaining_time);
|
||||
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
|
||||
ss >> remaining_time;
|
||||
EXPECT_EQ(0, remaining_time);
|
||||
|
||||
EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL],
|
||||
policy->renewal_server_url());
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
18
libwvdrmengine/cdm/include/properties_configuration.h
Normal file
18
libwvdrmengine/cdm/include/properties_configuration.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef CDM_BASE_PROPERTIES_CONFIGURATION_H_
|
||||
#define CDM_BASE_PROPERTIES_CONFIGURATION_H_
|
||||
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "properties.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// set property values below
|
||||
static CdmBooleanProperties kCdmBooleanProperties[] = {
|
||||
{ .name = kPropertyKeyBeginLicenseUsageWhenReceived, .value = false }
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_
|
||||
@@ -26,7 +26,7 @@ class WvContentDecryptionModule {
|
||||
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmNameValueMap& app_parameters,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request);
|
||||
|
||||
// Accept license response and extract key info.
|
||||
@@ -36,9 +36,12 @@ class WvContentDecryptionModule {
|
||||
// Cancel session
|
||||
virtual CdmResponseType CancelKeyRequest(const CdmSessionId& session_id);
|
||||
|
||||
// Query system information
|
||||
virtual CdmResponseType QueryStatus(CdmQueryMap* key_info);
|
||||
|
||||
// Query license information
|
||||
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmNameValueMap* key_info);
|
||||
CdmQueryMap* key_info);
|
||||
|
||||
// Provisioning related methods
|
||||
virtual CdmResponseType GetProvisioningRequest(
|
||||
@@ -61,7 +64,7 @@ class WvContentDecryptionModule {
|
||||
size_t encrypted_size,
|
||||
const std::vector<uint8_t>& iv,
|
||||
size_t block_offset,
|
||||
void* decrypted_buffer);
|
||||
uint8_t* decrypted_buffer);
|
||||
|
||||
// Event listener related methods
|
||||
virtual bool AttachEventListener(CdmSessionId& session_id,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
int64_t GetCurrentTime() {
|
||||
int64_t Clock::GetCurrentTime() {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
@@ -33,7 +33,7 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmNameValueMap& app_parameters,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request) {
|
||||
CdmKeySystem key_system;
|
||||
return cdm_engine_->GenerateKeyRequest(session_id, false, key_system,
|
||||
@@ -56,9 +56,14 @@ CdmResponseType WvContentDecryptionModule::CancelKeyRequest(
|
||||
return cdm_engine_->CancelKeyRequest(session_id, false, key_system);
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::QueryStatus(
|
||||
CdmQueryMap* key_info) {
|
||||
return cdm_engine_->QueryStatus(key_info);
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::QueryKeyStatus(
|
||||
const CdmSessionId& session_id,
|
||||
CdmNameValueMap* key_info) {
|
||||
CdmQueryMap* key_info) {
|
||||
return cdm_engine_->QueryKeyStatus(session_id, key_info);
|
||||
}
|
||||
|
||||
@@ -91,7 +96,7 @@ CdmResponseType WvContentDecryptionModule::Decrypt(
|
||||
size_t encrypted_size,
|
||||
const std::vector<uint8_t>& iv,
|
||||
size_t block_offset,
|
||||
void* decrypted_buffer) {
|
||||
uint8_t* decrypted_buffer) {
|
||||
return cdm_engine_->Decrypt(session_id, is_encrypted, key_id,
|
||||
encrypted_buffer, encrypted_size, iv,
|
||||
block_offset, decrypted_buffer);
|
||||
|
||||
@@ -15,6 +15,10 @@ test_name := license_unittest
|
||||
test_src_dir := ../core/test
|
||||
include $(LOCAL_PATH)/unit-test.mk
|
||||
|
||||
test_name := policy_engine_unittest
|
||||
test_src_dir := ../core/test
|
||||
include $(LOCAL_PATH)/unit-test.mk
|
||||
|
||||
test_name := request_license_test
|
||||
test_src_dir := .
|
||||
include $(LOCAL_PATH)/unit-test.mk
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "log.h"
|
||||
#include "string_conversions.h"
|
||||
#include "url_request.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_content_decryption_module.h"
|
||||
|
||||
namespace {
|
||||
@@ -33,7 +34,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
protected:
|
||||
void GenerateKeyRequest(const std::string& key_system,
|
||||
const std::string& init_data) {
|
||||
wvcdm::CdmNameValueMap app_parameters;
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_,
|
||||
init_data,
|
||||
kLicenseTypeStreaming,
|
||||
@@ -45,7 +46,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
const std::string& init_data) {
|
||||
// TODO application makes a license request, CDM will renew the license
|
||||
// when appropriate.
|
||||
wvcdm::CdmNameValueMap app_parameters;
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_,
|
||||
init_data,
|
||||
kLicenseTypeStreaming,
|
||||
@@ -100,7 +101,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
if (is_renewal) {
|
||||
// TODO application makes a license request, CDM will renew the license
|
||||
// when appropriate
|
||||
wvcdm::CdmNameValueMap app_parameters;
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_,
|
||||
init_data,
|
||||
kLicenseTypeStreaming,
|
||||
@@ -153,6 +154,50 @@ TEST_F(WvCdmRequestLicenseTest, LicenseRenewal) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
CdmQueryMap query_info;
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryKeyStatus(session_id_, &query_info));
|
||||
|
||||
EXPECT_EQ(wvcdm::QUERY_VALUE_STREAMING,
|
||||
query_info[wvcdm::QUERY_KEY_LICENSE_TYPE]);
|
||||
EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE,
|
||||
query_info[wvcdm::QUERY_KEY_PLAY_ALLOWED]);
|
||||
EXPECT_EQ(wvcdm::QUERY_VALUE_FALSE,
|
||||
query_info[wvcdm::QUERY_KEY_PERSIST_ALLOWED]);
|
||||
EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE,
|
||||
query_info[wvcdm::QUERY_KEY_RENEW_ALLOWED]);
|
||||
|
||||
int64_t remaining_time;
|
||||
std::istringstream ss;
|
||||
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
|
||||
ss >> remaining_time;
|
||||
EXPECT_LE(0, remaining_time);
|
||||
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
|
||||
ss >> remaining_time;
|
||||
EXPECT_LE(0, remaining_time);
|
||||
|
||||
EXPECT_LE(0, (int)query_info[QUERY_KEY_RENEWAL_SERVER_URL].size());
|
||||
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, QueryStatus) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
CdmQueryMap query_info;
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryStatus(&query_info));
|
||||
|
||||
EXPECT_EQ(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3,
|
||||
query_info[wvcdm::QUERY_KEY_SECURITY_LEVEL]);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
@@ -20,6 +20,8 @@ LOCAL_C_INCLUDES := \
|
||||
bionic \
|
||||
external/gtest/include \
|
||||
external/stlport/stlport \
|
||||
$(LOCAL_PATH)/core/test/include \
|
||||
vendor/widevine/libwvdrmengine/test/gmock/include \
|
||||
vendor/widevine/libwvdrmengine/cdm/core/include \
|
||||
vendor/widevine/libwvdrmengine/cdm/core/test \
|
||||
vendor/widevine/libwvdrmengine/cdm/include
|
||||
@@ -31,18 +33,20 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers)
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libcdm \
|
||||
libgmock \
|
||||
libgtest \
|
||||
libgtest_main \
|
||||
libl3crypto \
|
||||
libprotobuf-cpp-2.3.0-lite
|
||||
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := \
|
||||
license_protocol_protos
|
||||
cdm_protos
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libstlport \
|
||||
libchromium_net \
|
||||
libcrypto \
|
||||
liboemcrypto \
|
||||
libdl \
|
||||
libstlport \
|
||||
libutils
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
@@ -2,18 +2,20 @@ LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
|
||||
# TODO(fredgc): remove mock code as real code starts working.
|
||||
LOCAL_SRC_FILES := \
|
||||
$(TARGET_ARCH)/l3crypto_engine_mock.cpp \
|
||||
$(TARGET_ARCH)/l3crypto_key_mock.cpp \
|
||||
$(TARGET_ARCH)/l3crypto_keybox_mock.cpp \
|
||||
$(TARGET_ARCH)/l3crypto_mock.cpp \
|
||||
$(TARGET_ARCH)/lock.cpp \
|
||||
$(TARGET_ARCH)/log.cpp \
|
||||
$(TARGET_ARCH)/string_conversions.cpp \
|
||||
$(TARGET_ARCH)/wvcrc.cpp \
|
||||
$(TARGET_ARCH)/entry_points.cpp \
|
||||
../oemcrypto/mock/src/oemcrypto_engine_mock.cpp \
|
||||
../oemcrypto/mock/src/oemcrypto_key_mock.cpp \
|
||||
../oemcrypto/mock/src/oemcrypto_keybox_mock.cpp \
|
||||
../oemcrypto/mock/src/lock.cpp \
|
||||
../oemcrypto/mock/src/log.cpp \
|
||||
../oemcrypto/mock/src/string_conversions.cpp \
|
||||
../oemcrypto/mock/src/wvcrc.cpp \
|
||||
|
||||
# TODO(fredgc): remove mock include when real code starts working.
|
||||
LOCAL_C_INCLUDES += \
|
||||
vendor/widevine/libwvdrmengine/oemcrypto/mock/src \
|
||||
bionic \
|
||||
external/openssh \
|
||||
external/openssl/include \
|
||||
@@ -29,7 +31,6 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
libcutils \
|
||||
libdl \
|
||||
liblog \
|
||||
liboemcrypto \
|
||||
libstlport \
|
||||
libutils \
|
||||
libz \
|
||||
|
||||
1162
libwvdrmengine/level3/arm/entry_points.cpp
Normal file
1162
libwvdrmengine/level3/arm/entry_points.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -21,12 +21,14 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libgmock \
|
||||
libgmock_main \
|
||||
libgtest \
|
||||
libl3crypto \
|
||||
libprotobuf-cpp-2.3.0-lite \
|
||||
libwvdrmcryptoplugin \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcrypto \
|
||||
libdl \
|
||||
liblog \
|
||||
liboemcrypto \
|
||||
libstlport \
|
||||
libutils \
|
||||
|
||||
@@ -43,7 +45,7 @@ LOCAL_C_INCLUDES += \
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES += $(proto_generated_headers)
|
||||
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := \
|
||||
license_protocol_protos \
|
||||
cdm_protos
|
||||
|
||||
# End protobuf section
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ status_t WVDrmPlugin::getKeyRequest(
|
||||
CdmInitData cdmInitData(initData.begin(), initData.end());
|
||||
// TODO: Do something with mimeType?
|
||||
|
||||
CdmNameValueMap cdmParameters;
|
||||
CdmAppParameterMap cdmParameters;
|
||||
for (size_t i = 0; i < optionalParameters.size(); ++i) {
|
||||
const String8& key = optionalParameters.keyAt(i);
|
||||
const String8& value = optionalParameters.valueAt(i);
|
||||
@@ -132,7 +132,7 @@ status_t WVDrmPlugin::queryKeyStatus(
|
||||
const Vector<uint8_t>& sessionId,
|
||||
KeyedVector<String8, String8>& infoMap) const {
|
||||
CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end());
|
||||
CdmNameValueMap cdmLicenseInfo;
|
||||
CdmQueryMap cdmLicenseInfo;
|
||||
|
||||
CdmResponseType res = mCDM->QueryKeyStatus(cdmSessionId, &cdmLicenseInfo);
|
||||
|
||||
@@ -141,7 +141,7 @@ status_t WVDrmPlugin::queryKeyStatus(
|
||||
}
|
||||
|
||||
infoMap.clear();
|
||||
for (CdmNameValueMap::const_iterator iter = cdmLicenseInfo.begin();
|
||||
for (CdmQueryMap::const_iterator iter = cdmLicenseInfo.begin();
|
||||
iter != cdmLicenseInfo.end();
|
||||
++iter) {
|
||||
const string& cdmKey = iter->first;
|
||||
|
||||
@@ -21,12 +21,14 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libgmock \
|
||||
libgmock_main \
|
||||
libgtest \
|
||||
libl3crypto \
|
||||
libprotobuf-cpp-2.3.0-lite \
|
||||
libwvdrmdrmplugin \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcrypto \
|
||||
libdl \
|
||||
liblog \
|
||||
liboemcrypto \
|
||||
libstlport \
|
||||
libutils \
|
||||
|
||||
@@ -43,7 +45,7 @@ LOCAL_C_INCLUDES += \
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES += $(proto_generated_headers)
|
||||
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := \
|
||||
license_protocol_protos \
|
||||
cdm_protos
|
||||
|
||||
# End protobuf section
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
MOCK_METHOD5(GenerateKeyRequest, CdmResponseType(const CdmSessionId&,
|
||||
const CdmInitData&,
|
||||
const CdmLicenseType,
|
||||
CdmNameValueMap&,
|
||||
CdmAppParameterMap&,
|
||||
CdmKeyMessage*));
|
||||
|
||||
MOCK_METHOD2(AddKey, CdmResponseType(const CdmSessionId&,
|
||||
@@ -34,7 +34,7 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
MOCK_METHOD1(CancelKeyRequest, CdmResponseType(const CdmSessionId&));
|
||||
|
||||
MOCK_METHOD2(QueryKeyStatus, CdmResponseType(const CdmSessionId&,
|
||||
CdmNameValueMap*));
|
||||
CdmQueryMap*));
|
||||
|
||||
MOCK_METHOD2(GetProvisioningRequest, CdmResponseType(CdmProvisioningRequest*,
|
||||
std::string*));
|
||||
|
||||
@@ -91,7 +91,7 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
||||
Vector<uint8_t> request;
|
||||
|
||||
KeyedVector<String8, String8> parameters;
|
||||
CdmNameValueMap cdmParameters;
|
||||
CdmAppParameterMap cdmParameters;
|
||||
|
||||
parameters.add(String8("paddingScheme"), String8("PKCS7"));
|
||||
cdmParameters["paddingScheme"] = "PKCS7";
|
||||
@@ -110,7 +110,7 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
||||
kInitDataSize),
|
||||
kLicenseTypeOffline, cdmParameters, _))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmRequest),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::KEY_MESSAGE)));
|
||||
|
||||
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId,
|
||||
ElementsAreArray(initDataRaw,
|
||||
@@ -118,21 +118,21 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
||||
kLicenseTypeStreaming, cdmParameters,
|
||||
_))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmRequest),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::KEY_MESSAGE)));
|
||||
}
|
||||
|
||||
status_t res = plugin.getLicenseRequest(sessionId, initData,
|
||||
String8("video/h264"),
|
||||
DrmPlugin::kLicenseType_Offline,
|
||||
parameters, request, defaultUrl);
|
||||
status_t res = plugin.getKeyRequest(sessionId, initData,
|
||||
String8("video/h264"),
|
||||
DrmPlugin::kKeyType_Offline,
|
||||
parameters, request, defaultUrl);
|
||||
|
||||
ASSERT_EQ(OK, res);
|
||||
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
||||
EXPECT_TRUE(defaultUrl.isEmpty());
|
||||
|
||||
res = plugin.getLicenseRequest(sessionId, initData, String8("video/h264"),
|
||||
DrmPlugin::kLicenseType_Streaming, parameters,
|
||||
request, defaultUrl);
|
||||
res = plugin.getKeyRequest(sessionId, initData, String8("video/h264"),
|
||||
DrmPlugin::kKeyType_Streaming, parameters,
|
||||
request, defaultUrl);
|
||||
|
||||
ASSERT_EQ(OK, res);
|
||||
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
||||
@@ -152,33 +152,27 @@ TEST_F(WVDrmPluginTest, AddsKeys) {
|
||||
Vector<uint8_t> response;
|
||||
response.appendArray(responseRaw, kResponseSize);
|
||||
|
||||
// TODO: Do something with the key set ID.
|
||||
Vector<uint8_t> ignoredKeySetId;
|
||||
|
||||
EXPECT_CALL(cdm, AddKey(cdmSessionId, ElementsAreArray(responseRaw,
|
||||
kResponseSize)))
|
||||
.Times(1);
|
||||
.WillOnce(Return(wvcdm::KEY_ADDED));
|
||||
|
||||
status_t res = plugin.provideLicenseResponse(sessionId, response);
|
||||
status_t res = plugin.provideKeyResponse(sessionId, response,
|
||||
ignoredKeySetId);
|
||||
|
||||
ASSERT_EQ(OK, res);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, CancelsKeyRequests) {
|
||||
MockCDM cdm;
|
||||
WVDrmPlugin plugin(&cdm);
|
||||
|
||||
EXPECT_CALL(cdm, CancelKeyRequest(cdmSessionId))
|
||||
.Times(1);
|
||||
|
||||
status_t res = plugin.removeLicense(sessionId);
|
||||
|
||||
ASSERT_EQ(OK, res);
|
||||
}
|
||||
// TODO: Reinstate removeKeys() test once its behavior is finalized.
|
||||
|
||||
TEST_F(WVDrmPluginTest, QueriesKeyStatus) {
|
||||
MockCDM cdm;
|
||||
WVDrmPlugin plugin(&cdm);
|
||||
|
||||
KeyedVector<String8, String8> expectedLicenseStatus;
|
||||
CdmNameValueMap cdmLicenseStatus;
|
||||
CdmQueryMap cdmLicenseStatus;
|
||||
|
||||
expectedLicenseStatus.add(String8("areTheKeysAllRight"), String8("yes"));
|
||||
cdmLicenseStatus["areTheKeysAllRight"] = "yes";
|
||||
@@ -193,7 +187,7 @@ TEST_F(WVDrmPluginTest, QueriesKeyStatus) {
|
||||
|
||||
KeyedVector<String8, String8> licenseStatus;
|
||||
|
||||
status_t res = plugin.queryLicenseStatus(sessionId, licenseStatus);
|
||||
status_t res = plugin.queryKeyStatus(sessionId, licenseStatus);
|
||||
|
||||
ASSERT_EQ(OK, res);
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define OEMCRYPTO_VERSION "5.0"
|
||||
#define OEMCRYPTO_VERSION "7.0"
|
||||
static const char oec_version[] = OEMCRYPTO_VERSION;
|
||||
static const uint32_t oec_latest_version = 7;
|
||||
|
||||
typedef uint32_t OEMCrypto_SESSION;
|
||||
|
||||
@@ -144,6 +145,7 @@ typedef struct {
|
||||
size_t key_id_length;
|
||||
const uint8_t* key_data_iv;
|
||||
const uint8_t* key_data;
|
||||
size_t key_data_length;
|
||||
const uint8_t* key_control_iv;
|
||||
const uint8_t* key_control;
|
||||
} OEMCrypto_KeyObject;
|
||||
@@ -153,12 +155,12 @@ typedef struct {
|
||||
* Points to the relevant fields for renewing a content key. The fields are
|
||||
* extracted from the License Renewal Response message offered to
|
||||
* OEMCrypto_RefreshKeys(). Each field points to one of the components of
|
||||
* the key. All fields are 128 bits (16 bytes):
|
||||
* the key.
|
||||
* key_id - the unique id of this key.
|
||||
* key_control_iv - the IV for performing AES-128-CBC decryption of the
|
||||
* key_control field.
|
||||
* key_control field. 16 bytes.
|
||||
* key_control - the key control block. It is encrypted (AES-128-CBC) with
|
||||
* the content key from the key_data field.
|
||||
* the content key from the key_data field. 16 bytes.
|
||||
*
|
||||
* The key_data is unchanged from the original OEMCrypto_LoadKeys() call. Some
|
||||
* Key Control Block fields, especially those related to key lifetime, may
|
||||
@@ -174,6 +176,17 @@ typedef struct {
|
||||
const uint8_t* key_control;
|
||||
} OEMCrypto_KeyRefreshObject;
|
||||
|
||||
/*
|
||||
* OEMCrypto_Algorithm
|
||||
* This is a list of valid algorithms for OEMCrypto_Generic_* functions.
|
||||
* Some are valid for encryption/decryption, and some for signing/verifying.
|
||||
*/
|
||||
typedef enum OEMCrypto_Algorithm {
|
||||
OEMCrypto_AES_CBC_128_NO_PADDING = 0,
|
||||
OEMCrypto_HMAC_SHA256 = 1,
|
||||
} OEMCrypto_Algorithm;
|
||||
|
||||
/* Obfuscation Renames. */
|
||||
#define OEMCrypto_Initialize _oecc01
|
||||
#define OEMCrypto_Terminate _oecc02
|
||||
#define OEMCrypto_InstallKeybox _oecc03
|
||||
@@ -195,6 +208,12 @@ typedef struct {
|
||||
#define OEMCrypto_LoadDeviceRSAKey _oecc19
|
||||
#define OEMCrypto_GenerateRSASignature _oecc20
|
||||
#define OEMCrypto_DeriveKeysFromSessionKey _oecc21
|
||||
#define OEMCrypto_APIVersion _oecc22
|
||||
#define OEMCrypto_SecurityLevel _oecc23
|
||||
#define OEMCrypto_Generic_Encrypt _oecc24
|
||||
#define OEMCrypto_Generic_Decrypt _oecc25
|
||||
#define OEMCrypto_Generic_Sign _oecc26
|
||||
#define OEMCrypto_Generic_Virify _oecc27
|
||||
|
||||
/*
|
||||
* OEMCrypto_Initialize
|
||||
@@ -212,6 +231,9 @@ typedef struct {
|
||||
* Returns:
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware
|
||||
*
|
||||
* Version:
|
||||
* This method is supported by all API versions.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_Initialize(void);
|
||||
|
||||
@@ -232,6 +254,9 @@ OEMCryptoResult OEMCrypto_Initialize(void);
|
||||
* Returns:
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto hardware
|
||||
*
|
||||
* Version:
|
||||
* This method is all API versions.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_Terminate(void);
|
||||
|
||||
@@ -253,6 +278,9 @@ OEMCryptoResult OEMCrypto_Terminate(void);
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_TOO_MANY_SESSIONS failed because too many sessions are open
|
||||
* OEMCrypto_ERROR_OPEN_SESSION_FAILED failed to initialize the crypto session
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session);
|
||||
|
||||
@@ -274,6 +302,9 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session);
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_INVALID_SESSION no open session with that id.
|
||||
* OEMCrypto_ERROR_CLOSE_SESSION_FAILED failed to terminate the crypto session
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session);
|
||||
|
||||
@@ -315,6 +346,9 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session);
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
|
||||
OEMCrypto_SESSION session,
|
||||
@@ -351,6 +385,9 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GenerateNonce(
|
||||
OEMCrypto_SESSION session,
|
||||
@@ -384,8 +421,12 @@ OEMCryptoResult OEMCrypto_GenerateNonce(
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_NO_DEVICE_KEY
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_SHORT_BUFFER
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GenerateSignature(
|
||||
OEMCrypto_SESSION session,
|
||||
@@ -459,6 +500,9 @@ OEMCryptoResult OEMCrypto_GenerateSignature(
|
||||
* OEMCrypto_ERROR_SIGNATURE_FAILURE
|
||||
* OEMCrypto_ERROR_INVALID_NONCE
|
||||
* OEMCrypto_ERROR_TOO_MANY_KEYS
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
@@ -510,6 +554,9 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
* OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
* OEMCrypto_ERROR_INVALID_NONCE
|
||||
* OEMCrypto_ERROR_SIGNATURE_FAILURE
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
*/
|
||||
OEMCryptoResult
|
||||
OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
|
||||
@@ -564,6 +611,9 @@ OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
|
||||
* OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key
|
||||
* OEMCrypto_ERROR_CONTROL_INVALID invalid or unsupported control input
|
||||
* OEMCrypto_ERROR_KEYBOX_INVALID cannot decrypt and read from Keybox
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
|
||||
const uint8_t* key_id,
|
||||
@@ -636,6 +686,9 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
* OEMCrypto_ERROR_DECRYPT_FAILED
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
*/
|
||||
OEMCryptoResult
|
||||
OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
|
||||
@@ -667,6 +720,9 @@ OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
|
||||
* Returns:
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_WRITE_KEYBOX failed to handle and store Keybox
|
||||
*
|
||||
* Version:
|
||||
* This method is all API versions.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,
|
||||
size_t keyBoxLength);
|
||||
@@ -693,6 +749,9 @@ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,
|
||||
* OEMCrypto_SUCCESS
|
||||
* OEMCrypto_ERROR_BAD_MAGIC
|
||||
* OEMCrypto_ERROR_BAD_CRC
|
||||
*
|
||||
* Version:
|
||||
* This method is supported by all API versions.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(void);
|
||||
|
||||
@@ -714,6 +773,9 @@ OEMCryptoResult OEMCrypto_IsKeyboxValid(void);
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_SHORT_BUFFER buffer is too small to return the device ID
|
||||
* OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id
|
||||
*
|
||||
* Version:
|
||||
* This method is supported by all API versions.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
|
||||
size_t *idLength);
|
||||
@@ -743,6 +805,9 @@ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_SHORT_BUFFER the buffer is too small to return the KeyData
|
||||
* OEMCrypto_ERROR_NO_KEYDATA failed to return KeyData
|
||||
*
|
||||
* Version:
|
||||
* This method is supported by all API versions.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
|
||||
size_t *keyDataLength);
|
||||
@@ -766,6 +831,9 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_RNG_FAILED failed to generate random number
|
||||
* OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported
|
||||
*
|
||||
* Version:
|
||||
* This method is supported by all API versions.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData,
|
||||
size_t dataLength);
|
||||
@@ -795,6 +863,9 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData,
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_WRAP_KEYBOX failed to wrap Keybox
|
||||
* OEMCrypto_ERROR_NOT_IMPLEMENTED
|
||||
*
|
||||
* Version:
|
||||
* This method is supported by all API versions.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,
|
||||
size_t keyBoxLength,
|
||||
@@ -848,6 +919,9 @@ OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,
|
||||
* OEMCrypto_ERROR_SIGNATURE_FAILURE
|
||||
* OEMCrypto_ERROR_INVALID_NONCE
|
||||
* OEMCrypto_ERROR_SHORT_BUFFER
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API versions 6.
|
||||
*/
|
||||
|
||||
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
@@ -855,7 +929,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
uint32_t *nonce,
|
||||
const uint32_t *nonce,
|
||||
const uint8_t* enc_rsa_key,
|
||||
size_t enc_rsa_key_length,
|
||||
const uint8_t* enc_rsa_key_iv,
|
||||
@@ -889,6 +963,9 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_ERROR_INVALID_RSA_KEY
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 6.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
const uint8_t* wrapped_rsa_key,
|
||||
@@ -923,6 +1000,9 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
* OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small.
|
||||
* OEMCrypto_ERROR_CLOSE_SESSION_FAILED illegal/unrecognized handle or the
|
||||
* security engine is not properly initialized.
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 6.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
@@ -966,6 +1046,9 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 6.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(OEMCrypto_SESSION session,
|
||||
const uint8_t* enc_session_key,
|
||||
@@ -975,6 +1058,218 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(OEMCrypto_SESSION session,
|
||||
const uint8_t *enc_key_context,
|
||||
size_t enc_key_context_length);
|
||||
|
||||
|
||||
/*
|
||||
* OEMCrypto_APIVersion()
|
||||
*
|
||||
* Description:
|
||||
* This function returns the current API version number. Because this
|
||||
* API is part of a shared library, the version number allows the calling
|
||||
* application to avoid version mis-match errors.
|
||||
*
|
||||
* There is a possibility that some API methods will be backwards compatible,
|
||||
* or backwards compatible at a reduced security level.
|
||||
*
|
||||
* There is no plan to introduce forward-compatibility. I.e. applications
|
||||
* will reject a library with a newer version of the API.
|
||||
*
|
||||
* Returns:
|
||||
* The current version number.
|
||||
*
|
||||
* Version:
|
||||
* This method should change in all API versions.
|
||||
*/
|
||||
uint32_t OEMCrypto_APIVersion();
|
||||
|
||||
/*
|
||||
* OEMCrypto_SecurityLevel()
|
||||
*
|
||||
* Description:
|
||||
* This function returns the security level of the OEMCrypto library.
|
||||
*
|
||||
* Since this function is spoofable, it is not relied on for security
|
||||
* purposes. It is for information only.
|
||||
*
|
||||
* Returns:
|
||||
* A null terminated string. Useful values are "L1", "L2" or "L3".
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 6.
|
||||
*/
|
||||
const char* OEMCrypto_SecurityLevel();
|
||||
|
||||
/*
|
||||
* OEMCryptoResult OEMCrypto_Generic_Encrypt
|
||||
*
|
||||
* This function encrypts a generic buffer of data using the current key.
|
||||
*
|
||||
* Verification:
|
||||
* The following checks should be performed. If any check fails, an error is
|
||||
* returned, and the data is not encrypted.
|
||||
*
|
||||
* The control bit for the current key shall have the Allow_Encrypt set. If
|
||||
* not, return OEMCrypto_ERROR_UNKNOWN_FAILURE.
|
||||
*
|
||||
* Parameters:
|
||||
* [in] session: crypto session identifier.
|
||||
* [in] in_buffer: pointer to memory containing data to be encrypted.
|
||||
* [in] buffer_length: length of the buffer, in bytes.
|
||||
* [in] iv: IV for encrypting data. Size is specified by the algorithm.
|
||||
* [in] algorithm: Specifies which encryption algorithm to use. See
|
||||
* OEMCrypto_Algorithm for valid values.
|
||||
* [out] out_buffer: pointer to buffer in which encrypted data should be stored.
|
||||
*
|
||||
* Returns:
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_NO_DEVICE_KEY
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
*
|
||||
* Threading:
|
||||
* This function may be called simultaneously with functions on other sessions,
|
||||
* but not with other functions on this session.
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 7.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session,
|
||||
const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/*
|
||||
* OEMCrypto_Generic_Decrypt
|
||||
*
|
||||
* This function decrypts a generic buffer of data using the current key.
|
||||
*
|
||||
* Verification:
|
||||
* The following checks should be performed. If any check fails, an error is
|
||||
* returned, and the data is not decrypted.
|
||||
*
|
||||
* The control bit for the current key shall have the Allow_Decrypt set. If
|
||||
* not, return OEMCrypto_ERROR_DECRYPT_FAILED.
|
||||
* If the current key’s control block has the Data_Path_Type bit set, then
|
||||
* return OEMCrypto_ERROR_DECRYPT_FAILED.
|
||||
* If the current key’s control block has the HDCP bit set, then return
|
||||
* OEMCrypto_ERROR_DECRYPT_FAILED.
|
||||
*
|
||||
* Parameters:
|
||||
* [in] session: crypto session identifier.
|
||||
* [in] in_buffer: pointer to memory containing data to be encrypted.
|
||||
* [in] buffer_length: length of the buffer, in bytes.
|
||||
* [in] iv: IV for encrypting data. Size depends on the algorithm.
|
||||
* [in] algorithm: Specifies which encryption algorithm to use. See
|
||||
* OEMCrypto_Algorithm for valid values.
|
||||
* [out] out_buffer: pointer to buffer in which decrypted data should be stored.
|
||||
*
|
||||
* Returns:
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_NO_DEVICE_KEY
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
*
|
||||
* Threading:
|
||||
* This function may be called simultaneously with functions on other sessions,
|
||||
* but not with other functions on this session.
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 7.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session,
|
||||
const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/*
|
||||
* OEMCrypto_Generic_Sign
|
||||
*
|
||||
* This function signs a generic buffer of data using the current key.
|
||||
*
|
||||
* Verification
|
||||
* The following checks should be performed. If any check fails,
|
||||
* an error is returned, and the signature is not generated.
|
||||
*
|
||||
* The control bit for the current key shall have the Allow_Sign set.
|
||||
*
|
||||
* Parameters
|
||||
* [in] session: crypto session identifier.
|
||||
* [in] in_buffer: pointer to memory containing data to be encrypted.
|
||||
* [in] buffer_length: length of the buffer, in bytes.
|
||||
* [in] algorithm: Specifies which algorithm to use. See
|
||||
* OEMCrypto_Algorithm for valid values.
|
||||
* [out] signature: pointer to buffer in which signature should be stored.
|
||||
* [in/out] signature_length: (in) length of the signature buffer, in bytes.
|
||||
* (out) actual length of the signature
|
||||
*
|
||||
* Returns
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to hold
|
||||
* buffer.
|
||||
* OEMCrypto_ERROR_NO_DEVICE_KEY
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* Threading
|
||||
* This function may be called simultaneously with functions on other sessions,
|
||||
* but not with other functions on this session.
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 7.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
|
||||
const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length);
|
||||
|
||||
/*
|
||||
* OEMCrypto_Generic_Verify
|
||||
* This function verfies the signature of a generic buffer of data using the
|
||||
* current key.
|
||||
*
|
||||
* Verification
|
||||
* The following checks should be performed. If any check fails, an error is
|
||||
* returned, and the data is not signed.
|
||||
*
|
||||
* The control bit for the current key shall have the Allow_Verify set.
|
||||
* The signature of the message shall be computed, and the API shall verify the
|
||||
* computed signature matches the signature passed in. If not, return
|
||||
* OEMCrypto_ERROR_SIGNATURE_FAILURE
|
||||
*
|
||||
* Parameters
|
||||
* [in] session: crypto session identifier.
|
||||
* [in] in_buffer: pointer to memory containing data to be encrypted.
|
||||
* [in] buffer_length: length of the buffer, in bytes.
|
||||
* [in] algorithm: Specifies which algorithm to use. Current valid value is
|
||||
* HMAC_SHA256.
|
||||
* [in] signature: pointer to signature buffer.
|
||||
* [in] signature_length: length of the signature buffer, in bytes.
|
||||
*
|
||||
* Returns:
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_SIGNATURE_FAILURE
|
||||
* OEMCrypto_ERROR_NO_DEVICE_KEY
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
*
|
||||
* Threading:
|
||||
* This function may be called simultaneously with functions on other sessions,
|
||||
* but not with other functions on this session.
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 7.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session,
|
||||
const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -32,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
libutils \
|
||||
libz \
|
||||
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)
|
||||
LOCAL_MODULE := liboemcrypto
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -17,7 +17,7 @@ typedef enum {
|
||||
LOG_VERBOSE
|
||||
} LogPriority;
|
||||
|
||||
void log_write(LogPriority priority, const char *fmt, ...);
|
||||
void log_write(LogPriority priority, const char* fmt, ...);
|
||||
|
||||
// Log APIs
|
||||
#define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__))
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
|
||||
static const int kPssSaltLength = 20;
|
||||
|
||||
namespace {
|
||||
// Increment counter for AES-CTR
|
||||
void ctr128_inc(uint8_t* counter) {
|
||||
@@ -36,7 +39,7 @@ void ctr128_inc(uint8_t* counter) {
|
||||
void dump_openssl_error() {
|
||||
while (unsigned long err = ERR_get_error()) {
|
||||
char buffer[120];
|
||||
LOGE("openssl error: %lu: %s",
|
||||
LOGE("openssl error -- %lu -- %s",
|
||||
err, ERR_error_string(err, buffer));
|
||||
}
|
||||
}
|
||||
@@ -139,6 +142,13 @@ bool SessionContext::DeriveKeys(const std::vector<uint8_t>& mac_key_context,
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0 // Print Derived Keys to stdout.
|
||||
std::cout << " mac_key_context = " << wvcdm::b2a_hex(mac_key_context) << std::endl;
|
||||
std::cout << " enc_key_context = " << wvcdm::b2a_hex(enc_key_context) << std::endl;
|
||||
std::cout << " mac_key = " << wvcdm::b2a_hex(mac_key) << std::endl;
|
||||
std::cout << " enc_key = " << wvcdm::b2a_hex(enc_key) << std::endl;
|
||||
#endif
|
||||
|
||||
set_mac_key(mac_key);
|
||||
set_encryption_key(enc_key);
|
||||
return true;
|
||||
@@ -151,21 +161,40 @@ bool SessionContext::RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
|
||||
LOGE("[RSADeriveKeys(): no RSA key set]");
|
||||
return false;
|
||||
}
|
||||
session_key_.resize(wvcdm::KEY_SIZE);
|
||||
if (-1 == RSA_private_decrypt(wvcdm::KEY_SIZE, &enc_session_key[0],
|
||||
&session_key_[0], rsa_key_,
|
||||
RSA_PKCS1_OAEP_PADDING)) {
|
||||
if (enc_session_key.size() != static_cast<size_t>(RSA_size(rsa_key_))) {
|
||||
LOGE("[RSADeriveKeys(): encrypted session key is wrong size:%zu, should be %d]",
|
||||
enc_session_key.size(), RSA_size(rsa_key_));
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
session_key_.resize(RSA_size(rsa_key_));
|
||||
int decrypted_size = RSA_private_decrypt(enc_session_key.size(),
|
||||
&enc_session_key[0],
|
||||
&session_key_[0], rsa_key_,
|
||||
RSA_PKCS1_OAEP_PADDING);
|
||||
if (-1 == decrypted_size) {
|
||||
LOGE("[RSADeriveKeys(): error decrypting session key.]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
session_key_.resize(decrypted_size);
|
||||
if (decrypted_size != static_cast<int>(wvcdm::KEY_SIZE)) {
|
||||
LOGE("[RSADeriveKeys(): error. session key is wrong size: %d.]",
|
||||
decrypted_size);
|
||||
dump_openssl_error();
|
||||
session_key_.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate derived key for mac key
|
||||
std::vector<uint8_t> mac_key;
|
||||
std::vector<uint8_t> mac_key_part2;
|
||||
if (!DeriveKey(session_key_, mac_key_context, 1, &mac_key)) {
|
||||
session_key_.clear();
|
||||
return false;
|
||||
}
|
||||
if (!DeriveKey(session_key_, mac_key_context, 2, &mac_key_part2)) {
|
||||
session_key_.clear();
|
||||
return false;
|
||||
}
|
||||
mac_key.insert(mac_key.end(), mac_key_part2.begin(), mac_key_part2.end());
|
||||
@@ -173,9 +202,18 @@ bool SessionContext::RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
|
||||
// Generate derived key for encryption key
|
||||
std::vector<uint8_t> enc_key;
|
||||
if (!DeriveKey(session_key_, enc_key_context, 1, &enc_key)) {
|
||||
session_key_.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0 // Print Derived Keys to stdout.
|
||||
std::cout << " mac_key_context = " << wvcdm::b2a_hex(mac_key_context) << std::endl;
|
||||
std::cout << " enc_key_context = " << wvcdm::b2a_hex(enc_key_context) << std::endl;
|
||||
std::cout << " session_key = " << wvcdm::b2a_hex(session_key_) << std::endl;
|
||||
std::cout << " mac_key = " << wvcdm::b2a_hex(mac_key) << std::endl;
|
||||
std::cout << " enc_key = " << wvcdm::b2a_hex(enc_key) << std::endl;
|
||||
#endif
|
||||
|
||||
set_mac_key(mac_key);
|
||||
set_encryption_key(enc_key);
|
||||
return true;
|
||||
@@ -212,6 +250,14 @@ bool SessionContext::GenerateSignature(const uint8_t* message,
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t SessionContext::RSASignatureSize() {
|
||||
if (!rsa_key_) {
|
||||
LOGE("[GenerateRSASignature(): no RSA key set]");
|
||||
return 0;
|
||||
}
|
||||
return static_cast<size_t>(RSA_size(rsa_key_));
|
||||
}
|
||||
|
||||
bool SessionContext::GenerateRSASignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
@@ -229,19 +275,30 @@ bool SessionContext::GenerateRSASignature(const uint8_t* message,
|
||||
*signature_length = RSA_size(rsa_key_);
|
||||
return false;
|
||||
}
|
||||
// TODO(fredgc): This uses the wrong algorithm for signing.
|
||||
// This code needs to be fixed!!
|
||||
LOGE("COmputing signature using RSASSA-PKCS1v1.5 instead of RSASSA-PSS");
|
||||
|
||||
// Hash the message using SHA1.
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
if (!SHA1(message, message_length, hash)) {
|
||||
LOGE("[GeneratRSASignature(): error creating signature hash.]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
int ret = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH,
|
||||
signature, signature_length, rsa_key_);
|
||||
if (ret != 1) {
|
||||
LOGE("[GeneratRSASignature(): error signing signature hash.]");
|
||||
|
||||
// Add PSS padding.
|
||||
uint8_t padded_digest[*signature_length];
|
||||
int status = RSA_padding_add_PKCS1_PSS(rsa_key_, padded_digest, hash,
|
||||
EVP_sha1(), kPssSaltLength);
|
||||
if (status == -1) {
|
||||
LOGE("[GeneratRSASignature(): error padding hash.]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Encrypt PSS padded digest.
|
||||
status = RSA_private_encrypt(*signature_length, padded_digest, signature,
|
||||
rsa_key_, RSA_NO_PADDING);
|
||||
if (status == -1) {
|
||||
LOGE("[GeneratRSASignature(): error in private encrypt.]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
@@ -387,10 +444,17 @@ bool SessionContext::EncryptRSAKey(uint8_t* wrapped_rsa_key,
|
||||
uint8_t* p = &buffer[0];
|
||||
int len = i2d_RSAPrivateKey(rsa_key_, &p);
|
||||
if (len < 0) {
|
||||
LOGE("[RewrapRSAKey(): Could not decode rsa key]");
|
||||
LOGE("[EncryptRSAKey(): Could not decode rsa key]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
if (static_cast<size_t>(len) >= wrapped_rsa_key_length) {
|
||||
LOGE("[EncryptRSAKey(): padding is wrong size: len=%d, size=%zu",
|
||||
len, wrapped_rsa_key_length);
|
||||
return false;
|
||||
}
|
||||
size_t padding = wrapped_rsa_key_length - len;
|
||||
memset(&buffer[len], static_cast<uint8_t>(padding), padding);
|
||||
|
||||
// Encrypt rsa key with keybox.
|
||||
uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE];
|
||||
@@ -409,28 +473,35 @@ bool SessionContext::LoadRSAKey(const uint8_t* enc_rsa_key,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
std::vector<uint8_t> enc_rsa_key_v(enc_rsa_key,
|
||||
enc_rsa_key + enc_rsa_key_length);
|
||||
std::vector<uint8_t> iv(enc_rsa_key_iv, enc_rsa_key_iv + wvcdm::KEY_IV_SIZE);
|
||||
std::vector<uint8_t> rsa_key; // unencrypted.
|
||||
|
||||
// Validate message signature
|
||||
if (!ValidateMessage(message, message_length, signature, signature_length)) {
|
||||
LOGE("[LoadRSAKey(): Could not verify signature]");
|
||||
return false;
|
||||
}
|
||||
if (!ce_->DecryptMessage(this, encryption_key_, iv,
|
||||
enc_rsa_key_v, &rsa_key)) {
|
||||
LOGE("[LoadRSAKey(): Could not decrypt key data]");
|
||||
|
||||
uint8_t iv[wvcdm::KEY_IV_SIZE];
|
||||
uint8_t* clear = new uint8_t[enc_rsa_key_length];
|
||||
memcpy(iv, enc_rsa_key_iv, 16);
|
||||
AES_KEY aes_key;
|
||||
AES_set_decrypt_key(&encryption_key_[0], 128, &aes_key);
|
||||
AES_cbc_encrypt(enc_rsa_key, clear, enc_rsa_key_length, &aes_key,
|
||||
iv, AES_DECRYPT);
|
||||
size_t padding = clear[enc_rsa_key_length - 1];
|
||||
if (padding > 16) {
|
||||
LOGE("[LoadRSAKey(): Encrypted RSA has bad padding]");
|
||||
return false;
|
||||
}
|
||||
size_t rsa_key_length = enc_rsa_key_length - padding;
|
||||
|
||||
if (rsa_key_) {
|
||||
RSA_free(rsa_key_);
|
||||
rsa_key_ = NULL;
|
||||
}
|
||||
uint8_t const* p = &rsa_key[0];
|
||||
RSA* rsa = d2i_RSAPrivateKey(0, &p, rsa_key.size());
|
||||
rsa_key_ = rsa;
|
||||
uint8_t const* p = clear;
|
||||
rsa_key_ = d2i_RSAPrivateKey(0, &p, rsa_key_length);
|
||||
delete[] clear;
|
||||
|
||||
if (! rsa_key_) {
|
||||
LOGE("[LoadRSAKey(): Could decode unencrypted rsa key]");
|
||||
dump_openssl_error();
|
||||
@@ -450,6 +521,191 @@ bool SessionContext::LoadRSAKey(const uint8_t* enc_rsa_key,
|
||||
}
|
||||
}
|
||||
|
||||
bool SessionContext::Generic_Encrypt(const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer) {
|
||||
// Check there is a content key
|
||||
if (current_content_key() == NULL) {
|
||||
LOGE("[Generic_Encrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
|
||||
return false;
|
||||
}
|
||||
const std::vector<uint8_t>& key = current_content_key()->value();
|
||||
const KeyControlBlock& control = current_content_key()->control();
|
||||
// Set the AES key.
|
||||
if (static_cast<int>(key.size()) != AES_BLOCK_SIZE) {
|
||||
LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size.");
|
||||
return false;
|
||||
}
|
||||
if (!(control.control_bits() & kControlAllowEncrypt)) {
|
||||
LOGE("[Generic_Encrypt(): control bit says not allowed.");
|
||||
return false;
|
||||
}
|
||||
if (control.duration() > 0) {
|
||||
if (control.duration() < CurrentTimer()) {
|
||||
LOGE("[Generic_Encrypt(): key expired.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
|
||||
LOGE("[Generic_Encrypt(): algorithm bad.");
|
||||
return false;
|
||||
}
|
||||
if( buffer_length % AES_BLOCK_SIZE != 0 ) {
|
||||
LOGE("[Generic_Encrypt(): buffers size bad.");
|
||||
return false;
|
||||
}
|
||||
const uint8_t* key_u8 = &key[0];
|
||||
AES_KEY aes_key;
|
||||
if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) {
|
||||
LOGE("[Generic_Encrypt(): FAILURE]");
|
||||
return false;
|
||||
}
|
||||
uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE];
|
||||
memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE);
|
||||
AES_cbc_encrypt(in_buffer, out_buffer, buffer_length,
|
||||
&aes_key, iv_buffer, AES_ENCRYPT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::Generic_Decrypt(const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer) {
|
||||
// Check there is a content key
|
||||
if (current_content_key() == NULL) {
|
||||
LOGE("[Generic_Decrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
|
||||
return false;
|
||||
}
|
||||
const std::vector<uint8_t>& key = current_content_key()->value();
|
||||
const KeyControlBlock& control = current_content_key()->control();
|
||||
// Set the AES key.
|
||||
if (static_cast<int>(key.size()) != AES_BLOCK_SIZE) {
|
||||
LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size.");
|
||||
return false;
|
||||
}
|
||||
if (!(control.control_bits() & kControlAllowDecrypt)) {
|
||||
LOGE("[Generic_Decrypt(): control bit says not allowed.");
|
||||
return false;
|
||||
}
|
||||
if (control.duration() > 0) {
|
||||
if (control.duration() < CurrentTimer()) {
|
||||
LOGE("[Generic_Decrypt(): key expired.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
|
||||
LOGE("[Generic_Decrypt(): bad algorithm.");
|
||||
return false;
|
||||
}
|
||||
if( buffer_length % AES_BLOCK_SIZE != 0 ) {
|
||||
LOGE("[Generic_Decrypt(): bad buffer size.");
|
||||
return false;
|
||||
}
|
||||
const uint8_t* key_u8 = &key[0];
|
||||
AES_KEY aes_key;
|
||||
if (AES_set_decrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) {
|
||||
LOGE("[Generic_Decrypt(): FAILURE]");
|
||||
return false;
|
||||
}
|
||||
uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE];
|
||||
memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE);
|
||||
AES_cbc_encrypt(in_buffer, out_buffer, buffer_length,
|
||||
&aes_key, iv_buffer, AES_DECRYPT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::Generic_Sign(const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
// Check there is a content key
|
||||
if (current_content_key() == NULL) {
|
||||
LOGE("[Generic_Sign(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
|
||||
return false;
|
||||
}
|
||||
if (*signature_length < SHA256_DIGEST_LENGTH) {
|
||||
*signature_length = SHA256_DIGEST_LENGTH;
|
||||
LOGE("[Generic_Sign(): bad signature length.");
|
||||
return false;
|
||||
}
|
||||
const std::vector<uint8_t>& key = current_content_key()->value();
|
||||
const KeyControlBlock& control = current_content_key()->control();
|
||||
if (static_cast<int>(key.size()) != SHA256_DIGEST_LENGTH) {
|
||||
LOGE("[Generic_Sign(): CONTENT_KEY has wrong size.");
|
||||
return false;
|
||||
}
|
||||
if (!(control.control_bits() & kControlAllowSign)) {
|
||||
LOGE("[Generic_Sign(): control bit says not allowed.");
|
||||
return false;
|
||||
}
|
||||
if (control.duration() > 0) {
|
||||
if (control.duration() < CurrentTimer()) {
|
||||
LOGE("[Generic_Sign(): key expired.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_HMAC_SHA256 ) {
|
||||
LOGE("[Generic_Sign(): bad algorithm.");
|
||||
return false;
|
||||
}
|
||||
unsigned int md_len = *signature_length;
|
||||
if (HMAC(EVP_sha256(), &key[0], SHA256_DIGEST_LENGTH,
|
||||
in_buffer, buffer_length, signature, &md_len)) {
|
||||
*signature_length = md_len;
|
||||
return true;
|
||||
}
|
||||
LOGE("[Generic_Sign(): hmac failed.");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SessionContext::Generic_Verify(const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
// Check there is a content key
|
||||
if (current_content_key() == NULL) {
|
||||
LOGE("[Decrypt_Verify(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
|
||||
return false;
|
||||
}
|
||||
if (signature_length < SHA256_DIGEST_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
const std::vector<uint8_t>& key = current_content_key()->value();
|
||||
const KeyControlBlock& control = current_content_key()->control();
|
||||
if (static_cast<int>(key.size()) != SHA256_DIGEST_LENGTH) {
|
||||
LOGE("[Generic_Verify(): CONTENT_KEY has wrong size.");
|
||||
return false;
|
||||
}
|
||||
if (!(control.control_bits() & kControlAllowVerify)) {
|
||||
LOGE("[Generic_Verify(): control bit says not allowed.");
|
||||
return false;
|
||||
}
|
||||
if (control.duration() > 0) {
|
||||
if (control.duration() < CurrentTimer()) {
|
||||
LOGE("[Generic_Verify(): key expired.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_HMAC_SHA256 ) {
|
||||
LOGE("[Generic_Verify(): bad algorithm.");
|
||||
return false;
|
||||
}
|
||||
unsigned int md_len = signature_length;
|
||||
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
|
||||
if (HMAC(EVP_sha256(), &key[0], SHA256_DIGEST_LENGTH,
|
||||
in_buffer, buffer_length, computed_signature, &md_len)) {
|
||||
return (0 == memcmp( signature, computed_signature, SHA256_DIGEST_LENGTH));
|
||||
}
|
||||
LOGE("[Generic_Verify(): HMAC failed.");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SessionContext::RefreshKey(const KeyId& key_id,
|
||||
const std::vector<uint8_t>& key_control,
|
||||
@@ -582,7 +838,6 @@ bool CryptoEngine::DecryptMessage(SessionContext* session,
|
||||
LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return false;
|
||||
}
|
||||
|
||||
decrypted->resize(message.size());
|
||||
uint8_t iv_buffer[16];
|
||||
memcpy(iv_buffer, &iv[0], 16);
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "oemcrypto_keybox_mock.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
// TODO(fredgc,gmorgan): Revisit the need to keep interface separate.
|
||||
// For now, we need to include the enum OEMCrypto_Algorithm.
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
enum BufferType {
|
||||
@@ -100,15 +104,35 @@ class SessionContext {
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length);
|
||||
size_t RSASignatureSize();
|
||||
bool GenerateRSASignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length);
|
||||
|
||||
bool ValidateMessage(const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length);
|
||||
bool Generic_Encrypt(const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer);
|
||||
bool Generic_Decrypt(const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer);
|
||||
bool Generic_Sign(const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length);
|
||||
bool Generic_Verify(const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length);
|
||||
void StartTimer();
|
||||
uint32_t CurrentTimer(); // (seconds).
|
||||
bool InstallKey(const KeyId& key_id,
|
||||
|
||||
@@ -29,6 +29,10 @@ enum KeyType {
|
||||
const uint32_t kControlObserveDataPath = (1<<31);
|
||||
const uint32_t kControlObserveHDCP = (1<<30);
|
||||
const uint32_t kControlObserveCGMS = (1<<29);
|
||||
const uint32_t kControlAllowEncrypt = (1<<8);
|
||||
const uint32_t kControlAllowDecrypt = (1<<7);
|
||||
const uint32_t kControlAllowSign = (1<<6);
|
||||
const uint32_t kControlAllowVerify = (1<<5);
|
||||
const uint32_t kControlDataPathSecure = (1<<4);
|
||||
const uint32_t kControlNonceEnabled = (1<<3);
|
||||
const uint32_t kControlHDCPRequired = (1<<2);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "log.h"
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
#include "openssl/rand.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
@@ -207,6 +208,11 @@ OEMCryptoResult OEMCrypto_GenerateSignature(
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
if (*signature_length < SHA256_DIGEST_LENGTH) {
|
||||
*signature_length = SHA256_DIGEST_LENGTH;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0) {
|
||||
LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
@@ -304,7 +310,7 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
if (!RangeCheck(message, message_length, key_array[i].key_id,
|
||||
key_array[i].key_id_length, false) ||
|
||||
!RangeCheck(message, message_length, key_array[i].key_data,
|
||||
wvcdm::KEY_SIZE, false) ||
|
||||
key_array[i].key_data_length, false) ||
|
||||
!RangeCheck(message, message_length, key_array[i].key_data_iv,
|
||||
wvcdm::KEY_IV_SIZE, false) ||
|
||||
!RangeCheck(message, message_length, key_array[i].key_control,
|
||||
@@ -334,7 +340,7 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
key_id.assign(key_array[i].key_id,
|
||||
key_array[i].key_id + key_array[i].key_id_length);
|
||||
enc_key_data.assign(key_array[i].key_data,
|
||||
key_array[i].key_data + wvcdm::KEY_SIZE);
|
||||
key_array[i].key_data + key_array[i].key_data_length);
|
||||
key_data_iv.assign(key_array[i].key_data_iv,
|
||||
key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE);
|
||||
if (key_array[i].key_control == NULL) {
|
||||
@@ -653,7 +659,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
uint32_t* nonce,
|
||||
const uint32_t* nonce,
|
||||
const uint8_t* enc_rsa_key,
|
||||
size_t enc_rsa_key_length,
|
||||
const uint8_t* enc_rsa_key_iv,
|
||||
@@ -673,12 +679,12 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
}
|
||||
// For the reference implementation, the wrapped key and the encrypted
|
||||
// key are the same size -- just encrypted with different keys.
|
||||
// We add 32 bytes for a context, and 32 bytes for a signature.
|
||||
// We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature.
|
||||
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
|
||||
size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
|
||||
|
||||
if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) {
|
||||
LOGW("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
|
||||
LOGW("[OEMCrypto_RewrapDeviceRSAKey(): Wrapped Keybox Short Buffer]");
|
||||
*wrapped_rsa_key_length = buffer_size;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
@@ -699,13 +705,13 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
}
|
||||
|
||||
// Range check
|
||||
if (!RangeCheck(message, message_length, reinterpret_cast<uint8_t*>(nonce),
|
||||
if (!RangeCheck(message, message_length, reinterpret_cast<const uint8_t*>(nonce),
|
||||
sizeof(uint32_t), true) ||
|
||||
!RangeCheck(message, message_length, enc_rsa_key, enc_rsa_key_length,
|
||||
true) ||
|
||||
!RangeCheck(message, message_length, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE,
|
||||
true)) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]");
|
||||
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): - range check.]");
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
|
||||
@@ -739,13 +745,6 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
size_t sig_length = sizeof(wrapped->signature);
|
||||
if (!session_ctx->GenerateSignature(wrapped->context, // start signing here.
|
||||
buffer_size - sizeof(wrapped->signature),
|
||||
wrapped->signature,
|
||||
&sig_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
// Encrypt rsa key with keybox.
|
||||
if (!session_ctx->EncryptRSAKey(wrapped->enc_rsa_key,
|
||||
@@ -754,8 +753,19 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
size_t sig_length = sizeof(wrapped->signature);
|
||||
if (!session_ctx->GenerateSignature(wrapped->context, // start signing here.
|
||||
buffer_size - sizeof(wrapped->signature),
|
||||
wrapped->signature,
|
||||
&sig_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
if (trace_all_calls) {
|
||||
dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length);
|
||||
dump_hex("signature", wrapped->signature, sizeof(wrapped->signature));
|
||||
dump_hex("context", wrapped->context, sizeof(wrapped->context));
|
||||
dump_hex("iv", wrapped->iv, sizeof(wrapped->iv));
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
@@ -764,9 +774,14 @@ extern "C"
|
||||
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
const uint8_t* wrapped_rsa_key,
|
||||
size_t wrapped_rsa_key_length) {
|
||||
const WrappedRSAKey* wrapped
|
||||
= reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n");
|
||||
dump_hex("wrapped_rsa_key", wrapped_rsa_key, wrapped_rsa_key_length);
|
||||
dump_hex("signature", wrapped->signature, sizeof(wrapped->signature));
|
||||
dump_hex("context", wrapped->context, sizeof(wrapped->context));
|
||||
dump_hex("iv", wrapped->iv, sizeof(wrapped->iv));
|
||||
}
|
||||
|
||||
if (wrapped_rsa_key == NULL) {
|
||||
@@ -784,9 +799,6 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
// Now we generate a wrapped keybox.
|
||||
const WrappedRSAKey* wrapped
|
||||
= reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
|
||||
const std::vector<uint8_t> mac_ctx_str(wrapped->context,
|
||||
wrapped->context + sizeof(wrapped->context));
|
||||
// Generate mac and encryption keys for encrypting the signature.
|
||||
@@ -821,8 +833,7 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0) {
|
||||
if (signature_length == 0) {
|
||||
LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
@@ -832,6 +843,19 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
|
||||
LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
size_t required_size = session_ctx->RSASignatureSize();
|
||||
if (*signature_length < required_size) {
|
||||
*signature_length = required_size;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0) {
|
||||
LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
if (session_ctx->GenerateRSASignature(message,
|
||||
message_length,
|
||||
signature,
|
||||
@@ -890,4 +914,133 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
uint32_t OEMCrypto_APIVersion() {
|
||||
return oec_latest_version;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
const char* OEMCrypto_SecurityLevel() {
|
||||
return "L3";
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session,
|
||||
const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer) {
|
||||
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
if (in_buffer == NULL || buffer_length == 0 ||
|
||||
iv == NULL || out_buffer == NULL) {
|
||||
LOGE("[OEMCrypto_Generic_Enrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!session_ctx->Generic_Encrypt(in_buffer, buffer_length, iv, algorithm,
|
||||
out_buffer)) {
|
||||
LOGE("[OEMCrypto_Generic_Enrypt(): OEMCrypto_ERROR_UNKNOWN_FAILURE]");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session,
|
||||
const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer) {
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
if (!session_ctx->Generic_Decrypt(in_buffer, buffer_length, iv, algorithm,
|
||||
out_buffer)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (in_buffer == NULL || buffer_length == 0 ||
|
||||
iv == NULL || out_buffer == NULL) {
|
||||
LOGE("[OEMCrypto_Generic_Decrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
|
||||
const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_Generic_Sign(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_Generic_Sign(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
if (*signature_length < SHA256_DIGEST_LENGTH) {
|
||||
*signature_length = SHA256_DIGEST_LENGTH;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
if (in_buffer == NULL || buffer_length == 0 || signature == NULL) {
|
||||
LOGE("[OEMCrypto_Generic_Sign(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!session_ctx->Generic_Sign(in_buffer, buffer_length, algorithm,
|
||||
signature, signature_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session,
|
||||
const uint8_t* in_buffer,
|
||||
size_t buffer_length,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_Generic_Verify(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_Generic_Verify(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
if (signature_length != SHA256_DIGEST_LENGTH) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (in_buffer == NULL || buffer_length == 0 || signature == NULL) {
|
||||
LOGE("[OEMCrypto_Generic_Verify(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!session_ctx->Generic_Verify(in_buffer, buffer_length, algorithm,
|
||||
signature, signature_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
}; // namespace wvoec_mock
|
||||
|
||||
@@ -38,7 +38,7 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
|
||||
unsigned char lsb = 0; // least significant 4 bits
|
||||
if (!CharToDigit(byte[i * 2], &msb) ||
|
||||
!CharToDigit(byte[i * 2 + 1], &lsb)) {
|
||||
LOGE("Invalid hex value %c%c at index %d", byte[i*2], byte[i*2+1], i);
|
||||
LOGE("Invalid hex value %c%c at index %d", byte[i * 2], byte[i * 2 + 1], i);
|
||||
return array;
|
||||
}
|
||||
array.push_back((msb << 4) | lsb);
|
||||
|
||||
@@ -80,7 +80,7 @@ uint32_t wvrunningcrc32(uint8_t* p_begin, int i_count, uint32_t i_crc) {
|
||||
|
||||
/* Calculate the CRC */
|
||||
while (i_count > 0) {
|
||||
i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin) ];
|
||||
i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin)];
|
||||
p_begin++;
|
||||
i_count--;
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ LOCAL_C_INCLUDES += \
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libgtest \
|
||||
libgtest_main \
|
||||
libl3crypto \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcrypto \
|
||||
libcutils \
|
||||
libdl \
|
||||
liblog \
|
||||
liboemcrypto \
|
||||
libstlport \
|
||||
libutils \
|
||||
libz \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,9 @@ LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
WVCreateDrmPluginFactory_test.cpp \
|
||||
WVDrmPluginFactory_test.cpp \
|
||||
../../src/WVCreateDrmPluginFactory.cpp \
|
||||
../../src/WVDrmPluginFactory.cpp \
|
||||
WVCreatePluginFactories_test.cpp \
|
||||
WVCryptoFactory_test.cpp \
|
||||
WVDrmFactory_test.cpp \
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
bionic \
|
||||
@@ -14,17 +13,18 @@ LOCAL_C_INCLUDES := \
|
||||
frameworks/av/include \
|
||||
frameworks/native/include \
|
||||
vendor/widevine/libwvdrmengine/include \
|
||||
vendor/widevine/libwvdrmengine/oemcrypto/include \
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libgtest \
|
||||
libgtest_main \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcrypto \
|
||||
libdl \
|
||||
liblog \
|
||||
libstlport \
|
||||
libutils \
|
||||
libwvdrmengine \
|
||||
|
||||
LOCAL_MODULE := libwvdrmengine_test
|
||||
|
||||
|
||||
23
libwvdrmengine/test/unit/WVCreatePluginFactories_test.cpp
Normal file
23
libwvdrmengine/test/unit/WVCreatePluginFactories_test.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "utils/UniquePtr.h"
|
||||
#include "WVCreatePluginFactories.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
TEST(CreatePluginFactoriesTest, CreatesDrmFactory) {
|
||||
UniquePtr<DrmFactory> factory(createDrmFactory());
|
||||
|
||||
EXPECT_NE((DrmFactory*)NULL, factory.get()) <<
|
||||
"createDrmFactory() returned null";
|
||||
}
|
||||
|
||||
TEST(CreatePluginFactoriesTest, CreatesCryptoFactory) {
|
||||
UniquePtr<CryptoFactory> factory(createCryptoFactory());
|
||||
|
||||
EXPECT_NE((CryptoFactory*)NULL, factory.get()) <<
|
||||
"createCryptoFactory() returned null";
|
||||
}
|
||||
41
libwvdrmengine/test/unit/WVCryptoFactory_test.cpp
Normal file
41
libwvdrmengine/test/unit/WVCryptoFactory_test.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "utils/UniquePtr.h"
|
||||
#include "WVCryptoFactory.h"
|
||||
|
||||
using namespace wvdrm;
|
||||
|
||||
const uint8_t kWidevineUUID[16] = {
|
||||
0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE,
|
||||
0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED
|
||||
};
|
||||
|
||||
const uint8_t kOldNetflixWidevineUUID[16] = {
|
||||
0x29,0x70,0x1F,0xE4,0x3C,0xC7,0x4A,0x34,
|
||||
0x8C,0x5B,0xAE,0x90,0xC7,0x43,0x9A,0x47
|
||||
};
|
||||
|
||||
const uint8_t kUnknownUUID[16] = {
|
||||
0x6A,0x7F,0xAA,0xB0,0x83,0xC7,0x9E,0x20,
|
||||
0x08,0xBC,0xEF,0x32,0x34,0x1A,0x9A,0x26
|
||||
};
|
||||
|
||||
TEST(WVCryptoFactoryTest, SupportsSupportedCryptoSchemes) {
|
||||
UniquePtr<WVCryptoFactory> factory(new WVCryptoFactory());
|
||||
|
||||
EXPECT_TRUE(factory->isCryptoSchemeSupported(kWidevineUUID)) <<
|
||||
"WVPluginFactory does not support Widevine's UUID";
|
||||
|
||||
EXPECT_TRUE(factory->isCryptoSchemeSupported(kOldNetflixWidevineUUID)) <<
|
||||
"WVPluginFactory does not support the old Netflix Widevine UUID";
|
||||
}
|
||||
|
||||
TEST(WVCryptoFactoryTest, DoesNotSupportUnsupportedCryptoSchemes) {
|
||||
UniquePtr<WVCryptoFactory> factory(new WVCryptoFactory());
|
||||
|
||||
EXPECT_FALSE(factory->isCryptoSchemeSupported(kUnknownUUID)) <<
|
||||
"WVPluginFactory incorrectly claims to support an unknown UUID";
|
||||
}
|
||||
41
libwvdrmengine/test/unit/WVDrmFactory_test.cpp
Normal file
41
libwvdrmengine/test/unit/WVDrmFactory_test.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "utils/UniquePtr.h"
|
||||
#include "WVDrmFactory.h"
|
||||
|
||||
using namespace wvdrm;
|
||||
|
||||
const uint8_t kWidevineUUID[16] = {
|
||||
0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE,
|
||||
0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED
|
||||
};
|
||||
|
||||
const uint8_t kOldNetflixWidevineUUID[16] = {
|
||||
0x29,0x70,0x1F,0xE4,0x3C,0xC7,0x4A,0x34,
|
||||
0x8C,0x5B,0xAE,0x90,0xC7,0x43,0x9A,0x47
|
||||
};
|
||||
|
||||
const uint8_t kUnknownUUID[16] = {
|
||||
0x6A,0x7F,0xAA,0xB0,0x83,0xC7,0x9E,0x20,
|
||||
0x08,0xBC,0xEF,0x32,0x34,0x1A,0x9A,0x26
|
||||
};
|
||||
|
||||
TEST(WVDrmFactoryTest, SupportsSupportedCryptoSchemes) {
|
||||
UniquePtr<WVDrmFactory> factory(new WVDrmFactory());
|
||||
|
||||
EXPECT_TRUE(factory->isCryptoSchemeSupported(kWidevineUUID)) <<
|
||||
"WVPluginFactory does not support Widevine's UUID";
|
||||
|
||||
EXPECT_TRUE(factory->isCryptoSchemeSupported(kOldNetflixWidevineUUID)) <<
|
||||
"WVPluginFactory does not support the old Netflix Widevine UUID";
|
||||
}
|
||||
|
||||
TEST(WVDrmFactoryTest, DoesNotSupportUnsupportedCryptoSchemes) {
|
||||
UniquePtr<WVDrmFactory> factory(new WVDrmFactory());
|
||||
|
||||
EXPECT_FALSE(factory->isCryptoSchemeSupported(kUnknownUUID)) <<
|
||||
"WVPluginFactory incorrectly claims to support an unknown UUID";
|
||||
}
|
||||
Reference in New Issue
Block a user