Merge "Changes from Widevine CDM repo"

This commit is contained in:
Jeff Tinker
2014-03-19 18:02:48 +00:00
committed by Android (Google) Code Review
45 changed files with 856 additions and 711 deletions

View File

@@ -5,7 +5,6 @@
#include "certificate_provisioning.h" #include "certificate_provisioning.h"
#include "oemcrypto_adapter.h" #include "oemcrypto_adapter.h"
#include "timer.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
namespace wvcdm { namespace wvcdm {
@@ -18,121 +17,114 @@ class WvCdmEventListener;
typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap; typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap;
typedef std::map<CdmKeySetId, CdmSessionId> CdmReleaseKeySetMap; typedef std::map<CdmKeySetId, CdmSessionId> CdmReleaseKeySetMap;
class CdmEngine : public TimerHandler { class CdmEngine {
public: public:
CdmEngine(); CdmEngine();
virtual ~CdmEngine(); virtual ~CdmEngine();
// Session related methods // Session related methods
virtual CdmResponseType OpenSession( CdmResponseType OpenSession(const CdmKeySystem& key_system,
const CdmKeySystem& key_system, const CdmClientPropertySet* property_set,
const CdmClientPropertySet* property_set, CdmSessionId* session_id);
CdmSessionId* session_id); CdmResponseType CloseSession(const CdmSessionId& session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id);
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
// License related methods // License related methods
// Construct a valid license request // Construct a valid license request
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id, const CdmKeySetId& key_set_id,
const CdmInitData& init_data, const CdmInitData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request, CdmKeyMessage* key_request,
std::string* server_url); std::string* server_url);
// Accept license response and extract key info. // Accept license response and extract key info.
virtual CdmResponseType AddKey(const CdmSessionId& session_id, CdmResponseType AddKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data, const CdmKeyResponse& key_data,
CdmKeySetId* key_set_id); CdmKeySetId* key_set_id);
virtual CdmResponseType RestoreKey(const CdmSessionId& session_id, CdmResponseType RestoreKey(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id); const CdmKeySetId& key_set_id);
CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); CdmResponseType CancelKeyRequest(const CdmSessionId& session_id);
// Construct valid renewal request for the current session keys. // Construct valid renewal request for the current session keys.
virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
CdmKeyMessage* key_request, CdmKeyMessage* key_request,
std::string* server_url); std::string* server_url);
// Accept renewal response and update key info. // Accept renewal response and update key info.
virtual CdmResponseType RenewKey(const CdmSessionId& session_id, CdmResponseType RenewKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data); const CdmKeyResponse& key_data);
// Query system information // Query system information
virtual CdmResponseType QueryStatus(CdmQueryMap* info); CdmResponseType QueryStatus(CdmQueryMap* info);
// Query session information // Query session information
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id, virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info); CdmQueryMap* key_info);
// Query license information // Query license information
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info); CdmQueryMap* key_info);
// Query seesion control information // Query seesion control information
virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
CdmQueryMap* key_info); CdmQueryMap* key_info);
// Provisioning related methods // Provisioning related methods
virtual CdmResponseType GetProvisioningRequest( CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request,
CdmProvisioningRequest* request, std::string* default_url);
std::string* default_url);
virtual CdmResponseType HandleProvisioningResponse( CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response);
CdmProvisioningResponse& response);
// Secure stop related methods // Secure stop related methods
virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops); CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
virtual CdmResponseType ReleaseSecureStops( CdmResponseType ReleaseSecureStops(
const CdmSecureStopReleaseMessage& message); const CdmSecureStopReleaseMessage& message);
// Decryption and key related methods // Decryption and key related methods
// Accept encrypted buffer and return decrypted data. // Accept encrypted buffer and return decrypted data.
virtual CdmResponseType Decrypt(const CdmSessionId& session_id, CdmResponseType Decrypt(const CdmSessionId& session_id,
const CdmDecryptionParameters& parameters); const CdmDecryptionParameters& parameters);
size_t SessionSize() const { return sessions_.size(); }
// Is the key known to any session? // Is the key known to any session?
virtual bool IsKeyValid(const KeyId& key_id); bool IsKeyLoaded(const KeyId& key_id);
virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId); bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
// Event listener related methods // Event listener related methods
virtual bool AttachEventListener(const CdmSessionId& session_id, bool AttachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener); WvCdmEventListener* listener);
virtual bool DetachEventListener(const CdmSessionId& session_id, bool DetachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener); WvCdmEventListener* listener);
// Parse a blob of multiple concatenated PSSH atoms to extract the first // Parse a blob of multiple concatenated PSSH atoms to extract the first
// widevine pssh // widevine pssh
static bool ExtractWidevinePssh(const CdmInitData& init_data, static bool ExtractWidevinePssh(const CdmInitData& init_data,
CdmInitData* output); CdmInitData* output);
// Timer expiration method
void OnTimerEvent();
private: private:
// private methods // private methods
// Cancel all sessions // Cancel all sessions
virtual bool CancelSessions(); bool CancelSessions();
virtual bool ValidateKeySystem(const CdmKeySystem& key_system); bool ValidateKeySystem(const CdmKeySystem& key_system);
// timer related methods to drive policy decisions void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
virtual void EnablePolicyTimer();
virtual void DisablePolicyTimer(bool force);
virtual void OnTimerEvent();
virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
// instance variables // instance variables
CdmSessionMap sessions_; CdmSessionMap sessions_;
CdmReleaseKeySetMap release_key_sets_; CdmReleaseKeySetMap release_key_sets_;
CertificateProvisioning cert_provisioning_; CertificateProvisioning cert_provisioning_;
SecurityLevel cert_provisioning_requested_security_level_; SecurityLevel cert_provisioning_requested_security_level_;
// policy timer
Timer policy_timer_;
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine); CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
}; };

View File

