Import updates to the Widevine CENC DRM Plugin
This change incorporates the following CLs from the Widevine
cdm repository:
Update the java request/response test app to match Drm API changes
Don't build the mock liboemcrypto.so by default
Do not build CDM tests by default
Fix Build Break in DrmEngine Unit Tests
Fix Build Break in WVDrmPlugin
Initial version of roadmap for CDM projects.
Implement License Query
Implement Generic DRM in OEMCrypto Reference Implementation
Add key_data_length field when calling OEMCrypto_LoadKeys
Policy engine unittests
Generalized DRM API for OEMCrypto
Fixes proto buf libraries build.
Add Version Number to OEMCrypto API
Test key control block duration field in OEMCrypto
Add fix for missing crypto offset.
Fixed android/media*/test builds and added proto files for Cert. provisioning
Refactor and clean up callback code in CDM.
Add "device_id" name-value pair to LicenseRequest::ClientIdentification
Separate unit and end-to-end tests from the top level makefie.
Includes changes for 'fall back to l3 oemcrypto lib' in top level makefile.
Fall Back to Level 3 if Level 1 Fails
Fix compilation error in wvcdm_unittest.
Fix Android build break due to Decrypt() signature change in cdm_engine.h.
Wire up callbacks and errors in the Steel proxy.
Fix lock assert if there is no keybox on the device.
RSA Certificate Unit Test
Change Generic_Verify signature to constant.
Change-Id: I2e42db9d0b4f8d4e833675ae81d0714509bbfd2c
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