am f3ec8c19: Import updates to the Widevine CENC DRM Plugin

* commit 'f3ec8c19d60627bbee65e115280b93be8b2e0281':
  Import updates to the Widevine CENC DRM Plugin
This commit is contained in:
Jeff Tinker
2013-04-03 20:16:24 -07:00
committed by Android Git Automerger
54 changed files with 5944 additions and 751 deletions

View File

@@ -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 :=

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -38,6 +38,8 @@ class CryptoEngine {
bool GetToken(std::string* token);
CdmResponseType Query(CdmQueryMap* info);
private:
void DeleteInstance();

View File

@@ -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:

View File

@@ -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);

View File

@@ -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_

View 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_

View File

@@ -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_

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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();

View 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;
}

View 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;
}

View File

@@ -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
View 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");

View 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;
}

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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,

View File

@@ -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) {

View 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

View 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_

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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 \

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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*));

View File

@@ -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);

View File

@@ -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 keys control block has the Data_Path_Type bit set, then
* return OEMCrypto_ERROR_DECRYPT_FAILED.
* If the current keys 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

View File

@@ -32,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libz \
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)
LOCAL_MODULE := liboemcrypto
include $(BUILD_SHARED_LIBRARY)

View File

@@ -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__))

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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--;
}

View File

@@ -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

View File

@@ -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

View 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";
}

View 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";
}

View 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";
}