@@ -79,7 +79,7 @@ class CdmSession {
// ReleaseKey() - Accept response and release key. // ReleaseKey() - Accept response and release key.
CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); CdmResponseType ReleaseKey(const CdmKeyResponse& key_response);
bool IsKeyValid(const KeyId& key_id); bool IsKeyLoaded(const KeyId& key_id);
bool AttachEventListener(WvCdmEventListener* listener); bool AttachEventListener(WvCdmEventListener* listener);
bool DetachEventListener(WvCdmEventListener* listener); bool DetachEventListener(WvCdmEventListener* listener);
@@ -119,8 +119,6 @@ class CdmSession {
// license type release and offline related information // license type release and offline related information
CdmKeySetId key_set_id_; CdmKeySetId key_set_id_;
KeyId key_id_;
// Used for certificate based licensing // Used for certificate based licensing
std::string wrapped_key_; std::string wrapped_key_;
bool is_certificate_loaded_; bool is_certificate_loaded_;

View File

@@ -63,7 +63,6 @@ class CryptoSession {
std::string* wrapped_rsa_key); std::string* wrapped_rsa_key);
// Media data path // Media data path
bool SelectKey(const std::string& key_id);
CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
bool GetRandom(size_t data_length, uint8_t* random_data); bool GetRandom(size_t data_length, uint8_t* random_data);
@@ -80,6 +79,8 @@ class CryptoSession {
size_t GetOffset(std::string message, std::string field); size_t GetOffset(std::string message, std::string field);
bool SetDestinationBufferType(); bool SetDestinationBufferType();
bool SelectKey(const std::string& key_id);
static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature
static Lock crypto_lock_; static Lock crypto_lock_;
static bool initialized_; static bool initialized_;

View File

@@ -59,6 +59,9 @@ class DeviceFiles {
bool RetrieveFile(const char* name, std::string* data); bool RetrieveFile(const char* name, std::string* data);
private: private:
// Certificate and offline licenses are now stored in security
// level specific directories. In an earlier version they were
// stored in a common directory and need to be copied over.
virtual void SecurityLevelPathBackwardCompatibility(); virtual void SecurityLevelPathBackwardCompatibility();
File* file_; File* file_;

View File

@@ -12,6 +12,8 @@ namespace wvcdm {
// File class. The implementation is platform dependent. // File class. The implementation is platform dependent.
class File { class File {
public: public:
class Impl;
// defines as bit flag // defines as bit flag
enum OpenFlags { enum OpenFlags {
kNoFlags = 0, kNoFlags = 0,
@@ -39,7 +41,6 @@ class File {
virtual ssize_t FileSize(const std::string& file_path); virtual ssize_t FileSize(const std::string& file_path);
private: private:
class Impl;
Impl *impl_; Impl *impl_;
CORE_DISALLOW_COPY_AND_ASSIGN(File); CORE_DISALLOW_COPY_AND_ASSIGN(File);

View File

@@ -27,7 +27,12 @@ class PolicyEngine {
// status is not calculated to avoid overhead in the decryption path. // status is not calculated to avoid overhead in the decryption path.
inline bool can_decrypt() { return can_decrypt_; } inline bool can_decrypt() { return can_decrypt_; }
void OnTimerEvent(bool& event_occurred, CdmEventType& event); // OnTimerEvent is called when a timer fires. It notifies the Policy Engine
// that the timer has fired and that it should check whether any events have
// occurred since the last timer event. If so, it sets event_occurred to true
// and sets event to point to the event that occurred. If not, it sets
// event_occurred to false.
void OnTimerEvent(bool* event_occurred, CdmEventType* event);
// SetLicense is used in handling the initial license response. It stores // SetLicense is used in handling the initial license response. It stores
// an exact copy of the policy information stored in the license. // an exact copy of the policy information stored in the license.

View File

@@ -14,7 +14,7 @@
namespace wvcdm { namespace wvcdm {
typedef std::map<CdmSessionId, const CdmClientPropertySet*> typedef std::map<CdmSessionId, const CdmClientPropertySet*>
CdmClientPropertySetMap; CdmClientPropertySetMap;
// This class saves information about features and properties enabled // This class saves information about features and properties enabled
// for a given platform. At initialization it initializes properties from // for a given platform. At initialization it initializes properties from
@@ -42,9 +42,7 @@ class Properties {
static inline bool use_certificates_as_identification() { static inline bool use_certificates_as_identification() {
return use_certificates_as_identification_; return use_certificates_as_identification_;
} }
static inline bool extract_pssh_data() { static inline bool extract_pssh_data() { return extract_pssh_data_; }
return extract_pssh_data_;
}
static inline bool decrypt_with_empty_session_support() { static inline bool decrypt_with_empty_session_support() {
return decrypt_with_empty_session_support_; return decrypt_with_empty_session_support_;
} }
@@ -64,13 +62,12 @@ class Properties {
static bool GetSecurityLevelDirectories(std::vector<std::string>* dirs); static bool GetSecurityLevelDirectories(std::vector<std::string>* dirs);
static const std::string GetSecurityLevel(const CdmSessionId& session_id); static const std::string GetSecurityLevel(const CdmSessionId& session_id);
static const std::vector<uint8_t> GetServiceCertificate( static const std::vector<uint8_t> GetServiceCertificate(
const CdmSessionId& session_id); const CdmSessionId& session_id);
static bool UsePrivacyMode(const CdmSessionId& session_id); static bool UsePrivacyMode(const CdmSessionId& session_id);
static uint32_t GetSessionSharingId(const CdmSessionId& session_id); static uint32_t GetSessionSharingId(const CdmSessionId& session_id);
static bool AddSessionPropertySet( static bool AddSessionPropertySet(const CdmSessionId& session_id,
const CdmSessionId& session_id, const CdmClientPropertySet* property_set);
const CdmClientPropertySet* property_set);
static bool RemoveSessionPropertySet(const CdmSessionId& session_id); static bool RemoveSessionPropertySet(const CdmSessionId& session_id);
private: private:
@@ -94,16 +91,17 @@ class Properties {
static void set_use_certificates_as_identification(bool flag) { static void set_use_certificates_as_identification(bool flag) {
use_certificates_as_identification_ = flag; use_certificates_as_identification_ = flag;
} }
static void set_extract_pssh_data(bool flag) { static void set_extract_pssh_data(bool flag) { extract_pssh_data_ = flag; }
extract_pssh_data_ = flag;
}
static void set_decrypt_with_empty_session_support(bool flag) { static void set_decrypt_with_empty_session_support(bool flag) {
decrypt_with_empty_session_support_ = flag; decrypt_with_empty_session_support_ = flag;
} }
static void set_security_level_path_backward_compatibility_support(bool flag) { static void set_security_level_path_backward_compatibility_support(
bool flag) {
security_level_path_backward_compatibility_support_ = flag; security_level_path_backward_compatibility_support_ = flag;
} }
private:
static bool begin_license_usage_when_received_; static bool begin_license_usage_when_received_;
static bool require_explicit_renew_request_; static bool require_explicit_renew_request_;
static bool oem_crypto_use_secure_buffers_; static bool oem_crypto_use_secure_buffers_;

View File

@@ -11,6 +11,7 @@
namespace wvcdm { namespace wvcdm {
std::vector<uint8_t> a2b_hex(const std::string& b); std::vector<uint8_t> a2b_hex(const std::string& b);
std::vector<uint8_t> a2b_hex(const std::string& label, const std::string& b);
std::string a2bs_hex(const std::string& b); std::string a2bs_hex(const std::string& b);
std::string b2a_hex(const std::vector<uint8_t>& b); std::string b2a_hex(const std::vector<uint8_t>& b);
std::string b2a_hex(const std::string& b); std::string b2a_hex(const std::string& b);

View File

@@ -31,6 +31,8 @@ class TimerHandler {
class Timer { class Timer {
public: public:
class Impl;
Timer(); Timer();
~Timer(); ~Timer();
@@ -39,7 +41,6 @@ class Timer {
bool IsRunning(); bool IsRunning();
private: private:
class Impl;
Impl *impl_; Impl *impl_;
CORE_DISALLOW_COPY_AND_ASSIGN(Timer); CORE_DISALLOW_COPY_AND_ASSIGN(Timer);

View File

@@ -54,7 +54,7 @@ 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_L1 = "L1";
static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2"; static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2";
static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3"; static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3";
static const std::string QUERY_VALUE_SECURITY_LEVEL_Unknown = "Unknown"; static const std::string QUERY_VALUE_SECURITY_LEVEL_UNKNOWN = "Unknown";
} // namespace wvcdm } // namespace wvcdm

View File

@@ -8,7 +8,7 @@
namespace wvcdm { namespace wvcdm {
// Listener for events from the Content Decryption Module. // Listener for events from the Content Decryption Module.
// The caller of the CDM API must provide an implementation for onEvent // The caller of the CDM API must provide an implementation for OnEvent
// and signal its intent by using the Attach/DetachEventListener methods // and signal its intent by using the Attach/DetachEventListener methods
// in the WvContentDecryptionModule class. // in the WvContentDecryptionModule class.
class WvCdmEventListener { class WvCdmEventListener {
@@ -16,7 +16,7 @@ class WvCdmEventListener {
WvCdmEventListener() {} WvCdmEventListener() {}
virtual ~WvCdmEventListener() {} virtual ~WvCdmEventListener() {}
virtual void onEvent(const CdmSessionId& session_id, virtual void OnEvent(const CdmSessionId& session_id,
CdmEventType cdm_event) = 0; CdmEventType cdm_event) = 0;
private: private:

View File

@@ -28,6 +28,11 @@ typedef std::vector<uint8_t> CdmSecureStopReleaseMessage;
typedef std::string CdmProvisioningRequest; typedef std::string CdmProvisioningRequest;
typedef std::string CdmProvisioningResponse; typedef std::string CdmProvisioningResponse;
// Types for shared host/cdm interface pairs used to shared vendor data.
typedef std::pair<std::string, std::string> kStringPairs;
typedef std::vector<uint8_t> kVectorBytes;
typedef std::pair<std::string, kVectorBytes> kVectorPairs;
enum CdmResponseType { enum CdmResponseType {
NO_ERROR, NO_ERROR,
UNKNOWN_ERROR, UNKNOWN_ERROR,

View File

@@ -15,10 +15,6 @@
#include "wv_cdm_constants.h" #include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h" #include "wv_cdm_event_listener.h"
namespace {
const int kCdmPolicyTimerDurationSeconds = 1;
}
namespace wvcdm { namespace wvcdm {
CdmEngine::CdmEngine() CdmEngine::CdmEngine()
@@ -29,7 +25,6 @@ CdmEngine::CdmEngine()
CdmEngine::~CdmEngine() { CdmEngine::~CdmEngine() {
CancelSessions(); CancelSessions();
DisablePolicyTimer(true);
CdmSessionMap::iterator i(sessions_.begin()); CdmSessionMap::iterator i(sessions_.begin());
for (; i != sessions_.end(); ++i) for (; i != sessions_.end(); ++i)
delete i->second; delete i->second;
@@ -101,7 +96,6 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
CdmSession* session = iter->second; CdmSession* session = iter->second;
sessions_.erase(session_id); sessions_.erase(session_id);
DisablePolicyTimer(false);
delete session; delete session;
return NO_ERROR; return NO_ERROR;
} }
@@ -248,10 +242,6 @@ CdmResponseType CdmEngine::AddKey(
return sts; return sts;
} }
if (!license_type_release) {
EnablePolicyTimer();
}
return KEY_ADDED; return KEY_ADDED;
} }
@@ -286,9 +276,6 @@ CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) {
//TODO(gmorgan): Issue: what is semantics of canceling a key request. Should //TODO(gmorgan): Issue: what is semantics of canceling a key request. Should
//this call cancel all keys for the session? //this call cancel all keys for the session?
// TODO(jfore): We should disable the policy timer here if there are no
// active sessions. Sessions are currently not being destroyed here. We can
// add this logic once the semantics of canceling the key is worked out.
CdmSessionMap::iterator iter = sessions_.find(session_id); CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) { if (iter == sessions_.end()) {
@@ -296,8 +283,8 @@ CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) {
return KEY_ERROR; return KEY_ERROR;
} }
// TODO(edwinwong, rfrias): unload keys here // Re-initialize to release crypto session/keys without closing session
DisablePolicyTimer(false); iter->second->Init();
return NO_ERROR; return NO_ERROR;
} }
@@ -372,7 +359,8 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
break; break;
case kSecurityLevelUninitialized: case kSecurityLevelUninitialized:
case kSecurityLevelUnknown: case kSecurityLevelUnknown:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_Unknown; (*key_info)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break; break;
default: default:
return KEY_ERROR; return KEY_ERROR;
@@ -504,8 +492,13 @@ CdmResponseType CdmEngine::Decrypt(
} }
if (parameters.decrypt_buffer == NULL) { if (parameters.decrypt_buffer == NULL) {
LOGE("CdmEngine::Decrypt: no dest decrypt buffer"); if (!parameters.is_secure &&
return KEY_ERROR; !Properties::Properties::oem_crypto_use_fifo()) {
LOGE("CdmEngine::Decrypt: no dest decrypt buffer");
return KEY_ERROR;
} // else we must be level 1 direct and we don't need to return a buffer.
// TODO:(eschacker) look at renaming Properties::oem_crypto_use_fifo()
// to something like Properties::oem_crypto_use_direct_rendering().
} }
CdmSessionMap::iterator iter; CdmSessionMap::iterator iter;
@@ -514,7 +507,7 @@ CdmResponseType CdmEngine::Decrypt(
// Loop through the sessions to find the session containing the key_id. // Loop through the sessions to find the session containing the key_id.
for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) { for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyValid(*parameters.key_id)) break; if (iter->second->IsKeyLoaded(*parameters.key_id)) break;
} }
} else { } else {
iter = sessions_.find(session_id); iter = sessions_.find(session_id);
@@ -527,10 +520,10 @@ CdmResponseType CdmEngine::Decrypt(
return iter->second->Decrypt(parameters); return iter->second->Decrypt(parameters);
} }
bool CdmEngine::IsKeyValid(const KeyId& key_id) { bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
for (CdmSessionMap::iterator iter = sessions_.begin(); for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) { iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyValid(key_id)) { if (iter->second->IsKeyLoaded(key_id)) {
return true; return true;
} }
} }
@@ -545,14 +538,21 @@ bool CdmEngine::FindSessionForKey(
return false; return false;
} }
CdmSessionMap::iterator iter = sessions_.find(*session_id);
if (iter != sessions_.end()) {
if (iter->second->IsKeyLoaded(key_id)) {
return true;
}
}
uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id); uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id);
for (CdmSessionMap::iterator iter = sessions_.begin(); for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
iter != sessions_.end(); ++iter) { CdmSessionId local_session_id = iter->second->session_id();
CdmSessionId id = iter->second->session_id(); if (Properties::GetSessionSharingId(local_session_id) ==
if (Properties::GetSessionSharingId(id) == session_sharing_id) { session_sharing_id) {
if (iter->second->IsKeyValid(key_id)) { if (iter->second->IsKeyLoaded(key_id)) {
*session_id = id; *session_id = local_session_id;
return true; return true;
} }
} }
@@ -684,16 +684,6 @@ bool CdmEngine::ExtractWidevinePssh(
return false; return false;
} }
void CdmEngine::EnablePolicyTimer() {
if (!policy_timer_.IsRunning())
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);
}
void CdmEngine::DisablePolicyTimer(bool force) {
if ((sessions_.size() == 0 || force) && policy_timer_.IsRunning())
policy_timer_.Stop();
}
void CdmEngine::OnTimerEvent() { void CdmEngine::OnTimerEvent() {
for (CdmSessionMap::iterator iter = sessions_.begin(); for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) { iter != sessions_.end(); ++iter) {

View File

@@ -95,7 +95,10 @@ CdmResponseType CdmSession::RestoreOfflineSession(
} }
if (Properties::use_certificates_as_identification()) { if (Properties::use_certificates_as_identification()) {
if (!crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { if (is_certificate_loaded_ ||
crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
is_certificate_loaded_ = true;
} else {
return NEED_PROVISIONING; return NEED_PROVISIONING;
} }
} }
@@ -165,8 +168,7 @@ CdmResponseType CdmSession::GenerateKeyRequest(
if (is_certificate_loaded_ || if (is_certificate_loaded_ ||
crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
is_certificate_loaded_ = true; is_certificate_loaded_ = true;
} } else {
else {
reinitialize_session_ = true; reinitialize_session_ = true;
return NEED_PROVISIONING; return NEED_PROVISIONING;
} }
@@ -241,12 +243,12 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) { CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
if (crypto_session_.get() == NULL) { if (crypto_session_.get() == NULL) {
LOGW("CdmSession::QueryStatus: Invalid crypto session"); LOGE("CdmSession::QueryStatus: Invalid crypto session");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
if (!crypto_session_->IsOpen()) { if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::QueryStatus: Crypto session not open"); LOGE("CdmSession::QueryStatus: Crypto session not open");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
@@ -262,7 +264,8 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
break; break;
case kSecurityLevelUninitialized: case kSecurityLevelUninitialized:
case kSecurityLevelUnknown: case kSecurityLevelUnknown:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_Unknown; (*key_info)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break; break;
default: default:
return KEY_ERROR; return KEY_ERROR;
@@ -321,8 +324,10 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
// session keys. // session keys.
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request, CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
std::string* server_url) { std::string* server_url) {
if (!license_parser_.PrepareKeyUpdateRequest(true, key_request, server_url)) if (!license_parser_.PrepareKeyUpdateRequest(true, key_request, server_url)) {
LOGE("CdmSession::GenerateRenewalRequest: ERROR on prepare");
return KEY_ERROR; return KEY_ERROR;
}
if (license_type_ == kLicenseTypeOffline) { if (license_type_ == kLicenseTypeOffline) {
offline_key_renewal_request_ = *key_request; offline_key_renewal_request_ = *key_request;
@@ -364,7 +369,7 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
return sts; return sts;
} }
bool CdmSession::IsKeyValid(const KeyId& key_id) { bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
return license_parser_.IsKeyLoaded(key_id); return license_parser_.IsKeyLoaded(key_id);
} }
@@ -428,12 +433,12 @@ void CdmSession::OnTimerEvent() {
bool event_occurred = false; bool event_occurred = false;
CdmEventType event; CdmEventType event;
policy_engine_.OnTimerEvent(event_occurred, event); policy_engine_.OnTimerEvent(&event_occurred, &event);
if (event_occurred) { if (event_occurred) {
for (CdmEventListenerIter iter = listeners_.begin(); for (CdmEventListenerIter iter = listeners_.begin();
iter != listeners_.end(); ++iter) { iter != listeners_.end(); ++iter) {
(*iter)->onEvent(session_id_, event); (*iter)->OnEvent(session_id_, event);
} }
} }
} }
@@ -442,7 +447,7 @@ void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
if (key_set_id_ == key_set_id) { if (key_set_id_ == key_set_id) {
for (CdmEventListenerIter iter = listeners_.begin(); for (CdmEventListenerIter iter = listeners_.begin();
iter != listeners_.end(); ++iter) { iter != listeners_.end(); ++iter) {
(*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT); (*iter)->OnEvent(session_id_, LICENSE_EXPIRED_EVENT);
} }
} }
} }

View File

@@ -176,11 +176,20 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
if (!signed_response.has_signature() || !signed_response.has_message()) { bool error = false;
LOGE("HandleProvisioningResponse: signature or message not found"); if (!signed_response.has_signature()) {
return UNKNOWN_ERROR; LOGE("HandleProvisioningResponse: signature not found");
error = true;
} }
if (!signed_response.has_message()) {
LOGE("HandleProvisioningResponse: message not found");
error = true;
}
if (error)
return UNKNOWN_ERROR;
const std::string& signed_message = signed_response.message(); const std::string& signed_message = signed_response.message();
ProvisioningResponse provisioning_response; ProvisioningResponse provisioning_response;

View File

@@ -155,10 +155,7 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
return false; return false;
} }
id.resize(id_length + 1); device_id->assign(reinterpret_cast<char *>(&id[0]), id_length);
id[id_length] = '\0';
*device_id = reinterpret_cast<const char*>(&id[0]);
return true; return true;
} }
@@ -358,6 +355,8 @@ CdmResponseType CryptoSession::LoadKeys(const std::string& message,
if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) {
enc_mac_key = msg + GetOffset(message, mac_key); enc_mac_key = msg + GetOffset(message, mac_key);
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
} else {
LOGV("CryptoSession::LoadKeys: enc_mac_key not set");
} }
std::vector<OEMCrypto_KeyObject> load_key_array(num_keys); std::vector<OEMCrypto_KeyObject> load_key_array(num_keys);
for (int i = 0; i < num_keys; ++i) { for (int i = 0; i < num_keys; ++i) {
@@ -394,7 +393,7 @@ CdmResponseType CryptoSession::LoadKeys(const std::string& message,
} }
bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
LOGV("CryptoSession::LoadKeys: Lock"); LOGV("CryptoSession::LoadCertificatePrivateKey: Lock");
AutoLock auto_lock(crypto_lock_); AutoLock auto_lock(crypto_lock_);
LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_); LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_);
@@ -566,7 +565,7 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
AutoLock auto_lock(crypto_lock_); AutoLock auto_lock(crypto_lock_);
// Check if key needs to be selected // Check if key needs to be selected
if (params.is_encrypted) { if (params.is_encrypted) {
if (key_id_.compare(*params.key_id) != 0) { if (key_id_ != *params.key_id) {
if (SelectKey(*params.key_id)) { if (SelectKey(*params.key_id)) {
key_id_ = *params.key_id; key_id_ = *params.key_id;
} else { } else {
@@ -604,7 +603,7 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
switch (sts) { switch (sts) {
case OEMCrypto_SUCCESS: case OEMCrypto_SUCCESS:
break; return NO_ERROR;
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES: case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return INSUFFICIENT_CRYPTO_RESOURCES; return INSUFFICIENT_CRYPTO_RESOURCES;
case OEMCrypto_ERROR_KEY_EXPIRED: case OEMCrypto_ERROR_KEY_EXPIRED:
@@ -612,7 +611,6 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
default: default:
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
return NO_ERROR;
} }
bool CryptoSession::GenerateNonce(uint32_t* nonce) { bool CryptoSession::GenerateNonce(uint32_t* nonce) {

View File

@@ -22,8 +22,11 @@ namespace {
const char kCertificateFileName[] = "cert.bin"; const char kCertificateFileName[] = "cert.bin";
const char kLicenseFileNameExt[] = ".lic"; const char kLicenseFileNameExt[] = ".lic";
const char kWildcard[] = "*"; const char kWildcard[] = "*";
const char kPathDelimiter[] = "/"; const char kDirectoryDelimiter = '/';
const char *kSecurityLevelPathCompatibilityExclusionList[] = { "ay64.dat" }; const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"};
size_t kSecurityLevelPathCompatibilityExclusionListSize =
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
@@ -436,7 +439,6 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
} }
if (!file_->Open(path, File::kReadOnly | File::kBinary)) { if (!file_->Open(path, File::kReadOnly | File::kBinary)) {
LOGW("DeviceFiles::RetrieveFile: File open failed: %s", path.c_str());
return false; return false;
} }
@@ -457,27 +459,33 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
void DeviceFiles::SecurityLevelPathBackwardCompatibility() { void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
std::string path; std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to " LOGW(
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
"get base path"); "get base path");
return; return;
} }
std::vector<std::string> security_dirs; std::vector<std::string> security_dirs;
if (!Properties::GetSecurityLevelDirectories(&security_dirs)) { if (!Properties::GetSecurityLevelDirectories(&security_dirs)) {
LOGW("DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to " LOGW(
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
"get security directories"); "get security directories");
return; return;
} }
size_t pos = std::string::npos; size_t pos = std::string::npos;
for (size_t i = 0; i < security_dirs.size(); ++i) { for (size_t i = 0; i < security_dirs.size(); ++i) {
pos = path.rfind(security_dirs[i]); pos = path.find(security_dirs[i]);
if (std::string::npos != pos) if (pos != std::string::npos && pos > 0 &&
pos == path.size() - security_dirs[i].size() &&
path[pos - 1] == kDirectoryDelimiter) {
break; break;
}
} }
if (pos == std::string::npos) { if (pos == std::string::npos) {
LOGV("DeviceFiles::SecurityLevelPathBackwardCompatibility: Security level " LOGV(
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Security level "
"specific path not found. Check properties?"); "specific path not found. Check properties?");
return; return;
} }
@@ -485,16 +493,16 @@ void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
std::string from_dir(path, 0, pos); std::string from_dir(path, 0, pos);
std::vector<std::string> files; std::vector<std::string> files;
file_->List(from_dir, &files); if (!file_->List(from_dir, &files)) {
return;
}
for (size_t i = 0; i < files.size(); ++i) { for (size_t i = 0; i < files.size(); ++i) {
std::string from = from_dir + files[i]; std::string from = from_dir + files[i];
bool exclude = false; bool exclude = false;
for (size_t j = 0; for (size_t j = 0; j < kSecurityLevelPathCompatibilityExclusionListSize;
j < sizeof(kSecurityLevelPathCompatibilityExclusionList) / ++j) {
sizeof(const char*); if (files[i] == kSecurityLevelPathCompatibilityExclusionList[j]) {
j++) {
if (files[i].compare(kSecurityLevelPathCompatibilityExclusionList[j]) == 0) {
exclude = true; exclude = true;
break; break;
} }
@@ -504,8 +512,7 @@ void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
for (size_t j = 0; j < security_dirs.size(); ++j) { for (size_t j = 0; j < security_dirs.size(); ++j) {
std::string to_dir = from_dir + security_dirs[j]; std::string to_dir = from_dir + security_dirs[j];
if (!file_->Exists(to_dir)) if (!file_->Exists(to_dir)) file_->CreateDirectory(to_dir);
file_->CreateDirectory(to_dir);
std::string to = to_dir + files[i]; std::string to = to_dir + files[i];
file_->Copy(from, to); file_->Copy(from, to);
} }

View File

@@ -90,7 +90,7 @@ typedef OEMCryptoResult (*L1_GenerateRSASignature_t)(OEMCrypto_SESSION session,
size_t message_length, size_t message_length,
uint8_t* signature, uint8_t* signature,
size_t* signature_length, size_t* signature_length,
RSA_Padding_Scheme algorithm); RSA_Padding_Scheme padding_scheme);
typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)( typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)(
OEMCrypto_SESSION session, const uint8_t* enc_session_key, OEMCrypto_SESSION session, const uint8_t* enc_session_key,
size_t enc_session_key_length, const uint8_t* mac_key_context, size_t enc_session_key_length, const uint8_t* mac_key_context,
@@ -318,7 +318,7 @@ class Adapter {
} }
LevelSession get(OEMCrypto_SESSION session) { LevelSession get(OEMCrypto_SESSION session) {
AutoLock auto_lock(lookup_lock_); AutoLock auto_lock(session_map_lock_);
map_iterator pair = session_map_.find(session); map_iterator pair = session_map_.find(session);
if (pair == session_map_.end()) { if (pair == session_map_.end()) {
return LevelSession(); return LevelSession();
@@ -327,7 +327,6 @@ class Adapter {
} }
OEMCryptoResult OpenSession(OEMCrypto_SESSION* session, SecurityLevel level) { OEMCryptoResult OpenSession(OEMCrypto_SESSION* session, SecurityLevel level) {
AutoLock auto_lock(lookup_lock_);
LevelSession new_session; LevelSession new_session;
OEMCryptoResult result; OEMCryptoResult result;
if (level == kLevelDefault && level1_valid_) { if (level == kLevelDefault && level1_valid_) {
@@ -340,6 +339,7 @@ class Adapter {
*session = new_session.session + kLevel3Offset; *session = new_session.session + kLevel3Offset;
} }
if (result == OEMCrypto_SUCCESS) { if (result == OEMCrypto_SUCCESS) {
AutoLock auto_lock(session_map_lock_);
// Make sure session is not already in my list of sessions. // Make sure session is not already in my list of sessions.
while (session_map_.find(*session) != session_map_.end()) { while (session_map_.find(*session) != session_map_.end()) {
(*session)++; (*session)++;
@@ -350,7 +350,7 @@ class Adapter {
} }
OEMCryptoResult CloseSession(OEMCrypto_SESSION session) { OEMCryptoResult CloseSession(OEMCrypto_SESSION session) {
AutoLock auto_lock(lookup_lock_); AutoLock auto_lock(session_map_lock_);
map_iterator pair = session_map_.find(session); map_iterator pair = session_map_.find(session);
if (pair == session_map_.end()) { if (pair == session_map_.end()) {
return OEMCrypto_ERROR_INVALID_SESSION; return OEMCrypto_ERROR_INVALID_SESSION;
@@ -367,7 +367,7 @@ class Adapter {
struct FunctionPointers level1_; struct FunctionPointers level1_;
struct FunctionPointers level3_; struct FunctionPointers level3_;
std::map<OEMCrypto_SESSION, LevelSession> session_map_; std::map<OEMCrypto_SESSION, LevelSession> session_map_;
Lock lookup_lock_; Lock session_map_lock_;
// This is just for debugging the map between session ids. // This is just for debugging the map between session ids.
// If we add this to the level 3 session id, then the external session // If we add this to the level 3 session id, then the external session
// id will match the internal session id in the last two digits. // id will match the internal session id in the last two digits.
@@ -583,12 +583,13 @@ extern "C" OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature( extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme algorithm) { uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme padding_scheme) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session); LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->GenerateRSASignature(pair.session, message, message_length, return pair.fcn->GenerateRSASignature(pair.session, message, message_length,
signature, signature_length, algorithm); signature, signature_length,
padding_scheme);
} }
extern "C" OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( extern "C" OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(

View File

@@ -40,8 +40,8 @@ void PolicyEngine::Init(Clock* clock) {
clock_ = clock; clock_ = clock;
} }
void PolicyEngine::OnTimerEvent(bool& event_occured, CdmEventType& event) { void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
event_occured = false; *event_occurred = false;
int64_t current_time = clock_->GetCurrentTime(); int64_t current_time = clock_->GetCurrentTime();
// License expiration trumps all. // License expiration trumps all.
@@ -50,8 +50,8 @@ void PolicyEngine::OnTimerEvent(bool& event_occured, CdmEventType& event) {
license_state_ != kLicenseStateExpired) { license_state_ != kLicenseStateExpired) {
license_state_ = kLicenseStateExpired; license_state_ = kLicenseStateExpired;
can_decrypt_ = false; can_decrypt_ = false;
event = LICENSE_EXPIRED_EVENT; *event = LICENSE_EXPIRED_EVENT;
event_occured = true; *event_occurred = true;
return; return;
} }
@@ -91,8 +91,8 @@ void PolicyEngine::OnTimerEvent(bool& event_occured, CdmEventType& event) {
if (renewal_needed) { if (renewal_needed) {
UpdateRenewalRequest(current_time); UpdateRenewalRequest(current_time);
event = LICENSE_RENEWAL_NEEDED_EVENT; *event = LICENSE_RENEWAL_NEEDED_EVENT;
event_occured = true; *event_occurred = true;
} }
} }
@@ -109,6 +109,10 @@ void PolicyEngine::UpdateLicense(
if (!license.has_policy()) if (!license.has_policy())
return; return;
if (kLicenseStateExpired == license_state_) {
LOGD("PolicyEngine::UpdateLicense: updating an expired license");
}
policy_.MergeFrom(license.policy()); policy_.MergeFrom(license.policy());
if (!policy_.can_play()) { if (!policy_.can_play()) {

View File

@@ -5,7 +5,7 @@
#include "wv_cdm_constants.h" #include "wv_cdm_constants.h"
namespace { namespace {
const char *kSecurityLevelDirs[] = { "L1/", "L3/" }; const char* kSecurityLevelDirs[] = {"L1/", "L3/"};
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
@@ -30,20 +30,20 @@ void Properties::Init() {
kPropertyUseCertificatesAsIdentification; kPropertyUseCertificatesAsIdentification;
extract_pssh_data_ = kExtractPsshData; extract_pssh_data_ = kExtractPsshData;
decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport; decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport;
security_level_path_backward_compatibility_support_ = kSecurityLevelPathBackwardCompatibilitySupport; security_level_path_backward_compatibility_support_ =
kSecurityLevelPathBackwardCompatibilitySupport;
session_property_set_.reset(new CdmClientPropertySetMap()); session_property_set_.reset(new CdmClientPropertySetMap());
} }
bool Properties::AddSessionPropertySet( bool Properties::AddSessionPropertySet(
const CdmSessionId& session_id, const CdmSessionId& session_id, const CdmClientPropertySet* property_set) {
const CdmClientPropertySet* property_set) {
if (NULL == session_property_set_.get()) { if (NULL == session_property_set_.get()) {
return false; return false;
} }
std::pair<CdmClientPropertySetMap::iterator, bool> result = std::pair<CdmClientPropertySetMap::iterator, bool> result =
session_property_set_->insert( session_property_set_->insert(
std::pair<const CdmSessionId, std::pair<const CdmSessionId, const CdmClientPropertySet*>(
const CdmClientPropertySet*>(session_id, property_set)); session_id, property_set));
return result.second; return result.second;
} }
@@ -55,7 +55,7 @@ bool Properties::RemoveSessionPropertySet(const CdmSessionId& session_id) {
} }
const CdmClientPropertySet* Properties::GetCdmClientPropertySet( const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
const CdmSessionId& session_id) { const CdmSessionId& session_id) {
if (NULL != session_property_set_.get()) { if (NULL != session_property_set_.get()) {
CdmClientPropertySetMap::const_iterator it = CdmClientPropertySetMap::const_iterator it =
session_property_set_->find(session_id); session_property_set_->find(session_id);
@@ -112,7 +112,7 @@ uint32_t Properties::GetSessionSharingId(const CdmSessionId& session_id) {
} }
bool Properties::GetSecurityLevelDirectories(std::vector<std::string>* dirs) { bool Properties::GetSecurityLevelDirectories(std::vector<std::string>* dirs) {
dirs->resize(sizeof(kSecurityLevelDirs)/sizeof(const char*)); dirs->resize(sizeof(kSecurityLevelDirs) / sizeof(const char*));
for (size_t i = 0; i < dirs->size(); ++i) { for (size_t i = 0; i < dirs->size(); ++i) {
(*dirs)[i] = kSecurityLevelDirs[i]; (*dirs)[i] = kSecurityLevelDirs[i];
} }

View File

@@ -50,6 +50,16 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
return array; return array;
} }
// converts an ascii hex string(2 bytes per digit) into a decimal byte string
// dump the string with the label.
std::vector<uint8_t> a2b_hex(const std::string& label, const std::string& byte) {
std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]"
<< std::endl << std::endl;
return a2b_hex(byte);
}
std::string a2bs_hex(const std::string& byte) { std::string a2bs_hex(const std::string& byte) {
std::vector<uint8_t> array = a2b_hex(byte); std::vector<uint8_t> array = a2b_hex(byte);
return std::string(array.begin(), array.end()); return std::string(array.begin(), array.end());
@@ -141,7 +151,7 @@ std::string IntToString(int value) {
memset(buffer, 0, kOutputBufSize); memset(buffer, 0, kOutputBufSize);
snprintf(buffer, kOutputBufSize, "%d", value); snprintf(buffer, kOutputBufSize, "%d", value);
std::string out_string(buffer, sizeof(buffer)); std::string out_string(buffer);
return out_string; return out_string;
} }
@@ -153,7 +163,7 @@ std::string UintToString(unsigned int value) {
memset(buffer, 0, kOutputBufSize); memset(buffer, 0, kOutputBufSize);
snprintf(buffer, kOutputBufSize, "%u", value); snprintf(buffer, kOutputBufSize, "%u", value);
std::string out_string(buffer, sizeof(buffer)); std::string out_string(buffer);
return out_string; return out_string;
} }

View File

@@ -5,7 +5,7 @@
#if defined(CHROMIUM_BUILD) #if defined(CHROMIUM_BUILD)
#include "base/at_exit.h" #include "base/at_exit.h"
#include "base/message_loop.h" #include "base/message_loop/message_loop.h"
#endif #endif
#include "cdm_engine.h" #include "cdm_engine.h"
#include "config_test_env.h" #include "config_test_env.h"
@@ -19,7 +19,9 @@
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
namespace { namespace {
// Http OK response code.
const int kHttpOk = 200; const int kHttpOk = 200;
// Default license server, can be configured using --server command line option // Default license server, can be configured using --server command line option
// Default key id (pssh), can be configured using --keyid command line option // Default key id (pssh), can be configured using --keyid command line option
std::string g_client_auth; std::string g_client_auth;
@@ -88,12 +90,11 @@ namespace wvcdm {
class WvCdmEngineTest : public testing::Test { class WvCdmEngineTest : public testing::Test {
public: public:
virtual void SetUp() { virtual void SetUp() {
cdm_engine_.reset(new CdmEngine()); cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
cdm_engine_->OpenSession(g_key_system, NULL, &session_id_);
} }
virtual void TearDown() { virtual void TearDown() {
cdm_engine_->CloseSession(session_id_); cdm_engine_.CloseSession(session_id_);
} }
protected: protected:
@@ -110,21 +111,21 @@ class WvCdmEngineTest : public testing::Test {
} }
EXPECT_EQ(KEY_MESSAGE, EXPECT_EQ(KEY_MESSAGE,
cdm_engine_->GenerateKeyRequest(session_id_, cdm_engine_.GenerateKeyRequest(session_id_,
key_set_id, key_set_id,
init_data, init_data,
kLicenseTypeStreaming, kLicenseTypeStreaming,
app_parameters, app_parameters,
&key_msg_, &key_msg_,
&server_url)); &server_url));
} }
void GenerateRenewalRequest(const std::string& key_system, void GenerateRenewalRequest(const std::string& key_system,
const std::string& init_data) { const std::string& init_data) {
EXPECT_EQ(KEY_MESSAGE, EXPECT_EQ(KEY_MESSAGE,
cdm_engine_->GenerateRenewalRequest(session_id_, cdm_engine_.GenerateRenewalRequest(session_id_,
&key_msg_, &key_msg_,
&server_url_)); &server_url_));
} }
// posts a request and extracts the drm message from the response // posts a request and extracts the drm message from the response
@@ -164,18 +165,18 @@ class WvCdmEngineTest : public testing::Test {
std::string resp = GetKeyRequestResponse(server_url, std::string resp = GetKeyRequestResponse(server_url,
client_auth); client_auth);
CdmKeySetId key_set_id; CdmKeySetId key_set_id;
EXPECT_EQ(cdm_engine_->AddKey(session_id_, resp, &key_set_id), KEY_ADDED); EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp, &key_set_id), KEY_ADDED);
} }
void VerifyRenewalKeyResponse(const std::string& server_url, void VerifyRenewalKeyResponse(const std::string& server_url,
const std::string& client_auth, const std::string& client_auth,
std::string& init_data){ std::string& init_data) {
std::string resp = GetKeyRequestResponse(server_url, std::string resp = GetKeyRequestResponse(server_url,
client_auth); client_auth);
EXPECT_EQ(cdm_engine_->RenewKey(session_id_, resp), wvcdm::KEY_ADDED); EXPECT_EQ(cdm_engine_.RenewKey(session_id_, resp), wvcdm::KEY_ADDED);
} }
scoped_ptr<CdmEngine> cdm_engine_; CdmEngine cdm_engine_;
std::string key_msg_; std::string key_msg_;
std::string session_id_; std::string session_id_;
std::string server_url_; std::string server_url_;
@@ -198,7 +199,10 @@ TEST_F(WvCdmEngineTest, BaseMessageTest) {
TEST_F(WvCdmEngineTest, WrongMessageTest) { TEST_F(WvCdmEngineTest, WrongMessageTest) {
std::string wrong_message = a2bs_hex(g_wrong_key_id); std::string wrong_message = a2bs_hex(g_wrong_key_id);
GenerateKeyRequest(g_key_system, wrong_message); GenerateKeyRequest(g_key_system, wrong_message);
GetKeyRequestResponse(g_license_server, g_client_auth);
// We should receive a response with no license, i.e. the extracted license
// response message should be empty.
ASSERT_EQ("", GetKeyRequestResponse(g_license_server, g_client_auth));
} }
TEST_F(WvCdmEngineTest, NormalDecryption) { TEST_F(WvCdmEngineTest, NormalDecryption) {
@@ -313,7 +317,7 @@ int main(int argc, char **argv) {
#if defined(CHROMIUM_BUILD) #if defined(CHROMIUM_BUILD)
base::AtExitManager exit; base::AtExitManager exit;
MessageLoop ttr(MessageLoop::TYPE_IO); base::MessageLoop ttr(base::MessageLoop::TYPE_IO);
#endif #endif
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }

View File

@@ -5,7 +5,7 @@
namespace { namespace {
// Youtube Content Protection license server data // Youtube Content Protection license server data
const std::string kYtCpLicenseServer = const std::string kYtCpLicenseServer =
"http://kir03wwwg185.widevine.net/drm"; "http://wv-ref-eme-player.appspot.com/proxy";
const std::string kYtCpClientAuth = ""; const std::string kYtCpClientAuth = "";
const std::string kYtCpKeyId = const std::string kYtCpKeyId =
"000000347073736800000000" // blob size and pssh "000000347073736800000000" // blob size and pssh

View File

@@ -26,6 +26,7 @@ using ::testing::StrEq;
namespace { namespace {
const uint32_t kCertificateLen = 700; const uint32_t kCertificateLen = 700;
const uint32_t kWrappedKeyLen = 500; const uint32_t kWrappedKeyLen = 500;
const uint32_t kProtobufEstimatedLen = 75;
const std::string kTestCertificate = const std::string kTestCertificate =
"124B035F3D256A656F0E505A085E7A6C482B61035E0C4A540F7803137F4C3B45206B7F33" "124B035F3D256A656F0E505A085E7A6C482B61035E0C4A540F7803137F4C3B45206B7F33"
"347F4D7A005E56400F0955011F4E07072D0D46781817460974326A516E3944385760280E" "347F4D7A005E56400F0955011F4E07072D0D46781817460974326A516E3944385760280E"
@@ -209,7 +210,7 @@ LicenseInfo license_test_data[] = {
"0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF" "0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF"
"A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0" "A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0"
"BEA6CABACA1C2C"), "BEA6CABACA1C2C"),
"https://jmt17.google.com/video/license/GetCencLicense", "https://jmt17.google.com/video-dev/license/GetCencLicense",
wvcdm::a2bs_hex( wvcdm::a2bs_hex(
"0AAF150802100122A8150801121408011210303132333435363738394142" "0AAF150802100122A8150801121408011210303132333435363738394142"
"434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E" "434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E"
@@ -1038,7 +1039,7 @@ MATCHER_P(IsStrEq, str, "") {
MATCHER_P2(Contains, str1, str2, "") { MATCHER_P2(Contains, str1, str2, "") {
// Estimating the length of data. We can have gmock provide length // Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1 // as well as pointer to data but that will introduce a dependency on tr1
std::string data(arg, str1.size() + str2.size() + 75); std::string data(arg, str1.size() + str2.size() + kProtobufEstimatedLen);
return (data.find(str1) != std::string::npos && return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos); data.find(str2) != std::string::npos);
} }
@@ -1046,7 +1047,7 @@ MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") {
// Estimating the length of data. We can have gmock provide length // Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1 // as well as pointer to data but that will introduce a dependency on tr1
std::string data(arg, str1.size() + str2.size() + str3.size() + str4.size() + std::string data(arg, str1.size() + str2.size() + str3.size() + str4.size() +
str5.size() + str6.size() + 75); str5.size() + str6.size() + kProtobufEstimatedLen);
return (data.find(str1) != std::string::npos && return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos && data.find(str2) != std::string::npos &&
data.find(str3) != std::string::npos && data.find(str3) != std::string::npos &&

View File

@@ -7,7 +7,7 @@
#include "test_vectors.h" #include "test_vectors.h"
namespace { namespace {
const std::string kTestDirName = "test"; const std::string kTestDirName = "test_dir";
const std::string kTestFileName = "test.txt"; const std::string kTestFileName = "test.txt";
const std::string kTestFileName2 = "test2.txt"; const std::string kTestFileName2 = "test2.txt";
const std::string kTestFileNameExt = ".txt"; const std::string kTestFileNameExt = ".txt";
@@ -210,7 +210,7 @@ TEST_F(FileTest, CopyFile) {
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary)); EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size())); EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close(); wr_file.Close();
EXPECT_TRUE(file.Exists(path)); ASSERT_TRUE(file.Exists(path));
std::string path_copy = test_vectors::kTestDir + kTestFileName2; std::string path_copy = test_vectors::kTestDir + kTestFileName2;
EXPECT_FALSE(file.Exists(path_copy)); EXPECT_FALSE(file.Exists(path_copy));
@@ -257,9 +257,9 @@ TEST_F(FileTest, ListFiles) {
EXPECT_EQ(3u, files.size()); EXPECT_EQ(3u, files.size());
for (size_t i = 0; i < files.size(); ++i) { for (size_t i = 0; i < files.size(); ++i) {
EXPECT_TRUE(files[i].compare(kTestDirName) == 0 || EXPECT_TRUE(files[i] == kTestDirName ||
files[i].compare(kTestFileName) == 0 || files[i] == kTestFileName ||
files[i].compare(kTestFileName2) == 0); files[i] == kTestFileName2);
} }
} }

View File

@@ -57,7 +57,7 @@ class PolicyEngineTest : public ::testing::Test {
policy->set_renewal_recovery_duration_seconds(license_duration_ - policy->set_renewal_recovery_duration_seconds(license_duration_ -
license_renewal_delay_); // 10 minutes license_renewal_delay_); // 10 minutes
policy->set_renewal_server_url( policy->set_renewal_server_url(
"https://jmt17.google.com/video/license/GetCencLicense"); "https://jmt17.google.com/video-dev/license/GetCencLicense");
policy->set_renewal_delay_seconds(license_renewal_delay_); policy->set_renewal_delay_seconds(license_renewal_delay_);
policy->set_renewal_retry_interval_seconds( policy->set_renewal_retry_interval_seconds(
license_renewal_retry_interval_); license_renewal_retry_interval_);
@@ -97,7 +97,7 @@ TEST_F(PolicyEngineTest, PlaybackSuccess) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption(); policy_engine_->BeginDecryption();
@@ -115,7 +115,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption(); policy_engine_->BeginDecryption();
@@ -141,10 +141,10 @@ TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -171,10 +171,10 @@ TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -198,10 +198,10 @@ TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -226,10 +226,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -254,10 +254,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -282,10 +282,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -312,10 +312,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
@@ -343,13 +343,13 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -374,10 +374,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
@@ -389,7 +389,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) {
id->set_version(2); id->set_version(2);
policy_engine_->UpdateLicense(license_); policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
@@ -412,10 +412,10 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
@@ -425,13 +425,13 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
license_renewal_delay_ + 15); license_renewal_delay_ + 15);
policy_engine_->UpdateLicense(license_); policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -459,37 +459,37 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -519,37 +519,37 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
@@ -566,7 +566,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) {
policy_engine_->UpdateLicense(license_); policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
@@ -593,25 +593,25 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
license_.set_license_start_time(license_start_time_ + license_.set_license_start_time(license_start_time_ +
@@ -620,12 +620,12 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) {
id->set_version(2); id->set_version(2);
policy_engine_->UpdateLicense(license_); policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
@@ -647,13 +647,13 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption(); policy_engine_->BeginDecryption();
EXPECT_FALSE(policy_engine_->can_decrypt()); EXPECT_FALSE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred); EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
@@ -663,7 +663,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) {
id->set_version(2); id->set_version(2);
policy_engine_->UpdateLicense(license_); policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt()); EXPECT_TRUE(policy_engine_->can_decrypt());
@@ -723,7 +723,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_Offline) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption(); policy_engine_->BeginDecryption();
@@ -765,7 +765,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) {
bool event_occurred; bool event_occurred;
CdmEventType event; CdmEventType event;
policy_engine_->OnTimerEvent(event_occurred, event); policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred); EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption(); policy_engine_->BeginDecryption();

View File

@@ -114,12 +114,14 @@ int UrlRequest::GetResponse(std::string* message) {
bytes = socket_.Read(buffer_, kHttpBufferSize, kTimeoutInMs); bytes = socket_.Read(buffer_, kHttpBufferSize, kTimeoutInMs);
if (bytes > 0) { if (bytes > 0) {
response.append(buffer_, bytes); response.append(buffer_, bytes);
attempts = kSingleReadAttempt; if (bytes < static_cast<int>(kHttpBufferSize)) {
attempts = kSingleReadAttempt;
}
} else { } else {
if (bytes < 0) LOGE("read error = ", errno); if (bytes < 0) LOGE("read error = ", errno);
// bytes == 0 indicates nothing to read // bytes == 0 indicates nothing to read
} }
}; }
ConcatenateChunkedResponse(response, message); ConcatenateChunkedResponse(response, message);
LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str()); LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str());

View File

@@ -3,17 +3,19 @@
#ifndef CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ #ifndef CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_
#define CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ #define CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_
#include "wv_cdm_types.h"
#include <UniquePtr.h> #include <UniquePtr.h>
#include "lock.h"
#include "timer.h"
#include "wv_cdm_types.h"
namespace wvcdm { namespace wvcdm {
class CdmClientPropertySet; class CdmClientPropertySet;
class CdmEngine; class CdmEngine;
class WvCdmEventListener; class WvCdmEventListener;
class WvContentDecryptionModule { class WvContentDecryptionModule : public TimerHandler {
public: public:
WvContentDecryptionModule(); WvContentDecryptionModule();
virtual ~WvContentDecryptionModule(); virtual ~WvContentDecryptionModule();
@@ -91,6 +93,14 @@ class WvContentDecryptionModule {
private: private:
uint32_t GenerateSessionSharingId(); uint32_t GenerateSessionSharingId();
// timer related methods to drive policy decisions
void EnablePolicyTimer();
void DisablePolicyTimer(bool force);
void OnTimerEvent();
static Lock session_sharing_id_generation_lock_;
Timer policy_timer_;
// instance variables // instance variables
UniquePtr<CdmEngine> cdm_engine_; UniquePtr<CdmEngine> cdm_engine_;

View File

@@ -18,8 +18,12 @@
namespace { namespace {
const char kCurrentDirectory[] = "."; const char kCurrentDirectory[] = ".";
const char kParentDirectory[] = ".."; const char kParentDirectory[] = "..";
const char kPathDelimiter[] = "/"; const char kDirectoryDelimiter = '/';
const char kWildcard[] = "*"; const char kWildcard[] = "*";
bool IsCurrentOrParentDirectory(char* dir) {
return strcmp(dir, kCurrentDirectory) == 0 ||
strcmp(dir, kParentDirectory) == 0;
}
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
@@ -114,9 +118,8 @@ bool File::Remove(const std::string& path) {
// first remove files and dir within it // first remove files and dir within it
struct dirent* entry; struct dirent* entry;
while ((entry = readdir(dir)) != NULL) { while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, kCurrentDirectory) && if (!IsCurrentOrParentDirectory(entry->d_name)) {
(strcmp(entry->d_name, kParentDirectory))) { std::string path_to_remove = path + kDirectoryDelimiter;
std::string path_to_remove = path + kPathDelimiter;
path_to_remove += entry->d_name; path_to_remove += entry->d_name;
if (!Remove(path_to_remove)) { if (!Remove(path_to_remove)) {
closedir(dir); closedir(dir);
@@ -141,7 +144,7 @@ bool File::Remove(const std::string& path) {
} }
} else { } else {
// Handle wildcard specified file deletion // Handle wildcard specified file deletion
size_t delimiter_pos = path.rfind(kPathDelimiter, wildcard_pos); size_t delimiter_pos = path.rfind(kDirectoryDelimiter, wildcard_pos);
if (delimiter_pos == std::string::npos) { if (delimiter_pos == std::string::npos) {
LOGW("File::Remove: unable to find path delimiter before wildcard"); LOGW("File::Remove: unable to find path delimiter before wildcard");
return false; return false;
@@ -163,7 +166,7 @@ bool File::Remove(const std::string& path) {
if (strcmp(entry->d_name + filename_len - ext.size(), ext.c_str()) == if (strcmp(entry->d_name + filename_len - ext.size(), ext.c_str()) ==
0) { 0) {
std::string file_path_to_remove = std::string file_path_to_remove =
dir_path + kPathDelimiter + entry->d_name; dir_path + kDirectoryDelimiter + entry->d_name;
if (!Remove(file_path_to_remove)) { if (!Remove(file_path_to_remove)) {
closedir(dir); closedir(dir);
return false; return false;
@@ -180,34 +183,34 @@ bool File::Remove(const std::string& path) {
bool File::Copy(const std::string& src, const std::string& dest) { bool File::Copy(const std::string& src, const std::string& dest) {
struct stat stat_buf; struct stat stat_buf;
if (stat(src.c_str(), &stat_buf)) { if (stat(src.c_str(), &stat_buf)) {
LOGV("File::Copy: file %s does not exist: %d", src.c_str(), errno); LOGV("File::Copy: file %s stat error: %d", src.c_str(), errno);
return false; return false;
} }
int fd_src = open(src.c_str(), O_RDONLY); int fd_src = open(src.c_str(), O_RDONLY);
if (fd_src < 0) { if (fd_src < 0) {
LOGV("File::Copy: unable to open file %s: %d", src.c_str(), errno); LOGW("File::Copy: unable to open file %s: %d", src.c_str(), errno);
return false; return false;
} }
int fd_dest = open(dest.c_str(), O_WRONLY|O_CREAT, stat_buf.st_mode); int fd_dest = open(dest.c_str(), O_WRONLY | O_CREAT, stat_buf.st_mode);
if (fd_dest < 0) { if (fd_dest < 0) {
LOGV("File::Copy: unable to open file %s: %d", dest.c_str(), errno); LOGW("File::Copy: unable to open file %s: %d", dest.c_str(), errno);
close(fd_src); close(fd_src);
return false; return false;
} }
off_t offset = 0; off_t offset = 0;
bool sts = true; bool status = true;
if (sendfile(fd_dest, fd_src, &offset, stat_buf.st_size) < 0) { if (sendfile(fd_dest, fd_src, &offset, stat_buf.st_size) < 0) {
LOGV("File::Copy: unable to copy %s to %s: %d", src.c_str(), dest.c_str(), LOGV("File::Copy: unable to copy %s to %s: %d", src.c_str(), dest.c_str(),
errno); errno);
sts = false; status = false;
} }
close(fd_src); close(fd_src);
close(fd_dest); close(fd_dest);
return sts; return status;
} }
bool File::List(const std::string& path, std::vector<std::string>* files) { bool File::List(const std::string& path, std::vector<std::string>* files) {
@@ -221,16 +224,16 @@ bool File::List(const std::string& path, std::vector<std::string>* files) {
return false; return false;
} }
DIR* dir; DIR* dir = opendir(path.c_str());
if ((dir = opendir(path.c_str())) == NULL) { if (dir == NULL) {
LOGW("File::List: unable to open directory %s: %d", path.c_str(), errno); LOGW("File::List: unable to open directory %s: %d", path.c_str(), errno);
return false; return false;
} }
files->clear();
struct dirent* entry; struct dirent* entry;
while ((entry = readdir(dir)) != NULL) { while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, kCurrentDirectory) && if (!IsCurrentOrParentDirectory(entry->d_name)) {
(strcmp(entry->d_name, kParentDirectory))) {
files->push_back(entry->d_name); files->push_back(entry->d_name);
} }
} }
@@ -241,31 +244,29 @@ bool File::List(const std::string& path, std::vector<std::string>* files) {
bool File::CreateDirectory(std::string path) { bool File::CreateDirectory(std::string path) {
size_t size = path.size(); size_t size = path.size();
if ((size == 1) && (path[0] == kPathDelimiter[0])) return true; if ((size == 1) && (path[0] == kDirectoryDelimiter)) return true;
if (size <= 1) return false; if (size <= 1) return false;
if (path.at(size - 1) == kPathDelimiter[0]) { size_t pos = path.find(kDirectoryDelimiter, 1);
--size;
path.resize(size);
}
size_t pos = path.find(kPathDelimiter[0], 1);
while (pos < size) { while (pos < size) {
path.at(pos) = '\0'; path[pos] = '\0';
if (mkdir(path.c_str(), 0775) != 0) { if (mkdir(path.c_str(), 0775) != 0) {
if (errno != EEXIST) { if (errno != EEXIST) {
LOGW("File::CreateDirectory: mkdir failed: %d\n", errno); LOGW("File::CreateDirectory: mkdir failed: %d\n", errno);
return false; return false;
} }
} }
path.at(pos) = kPathDelimiter[0]; path[pos] = kDirectoryDelimiter;
pos = path.find(kPathDelimiter[0], pos + 1); pos = path.find(kDirectoryDelimiter, pos + 1);
} }
if (mkdir(path.c_str(), 0775) != 0) {
if (errno != EEXIST) { if (path[size - 1] != kDirectoryDelimiter) {
LOGW("File::CreateDirectory: mkdir failed: %d\n", errno); if (mkdir(path.c_str(), 0775) != 0) {
return false; if (errno != EEXIST) {
LOGW("File::CreateDirectory: mkdir failed: %d\n", errno);
return false;
}
} }
} }
return true; return true;

View File

@@ -11,18 +11,27 @@
#include "wv_cdm_constants.h" #include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h" #include "wv_cdm_event_listener.h"
namespace {
const int kCdmPolicyTimerDurationSeconds = 1;
}
namespace wvcdm { namespace wvcdm {
Lock WvContentDecryptionModule::session_sharing_id_generation_lock_;
WvContentDecryptionModule::WvContentDecryptionModule() WvContentDecryptionModule::WvContentDecryptionModule()
: cdm_engine_(new CdmEngine()) {} : cdm_engine_(new CdmEngine()) {}
WvContentDecryptionModule::~WvContentDecryptionModule() {} WvContentDecryptionModule::~WvContentDecryptionModule() {
DisablePolicyTimer(true);
}
CdmResponseType WvContentDecryptionModule::OpenSession( CdmResponseType WvContentDecryptionModule::OpenSession(
const CdmKeySystem& key_system, const CdmKeySystem& key_system,
CdmClientPropertySet* property_set, CdmClientPropertySet* property_set,
CdmSessionId* session_id) { CdmSessionId* session_id) {
if (property_set && property_set->is_session_sharing_enabled()) { if (property_set && property_set->is_session_sharing_enabled()) {
AutoLock auto_lock(session_sharing_id_generation_lock_);
if (property_set->session_sharing_id() == 0) if (property_set->session_sharing_id() == 0)
property_set->set_session_sharing_id(GenerateSessionSharingId()); property_set->set_session_sharing_id(GenerateSessionSharingId());
} }
@@ -32,7 +41,9 @@ CdmResponseType WvContentDecryptionModule::OpenSession(
CdmResponseType WvContentDecryptionModule::CloseSession( CdmResponseType WvContentDecryptionModule::CloseSession(
const CdmSessionId& session_id) { const CdmSessionId& session_id) {
return cdm_engine_->CloseSession(session_id); CdmResponseType sts = cdm_engine_->CloseSession(session_id);
DisablePolicyTimer(false);
return sts;
} }
CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
@@ -49,13 +60,19 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
if (sts != NO_ERROR) if (sts != NO_ERROR)
return sts; return sts;
} }
sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, init_data,
init_data, license_type, license_type, app_parameters,
app_parameters, key_request, key_request, server_url);
server_url);
if (license_type == kLicenseTypeRelease && sts != KEY_MESSAGE) { switch(license_type) {
cdm_engine_->CloseKeySetSession(key_set_id); case kLicenseTypeRelease:
if (sts != KEY_MESSAGE)
cdm_engine_->CloseKeySetSession(key_set_id);
break;
default:
if (sts == KEY_MESSAGE)
EnablePolicyTimer();
break;
} }
return sts; return sts;
} }
@@ -124,16 +141,17 @@ CdmResponseType WvContentDecryptionModule::Decrypt(
const CdmSessionId& session_id, const CdmSessionId& session_id,
bool validate_key_id, bool validate_key_id,
const CdmDecryptionParameters& parameters) { const CdmDecryptionParameters& parameters) {
CdmSessionId id = session_id; CdmSessionId local_session_id = session_id;
if (validate_key_id && if (validate_key_id &&
Properties::GetSessionSharingId(session_id) != 0) { Properties::GetSessionSharingId(session_id) != 0) {
bool status = cdm_engine_->FindSessionForKey(*parameters.key_id, &id); bool status = cdm_engine_->FindSessionForKey(*parameters.key_id,
&local_session_id);
if (!status) { if (!status) {
LOGE("WvContentDecryptionModule::Decrypt: unable to find session"); LOGE("WvContentDecryptionModule::Decrypt: unable to find session");
return NEED_KEY; return NEED_KEY;
} }
} }
return cdm_engine_->Decrypt(id, parameters); return cdm_engine_->Decrypt(local_session_id, parameters);
} }
bool WvContentDecryptionModule::AttachEventListener( bool WvContentDecryptionModule::AttachEventListener(
@@ -146,9 +164,25 @@ bool WvContentDecryptionModule::DetachEventListener(
return cdm_engine_->DetachEventListener(session_id, listener); return cdm_engine_->DetachEventListener(session_id, listener);
} }
void WvContentDecryptionModule::EnablePolicyTimer() {
if (!policy_timer_.IsRunning())
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);
}
void WvContentDecryptionModule::DisablePolicyTimer(bool force) {
if ((cdm_engine_->SessionSize() == 0 || force) && policy_timer_.IsRunning())
policy_timer_.Stop();
}
void WvContentDecryptionModule::OnTimerEvent() {
cdm_engine_->OnTimerEvent();
}
uint32_t WvContentDecryptionModule::GenerateSessionSharingId() { uint32_t WvContentDecryptionModule::GenerateSessionSharingId() {
static int next_session_sharing_id = 0; static int next_session_sharing_id = 0;
return ++next_session_sharing_id; return ++next_session_sharing_id;
} }
} // namespace wvcdm } // namespace wvcdm

View File

@@ -21,6 +21,9 @@
namespace { namespace {
const char kPathDelimiter = '/'; const char kPathDelimiter = '/';
// Http OK response code.
const int kHttpOk = 200;
// Default license server, can be configured using --server command line option // Default license server, can be configured using --server command line option
// Default key id (pssh), can be configured using --keyid command line option // Default key id (pssh), can be configured using --keyid command line option
std::string g_client_auth; std::string g_client_auth;
@@ -239,10 +242,10 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
return service_certificate_; return service_certificate_;
} }
virtual bool use_privacy_mode() const { return use_privacy_mode_; } virtual bool use_privacy_mode() const { return use_privacy_mode_; }
bool is_session_sharing_enabled() const { virtual bool is_session_sharing_enabled() const {
return is_session_sharing_enabled_; return is_session_sharing_enabled_;
} }
uint32_t session_sharing_id() const { return session_sharing_id_; } virtual uint32_t session_sharing_id() const { return session_sharing_id_; }
void set_security_level(const std::string& security_level) { void set_security_level(const std::string& security_level) {
if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) || if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) ||
@@ -273,7 +276,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
class TestWvCdmEventListener : public WvCdmEventListener { class TestWvCdmEventListener : public WvCdmEventListener {
public: public:
TestWvCdmEventListener() : WvCdmEventListener() {} TestWvCdmEventListener() : WvCdmEventListener() {}
virtual void onEvent(const CdmSessionId& id, CdmEventType event) { virtual void OnEvent(const CdmSessionId& id, CdmEventType event) {
session_id_ = id; session_id_ = id;
event_type_ = event; event_type_ = event;
} }
@@ -333,8 +336,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
// Post a request and extract the drm message from the response // Post a request and extract the drm message from the response
std::string GetKeyRequestResponse(const std::string& server_url, std::string GetKeyRequestResponse(const std::string& server_url,
const std::string& client_auth, const std::string& client_auth) {
int expected_response) {
// Use secure connection and chunk transfer coding. // Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth, g_port, UrlRequest url_request(server_url + client_auth, g_port,
g_use_secure_transfer, g_use_chunked_transfer); g_use_secure_transfer, g_use_chunked_transfer);
@@ -345,15 +347,11 @@ class WvCdmRequestLicenseTest : public testing::Test {
std::string message; std::string message;
int resp_bytes = url_request.GetResponse(&message); int resp_bytes = url_request.GetResponse(&message);
// Youtube server returns 400 for invalid message while play server returns
// 500, so just test inequity here for invalid message
int status_code = url_request.GetStatusCode(message); int status_code = url_request.GetStatusCode(message);
if (expected_response == 200) { EXPECT_EQ(kHttpOk, status_code);
EXPECT_EQ(200, status_code);
}
std::string drm_msg; std::string drm_msg;
if (200 == status_code) { if (kHttpOk == status_code) {
LicenseRequest lic_request; LicenseRequest lic_request;
lic_request.GetDrmMessage(message, drm_msg); lic_request.GetDrmMessage(message, drm_msg);
LOGV("HTTP response body: (%u bytes)", drm_msg.size()); LOGV("HTTP response body: (%u bytes)", drm_msg.size());
@@ -363,8 +361,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
// Post a request and extract the signed provisioning message from // Post a request and extract the signed provisioning message from
// the HTTP response. // the HTTP response.
std::string GetCertRequestResponse(const std::string& server_url, std::string GetCertRequestResponse(const std::string& server_url) {
int expected_response) {
// Use secure connection and chunk transfer coding. // Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url, kDefaultHttpsPort, true, true); UrlRequest url_request(server_url, kDefaultHttpsPort, true, true);
if (!url_request.is_connected()) { if (!url_request.is_connected()) {
@@ -376,21 +373,15 @@ class WvCdmRequestLicenseTest : public testing::Test {
int resp_bytes = url_request.GetResponse(&message); int resp_bytes = url_request.GetResponse(&message);
LOGD("end %d bytes response dump", resp_bytes); LOGD("end %d bytes response dump", resp_bytes);
// Youtube server returns 400 for invalid message while play server returns
// 500, so just test inequity here for invalid message
int status_code = url_request.GetStatusCode(message); int status_code = url_request.GetStatusCode(message);
if (expected_response == 200) { EXPECT_EQ(kHttpOk, status_code);
EXPECT_EQ(200, status_code);
} else {
EXPECT_NE(200, status_code);
}
return message; return message;
} }
void VerifyKeyRequestResponse(const std::string& server_url, void VerifyKeyRequestResponse(const std::string& server_url,
const std::string& client_auth, const std::string& client_auth,
std::string& init_data, bool is_renewal) { std::string& init_data, bool is_renewal) {
std::string resp = GetKeyRequestResponse(server_url, client_auth, 200); std::string resp = GetKeyRequestResponse(server_url, client_auth);
if (is_renewal) { if (is_renewal) {
// TODO application makes a license request, CDM will renew the license // TODO application makes a license request, CDM will renew the license
@@ -403,20 +394,24 @@ class WvCdmRequestLicenseTest : public testing::Test {
} }
} }
std::string GetSecurityLevel(TestWvCdmClientPropertySet* property_set) {
decryptor_.OpenSession(g_key_system, property_set, &session_id_);
CdmQueryMap query_info;
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_.QuerySessionStatus(session_id_, &query_info));
CdmQueryMap::iterator itr =
query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
EXPECT_TRUE(itr != query_info.end());
decryptor_.CloseSession(session_id_);
return itr->second;
}
wvcdm::WvContentDecryptionModule decryptor_; wvcdm::WvContentDecryptionModule decryptor_;
CdmKeyMessage key_msg_; CdmKeyMessage key_msg_;
CdmSessionId session_id_; CdmSessionId session_id_;
CdmKeySetId key_set_id_; CdmKeySetId key_set_id_;
}; };
class WvCdmDecryptionTest
: public WvCdmRequestLicenseTest,
public ::testing::WithParamInterface<SubSampleInfo*> {};
class WvCdmSessionSharingTest
: public WvCdmRequestLicenseTest,
public ::testing::WithParamInterface<SessionSharingSubSampleInfo*> {};
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
decryptor_.OpenSession(g_key_system, NULL, &session_id_); decryptor_.OpenSession(g_key_system, NULL, &session_id_);
std::string provisioning_server_url; std::string provisioning_server_url;
@@ -426,7 +421,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
std::string response = std::string response =
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url());
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
@@ -445,12 +440,12 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
std::string response = std::string response =
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url());
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
response = response =
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url());
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::UNKNOWN_ERROR, EXPECT_EQ(wvcdm::UNKNOWN_ERROR,
decryptor_.HandleProvisioningResponse(response)); decryptor_.HandleProvisioningResponse(response));
@@ -471,17 +466,16 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
property_set_L3.set_use_privacy_mode(false); property_set_L3.set_use_privacy_mode(false);
CdmResponseType sts = decryptor_.OpenSession(g_key_system, &property_set_L3, CdmResponseType sts =
&session_id_L3); decryptor_.OpenSession(g_key_system, &property_set_L3, &session_id_L3);
if (NEED_PROVISIONING == sts) { if (NEED_PROVISIONING == sts) {
std::string provisioning_server_url; std::string provisioning_server_url;
EXPECT_EQ( EXPECT_EQ(NO_ERROR, decryptor_.GetProvisioningRequest(
NO_ERROR, &key_msg_, &provisioning_server_url));
decryptor_.GetProvisioningRequest(&key_msg_, &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
std::string response = std::string response =
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url());
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set_L3, EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set_L3,
@@ -498,7 +492,7 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)); !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3));
EXPECT_TRUE(Properties::UsePrivacyMode(session_id_L1)); EXPECT_TRUE(Properties::UsePrivacyMode(session_id_L1));
EXPECT_EQ(Properties::GetSecurityLevel(session_id_L3), EXPECT_EQ(Properties::GetSecurityLevel(session_id_L3),
QUERY_VALUE_SECURITY_LEVEL_L3); QUERY_VALUE_SECURITY_LEVEL_L3);
EXPECT_FALSE(Properties::UsePrivacyMode(session_id_L3)); EXPECT_FALSE(Properties::UsePrivacyMode(session_id_L3));
security_level = Properties::GetSecurityLevel(session_id_Ln); security_level = Properties::GetSecurityLevel(session_id_Ln);
EXPECT_TRUE(security_level.empty() || EXPECT_TRUE(security_level.empty() ||
@@ -520,17 +514,16 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
EXPECT_EQ(NEED_PROVISIONING, EXPECT_EQ(NEED_PROVISIONING,
decryptor_.OpenSession(g_key_system, &property_set, &session_id_)); decryptor_.OpenSession(g_key_system, &property_set, &session_id_));
std::string provisioning_server_url; std::string provisioning_server_url;
EXPECT_EQ(NO_ERROR, EXPECT_EQ(NO_ERROR, decryptor_.GetProvisioningRequest(
decryptor_.GetProvisioningRequest(&key_msg_, &key_msg_, &provisioning_server_url));
&provisioning_server_url));
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
std::string response = std::string response =
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url());
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set, EXPECT_EQ(NO_ERROR,
&session_id_)); decryptor_.OpenSession(g_key_system, &property_set, &session_id_));
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
@@ -543,8 +536,8 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeTest) {
decryptor_.OpenSession(g_key_system, &property_set, &session_id_); decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
std::string resp = GetKeyRequestResponse(g_license_server, std::string resp =
g_client_auth, 200); GetKeyRequestResponse(g_license_server, g_client_auth);
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_), EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_),
wvcdm::NEED_KEY); wvcdm::NEED_KEY);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
@@ -552,7 +545,8 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeTest) {
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeWithServiceCertificateTest) { TEST_F(WvCdmRequestLicenseTest,
DISABLED_PrivacyModeWithServiceCertificateTest) {
TestWvCdmClientPropertySet property_set; TestWvCdmClientPropertySet property_set;
property_set.set_use_privacy_mode(true); property_set.set_use_privacy_mode(true);
@@ -563,68 +557,10 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeWithServiceCertificateTest)
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
SessionSharingSubSampleInfo* session_sharing_info = GetParam();
TestWvCdmClientPropertySet property_set;
property_set.set_session_sharing_mode(
session_sharing_info->session_sharing_enabled);
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
CdmSessionId gp_session_id_1 = session_id_;
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
// TODO(rfrias): Move content information to ConfigTestEnv
std::string gp_client_auth2 =
"?source=YOUTUBE&video_id=z3S_NhwueaM&oauth=ya.gtsqawidevine";
std::string gp_key_id2 =
wvcdm::a2bs_hex(
"000000347073736800000000" // blob size and pssh
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
"08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
CdmSessionId gp_session_id_2 = session_id_;
GenerateKeyRequest(g_key_system, gp_key_id2, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, gp_client_auth2, gp_key_id2, false);
SubSampleInfo* data = session_sharing_info->sub_sample;
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(&data->key_id,
&data->encrypt_data.front(),
data->encrypt_data.size(),
&data->iv,
data->block_offset,
&decrypt_buffer[0]);
decryption_parameters.is_encrypted = data->is_encrypted;
decryption_parameters.is_secure = data->is_secure;
if (session_sharing_info->session_sharing_enabled || !data->is_encrypted) {
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(gp_session_id_2,
data->validate_key_id,
decryption_parameters));
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
decrypt_buffer.begin()));
} else {
EXPECT_EQ(NEED_KEY, decryptor_.Decrypt(gp_session_id_2,
data->validate_key_id,
decryption_parameters));
}
decryptor_.CloseSession(gp_session_id_1);
decryptor_.CloseSession(gp_session_id_2);
}
INSTANTIATE_TEST_CASE_P(
Cdm, WvCdmSessionSharingTest,
::testing::Range(&session_sharing_sub_samples[0],
&session_sharing_sub_samples[6]));
TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) { TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) {
decryptor_.OpenSession(g_key_system, NULL, &session_id_); decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
GetKeyRequestResponse(g_license_server, g_client_auth, 200); GetKeyRequestResponse(g_license_server, g_client_auth);
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
@@ -633,7 +569,9 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) {
std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id); std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id);
GenerateKeyRequest(g_key_system, wrong_message, kLicenseTypeStreaming); GenerateKeyRequest(g_key_system, wrong_message, kLicenseTypeStreaming);
GetKeyRequestResponse(g_license_server, g_client_auth, 500); // We should receive a response with no license, i.e. the extracted license
// response message should be empty.
EXPECT_EQ("", GetKeyRequestResponse(g_license_server, g_client_auth));
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
@@ -666,7 +604,7 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
TEST_F(WvCdmRequestLicenseTest, DISABLED_ReleaseOfflineKeyTest) { TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
decryptor_.OpenSession(g_key_system, NULL, &session_id_); decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -688,7 +626,7 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_ReleaseOfflineKeyTest) {
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
} }
TEST_F(WvCdmRequestLicenseTest, DISABLED_ExpiryOnReleaseOfflineKeyTest) { TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
decryptor_.OpenSession(g_key_system, NULL, &session_id_); decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -744,7 +682,7 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) {
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
TEST_F(WvCdmRequestLicenseTest, QuerySessionStatus) { TEST_F(WvCdmRequestLicenseTest, QueryUnmodifiedSessionStatus) {
// Test that the global value is returned when no properties are modifying it. // Test that the global value is returned when no properties are modifying it.
CdmQueryMap system_query_info; CdmQueryMap system_query_info;
CdmQueryMap::iterator system_itr; CdmQueryMap::iterator system_itr;
@@ -752,30 +690,15 @@ TEST_F(WvCdmRequestLicenseTest, QuerySessionStatus) {
system_itr = system_query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL); system_itr = system_query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
ASSERT_TRUE(system_itr != system_query_info.end()); ASSERT_TRUE(system_itr != system_query_info.end());
decryptor_.OpenSession(g_key_system, NULL, &session_id_); EXPECT_EQ(system_itr->second, GetSecurityLevel(NULL));
CdmQueryMap unmodified_query_info; }
CdmQueryMap::iterator unmodified_itr;
ASSERT_EQ(wvcdm::NO_ERROR,
decryptor_.QuerySessionStatus(session_id_, &unmodified_query_info));
unmodified_itr = unmodified_query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
ASSERT_TRUE(unmodified_itr != unmodified_query_info.end());
EXPECT_EQ(system_itr->second, unmodified_itr->second);
decryptor_.CloseSession(session_id_);
TEST_F(WvCdmRequestLicenseTest, QueryModifiedSessionStatus) {
// Test that L3 is returned when properties downgrade security. // Test that L3 is returned when properties downgrade security.
TestWvCdmClientPropertySet property_set_L3; TestWvCdmClientPropertySet property_set_L3;
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
decryptor_.OpenSession(g_key_system, &property_set_L3, &session_id_); EXPECT_EQ(QUERY_VALUE_SECURITY_LEVEL_L3, GetSecurityLevel(&property_set_L3));
CdmQueryMap modified_query_info;
CdmQueryMap::iterator modified_itr;
ASSERT_EQ(wvcdm::NO_ERROR,
decryptor_.QuerySessionStatus(session_id_, &modified_query_info));
modified_itr = modified_query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
ASSERT_TRUE(modified_itr != modified_query_info.end());
EXPECT_EQ(QUERY_VALUE_SECURITY_LEVEL_L3, modified_itr->second);
decryptor_.CloseSession(session_id_);
} }
TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) {
@@ -891,20 +814,15 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) {
size_t pos = std::string::npos; size_t pos = std::string::npos;
for (size_t i = 0; i < security_dirs.size(); i++) { for (size_t i = 0; i < security_dirs.size(); i++) {
pos = base_path.rfind(security_dirs[i]); pos = base_path.rfind(security_dirs[i]);
if (std::string::npos != pos) if (std::string::npos != pos) break;
break;
} }
EXPECT_NE(std::string::npos, pos); EXPECT_NE(std::string::npos, pos);
std::string old_base_path(base_path, 0, pos); std::string old_base_path(base_path, 0, pos);
std::string path(old_base_path);
path += kPathDelimiter;
size_t path_len = path.size();
File file; File file;
for (size_t i = 0; i < security_dirs.size(); i++) { for (size_t i = 0; i < security_dirs.size(); i++) {
path.append(security_dirs[i]); std::string path = old_base_path + kPathDelimiter + security_dirs[i];
file.Remove(path); file.Remove(path);
path.resize(path_len);
} }
decryptor_.OpenSession(g_key_system, NULL, &session_id_); decryptor_.OpenSession(g_key_system, NULL, &session_id_);
@@ -913,7 +831,7 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) {
&key_msg_, &provisioning_server_url)); &key_msg_, &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
std::string response = std::string response =
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url());
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
@@ -964,56 +882,21 @@ TEST_F(WvCdmRequestLicenseTest, SecurityLevelPathBackwardCompatibility) {
decryptor_.GenerateKeyRequest(session_id_, key_set_id, g_key_id, decryptor_.GenerateKeyRequest(session_id_, key_set_id, g_key_id,
kLicenseTypeStreaming, app_parameters, kLicenseTypeStreaming, app_parameters,
&key_msg_, &server_url)); &key_msg_, &server_url));
EXPECT_EQ(NO_ERROR, EXPECT_EQ(NO_ERROR, decryptor_.GetProvisioningRequest(
decryptor_.GetProvisioningRequest(&key_msg_, &key_msg_, &provisioning_server_url));
&provisioning_server_url));
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
response = response =
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url());
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set, EXPECT_EQ(NO_ERROR,
&session_id_)); decryptor_.OpenSession(g_key_system, &property_set, &session_id_));
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
TEST_P(WvCdmDecryptionTest, DecryptionTest) {
SubSampleInfo* data = GetParam();
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
if (data->retrieve_key) {
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
}
for (size_t i = 0; i < data->num_of_subsamples; i++) {
std::vector<uint8_t> decrypt_buffer((data + i)->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&(data + i)->key_id, &(data + i)->encrypt_data.front(),
(data + i)->encrypt_data.size(), &(data + i)->iv,
(data + i)->block_offset, &decrypt_buffer[0]);
decryption_parameters.is_encrypted = (data + i)->is_encrypted;
decryption_parameters.is_secure = (data + i)->is_secure;
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
(data+i)->validate_key_id,
decryption_parameters));
EXPECT_TRUE(std::equal((data + i)->decrypt_data.begin(),
(data + i)->decrypt_data.end(),
decrypt_buffer.begin()));
}
decryptor_.CloseSession(session_id_);
}
INSTANTIATE_TEST_CASE_P(
Cdm, WvCdmDecryptionTest,
::testing::Values(&clear_sub_sample, &clear_sub_sample_no_key,
&single_encrypted_sub_sample,
&switch_key_encrypted_sub_sample[0],
&partial_single_encrypted_sub_sample));
TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) { TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) {
decryptor_.OpenSession(g_key_system, NULL, &session_id_); decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
@@ -1181,6 +1064,107 @@ TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) {
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
*/ */
class WvCdmSessionSharingTest
: public WvCdmRequestLicenseTest,
public ::testing::WithParamInterface<SessionSharingSubSampleInfo*> {};
TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
SessionSharingSubSampleInfo* session_sharing_info = GetParam();
TestWvCdmClientPropertySet property_set;
property_set.set_session_sharing_mode(
session_sharing_info->session_sharing_enabled);
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
CdmSessionId gp_session_id_1 = session_id_;
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
// TODO(rfrias): Move content information to ConfigTestEnv
std::string gp_client_auth2 =
"?source=YOUTUBE&video_id=z3S_NhwueaM&oauth=ya.gtsqawidevine";
std::string gp_key_id2 =
wvcdm::a2bs_hex(
"000000347073736800000000" // blob size and pssh
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
"08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
CdmSessionId gp_session_id_2 = session_id_;
GenerateKeyRequest(g_key_system, gp_key_id2, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, gp_client_auth2, gp_key_id2, false);
SubSampleInfo* data = session_sharing_info->sub_sample;
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(&data->key_id,
&data->encrypt_data.front(),
data->encrypt_data.size(),
&data->iv,
data->block_offset,
&decrypt_buffer[0]);
decryption_parameters.is_encrypted = data->is_encrypted;
decryption_parameters.is_secure = data->is_secure;
if (session_sharing_info->session_sharing_enabled || !data->is_encrypted) {
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(gp_session_id_2,
data->validate_key_id,
decryption_parameters));
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
decrypt_buffer.begin()));
} else {
EXPECT_EQ(NEED_KEY, decryptor_.Decrypt(gp_session_id_2,
data->validate_key_id,
decryption_parameters));
}
decryptor_.CloseSession(gp_session_id_1);
decryptor_.CloseSession(gp_session_id_2);
}
INSTANTIATE_TEST_CASE_P(
Cdm, WvCdmSessionSharingTest,
::testing::Range(&session_sharing_sub_samples[0],
&session_sharing_sub_samples[6]));
class WvCdmDecryptionTest
: public WvCdmRequestLicenseTest,
public ::testing::WithParamInterface<SubSampleInfo*> {};
TEST_P(WvCdmDecryptionTest, DecryptionTest) {
SubSampleInfo* data = GetParam();
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
if (data->retrieve_key) {
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
}
for (size_t i = 0; i < data->num_of_subsamples; i++) {
std::vector<uint8_t> decrypt_buffer((data + i)->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&(data + i)->key_id, &(data + i)->encrypt_data.front(),
(data + i)->encrypt_data.size(), &(data + i)->iv,
(data + i)->block_offset, &decrypt_buffer[0]);
decryption_parameters.is_encrypted = (data + i)->is_encrypted;
decryption_parameters.is_secure = (data + i)->is_secure;
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
(data+i)->validate_key_id,
decryption_parameters));
EXPECT_TRUE(std::equal((data + i)->decrypt_data.begin(),
(data + i)->decrypt_data.end(),
decrypt_buffer.begin()));
}
decryptor_.CloseSession(session_id_);
}
INSTANTIATE_TEST_CASE_P(
Cdm, WvCdmDecryptionTest,
::testing::Values(&clear_sub_sample, &clear_sub_sample_no_key,
&single_encrypted_sub_sample,
&switch_key_encrypted_sub_sample[0],
&partial_single_encrypted_sub_sample));
} // namespace wvcdm } // namespace wvcdm
void show_menu(char* prog_name) { void show_menu(char* prog_name) {
@@ -1190,40 +1174,36 @@ void show_menu(char* prog_name) {
<< std::endl; << std::endl;
std::cout << " e.g. adb shell '" << prog_name << " --server=\"url\"'" std::cout << " e.g. adb shell '" << prog_name << " --server=\"url\"'"
<< std::endl; << std::endl;
std::cout << " or adb shell '" << prog_name << " -u\"url\"'" std::cout << " or adb shell '" << prog_name << " -u\"url\"'" << std::endl
<< std::endl << std::endl; << std::endl;
std::cout << std::setw(35) << std::left << " -c/--chunked_transfer"; std::cout << std::setw(35) << std::left << " -c/--chunked_transfer";
std::cout << "specifies chunked transfer encoding in request" std::cout << "specifies chunked transfer encoding in request" << std::endl
<< std::endl << std::endl; << std::endl;
std::cout << std::setw(35) << std::left << " -f/--use_full_path"; std::cout << std::setw(35) << std::left << " -f/--use_full_path";
std::cout << "specify server url is not a proxy server" << std::endl; std::cout << "specify server url is not a proxy server" << std::endl;
std::cout << std::endl; std::cout << std::endl;
std::cout << std::setw(35) << std::left std::cout << std::setw(35) << std::left << " -i/--license_server_id=<gp/cp>";
<< " -i/--license_server_id=<gp/cp>";
std::cout << "specifies which default server settings to use: " << std::endl; std::cout << "specifies which default server settings to use: " << std::endl;
std::cout << std::setw(35) << std::left << " "; std::cout << std::setw(35) << std::left << " ";
std::cout << "gp (case sensitive) for GooglePlay server" << std::endl; std::cout << "gp (case sensitive) for GooglePlay server" << std::endl;
std::cout << std::setw(35) << std::left << " "; std::cout << std::setw(35) << std::left << " ";
std::cout << "cp (case sensitive) for Youtube Content Protection server" std::cout << "cp (case sensitive) for Youtube Content Protection server"
<< std::endl << std::endl;
std::cout << std::setw(35) << std::left << " -k/--keyid=<key_id>";
std::cout << "configure the key id or pssh, in hex format"
<< std::endl << std::endl; << std::endl << std::endl;
std::cout << std::setw(35) << std::left std::cout << std::setw(35) << std::left << " -k/--keyid=<key_id>";
<< " -p/--port=<port>"; std::cout << "configure the key id or pssh, in hex format" << std::endl
<< std::endl;
std::cout << std::setw(35) << std::left << " -p/--port=<port>";
std::cout << "specifies the connection port" << std::endl << std::endl; std::cout << "specifies the connection port" << std::endl << std::endl;
std::cout << std::setw(35) << std::left std::cout << std::setw(35) << std::left << " -s/--secure_transfer";
<< " -s/--secure_transfer";
std::cout << "use https transfer protocol" << std::endl << std::endl; std::cout << "use https transfer protocol" << std::endl << std::endl;
std::cout << std::setw(35) << std::left std::cout << std::setw(35) << std::left << " -u/--server=<server_url>";
<< " -u/--server=<server_url>";
std::cout std::cout
<< "configure the license server url, please include http[s] in the url" << "configure the license server url, please include http[s] in the url"
<< std::endl << std::endl; << std::endl << std::endl;
@@ -1234,15 +1214,14 @@ int main(int argc, char** argv) {
bool show_usage = false; bool show_usage = false;
static const struct option long_options[] = { static const struct option long_options[] = {
{ "chunked_transfer", no_argument, NULL, 'c' }, {"chunked_transfer", no_argument, NULL, 'c'},
{ "keyid", required_argument, NULL, 'k' }, {"keyid", required_argument, NULL, 'k'},
{ "license_server_id", required_argument, NULL, 'i' }, {"license_server_id", required_argument, NULL, 'i'},
{ "license_server_url", required_argument, NULL, 'u' }, {"license_server_url", required_argument, NULL, 'u'},
{ "port", required_argument, NULL, 'p' }, {"port", required_argument, NULL, 'p'},
{ "secure_transfer", no_argument, NULL, 's' }, {"secure_transfer", no_argument, NULL, 's'},
{ "use_full_path", no_argument, NULL, 'f' }, {"use_full_path", no_argument, NULL, 'f'},
{ NULL, 0, NULL, '\0' } {NULL, 0, NULL, '\0'}};
};
int option_index = 0; int option_index = 0;
int opt = 0; int opt = 0;

View File

@@ -120,7 +120,7 @@ class WVDrmPlugin : public android::DrmPlugin,
const Vector<uint8_t>& signature, const Vector<uint8_t>& signature,
bool& match); bool& match);
virtual void onEvent(const CdmSessionId& cdmSessionId, virtual void OnEvent(const CdmSessionId& cdmSessionId,
CdmEventType cdmEventType); CdmEventType cdmEventType);
private: private:

View File

@@ -727,7 +727,7 @@ status_t WVDrmPlugin::verify(const Vector<uint8_t>& sessionId,
} }
} }
void WVDrmPlugin::onEvent(const CdmSessionId& cdmSessionId, void WVDrmPlugin::OnEvent(const CdmSessionId& cdmSessionId,
CdmEventType cdmEventType) { CdmEventType cdmEventType) {
Vector<uint8_t> sessionId; Vector<uint8_t> sessionId;
EventType eventType = kDrmPluginEventVendorDefined; EventType eventType = kDrmPluginEventVendorDefined;

View File

@@ -1141,8 +1141,8 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) {
status_t res = plugin.setListener(listener); status_t res = plugin.setListener(listener);
ASSERT_EQ(OK, res); ASSERT_EQ(OK, res);
plugin.onEvent(cdmSessionId, LICENSE_EXPIRED_EVENT); plugin.OnEvent(cdmSessionId, LICENSE_EXPIRED_EVENT);
plugin.onEvent(cdmSessionId, LICENSE_RENEWAL_NEEDED_EVENT); plugin.OnEvent(cdmSessionId, LICENSE_RENEWAL_NEEDED_EVENT);
} }
TEST_F(WVDrmPluginTest, GeneratesProvisioningNeededEvent) { TEST_F(WVDrmPluginTest, GeneratesProvisioningNeededEvent) {

View File

@@ -192,7 +192,8 @@ typedef enum OEMCrypto_Algorithm {
#define OEMCrypto_FirstSubsample 1 #define OEMCrypto_FirstSubsample 1
#define OEMCrypto_LastSubsample 2 #define OEMCrypto_LastSubsample 2
/* OEMCrypto_Usage_Entry_Status. /*
* OEMCrypto_Usage_Entry_Status.
* Valid values for status in the usage table. * Valid values for status in the usage table.
*/ */
typedef enum OEMCrypto_Usage_Entry_Status { typedef enum OEMCrypto_Usage_Entry_Status {
@@ -215,7 +216,8 @@ typedef struct {
uint8_t pst[]; uint8_t pst[];
} OEMCrypto_PST_Report; } OEMCrypto_PST_Report;
/* OEMCrypto_Clock_Security_Level. /*
* OEMCrypto_Clock_Security_Level.
* Valid values for clock_security_level in OEMCrypto_PST_Report. * Valid values for clock_security_level in OEMCrypto_PST_Report.
*/ */
typedef enum OEMCrypto_Clock_Security_Level { typedef enum OEMCrypto_Clock_Security_Level {
@@ -231,7 +233,9 @@ typedef enum RSA_Padding_Scheme {
} RSA_Padding_Scheme; } RSA_Padding_Scheme;
/* Obfuscation Renames. */ /*
* Obfuscation Renames.
*/
#define OEMCrypto_Initialize _oecc01 #define OEMCrypto_Initialize _oecc01
#define OEMCrypto_Terminate _oecc02 #define OEMCrypto_Terminate _oecc02
#define OEMCrypto_InstallKeybox _oecc03 #define OEMCrypto_InstallKeybox _oecc03
@@ -270,23 +274,22 @@ typedef enum RSA_Padding_Scheme {
/* /*
* OEMCrypto_Initialize * OEMCrypto_Initialize
* *
* Description:
Initializes the crypto hardware. * Initialize the crypto firmware/hardware.
*
Parameters: * Parameters:
None * N/A
*
Returns: * Threading:
OEMCrypto_SUCCESS success * No other function calls will be made while this function is running. This
OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware * function will not be called again before OEMCrypto_Terminate.
*
Threading: * Returns:
No other function calls will be made while this function is running. This * OEMCrypto_SUCCESS success
function will not be called again before OEMCrypto_Terminate(). * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware
*
Version: * Version:
This method is supported by all API versions. * This method is supported by all API versions.
*/ */
OEMCryptoResult OEMCrypto_Initialize(void); OEMCryptoResult OEMCrypto_Initialize(void);
@@ -428,8 +431,12 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
* *
* Because the nonce will be used to prevent replay attacks, it is desirable * Because the nonce will be used to prevent replay attacks, it is desirable
* that a rogue application cannot rapidly call this function until a * that a rogue application cannot rapidly call this function until a
* repeated nonce is created randomly. With this in mind, we require that * repeated nonce is created randomly. With this in mind, if more than 20
* creation of more than 20 nonces will take at least one full second. * nonces are requested within one second, OEMCrypto will return an error
* after the 20th and not generate any more nonces for the rest of the
* second. After an error, if the application waits at least one second
* before requesting more nonces, then OEMCrypto will reset the error
* condition and generate valid nonces again.
* *
* Parameters: * Parameters:
* session (in) - crypto session identifier. * session (in) - crypto session identifier.
@@ -1028,52 +1035,52 @@ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,
/* /*
* OEMCrypto_IsKeyboxValid * OEMCrypto_IsKeyboxValid
* *
Validates the Widevine Keybox loaded into the security processor device. This * Description:
method verifies two fields in the keybox: * Validate the Widevine Keybox stored on the device.
*
Verify the MAGIC field contains a valid signature (such as, kbox). * The API performs two verification steps on the Keybox. It first verifies
* the MAGIC field contains a valid signature (must be 'kbox'). The API then
Compute the CRC using CRC-32-POSIX-1003.2 standard and compare the checksum * computes the CRC using CRC-32 (Posix 1003.2 standard) and compares the
to the CRC stored in the Keybox. The CRC is computed over the entire Keybox * checksum to the CRC stored in the Keybox. The CRC is computed over the
excluding the 4 bytes of the CRC (for example, Keybox[0..123]). For a * entire Keybox excluding the 4 CRC bytes (i.e. Keybox[0..123]).
description of the fields stored in the keybox, see Keybox Definition. *
* Parameters:
Parameters: * none
none *
* Threading:
Returns: * This function may be called simultaneously with any session functions.
OEMCrypto_SUCCESS *
OEMCrypto_ERROR_BAD_MAGIC * Returns:
OEMCrypto_ERROR_BAD_CRC * OEMCrypto_SUCCESS
* OEMCrypto_ERROR_BAD_MAGIC
Threading: * OEMCrypto_ERROR_BAD_CRC
This function may be called simultaneously with any session functions. *
* Version:
Version: * This method is supported by all API versions.
This method is supported in all API versions.
*/ */
OEMCryptoResult OEMCrypto_IsKeyboxValid(void); OEMCryptoResult OEMCrypto_IsKeyboxValid(void);
/* /*
* OEMCrypto_GetDeviceID * OEMCrypto_GetDeviceID
* *
Retrieve DeviceID from the Keybox. * Description:
* Retrieve the device's unique identifier from the Keybox.
Parameters: *
[out] deviceId - pointer to the buffer that receives the Device ID * Parameters:
[in/out] idLength on input, size of the callers device ID buffer. On * deviceId (out) - pointer to the buffer that receives the Device ID
output, the number of bytes written into the buffer. * idLength (in/out) - on input, size of the caller's device ID buffer.
* On output, the number of bytes written into the buffer.
Returns: *
OEMCrypto_SUCCESS success * Threading:
OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return device ID * This function may be called simultaneously with any session functions.
OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id *
* Returns:
Threading: * OEMCrypto_SUCCESS success
This function may be called simultaneously with any session functions. * 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 in all API versions. * Version:
* This method is supported by all API versions.
*/ */
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
size_t *idLength); size_t *idLength);
@@ -1081,24 +1088,31 @@ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
/* /*
* OEMCrypto_GetKeyData * OEMCrypto_GetKeyData
* *
Return the Key Data field from the Keybox. * Description:
* Returns the Key Data field from the Keybox. The Key Data field does not
Parameters: * need to be encrypted by an OEM root key, but may be if desired.
[out] keyData - pointer to the buffer to hold the Key Data field from the *
Keybox * If the Key Data field was encrypted with an OEM root key when the Keybox
[in/out] keyDataLength on input, the allocated buffer size. On output, the * was stored on the device, then this function should decrypt it and return
number of bytes in Key Data * the clear Key Data. If the Key Data was not encrypted, then this function
* should just access and return the clear Key data.
Returns: *
OEMCrypto_SUCCESS success * Parameters:
OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return KeyData * keyData (out) - pointer to a caller-managed buffer to hold the Key Data
OEMCrypto_ERROR_NO_KEYDATA * field from the Keybox
* dataLength (in/out) - on input, the allocated buffer size. On output,
Threading: * the number of bytes in KeyData.
This function may be called simultaneously with any session functions. *
* Threading:
Version: * This function may be called simultaneously with any session functions.
This method is supported in all API versions. *
* Returns:
* 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, OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
size_t *keyDataLength); size_t *keyDataLength);
@@ -1106,23 +1120,25 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
/* /*
* OEMCrypto_GetRandom * OEMCrypto_GetRandom
* *
Returns a buffer filled with hardware-generated random bytes, if supported by * Description:
the hardware. * Return a buffer filled with hardware-generated random bytes. If the
* hardware feature does not exist, return OEMCrypto_ERROR_RNG_NOT_SUPPORTED.
Parameters: *
[out] randomData - pointer to the buffer that receives random data * Parameters:
[in] dataLength - length of the random data buffer in bytes * randomData (out) - Pointer to caller-manager buffer that will receive the
* random data.
Returns: * dataLength (in) - Length of the random data buffer in bytes.
OEMCrypto_SUCCESS success *
OEMCrypto_ERROR_RNG_FAILED failed to generate random number * Threading:
OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported * This function may be called simultaneously with any session functions.
*
Threading: * Returns:
This function may be called simultaneously with any session functions. * OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_RNG_FAILED failed to generate random number
Version: * OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported
This method is supported in all API versions. *
* Version:
* This method is supported by all API versions.
*/ */
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData,
size_t dataLength); size_t dataLength);
@@ -1263,8 +1279,6 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
* - padding. This is the wrapped key generated * - padding. This is the wrapped key generated
* - by OEMCrypto_RewrapDeviceRSAKey. * - by OEMCrypto_RewrapDeviceRSAKey.
* wrapped_rsa_key_length (in) - length of the wrapped key buffer, in bytes. * wrapped_rsa_key_length (in) - length of the wrapped key buffer, in bytes.
* wrapped_rsa_key_iv (in) - The initialization vector used to encrypt
* - wrapped_rsa_key.
* *
* Threading: * Threading:
* This function may be called simultaneously with functions on other sessions, * This function may be called simultaneously with functions on other sessions,
@@ -1546,41 +1560,25 @@ bool OEMCrypto_SupportsUsageTable();
* that entry is “inactive”, then return OEMCrypto_ERROR_INVALID_SESSION. * that entry is “inactive”, then return OEMCrypto_ERROR_INVALID_SESSION.
* *
* Parameters: * Parameters:
* current (out) - this is the current HDCP version, based on the device itself, * session (in) - crypto session identifier.
* and the display to which it is connected. * in_buffer (in) - pointer to memory containing data to be encrypted.
* maximum (out) - this is the maximum supported HDCP version for the device, * buffer_length (in) - length of the buffer, in bytes.
* ignoring any attached device. * iv (in) - IV for encrypting data. Size is specified by the algorithm.
* * algorithm (in) - Specifies which encryption algorithm to use. See
* Threading: * OEMCrypto_Algorithm for valid values.
* This function may be called simultaneously with any other functions. * out_buffer (out) - pointer to buffer in which encrypted data should be stored.
* *
* Returns: * Returns:
* OEMCrypto_SUCCESS * OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_UNKNOWN_FAILURE * OEMCrypto_ERROR_KEY_EXPIRED
* * OEMCrypto_ERROR_NO_DEVICE_KEY
* Version: * OEMCrypto_ERROR_INVALID_SESSION
* This method changed in API version 9. * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES
*/ * OEMCrypto_ERROR_UNKNOWN_FAILURE
typedef uint8_t OEMCrypto_HDCP_Capability;
OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current,
OEMCrypto_HDCP_Capability *maximum);
/*
* OEMCrypto_SupportsUsageTable()
*
* Description:
* This is used to determine if the device can support a usage table. Since this
* function is spoofable, it is not relied on for security purposes. It is for
* information only. The usage table is described in the section above.
*
* Parameters:
* none
* *
* Threading: * Threading:
* This function may be called simultaneously with any other functions. * This function may be called simultaneously with functions on other sessions,
* * but not with other functions on this session.
* Returns:
* Returns true if the device can maintain a usage table. Returns false otherwise.
* *
* Version: * Version:
* This method changed in API version 9. * This method changed in API version 9.

View File

@@ -125,7 +125,7 @@ OEMCryptoResult Level3_GenerateRSASignature(OEMCrypto_SESSION session,
size_t message_length, size_t message_length,
uint8_t* signature, uint8_t* signature,
size_t *signature_length, size_t *signature_length,
RSA_Padding_Scheme algorithm); RSA_Padding_Scheme padding_scheme);
OEMCryptoResult Level3_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, OEMCryptoResult Level3_DeriveKeysFromSessionKey(OEMCrypto_SESSION session,
const uint8_t* enc_session_key, const uint8_t* enc_session_key,
size_t enc_session_key_length, size_t enc_session_key_length,

View File

@@ -31,8 +31,9 @@
static const int kPssSaltLength = 20; static const int kPssSaltLength = 20;
namespace { namespace {
// Increment counter for AES-CTR // Increment counter for AES-CTR. The CENC spec specifies we increment only
void ctr128_inc(uint8_t* counter) { // the low 64 bits of the IV counter, and leave the high 64 bits alone.
void ctr128_inc64(uint8_t* counter) {
uint32_t n = 16; uint32_t n = 16;
do { do {
if (++counter[--n] != 0) return; if (++counter[--n] != 0) return;
@@ -899,11 +900,11 @@ bool CryptoEngine::DecryptCTR(SessionContext* session,
const uint8_t* cipher_data, const uint8_t* cipher_data,
size_t cipher_data_length, size_t cipher_data_length,
bool is_encrypted, bool is_encrypted,
void* clear_data, uint8_t* clear_data,
BufferType buffer_type) { BufferType buffer_type) {
// If the data is clear, we do not need a current key selected. // If the data is clear, we do not need a current key selected.
if (!is_encrypted) { if (!is_encrypted && buffer_type != kBufferTypeDirect) {
memcpy(reinterpret_cast<uint8_t*>(clear_data), memcpy(reinterpret_cast<uint8_t*>(clear_data),
cipher_data, cipher_data_length); cipher_data, cipher_data_length);
return true; return true;
@@ -961,20 +962,20 @@ bool CryptoEngine::DecryptCTR(SessionContext* session,
// Encrypt the IV. // Encrypt the IV.
uint8_t ecount_buf[AES_BLOCK_SIZE]; uint8_t ecount_buf[AES_BLOCK_SIZE];
if (block_offset != 0) {
// The context is needed only when not starting a new block.
AES_encrypt(aes_iv, ecount_buf, &aes_key);
ctr128_inc(aes_iv);
}
// Decryption. // The CENC spec specifies we increment only the low 64 bits of the IV
unsigned int block_offset_cur = block_offset; // counter, and leave the high 64 bits alone. This is different from the
AES_ctr128_encrypt( // OpenSSL implementation, which increments the entire 128 bit iv. That is
cipher_data, reinterpret_cast<uint8_t*>(clear_data), cipher_data_length, // why we implement the CTR loop ourselves.
&aes_key, aes_iv, ecount_buf, &block_offset_cur); size_t l = 0;
if (block_offset_cur != ((block_offset + cipher_data_length) % AES_BLOCK_SIZE)) { while (l < cipher_data_length) {
LOGE("[DecryptCTR(): FAILURE: byte offset wrong.]"); AES_encrypt(aes_iv, ecount_buf, &aes_key);
return false; for (int n = block_offset; n < AES_BLOCK_SIZE && l < cipher_data_length;
++n, ++l) {
clear_data[l] = cipher_data[l] ^ ecount_buf[n];
}
ctr128_inc64(aes_iv);
block_offset = 0;
} }
return true; return true;
} }

View File

@@ -251,7 +251,7 @@ class CryptoEngine {
const uint8_t* cipher_data, const uint8_t* cipher_data,
size_t cipher_data_length, size_t cipher_data_length,
bool is_encrypted, bool is_encrypted,
void* clear_data, uint8_t* clear_data,
BufferType buffer_type); BufferType buffer_type);
private: private:

View File

@@ -95,8 +95,10 @@ bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) {
const WidevineKeybox* keybox const WidevineKeybox* keybox
= reinterpret_cast<const WidevineKeybox*>(buffer); = reinterpret_cast<const WidevineKeybox*>(buffer);
size_t device_id_length
= strnlen(reinterpret_cast<const char*>(keybox->device_id_), 32);
device_id_.assign(keybox->device_id_, device_id_.assign(keybox->device_id_,
keybox->device_id_ + sizeof(keybox->device_id_)); keybox->device_id_ + device_id_length);
device_key_.setValue(reinterpret_cast<const char*>(keybox->device_key_), device_key_.setValue(reinterpret_cast<const char*>(keybox->device_key_),
sizeof(keybox->device_key_)); sizeof(keybox->device_key_));
device_key_.setType(KEYTYPE_DEVICE); device_key_.setType(KEYTYPE_DEVICE);

View File

@@ -895,7 +895,7 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
size_t message_length, size_t message_length,
uint8_t* signature, uint8_t* signature,
size_t* signature_length, size_t* signature_length,
RSA_Padding_Scheme algorithm) { RSA_Padding_Scheme padding_scheme) {
if (trace_all_calls) { if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n"); printf("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n");
dump_hex("message", message, message_length); dump_hex("message", message, message_length);
@@ -928,7 +928,7 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
return OEMCrypto_ERROR_INVALID_CONTEXT; return OEMCrypto_ERROR_INVALID_CONTEXT;
} }
if (algorithm != kSign_RSASSA_PSS) { if (padding_scheme != kSign_RSASSA_PSS) {
LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_NOT_IMPLEMENTED]"); LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_NOT_IMPLEMENTED]");
return OEMCrypto_ERROR_NOT_IMPLEMENTED; return OEMCrypto_ERROR_NOT_IMPLEMENTED;
} }

View File

@@ -34,7 +34,7 @@ const size_t kNumKeys = 4;
const size_t kBufferMaxLength = 256; const size_t kBufferMaxLength = 256;
#if defined(TEST_SPEED_MULTIPLIER) // Can slow test time limits when #if defined(TEST_SPEED_MULTIPLIER) // Can slow test time limits when
// debugging is slowing everything. // debugging is slowing everything.
const int kSpeedMultiplier = TEST_SPEED_MULTIPLIER1; const int kSpeedMultiplier = TEST_SPEED_MULTIPLIER;
#else #else
const int kSpeedMultiplier = 1; const int kSpeedMultiplier = 1;
#endif #endif
@@ -910,29 +910,39 @@ class Session {
OEMCrypto_GenerateNonce(session_id(), nonce)); OEMCrypto_GenerateNonce(session_id(), nonce));
} }
void GenerateDerivedKeys() { void FillDefaultContext(vector<uint8_t>* mac_context,
GenerateNonce(&nonce_); vector<uint8_t>* enc_context) {
vector<uint8_t> mac_context = wvcdm::a2b_hex( /* These context strings are normally created by the CDM layer above from
a license request message. */
*mac_context = wvcdm::a2b_hex(
"41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff"
"de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873"
"4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a"
"230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635"
"34333231180120002a0c31383836373837343035000000000100"); "34333231180120002a0c31383836373837343035000000000200");
vector<uint8_t> enc_context = wvcdm::a2b_hex( *enc_context = wvcdm::a2b_hex(
"454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95"
"c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb"
"e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408"
"0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231"
"180120002a0c31383836373837343035000000000080"); "180120002a0c31383836373837343035000000000080");
}
void GenerateDerivedKeys() {
GenerateNonce(&nonce_);
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
FillDefaultContext(&mac_context, &enc_context);
ASSERT_EQ(OEMCrypto_SUCCESS, ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GenerateDerivedKeys( OEMCrypto_GenerateDerivedKeys(
session_id(), session_id(),
&mac_context[0], mac_context.size(), &mac_context[0], mac_context.size(),
&enc_context[0], enc_context.size())); &enc_context[0], enc_context.size()));
mac_key_server_ = wvcdm::a2b_hex( mac_key_server_ = wvcdm::a2b_hex(
"9D41F0A77A76E071841C33B06104D106641421E651FBE55F0AED453CDA7713AC"); "3CFD60254786AF350B353B4FBB700AB382558400356866BA16C256BCD8C502BF");
mac_key_client_ = wvcdm::a2b_hex( mac_key_client_ = wvcdm::a2b_hex(
"125283F299AF42C191E1A989846B388BB16A6E50B2F67D4F876A3C1F662CD5C8"); "A9DE7B3E4E199ED8D1FBC29CD6B4C772CC4538C8B0D3E208B3E76F2EC0FD6F47");
enc_key_ = wvcdm::a2b_hex("D0BFC35DA9E33436E81C4229E78CB9F4"); enc_key_ = wvcdm::a2b_hex("D0BFC35DA9E33436E81C4229E78CB9F4");
} }
@@ -1309,28 +1319,13 @@ class Session {
return true; return true;
} }
bool GenerateRSASessionKey(vector<uint8_t>* enc_session_key, bool GenerateRSASessionKey(vector<uint8_t>* enc_session_key) {
vector<uint8_t>* mac_context,
vector<uint8_t>* enc_context) {
if (!public_rsa_) { if (!public_rsa_) {
cout << "No public RSA key loaded in test code.\n"; cout << "No public RSA key loaded in test code.\n";
return false; return false;
} }
vector<uint8_t> session_key = wvcdm::a2b_hex( vector<uint8_t> session_key = wvcdm::a2b_hex(
"6fa479c731d2770b6a61a5d1420bb9d1"); "6fa479c731d2770b6a61a5d1420bb9d1");
*mac_context = wvcdm::a2b_hex(
"41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff"
"de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873"
"4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a"
"230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635"
"34333231180120002a0c31383836373837343035000000000100");
*enc_context = wvcdm::a2b_hex(
"454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95"
"c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb"
"e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408"
"0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231"
"180120002a0c31383836373837343035000000000080");
enc_session_key->assign(RSA_size(public_rsa_), 0); enc_session_key->assign(RSA_size(public_rsa_), 0);
int status = RSA_public_encrypt(session_key.size(), int status = RSA_public_encrypt(session_key.size(),
&session_key[0], &session_key[0],
@@ -1350,13 +1345,12 @@ class Session {
wrapped_rsa_key.size())); wrapped_rsa_key.size()));
GenerateNonce(&nonce_); GenerateNonce(&nonce_);
vector<uint8_t> enc_session_key; vector<uint8_t> enc_session_key;
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
ASSERT_TRUE(PreparePublicKey(kTestRSAPublicKey2_2048, ASSERT_TRUE(PreparePublicKey(kTestRSAPublicKey2_2048,
sizeof(kTestRSAPublicKey2_2048))); sizeof(kTestRSAPublicKey2_2048)));
ASSERT_TRUE(GenerateRSASessionKey(&enc_session_key, &mac_context, ASSERT_TRUE(GenerateRSASessionKey(&enc_session_key));
&enc_context)); vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
FillDefaultContext(&mac_context, &enc_context);
ASSERT_EQ(OEMCrypto_SUCCESS, ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey( OEMCrypto_DeriveKeysFromSessionKey(
session_id(), &enc_session_key[0], enc_session_key.size(), session_id(), &enc_session_key[0], enc_session_key.size(),
@@ -1364,9 +1358,9 @@ class Session {
&enc_context[0], enc_context.size())); &enc_context[0], enc_context.size()));
mac_key_server_ = wvcdm::a2b_hex( mac_key_server_ = wvcdm::a2b_hex(
"B09CB4482675123B66F7A8303D803F6042F43404ED3DE020811CFC13BCDF4C65"); "1E451E59CB663DA1646194DD28880788ED8ED2EFF913CBD6A0D535D1D5A90381");
mac_key_client_ = wvcdm::a2b_hex( mac_key_client_ = wvcdm::a2b_hex(
"B09CB4482675123B66F7A8303D803F6042F43404ED3DE020811CFC13BCDF4C65"); "F9AAE74690909F2207B53B13307FCA096CA8C49CC6DFE3659873CB952889A74B");
enc_key_ = wvcdm::a2b_hex("CB477D09014D72C9B8DCE76C33EA43B3"); enc_key_ = wvcdm::a2b_hex("CB477D09014D72C9B8DCE76C33EA43B3");
} }
@@ -1553,24 +1547,22 @@ TEST_F(OEMCryptoClientTest, GetDeviceIdShortBuffer) {
OEMCryptoResult sts; OEMCryptoResult sts;
uint8_t dev_id[128]; uint8_t dev_id[128];
uint32_t req_len = 11; uint32_t req_len = 0;
for (int i = 0; i < 128; ++i) { for (int i = 0; i < 128; ++i) {
dev_id[i] = 0x55; dev_id[i] = 0x55;
} }
dev_id[127] = '\0'; dev_id[127] = '\0';
size_t dev_id_len = req_len; size_t dev_id_len = req_len;
sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len); sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len);
// cout << "GetDeviceIdShortBuffer: sts = " << (int)sts << " request = "
// << req_len << " required = " << dev_id_len << endl;
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
// On short buffer error, function should return minimum buffer length // On short buffer error, function should return minimum buffer length
ASSERT_TRUE(dev_id_len > req_len); ASSERT_TRUE(dev_id_len > req_len);
// Should also return short buffer if passed a zero length and a null buffer.
// cout << "NormalGetDeviceId: dev_id = " << dev_id dev_id_len = req_len;
// << " len = " << dev_id_len << endl; sts = OEMCrypto_GetDeviceID(NULL, &dev_id_len);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
// On short buffer error, function should return minimum buffer length
ASSERT_TRUE(dev_id_len > req_len);
testTearDown(); testTearDown();
} }
@@ -2578,6 +2570,98 @@ TEST_F(DISABLED_TestKeybox, DecryptWithOffset) {
testTearDown(); testTearDown();
} }
// Increment counter for AES-CTR. The CENC spec specifies we increment only
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This is
// different from the OpenSSL implementation, so we implement the CTR loop
// ourselves.
void ctr128_inc64(uint8_t* counter) {
uint32_t n = 16;
do {
if (++counter[--n] != 0) return;
} while (n>8);
}
vector<uint8_t> EncryptCTR(const vector<uint8_t> &key,
const vector<uint8_t> &iv,
const vector<uint8_t> &in, size_t block_offset) {
AES_KEY aes_key;
AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key);
uint8_t aes_iv[AES_BLOCK_SIZE];
memcpy(aes_iv, &iv[0], AES_BLOCK_SIZE);
// Encrypt the IV.
uint8_t ecount_buf[AES_BLOCK_SIZE];
vector<uint8_t> out( in.size());
size_t cipher_data_length = in.size();
size_t l = 0;
while (l < cipher_data_length) {
AES_encrypt(aes_iv, ecount_buf, &aes_key);
for (int n = block_offset; n < AES_BLOCK_SIZE && l < cipher_data_length;
++n, ++l) {
out[l] = in[l] ^ ecount_buf[n];
}
ctr128_inc64(aes_iv);
block_offset = 0;
}
return out;
}
TEST_F(DISABLED_TestKeybox, DecryptWithNearWrap) {
OEMCryptoResult sts;
testSetUp();
InstallKeybox(kDefaultKeybox, true);
Session& s = createSession("ONE");
s.open();
s.GenerateDerivedKeys();
s.LoadTestKeys(kDuration, 0, 0);
// Select the key (from FillSimpleMessage)
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
sts = OEMCrypto_SelectKey(s.session_id(),
&keyId[0],
keyId.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// Set up our expected input and output
vector<uint8_t> key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D");
vector<uint8_t> encryptionIv = wvcdm::a2b_hex(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
vector<uint8_t> unencryptedData = wvcdm::a2b_hex(
"f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8"
"a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f"
"7085b70bf9ad2c7f37025dd45f93e90304739b1ce098a52e7b99a90f92544a9b"
"dca6f49e0006c80a0cfa018600523ad30e483141fe720d045394815d5c875ad4"
"b4387b8d09b6119bd0943e51b0b9103034496b3a83ba593f79baa188aeb6e08f"
"f6475933e9ce1bb95fbb526424e7966e25830c20da73c65c6fbff110b08e4def"
"eae94f98296770275b0d738207a8217cd6118f6ebc6e393428f2268cfedf800e"
"a7ebc606471b9a9dfccd1589e86d88fde508261eaf190efd20554ce9e14ff3c9");
size_t block_offset = 5;
vector<uint8_t> encryptedData = EncryptCTR(key, encryptionIv, unencryptedData,
block_offset) ;
// Describe the output
uint8_t outputBuffer[256];
OEMCrypto_DestBufferDesc destBuffer;
destBuffer.type = OEMCrypto_BufferType_Clear;
destBuffer.buffer.clear.address = outputBuffer;
destBuffer.buffer.clear.max_length = sizeof(outputBuffer);
// Decrypt the data
sts = OEMCrypto_DecryptCTR(s.session_id(), &encryptedData[0],
encryptedData.size(), true, &encryptionIv[0],
block_offset, &destBuffer,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer,
unencryptedData.size()));
s.close();
testTearDown();
}
TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) { TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) {
OEMCryptoResult sts; OEMCryptoResult sts;
testSetUp(); testSetUp();
@@ -3378,7 +3462,7 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox {
uint8_t signature[SHA256_DIGEST_LENGTH]; uint8_t signature[SHA256_DIGEST_LENGTH];
SignBuffer(key_index, clear_buffer_, signature); SignBuffer(key_index, clear_buffer_, signature);
if( alter_data ) { if( alter_data ) {
signature[0] = 43; signature[0] ^= 42;
} }
sts = OEMCrypto_SelectKey(s.session_id(), sts = OEMCrypto_SelectKey(s.session_id(),