Migration from jb-mr2 to master for Widevine CDM

Android development of the widevine CDM has been done
on the jb-mr2 branch of the cdm code base.  This CL
contains a merge of that jb-mr2 work to CDM master, and
also reflects the evolution of the common Modular DRM
code base since jb-mr2 branched.

Change-Id: I1d7e1a12d092c00044a4298261146cb97808d4ef
This commit is contained in:
Jeff Tinker
2013-07-29 17:29:07 -07:00
parent edb987db07
commit 0190f99fb3
68 changed files with 4754 additions and 3601 deletions

View File

@@ -9,7 +9,8 @@ LOCAL_C_INCLUDES := \
external/stlport/stlport \
vendor/widevine/libwvdrmengine/cdm/core/include \
vendor/widevine/libwvdrmengine/cdm/include \
vendor/widevine/libwvdrmengine/oemcrypto/include
vendor/widevine/libwvdrmengine/oemcrypto/include \
vendor/widevine/libwvdrmengine/third_party/stringencoders/src
LOCAL_C_INCLUDES += \
external/openssl/include \
@@ -25,7 +26,7 @@ LOCAL_SRC_FILES := \
$(CORE_SRC_DIR)/buffer_reader.cpp \
$(CORE_SRC_DIR)/cdm_engine.cpp \
$(CORE_SRC_DIR)/cdm_session.cpp \
$(CORE_SRC_DIR)/crypto_engine.cpp \
$(CORE_SRC_DIR)/certificate_provisioning.cpp \
$(CORE_SRC_DIR)/crypto_session.cpp \
$(CORE_SRC_DIR)/device_files.cpp \
$(CORE_SRC_DIR)/license.cpp \
@@ -36,10 +37,11 @@ LOCAL_SRC_FILES := \
$(SRC_DIR)/file_store.cpp \
$(SRC_DIR)/lock.cpp \
$(SRC_DIR)/log.cpp \
$(SRC_DIR)/properties.cpp \
$(SRC_DIR)/properties_android.cpp \
$(SRC_DIR)/timer.cpp \
$(SRC_DIR)/wv_content_decryption_module.cpp
LOCAL_WHOLE_STATIC_LIBRARIES := libmodp_b64
LOCAL_MODULE := libcdm
LOCAL_MODULE_TAGS := optional

View File

@@ -3,7 +3,7 @@
#ifndef CDM_BASE_CDM_ENGINE_H_
#define CDM_BASE_CDM_ENGINE_H_
#include "crypto_session.h"
#include "certificate_provisioning.h"
#include "timer.h"
#include "wv_cdm_types.h"
@@ -19,117 +19,113 @@ typedef std::map<CdmKeySetId, CdmSessionId> CdmReleaseKeySetMap;
class CdmEngine : public TimerHandler {
public:
CdmEngine();
~CdmEngine();
virtual ~CdmEngine();
// Session related methods
CdmResponseType OpenSession(const CdmKeySystem& key_system,
CdmSessionId* session_id);
CdmResponseType CloseSession(const CdmSessionId& session_id);
CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id);
CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
CdmSessionId* session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id);
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
// License related methods
// Construct a valid license request
CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id,
const CdmInitData& init_data,
const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
std::string* server_url);
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id,
const CdmInitData& init_data,
const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
std::string* server_url);
// Accept license response and extract key info.
CdmResponseType AddKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data,
CdmKeySetId& key_set_id);
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data,
CdmKeySetId* key_set_id);
CdmResponseType RestoreKey(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id);
virtual CdmResponseType RestoreKey(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id);
// Cancel session and unload keys.
CdmResponseType CancelKeyRequest(const CdmSessionId& session_id);
// Construct valid renewal request for the current session keys.
CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
CdmKeyMessage* key_request,
std::string* server_url);
virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
CdmKeyMessage* key_request,
std::string* server_url);
// Accept renewal response and update key info.
CdmResponseType RenewKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data);
virtual CdmResponseType RenewKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data);
// Query system information
CdmResponseType QueryStatus(CdmQueryMap* info);
virtual CdmResponseType QueryStatus(CdmQueryMap* info);
// Query license information
CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info);
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info);
// Query seesion control information
CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
CdmQueryMap* key_info);
virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
CdmQueryMap* key_info);
// Provisioning related methods
CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request,
std::string* default_url);
virtual CdmResponseType GetProvisioningRequest(
CdmProvisioningRequest* request,
std::string* default_url);
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response);
virtual CdmResponseType HandleProvisioningResponse(
CdmProvisioningResponse& response);
// Secure stop related methods
CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
CdmResponseType ReleaseSecureStops(const CdmSecureStopReleaseMessage& message);
virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
virtual CdmResponseType ReleaseSecureStops(
const CdmSecureStopReleaseMessage& message);
// Decryption and key related methods
// Accept encrypted buffer and return decrypted data.
CdmResponseType Decrypt(const CdmSessionId& session_id,
bool is_encrypted,
bool is_secure,
const KeyId& key_id,
const uint8_t* encrypt_buffer,
size_t encrypt_length,
const std::vector<uint8_t>& iv,
size_t block_offset,
void* decrypt_buffer,
size_t decrypt_buffer_offset,
bool is_video);
virtual CdmResponseType Decrypt(const CdmSessionId& session_id,
bool is_encrypted,
bool is_secure,
const KeyId& key_id,
const uint8_t* encrypt_buffer,
size_t encrypt_length,
const std::vector<uint8_t>& iv,
size_t block_offset,
void* decrypt_buffer,
size_t decrypt_buffer_offset,
bool is_video);
// Is the key known to any session?
bool IsKeyValid(const KeyId& key_id);
virtual bool IsKeyValid(const KeyId& key_id);
// Event listener related methods
bool AttachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener);
bool DetachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener);
virtual bool AttachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener);
virtual bool DetachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener);
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// widevine pssh
static bool ExtractWidevinePssh(const CdmInitData& init_data,
CdmInitData* output);
private:
// private methods
// Cancel all sessions
bool CancelSessions();
void CleanupProvisioningSession();
void ComposeJsonRequestAsQueryString(const std::string& message,
CdmProvisioningRequest* request);
bool ParseJsonResponse(const CdmProvisioningResponse& json_str,
const std::string& start_substr,
const std::string& end_substr,
std::string* result);
bool ValidateKeySystem(const CdmKeySystem& key_system);
virtual bool CancelSessions();
virtual bool ValidateKeySystem(const CdmKeySystem& key_system);
// timer related methods to drive policy decisions
void EnablePolicyTimer();
void DisablePolicyTimer();
virtual void EnablePolicyTimer();
virtual void DisablePolicyTimer();
virtual void OnTimerEvent();
virtual void OnKeyReleaseEvent(CdmKeySetId key_set_id);
virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
// instance variables
CdmSession* provisioning_session_;
CdmSessionMap sessions_;
CertificateProvisioning cert_provisioning_;
CdmReleaseKeySetMap release_key_sets_;
// policy timer

View File

@@ -6,25 +6,27 @@
#include <set>
#include "crypto_session.h"
#include "device_files.h"
#include "license.h"
#include "policy_engine.h"
#include "wv_cdm_event_listener.h"
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
namespace wvcdm {
// TODO(kqyang): Do we need it? CdmKey not defined yet
// typedef std::map<KeyId, CdmKey*> CdmSessionKeys;
class WvCdmEventListener;
class CdmSession {
public:
CdmSession() : session_id_(GenerateSessionId()), license_received_(false),
reinitialize_session_(false), license_type_(kLicenseTypeStreaming) {}
CdmSession()
: session_id_(GenerateSessionId()),
crypto_session_(NULL),
license_received_(false),
reinitialize_session_(false),
license_type_(kLicenseTypeStreaming) {}
~CdmSession() {}
CdmResponseType Init();
CdmResponseType ReInit();
bool DestroySession();
CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id,
const CdmLicenseType license_type);
@@ -57,16 +59,11 @@ class CdmSession {
CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info);
// Decrypt() - Accept encrypted buffer and return decrypted data.
CdmResponseType Decrypt(bool is_encrypted,
bool is_secure,
const KeyId& key_id,
const uint8_t* encrypt_buffer,
size_t encrypt_length,
const std::vector<uint8_t>& iv,
size_t block_offset,
void* decrypt_buffer,
size_t decrypt_buffer_offset,
bool is_video);
CdmResponseType Decrypt(bool is_encrypted, bool is_secure,
const KeyId& key_id, const uint8_t* encrypt_buffer,
size_t encrypt_length, const std::vector<uint8_t>& iv,
size_t block_offset, void* decrypt_buffer,
size_t decrypt_buffer_offset, bool is_video);
// License renewal
// GenerateRenewalRequest() - Construct valid renewal request for the current
@@ -83,7 +80,7 @@ class CdmSession {
CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request,
std::string* server_url);
// RenewKey() - Accept renewal response and update key info.
// ReleaseKey() - Accept response and release key.
CdmResponseType ReleaseKey(const CdmKeyResponse& key_response);
bool IsKeyValid(const KeyId& key_id);
@@ -92,22 +89,22 @@ class CdmSession {
bool DetachEventListener(WvCdmEventListener* listener);
void OnTimerEvent();
void OnKeyReleaseEvent(CdmKeySetId key_set_id);
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
private:
// Generate unique ID for each new session.
CdmSessionId GenerateSessionId();
CdmKeySetId GenerateKeySetId(CdmInitData& pssh_data);
bool GenerateKeySetId(CdmKeySetId* key_set_id);
bool LoadDeviceCertificate(std::string* cert, std::string* wrapped_key);
bool StoreLicense(bool active);
bool StoreLicense(DeviceFiles::LicenseState state);
// instance variables
const CdmSessionId session_id_;
CdmKeySystem key_system_;
CdmLicense license_parser_;
CryptoSession* crypto_session_;
scoped_ptr<CryptoSession> crypto_session_;
PolicyEngine policy_engine_;
bool license_received_;
bool reinitialize_session_;
@@ -132,9 +129,6 @@ class CdmSession {
std::set<WvCdmEventListener*> listeners_;
// TODO(kqyang): CdmKey not defined yet
// CdmSessionKeys session_keys_;
CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession);
};

View File

@@ -0,0 +1,36 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef CDM_BASE_CERTIFICATE_PROVISIONING_H_
#define CDM_BASE_CERTIFICATE_PROVISIONING_H_
#include "crypto_session.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class CdmSession;
class CertificateProvisioning {
public:
CertificateProvisioning() {};
~CertificateProvisioning() {};
// Provisioning related methods
CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request,
std::string* default_url);
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response);
private:
void ComposeJsonRequestAsQueryString(const std::string& message,
CdmProvisioningRequest* request);
bool ParseJsonResponse(const CdmProvisioningResponse& json_str,
const std::string& start_substr,
const std::string& end_substr,
std::string* result);
CryptoSession crypto_session_;
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
};
} // namespace wvcdm
#endif // CDM_BASE_CERTIFICATE_PROVISIONING_H_

View File

@@ -1,77 +0,0 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// OEMCrypto Client - wrapper class for C-style OEMCrypto interface
//
#ifndef CDM_BASE_CRYPTO_ENGINE_H_
#define CDM_BASE_CRYPTO_ENGINE_H_
#include <stdint.h>
#include <map>
#include <string>
#include "crypto_session.h"
#include "lock.h"
#include "wv_cdm_types.h"
namespace wvcdm {
typedef std::map<CdmSessionId,CryptoSession*> CryptoSessionMap;
class CryptoEngine {
friend class CryptoSession;
private:
CryptoEngine();
~CryptoEngine();
public:
// get an instance of Crypto engine
static CryptoEngine* GetInstance();
bool Init();
bool Terminate();
bool ValidateKeybox();
CryptoSession* CreateSession(const CdmSessionId& session_id);
CryptoSession* FindSession(const CdmSessionId& session_id);
bool DestroySession(const CdmSessionId& session_id);
bool DestroySessions();
bool GetToken(std::string* token);
typedef enum {
kSecurityLevelL1,
kSecurityLevelL2,
kSecurityLevelL3,
kSecurityLevelUnknown
} SecurityLevel;
SecurityLevel GetSecurityLevel();
bool GetDeviceUniqueId(std::string* deviceId);
bool GetSystemId(uint32_t* systemId);
bool GetProvisioningId(std::string* provisioningId);
private:
void DeleteInstance();
static CryptoEngine* CreateSingleton();
CryptoSession* FindSessionInternal(const CdmSessionId& session_id);
static CryptoEngine* crypto_engine_;
static Lock crypto_engine_lock_;
bool initialized_;
mutable Lock crypto_lock_;
mutable Lock sessions_lock_;
CryptoSessionMap sessions_;
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine);
};
}; // namespace wvcdm
#endif // CDM_BASE_CRYPTO_ENGINE_H_

View File

@@ -8,48 +8,53 @@
#include <string>
#include <map>
#include "crypto_key.h"
#include "lock.h"
#include "OEMCryptoCENC.h"
#include "wv_cdm_types.h"
namespace wvcdm {
typedef std::map<CryptoKeyId,CryptoKey*> CryptoKeyMap;
class CryptoKey;
typedef std::map<CryptoKeyId, CryptoKey*> CryptoKeyMap;
class CryptoSession {
public:
CryptoSession();
explicit CryptoSession(const std::string& sname);
~CryptoSession();
typedef enum {
kSecurityLevelUninitialized,
kSecurityLevelL1,
kSecurityLevelL2,
kSecurityLevelL3,
kSecurityLevelUnknown
} SecurityLevel;
bool ValidateKeybox();
bool GetToken(std::string* token);
SecurityLevel GetSecurityLevel();
bool GetDeviceUniqueId(std::string* device_id);
bool GetSystemId(uint32_t* system_id);
bool GetProvisioningId(std::string* provisioning_id);
bool Open();
void Close();
bool IsValid() { return valid_; }
bool IsOpen() { return open_; }
bool SuccessStatus();
CryptoResult session_status() { return session_status_; }
CryptoSessionId oec_session_id() { return oec_session_id_; }
CdmSessionId cdm_session_id() { return cdm_session_id_; }
// Key request/response
void GenerateRequestId(std::string& req_id_str);
bool PrepareRequest(const std::string& key_deriv_message,
std::string* signature,
bool is_provisioning);
bool is_provisioning, std::string* signature);
bool PrepareRenewalRequest(const std::string& message,
std::string* signature);
bool LoadKeys(const std::string& message,
const std::string& signature,
const std::string& mac_key_iv,
const std::string& mac_key,
int num_keys,
const CryptoKey* key_array);
bool LoadKeys(const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
int num_keys, const CryptoKey* key_array);
bool LoadCertificatePrivateKey(std::string& wrapped_key);
bool RefreshKeys(const std::string& message,
const std::string& signature,
int num_keys,
const CryptoKey* key_array);
bool RefreshKeys(const std::string& message, const std::string& signature,
int num_keys, const CryptoKey* key_array);
bool GenerateNonce(uint32_t* nonce);
bool GenerateDerivedKeys(const std::string& message);
bool GenerateDerivedKeys(const std::string& message,
@@ -58,7 +63,6 @@ class CryptoSession {
const std::string& signature,
const std::string& nonce,
const std::string& enc_rsa_key,
size_t enc_rsa_key_length,
const std::string& rsa_key_iv,
std::string* wrapped_rsa_key);
@@ -74,33 +78,34 @@ class CryptoSession {
size_t decrypt_buffer_offset,
bool is_video);
private:
static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature
bool GetRandom(uint8_t* random_data, size_t data_length);
private:
void Init();
void Terminate();
void GenerateMacContext(const std::string& input_context,
std::string* deriv_context);
void GenerateEncryptContext(const std::string& input_context,
std::string* deriv_context);
bool GenerateSignature(const std::string& message,
std::string* signature,
bool use_rsa);
bool GenerateSignature(const std::string& message, bool use_rsa,
std::string* signature);
size_t GetOffset(std::string message, std::string field);
bool SetDestinationBufferType();
bool valid_;
static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature
static Lock crypto_lock_;
static bool initialized_;
static int session_count_;
bool open_;
CdmSessionId cdm_session_id_;
CryptoSessionId oec_session_id_;
CryptoResult session_status_;
OEMCryptoBufferType destination_buffer_type_;
bool is_destination_buffer_type_valid_;
CryptoKeyMap keys_;
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
};
}; // namespace wvcdm
}; // namespace wvcdm
#endif // CDM_BASE_CRYPTO_SESSSION_H_

View File

@@ -7,6 +7,8 @@
namespace wvcdm {
class File;
class DeviceFiles {
public:
typedef enum {
@@ -15,44 +17,50 @@ class DeviceFiles {
kLicenseStateUnknown,
} LicenseState;
static bool StoreCertificate(const std::string& certificate,
const std::string& wrapped_private_key);
static bool RetrieveCertificate(std::string* certificate,
std::string* wrapped_private_key);
DeviceFiles() {}
virtual ~DeviceFiles() {}
static bool StoreLicense(const std::string& key_set_id,
const LicenseState state,
const CdmInitData& pssh_data,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response,
const CdmKeyMessage& key_renewal_request,
const CdmKeyResponse& key_renewal_response,
const std::string& release_server_url);
static bool RetrieveLicense(const std::string& key_set_id,
LicenseState* state,
CdmInitData* pssh_data,
CdmKeyMessage* key_request,
CdmKeyResponse* key_response,
CdmKeyMessage* key_renewal_request,
CdmKeyResponse* key_renewal_response,
std::string* release_server_url);
static bool DeleteLicense(const std::string& key_set_id);
static bool LicenseExists(const std::string& key_set_id);
virtual bool Init(File* handle);
static std::string GetBasePath(const char* dir);
static const char* kBasePath;
static const char* kPathDelimiter;
static const char* kDeviceCertificateFileName;
static const char* kLicenseFileNameExt;
virtual bool StoreCertificate(const std::string& certificate,
const std::string& wrapped_private_key);
virtual bool RetrieveCertificate(std::string* certificate,
std::string* wrapped_private_key);
virtual bool StoreLicense(const std::string& key_set_id,
const LicenseState state,
const CdmInitData& pssh_data,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response,
const CdmKeyMessage& key_renewal_request,
const CdmKeyResponse& key_renewal_response,
const std::string& release_server_url);
virtual bool RetrieveLicense(const std::string& key_set_id,
LicenseState* state,
CdmInitData* pssh_data,
CdmKeyMessage* key_request,
CdmKeyResponse* key_response,
CdmKeyMessage* key_renewal_request,
CdmKeyResponse* key_renewal_response,
std::string* release_server_url);
virtual bool DeleteLicense(const std::string& key_set_id);
virtual bool LicenseExists(const std::string& key_set_id);
// For testing only
static std::string GetCertificateFileName();
static std::string GetLicenseFileNameExtension();
protected:
bool Hash(const std::string& data, std::string* hash);
bool StoreFile(const char* name, const std::string& data);
bool RetrieveFile(const char* name, std::string* data);
private:
static bool Hash(const std::string& data, std::string* hash);
static bool StoreFile(const char* name, const std::string& data);
static bool RetrieveFile(const char* name, std::string* data);
File* file_;
CORE_DISALLOW_COPY_AND_ASSIGN(DeviceFiles);
}; // namespace wvcdm
};
}
} // namespace wvcdm
#endif // CDM_BASE_DEVICE_FILES_H_

View File

@@ -17,28 +17,24 @@ class File {
kNoFlags = 0,
kBinary = 1,
kCreate = 2,
kReadOnly = 4, // defauts to read and write access
kReadOnly = 4, // defaults to read and write access
kTruncate = 8
};
File();
File(const std::string& file_path, int flags);
virtual ~File();
bool Open(const std::string& file_path, int flags);
void Close();
bool IsOpen();
bool IsBad();
virtual bool Open(const std::string& file_path, int flags);
virtual ssize_t Read(char* buffer, size_t bytes);
virtual ssize_t Write(const char* buffer, size_t bytes);
virtual void Close();
ssize_t Read(void *buf, size_t bytes);
ssize_t Write(const void* buf, size_t bytes);
static bool Exists(const std::string& file_path);
static bool Remove(const std::string& file_path);
static bool CreateDirectory(const std::string dir_path);
static bool IsDirectory(const std::string& dir_path);
static bool IsRegularFile(const std::string& file_path);
static ssize_t FileSize(const std::string& file_path);
virtual bool Exists(const std::string& file_path);
virtual bool Remove(const std::string& file_path);
virtual bool CreateDirectory(const std::string dir_path);
virtual bool IsDirectory(const std::string& dir_path);
virtual bool IsRegularFile(const std::string& file_path);
virtual ssize_t FileSize(const std::string& file_path);
private:
class Impl;

View File

@@ -3,19 +3,20 @@
#ifndef CDM_BASE_LICENSE_H_
#define CDM_BASE_LICENSE_H_
#include "license_protocol.pb.h"
#include "wv_cdm_types.h"
namespace wvcdm {
namespace video_widevine_server {
namespace sdk {
class SignedMessage;
}
} // namespace video_widevine_server
using video_widevine_server::sdk::LicenseIdentification;
using video_widevine_server::sdk::SignedMessage;
namespace wvcdm {
class CryptoSession;
class PolicyEngine;
class CdmLicense {
public:
CdmLicense();
@@ -42,9 +43,9 @@ class CdmLicense {
CdmKeyResponse& license_renewal_response);
private:
CdmResponseType HandleKeyErrorResponse(const SignedMessage& signed_message);
CdmResponseType HandleKeyErrorResponse(
const video_widevine_server::sdk::SignedMessage& signed_message);
LicenseIdentification license_id_;
CryptoSession* session_;
PolicyEngine* policy_engine_;
std::string server_url_;

View File

@@ -17,14 +17,23 @@ typedef enum {
LOG_VERBOSE
} LogPriority;
void log_write(LogPriority priority, const char *fmt, ...);
// Required to enable/disable verbose logging (LOGV) in Chromium. In Chromium,
// verbose logging level is controlled using command line switches --v (global)
// or --vmodule (per module). This function calls logging::InitLogging to
// initialize logging, which should have already been included in most Chromium
// based binaries. However, it is typically not included by default in
// unittests, in particular, the unittests in CDM core need to call InitLogging
// to be able to control verbose logging in command line.
void InitLogging(int argc, const char* const* argv);
void Log(const char* file, int line, LogPriority level, const char* fmt, ...);
// Log APIs
#define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__))
#define LOGW(...) ((void)log_write(wvcdm::LOG_WARN, __VA_ARGS__))
#define LOGI(...) ((void)log_write(wvcdm::LOG_INFO, __VA_ARGS__))
#define LOGD(...) ((void)log_write(wvcdm::LOG_DEBUG, __VA_ARGS__))
#define LOGV(...) ((void)log_write(wvcdm::LOG_VERBOSE, __VA_ARGS__))
#define LOGE(...) Log(__FILE__, __LINE__, wvcdm::LOG_ERROR, __VA_ARGS__)
#define LOGW(...) Log(__FILE__, __LINE__, wvcdm::LOG_WARN, __VA_ARGS__)
#define LOGI(...) Log(__FILE__, __LINE__, wvcdm::LOG_INFO, __VA_ARGS__)
#define LOGD(...) Log(__FILE__, __LINE__, wvcdm::LOG_DEBUG, __VA_ARGS__)
#define LOGV(...) Log(__FILE__, __LINE__, wvcdm::LOG_VERBOSE, __VA_ARGS__)
}; // namespace wvcdm

View File

@@ -30,21 +30,23 @@ class Properties {
static inline bool oem_crypto_use_secure_buffers() {
return oem_crypto_use_secure_buffers_;
}
static inline bool oem_crypto_use_fifo() {
return oem_crypto_use_fifo_;
}
static inline bool oem_crypto_use_fifo() { return oem_crypto_use_fifo_; }
static inline bool oem_crypto_use_userspace_buffers() {
return oem_crypto_use_userspace_buffers_;
}
static inline bool use_certificates_as_identification() {
return use_certificates_as_identification_;
}
static bool GetCompanyName(std::string& company_name);
static bool GetModelName(std::string& model_name);
static bool GetArchitectureName(std::string& arch_name);
static bool GetDeviceName(std::string& device_name);
static bool GetProductName(std::string& product_name);
static bool GetBuildInfo(std::string& build_info);
static inline bool extract_pssh_data() {
return extract_pssh_data_;
}
static bool GetCompanyName(std::string* company_name);
static bool GetModelName(std::string* model_name);
static bool GetArchitectureName(std::string* arch_name);
static bool GetDeviceName(std::string* device_name);
static bool GetProductName(std::string* product_name);
static bool GetBuildInfo(std::string* build_info);
static bool GetDeviceFilesBasePath(std::string* base_path);
private:
static void set_begin_license_usage_when_received(bool flag) {
@@ -65,6 +67,9 @@ class Properties {
static void set_use_certificates_as_identification(bool flag) {
use_certificates_as_identification_ = flag;
}
static void set_extract_pssh_data(bool flag) {
extract_pssh_data_ = flag;
}
static bool begin_license_usage_when_received_;
static bool require_explicit_renew_request_;
@@ -72,6 +77,7 @@ class Properties {
static bool oem_crypto_use_fifo_;
static bool oem_crypto_use_userspace_buffers_;
static bool use_certificates_as_identification_;
static bool extract_pssh_data_;
CORE_DISALLOW_COPY_AND_ASSIGN(Properties);
};

View File

@@ -0,0 +1,64 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// A simple and partial implementation of scoped_ptr class.
// The implementation is copied from gtest/include/gtest/internal/gtest-port.h.
//
#ifndef CDM_BASE_SCOPED_PTR_H_
#define CDM_BASE_SCOPED_PTR_H_
#include "wv_cdm_types.h"
namespace wvcdm {
// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
// automatically deletes the pointer it holds (if any).
// That is, scoped_ptr<T> owns the T object that it points to.
// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
// Also like T*, scoped_ptr<T> is thread-compatible, and once you
// dereference it, you get the thread safety guarantees of T.
//
// The size of scoped_ptr is small. On most compilers, sizeof(scoped_ptr<T>)
// == sizeof(T*).
//
// Current implementation targets having a strict subset of C++11's
// unique_ptr<> features. Known deficiencies include not supporting move-only
// deleteres, function pointers as deleters, and deleters with reference
// types.
// This implementation of scoped_ptr is PARTIAL, e.g. it does not support move,
// custom deleter etc.
template <typename T>
class scoped_ptr {
public:
typedef T element_type;
explicit scoped_ptr(T* p = NULL) : ptr_(p) {}
~scoped_ptr() { reset(); }
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
T* get() const { return ptr_; }
T* release() {
T* const ptr = ptr_;
ptr_ = NULL;
return ptr;
}
void reset(T* p = NULL) {
if (p != ptr_) {
if (sizeof(T) > 0) { // Makes sure T is a complete type.
delete ptr_;
}
ptr_ = p;
}
}
private:
T* ptr_;
CORE_DISALLOW_COPY_AND_ASSIGN(scoped_ptr);
};
}; // namespace wvcdm
#endif // CDM_BASE_SCOPED_PTR_H_

View File

@@ -15,6 +15,7 @@ std::string a2bs_hex(const std::string& b);
std::string b2a_hex(const std::vector<uint8_t>& b);
std::string b2a_hex(const std::string& b);
std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input);
std::string Base64SafeEncodeNoPad(const std::vector<uint8_t>& bin_input);
std::vector<uint8_t> Base64SafeDecode(const std::string& bin_input);
std::string HexEncode(const uint8_t* bytes, unsigned size);
std::string IntToString(int value);

View File

@@ -5,8 +5,6 @@
#include <string>
#include "wv_cdm_types.h"
namespace wvcdm {
static const size_t KEY_CONTROL_SIZE = 16;
// TODO(kqyang): Key ID size is not fixed in spec, but conventionally we
@@ -19,10 +17,9 @@ static const size_t KEY_SIZE = 16;
static const size_t MAC_KEY_SIZE = 32;
static const size_t KEYBOX_KEY_DATA_SIZE = 72;
static const std::string SESSION_ID_PREFIX = "sid";
static const std::string KEY_SET_ID_PREFIX = "ksid";
static const CdmKeySystem KEY_SYSTEM = "com.widevine";
static const char SESSION_ID_PREFIX[] = "sid";
static const char KEY_SET_ID_PREFIX[] = "ksid";
static const char KEY_SYSTEM[] = "com.widevine";
// define query keys, values here
static const std::string QUERY_KEY_LICENSE_TYPE = "LicenseType";

View File

@@ -11,22 +11,15 @@ namespace wvcdm {
// The caller of the CDM API must provide an implementation for onEvent
// and signal its intent by using the Attach/DetachEventListener methods
// in the WvContentDecryptionModule class.
// The listener may also specify, when the instance is created, whether to be
// notified about events for a particular session or all sessions.
class WvCdmEventListener {
public:
WvCdmEventListener() {}
WvCdmEventListener(CdmSessionId& session_id) : session_id_(session_id) {}
virtual ~WvCdmEventListener() {}
virtual void onEvent(const CdmSessionId& session_id,
CdmEventType cdm_event) = 0;
virtual CdmSessionId session_id() { return session_id_; }
private:
CdmSessionId session_id_;
CORE_DISALLOW_COPY_AND_ASSIGN(WvCdmEventListener);
};

View File

@@ -7,42 +7,22 @@
#include "buffer_reader.h"
#include "cdm_session.h"
#include "clock.h"
#include "crypto_engine.h"
#include "device_files.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
#ifndef CDM_POLICY_TIMER_DURATION_SECONDS
#define CDM_POLICY_TIMER_DURATION_SECONDS 1
#endif
namespace {
const std::string kDefaultProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const int kCdmPolicyTimerDurationSeconds = 1;
}
namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::ClientIdentification;
using video_widevine_server::sdk::ProvisioningRequest;
using video_widevine_server::sdk::ProvisioningResponse;
using video_widevine_server::sdk::SignedProvisioningMessage;
typedef std::map<CdmSessionId, CdmSession*>::const_iterator CdmSessionIter;
typedef std::map<CdmKeySetId, CdmSessionId>::iterator CdmReleaseKeySetIter;
CdmEngine::CdmEngine() : provisioning_session_(NULL) {
CdmEngine::CdmEngine() {
Properties::Init();
Clock clock;
srand(static_cast<int>(clock.GetCurrentTime() & 0xFFFFFFFF));
}
CdmEngine::~CdmEngine() {
@@ -69,28 +49,21 @@ CdmResponseType CdmEngine::OpenSession(
return KEY_ERROR;
}
CdmSession* new_session = new CdmSession();
if (!new_session) {
LOGE("CdmEngine::OpenSession: session creation failed");
return KEY_ERROR;
}
scoped_ptr<CdmSession> new_session(new CdmSession());
CdmSessionId new_session_id = new_session->session_id();
if (new_session_id.empty()) {
if (new_session->session_id().empty()) {
LOGE("CdmEngine::OpenSession: failure to generate session ID");
delete(new_session);
return UNKNOWN_ERROR;
}
CdmResponseType sts = new_session->Init();
if (sts != NO_ERROR) {
delete(new_session);
LOGE("CdmEngine::OpenSession: bad session init");
return sts;
}
sessions_[new_session_id] = new_session;
*session_id = new_session_id;
*session_id = new_session->session_id();
sessions_[*session_id] = new_session.release();
return NO_ERROR;
}
@@ -98,7 +71,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
LOGI("CdmEngine::OpenKeySetSession");
if (key_set_id.empty()) {
LOGI("CdmEngine::OpenKeySetSession: invalid key set id");
LOGE("CdmEngine::OpenKeySetSession: invalid key set id");
return KEY_ERROR;
}
@@ -115,24 +88,22 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::CloseSession");
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
return KEY_ERROR;
}
sessions_.erase(session_id);
if (sessions_.size() == 0)
DisablePolicyTimer();
iter->second->DestroySession();
delete iter->second;
sessions_.erase(session_id);
DisablePolicyTimer();
return NO_ERROR;
}
CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
LOGI("CdmEngine::CloseKeySetSession");
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
key_set_id.c_str());
@@ -169,7 +140,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
return UNKNOWN_ERROR;
}
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
key_set_id.c_str());
@@ -179,7 +150,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
id = iter->second;
}
CdmSessionIter iter = sessions_.find(id);
CdmSessionMap::iterator iter = sessions_.find(id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
id.c_str());
@@ -222,28 +193,33 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
CdmResponseType CdmEngine::AddKey(
const CdmSessionId& session_id,
const CdmKeyResponse& key_data,
CdmKeySetId& key_set_id) {
CdmKeySetId* key_set_id) {
LOGI("CdmEngine::AddKey");
CdmSessionId id = session_id;
bool license_type_release = session_id.empty();
if (license_type_release) {
if (key_set_id.empty()) {
LOGI("CdmEngine::AddKey: invalid key set id");
if (!key_set_id) {
LOGE("CdmEngine::AddKey: no key set id provided");
return KEY_ERROR;
}
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
if (key_set_id->empty()) {
LOGE("CdmEngine::AddKey: invalid key set id");
return KEY_ERROR;
}
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id);
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id.c_str());
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str());
return KEY_ERROR;
}
id = iter->second;
}
CdmSessionIter iter = sessions_.find(id);
CdmSessionMap::iterator iter = sessions_.find(id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
@@ -255,7 +231,7 @@ CdmResponseType CdmEngine::AddKey(
return KEY_ERROR;
}
CdmResponseType sts = iter->second->AddKey(key_data, &key_set_id);
CdmResponseType sts = iter->second->AddKey(key_data, key_set_id);
if (KEY_ADDED != sts) {
LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts);
@@ -279,7 +255,7 @@ CdmResponseType CdmEngine::RestoreKey(
return KEY_ERROR;
}
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
session_id.c_str());
@@ -298,13 +274,15 @@ CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) {
// active sessions. Sessions are currently not being destroyed here. We can
// add this logic once the semantics of canceling the key is worked out.
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::CancelKeyRequest: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}
// TODO(edwinwong, rfrias): unload keys here
delete iter->second;
sessions_.erase(session_id);
DisablePolicyTimer();
return NO_ERROR;
}
@@ -315,7 +293,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
std::string* server_url) {
LOGI("CdmEngine::GenerateRenewalRequest");
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
@@ -345,7 +323,7 @@ CdmResponseType CdmEngine::RenewKey(
const CdmKeyResponse& key_data) {
LOGI("CdmEngine::RenewKey");
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
@@ -367,22 +345,19 @@ CdmResponseType CdmEngine::RenewKey(
CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryStatus");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (!crypto_engine) {
return KEY_ERROR;
}
switch (crypto_engine->GetSecurityLevel()) {
case CryptoEngine::kSecurityLevelL1:
CryptoSession crypto_session;
switch (crypto_session.GetSecurityLevel()) {
case CryptoSession::kSecurityLevelL1:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
break;
case CryptoEngine::kSecurityLevelL2:
case CryptoSession::kSecurityLevelL2:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L2;
break;
case CryptoEngine::kSecurityLevelL3:
case CryptoSession::kSecurityLevelL3:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3;
break;
case CryptoEngine::kSecurityLevelUnknown:
case CryptoSession::kSecurityLevelUninitialized:
case CryptoSession::kSecurityLevelUnknown:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_Unknown;
break;
default:
@@ -390,13 +365,13 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
}
std::string deviceId;
bool success = crypto_engine->GetDeviceUniqueId(&deviceId);
bool success = crypto_session.GetDeviceUniqueId(&deviceId);
if (success) {
(*key_info)[QUERY_KEY_DEVICE_ID] = deviceId;
}
uint32_t system_id;
success = crypto_engine->GetSystemId(&system_id);
success = crypto_session.GetSystemId(&system_id);
if (success) {
std::ostringstream system_id_stream;
system_id_stream << system_id;
@@ -404,7 +379,7 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
}
std::string provisioning_id;
success = crypto_engine->GetProvisioningId(&provisioning_id);
success = crypto_session.GetProvisioningId(&provisioning_id);
if (success) {
(*key_info)[QUERY_KEY_PROVISIONING_ID] = provisioning_id;
}
@@ -416,7 +391,7 @@ CdmResponseType CdmEngine::QueryKeyStatus(
const CdmSessionId& session_id,
CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryKeyStatus");
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
@@ -428,7 +403,7 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
const CdmSessionId& session_id,
CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryKeyControlInfo");
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
@@ -436,63 +411,6 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
return iter->second->QueryKeyControlInfo(key_info);
}
/*
* The certificate provisioning process creates a cdm and a crypto session.
* The lives of these sessions are short and therefore, not added to the
* CdmSessionMap. We need to explicitly delete these objects when error occurs
* or when we are done with provisioning.
*/
void CdmEngine::CleanupProvisioningSession() {
if (provisioning_session_) {
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (crypto_engine) {
CdmSessionId cdm_session_id = provisioning_session_->session_id();
CryptoSession* crypto_session =
crypto_engine->FindSession(cdm_session_id);
if (crypto_session) {
LOGV("delete crypto session for id=%s", cdm_session_id.c_str());
delete crypto_session;
} else {
LOGE("CleanupProvisioningSession: cannot find crypto_session");
}
}
delete provisioning_session_;
provisioning_session_ = NULL;
}
}
/*
* This function converts SignedProvisioningRequest into base64 string.
* It then wraps it in JSON format expected by the Apiary frontend.
* Apiary requires the base64 encoding to replace '+' with minus '-',
* and '/' with underscore '_'; opposite to stubby's.
*
* Returns the JSON formated string in *request. The JSON string will be
* appended as a query parameter, i.e. signedRequest=<base 64 encoded
* SignedProvisioningRequest>. All base64 '=' padding chars must be removed.
*
* The JSON formated request takes the following format:
*
* base64 encoded message
*/
void CdmEngine::ComposeJsonRequestAsQueryString(
const std::string& message,
CdmProvisioningRequest* request) {
// performs base64 encoding for message
std::vector<uint8_t> message_vector(message.begin(), message.end());
std::string message_b64 = Base64SafeEncode(message_vector);
// removes trailing '=' padding characters;
// the encoded string can have at most 2 '=' padding chars, so start
// searching at the end minus four characters
size_t found_pos = message_b64.find("=", message_b64.size() - 4);
if (std::string::npos != found_pos) {
message_b64.resize(found_pos);
}
request->assign(message_b64);
}
/*
* Composes a device provisioning request and output the request in JSON format
* in *request. It also returns the default url for the provisioning server
@@ -504,143 +422,10 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
CdmProvisioningRequest* request,
std::string* default_url) {
if (!request || !default_url) {
LOGE("GetProvisioningRequest: invalid input parameters");
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
return UNKNOWN_ERROR;
}
default_url->assign(kDefaultProvisioningServerUrl);
if (provisioning_session_) {
CleanupProvisioningSession();
}
//
//---------------------------------------------------------------------------
// This function can be called before a cdm session is created.
// First creates a cdm session, then creates a crypto session.
//
CdmSession* cdm_session = new CdmSession();
if (!cdm_session) {
LOGE("GetProvisioningRequest: fails to create a cdm session");
return UNKNOWN_ERROR;
}
if (cdm_session->session_id().empty()) {
LOGE("GetProvisioningRequest: fails to generate session ID");
delete cdm_session;
return UNKNOWN_ERROR;
}
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (!crypto_engine) {
LOGE("GetProvisioningRequest: fails to create a crypto engine");
delete cdm_session;
return UNKNOWN_ERROR;
}
CdmSessionId cdm_session_id = cdm_session->session_id();
CryptoSession* crypto_session = crypto_engine->CreateSession(cdm_session_id);
if (!crypto_session) {
LOGE("GetProvisioningRequest: fails to create a crypto session");
delete cdm_session;
return UNKNOWN_ERROR;
}
// TODO(edwinwong): replace this cdm session pointer with crypto session
// pointer if feasible
provisioning_session_ = cdm_session;
LOGV("provisioning session id=%s", cdm_session_id.c_str());
//
//---------------------------------------------------------------------------
// Prepares device provisioning request.
//
ProvisioningRequest provisioning_request;
ClientIdentification* client_id = provisioning_request.mutable_client_id();
client_id->set_type(ClientIdentification::KEYBOX);
std::string token;
if (!crypto_engine->GetToken(&token)) {
LOGE("GetProvisioningRequest: fails to get token");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
client_id->set_token(token);
uint32_t nonce;
if (!crypto_session->GenerateNonce(&nonce)) {
LOGE("GetProvisioningRequest: fails to generate a nonce");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
// The provisioning server does not convert the nonce to uint32_t, it just
// passes the binary data to the response message.
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
provisioning_request.set_nonce(the_nonce);
std::string serialized_message;
provisioning_request.SerializeToString(&serialized_message);
// Derives signing and encryption keys and constructs signature.
std::string request_signature;
if (!crypto_session->PrepareRequest(serialized_message,
&request_signature, true)) {
request->clear();
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
if (request_signature.empty()) {
request->clear();
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
SignedProvisioningMessage signed_provisioning_msg;
signed_provisioning_msg.set_message(serialized_message);
signed_provisioning_msg.set_signature(request_signature);
std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request);
// converts request into JSON string
ComposeJsonRequestAsQueryString(serialized_request, request);
return NO_ERROR;
}
/*
* Parses the input json_str and locates substring using start_substr and
* end_stubstr. The found base64 substring is then decoded and returns
* in *result.
*
* Returns true for success and false if fails.
*/
bool CdmEngine::ParseJsonResponse(
const CdmProvisioningResponse& json_str,
const std::string& start_substr,
const std::string& end_substr,
std::string* result) {
std::string b64_string;
size_t start = json_str.find(start_substr);
if (start == json_str.npos) {
LOGE("ParseJsonResponse: cannot find start substring");
return false;
} else {
size_t end = json_str.find(end_substr, start + start_substr.length());
if (end == json_str.npos) {
LOGE("ParseJsonResponse cannot locate end substring");
return false;
}
size_t b64_string_size = end - start - start_substr.length();
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
}
// Decodes base64 substring and returns it in *result
std::vector<uint8_t> result_vector = Base64SafeDecode(b64_string);
result->assign(result_vector.begin(), result_vector.end());
return true;
return cert_provisioning_.GetProvisioningRequest(request, default_url);
}
/*
@@ -653,103 +438,10 @@ bool CdmEngine::ParseJsonResponse(
CdmResponseType CdmEngine::HandleProvisioningResponse(
CdmProvisioningResponse& response) {
if (response.empty()) {
LOGE("Empty provisioning response.");
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
return UNKNOWN_ERROR;
}
//---------------------------------------------------------------------------
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string serialized_signed_response;
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
&serialized_signed_response)) {
LOGE("Fails to extract signed serialized response from JSON response");
return UNKNOWN_ERROR;
}
//
//---------------------------------------------------------------------------
// Creates a crypto session using provisioning_session_.
//
if (!provisioning_session_) {
LOGE("HandleProvisioningResponse: invalid provisioning session");
return UNKNOWN_ERROR;
}
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (!crypto_engine) {
LOGE("HandleProvisioningResponse: fails to create a crypto engine");
return UNKNOWN_ERROR;
}
CdmSessionId cdm_session_id = provisioning_session_->session_id();
CryptoSession* crypto_session = crypto_engine->FindSession(cdm_session_id);
if (!crypto_session) {
LOGE("HandleProvisioningResponse: fails to find %s",
cdm_session_id.c_str());
return UNKNOWN_ERROR;
}
//---------------------------------------------------------------------------
// Authenticates provisioning response using D1s (server key derived from
// the provisioing request's input). Validate provisioning response and
// stores private device RSA key and certificate.
SignedProvisioningMessage signed_response;
if (!signed_response.ParseFromString(serialized_signed_response)) {
LOGE("Fails to parse signed serialized response");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
if (!signed_response.has_signature() || !signed_response.has_message()) {
LOGE("Invalid response - signature or message not found");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
const std::string& signed_message = signed_response.message();
ProvisioningResponse provisioning_response;
if (!provisioning_response.ParseFromString(signed_message)) {
LOGE("Fails to parse signed message");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
if (!provisioning_response.has_device_rsa_key()) {
LOGE("Invalid response - key not found");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
const std::string& nonce = provisioning_response.nonce();
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
const std::string& signature = signed_response.signature();
std::string wrapped_rsa_key;
if (!crypto_session->RewrapDeviceRSAKey(signed_message,
signature,
nonce,
enc_rsa_key,
enc_rsa_key.size(),
rsa_key_iv,
&wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
const std::string& device_certificate = provisioning_response.device_certificate();
DeviceFiles::StoreCertificate(device_certificate, wrapped_rsa_key);
//
//---------------------------------------------------------------------------
// Deletes cdm and crypto sessions created for provisioning.
//
CleanupProvisioningSession();
return NO_ERROR;
return cert_provisioning_.HandleProvisioningResponse(response);
}
CdmResponseType CdmEngine::GetSecureStops(
@@ -776,7 +468,7 @@ CdmResponseType CdmEngine::Decrypt(
void* decrypt_buffer,
size_t decrypt_buffer_offset,
bool is_video) {
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGW("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
@@ -789,7 +481,7 @@ CdmResponseType CdmEngine::Decrypt(
}
bool CdmEngine::IsKeyValid(const KeyId& key_id) {
for (CdmSessionIter iter = sessions_.begin();
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyValid(key_id)) {
return true;
@@ -802,7 +494,7 @@ bool CdmEngine::AttachEventListener(
const CdmSessionId& session_id,
WvCdmEventListener* listener) {
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
return false;
}
@@ -814,7 +506,7 @@ bool CdmEngine::DetachEventListener(
const CdmSessionId& session_id,
WvCdmEventListener* listener) {
CdmSessionIter iter = sessions_.find(session_id);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
return false;
}
@@ -923,28 +615,25 @@ bool CdmEngine::ExtractWidevinePssh(
}
void CdmEngine::EnablePolicyTimer() {
if (!policy_timer_.IsRunning())
policy_timer_.Start(this, CDM_POLICY_TIMER_DURATION_SECONDS);
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);
}
void CdmEngine::DisablePolicyTimer() {
if (policy_timer_.IsRunning())
if (sessions_.size() == 0 && policy_timer_.IsRunning())
policy_timer_.Stop();
}
void CdmEngine::OnTimerEvent() {
for (CdmSessionIter iter = sessions_.begin();
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->OnTimerEvent();
}
}
void CdmEngine::OnKeyReleaseEvent(CdmKeySetId key_set_id) {
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
for (CdmSessionIter iter = sessions_.begin();
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->OnKeyReleaseEvent(key_set_id);
}

View File

@@ -7,87 +7,67 @@
#include <sstream>
#include <stdlib.h>
#include "clock.h"
#include "cdm_engine.h"
#include "crypto_engine.h"
#include "clock.h"
#include "crypto_session.h"
#include "device_files.h"
#include "file_store.h"
#include "log.h"
#include "openssl/sha.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
namespace {
const size_t kKeySetIdLength = 14;
} // namespace
namespace wvcdm {
typedef std::set<WvCdmEventListener*>::iterator CdmEventListenerIter;
CdmResponseType CdmSession::Init() {
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (!crypto_engine) {
LOGE("CdmSession::Init failed to get CryptoEngine instance.");
return UNKNOWN_ERROR;
}
crypto_session_ = crypto_engine->CreateSession(session_id_);
if (!crypto_session_) {
LOGE("CdmSession::Init crypto session creation failure");
return UNKNOWN_ERROR;
}
scoped_ptr<CryptoSession> session(new CryptoSession());
if (!session->Open()) return UNKNOWN_ERROR;
std::string token;
if (Properties::use_certificates_as_identification()) {
if (!LoadDeviceCertificate(&token, &wrapped_key_)) {
LOGE("CdmSession::Init provisioning needed");
return NEED_PROVISIONING;
}
}
else {
if (!crypto_engine->GetToken(&token)) {
LOGE("CdmSession::Init token retrieval failure");
return UNKNOWN_ERROR;
}
if (!LoadDeviceCertificate(&token, &wrapped_key_)) return NEED_PROVISIONING;
} else {
if (!session->GetToken(&token)) return UNKNOWN_ERROR;
}
if (license_parser_.Init(token, crypto_session_, &policy_engine_))
return NO_ERROR;
else
if (!license_parser_.Init(token, session.get(), &policy_engine_))
return UNKNOWN_ERROR;
}
CdmResponseType CdmSession::ReInit() {
DestroySession();
return Init();
}
bool CdmSession::DestroySession() {
if (crypto_session_) {
delete crypto_session_;
crypto_session_ = NULL;
}
return true;
crypto_session_.reset(session.release());
license_received_ = false;
reinitialize_session_ = false;
return NO_ERROR;
}
CdmResponseType CdmSession::RestoreOfflineSession(
const CdmKeySetId& key_set_id,
const CdmLicenseType license_type) {
const CdmKeySetId& key_set_id, const CdmLicenseType license_type) {
key_set_id_ = key_set_id;
// Retrieve license information from persistent store
File file;
DeviceFiles handle;
if (!handle.Init(&file)) return UNKNOWN_ERROR;
DeviceFiles::LicenseState license_state;
if (!DeviceFiles::RetrieveLicense(key_set_id, &license_state,
&offline_pssh_data_,
&offline_key_request_,
&offline_key_response_,
&offline_key_renewal_request_,
&offline_key_renewal_response_,
&offline_release_server_url_)) {
if (!handle.RetrieveLicense(key_set_id, &license_state, &offline_pssh_data_,
&offline_key_request_, &offline_key_response_,
&offline_key_renewal_request_,
&offline_key_renewal_response_,
&offline_release_server_url_)) {
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
key_set_id.c_str());
key_set_id.c_str());
return UNKNOWN_ERROR;
}
if (license_state != DeviceFiles::kLicenseStateActive) {
if (license_state != DeviceFiles::kLicenseStateActive) {
LOGE("CdmSession::Init invalid offline license state = %s", license_state);
return UNKNOWN_ERROR;
}
@@ -117,21 +97,18 @@ bool CdmSession::VerifySession(const CdmKeySystem& key_system,
}
CdmResponseType CdmSession::GenerateKeyRequest(
const CdmInitData& init_data,
const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
const CdmInitData& init_data, const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
std::string* server_url) {
if (reinitialize_session_) {
CdmResponseType sts = ReInit();
CdmResponseType sts = Init();
if (sts != NO_ERROR) {
LOGW("CdmSession::GenerateKeyRequest: Reinitialization failed");
return sts;
}
reinitialize_session_ = false;
}
if (!crypto_session_) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
return UNKNOWN_ERROR;
}
@@ -145,17 +122,23 @@ CdmResponseType CdmSession::GenerateKeyRequest(
if (license_type_ == kLicenseTypeRelease) {
return GenerateReleaseRequest(key_request, server_url);
}
else if (license_received_) { // renewal
return Properties::require_explicit_renew_request() ?
UNKNOWN_ERROR : GenerateRenewalRequest(key_request, server_url);
}
else {
CdmInitData pssh_data;
if (!CdmEngine::ExtractWidevinePssh(init_data, &pssh_data)) {
} else if (license_received_) { // renewal
return Properties::require_explicit_renew_request()
? UNKNOWN_ERROR
: GenerateRenewalRequest(key_request, server_url);
} else {
if (init_data.empty()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return KEY_ERROR;
}
CdmInitData pssh_data = init_data;
if (Properties::extract_pssh_data()) {
if (!CdmEngine::ExtractWidevinePssh(init_data, &pssh_data)) {
return KEY_ERROR;
}
}
if (Properties::use_certificates_as_identification()) {
if (!crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
reinitialize_session_ = true;
@@ -163,11 +146,8 @@ CdmResponseType CdmSession::GenerateKeyRequest(
}
}
if (!license_parser_.PrepareKeyRequest(pssh_data,
license_type,
app_parameters,
key_request,
server_url)) {
if (!license_parser_.PrepareKeyRequest(
pssh_data, license_type, app_parameters, key_request, server_url)) {
return KEY_ERROR;
}
@@ -182,10 +162,9 @@ CdmResponseType CdmSession::GenerateKeyRequest(
}
// AddKey() - Accept license response and extract key info.
CdmResponseType CdmSession::AddKey(
const CdmKeyResponse& key_response,
CdmKeySetId* key_set_id) {
if (!crypto_session_) {
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
CdmKeySetId* key_set_id) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::AddKey: Invalid crypto session");
return UNKNOWN_ERROR;
}
@@ -197,25 +176,32 @@ CdmResponseType CdmSession::AddKey(
if (license_type_ == kLicenseTypeRelease) {
return ReleaseKey(key_response);
}
else if (license_received_) { // renewal
return Properties::require_explicit_renew_request() ?
UNKNOWN_ERROR : RenewKey(key_response);
}
else {
} else if (license_received_) { // renewal
return Properties::require_explicit_renew_request()
? UNKNOWN_ERROR
: RenewKey(key_response);
} else {
CdmResponseType sts = license_parser_.HandleKeyResponse(key_response);
if (sts != KEY_ADDED)
return sts;
if (sts != KEY_ADDED) return sts;
license_received_ = true;
if (license_type_ == kLicenseTypeOffline) {
offline_key_response_ = key_response;
key_set_id_ = GenerateKeySetId(offline_pssh_data_);
if (!StoreLicense(true)) {
if (!GenerateKeySetId(&key_set_id_)) {
LOGE("CdmSession::AddKey: Unable to generate key set Id");
return UNKNOWN_ERROR;
}
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::AddKey: Unable to store license");
ReInit();
CdmResponseType sts = Init();
if (sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Reinitialization failed");
return sts;
}
key_set_id_.clear();
return UNKNOWN_ERROR;
}
@@ -231,7 +217,7 @@ CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
}
CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
if ((!crypto_session_) || (!crypto_session_->IsOpen()))
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
return UNKNOWN_ERROR;
std::stringstream ss;
@@ -248,17 +234,12 @@ CdmResponseType CdmSession::CancelKeyRequest() {
}
// Decrypt() - Accept encrypted buffer and return decrypted data.
CdmResponseType CdmSession::Decrypt(bool is_encrypted,
bool is_secure,
const KeyId& key_id,
const uint8_t* encrypt_buffer,
size_t encrypt_length,
const std::vector<uint8_t>& iv,
size_t block_offset,
void* decrypt_buffer,
size_t decrypt_buffer_offset,
bool is_video) {
if (!crypto_session_ || !crypto_session_->IsOpen())
CdmResponseType CdmSession::Decrypt(
bool is_encrypted, bool is_secure, const KeyId& key_id,
const uint8_t* encrypt_buffer, size_t encrypt_length,
const std::vector<uint8_t>& iv, size_t block_offset, void* decrypt_buffer,
size_t decrypt_buffer_offset, bool is_video) {
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
return UNKNOWN_ERROR;
// Check if key needs to be selected
@@ -266,17 +247,15 @@ CdmResponseType CdmSession::Decrypt(bool is_encrypted,
if (key_id_.compare(key_id) != 0) {
if (crypto_session_->SelectKey(key_id)) {
key_id_ = key_id;
}
else {
} else {
return NEED_KEY;
}
}
}
return crypto_session_->Decrypt(is_encrypted, is_secure, encrypt_buffer,
encrypt_length, iv, block_offset,
decrypt_buffer, decrypt_buffer_offset,
is_video);
return crypto_session_->Decrypt(
is_encrypted, is_secure, encrypt_buffer, encrypt_length, iv, block_offset,
decrypt_buffer, decrypt_buffer_offset, is_video);
}
// License renewal
@@ -295,37 +274,34 @@ CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
// RenewKey() - Accept renewal response and update key info.
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(true,
key_response);
if (sts != KEY_ADDED)
return sts;
CdmResponseType sts =
license_parser_.HandleKeyUpdateResponse(true, key_response);
if (sts != KEY_ADDED) return sts;
if (license_type_ == kLicenseTypeOffline) {
offline_key_renewal_response_ = key_response;
if (!StoreLicense(true))
return UNKNOWN_ERROR;
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) return UNKNOWN_ERROR;
}
return KEY_ADDED;
}
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
std::string* server_url) {
if (license_parser_.PrepareKeyUpdateRequest(false, key_request,
server_url)) {
if (license_parser_.PrepareKeyUpdateRequest(false, key_request, server_url)) {
// Mark license as being released
if (!StoreLicense(false))
return UNKNOWN_ERROR;
return KEY_MESSAGE;
if (StoreLicense(DeviceFiles::kLicenseStateReleasing)) return KEY_MESSAGE;
}
return UNKNOWN_ERROR;
}
// ReleaseKey() - Accept release response and release license.
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false,
key_response);
DeviceFiles::DeleteLicense(key_set_id_);
CdmResponseType sts =
license_parser_.HandleKeyUpdateResponse(false, key_response);
File file;
DeviceFiles handle;
if (handle.Init(&file)) handle.DeleteLicense(key_set_id_);
return sts;
}
@@ -342,51 +318,51 @@ CdmSessionId CdmSession::GenerateSessionId() {
return SESSION_ID_PREFIX + IntToString(++session_num);
}
bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
if (!key_set_id) {
LOGW("CdmSession::GenerateKeySetId: key set id destination not provided");
return false;
}
CdmSessionId CdmSession::GenerateKeySetId(CdmInitData& pssh_data) {
Clock clock;
int64_t current_time = clock.GetCurrentTime();
std::string key_set_id;
std::vector<uint8_t> random_data(
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
while (key_set_id.empty()) {
int random = rand();
File file;
DeviceFiles handle;
if (!handle.Init(&file)) return false;
std::vector<uint8_t> hash(SHA256_DIGEST_LENGTH, 0);
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, pssh_data.data(), pssh_data.size());
SHA256_Update(&sha256, &current_time, sizeof(int64_t));
SHA256_Update(&sha256, &random, sizeof(random));
SHA256_Final(&hash[0], &sha256);
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
hash[i%(SHA256_DIGEST_LENGTH/4)] ^= hash[i];
}
hash.resize(SHA256_DIGEST_LENGTH/4);
key_set_id = KEY_SET_ID_PREFIX + b2a_hex(hash);
while (key_set_id->empty()) {
if (!crypto_session_->GetRandom(&random_data[0], random_data.size()))
return false;
if (DeviceFiles::LicenseExists(key_set_id)) { // key set collision
key_set_id.clear();
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
// key set collision
if (handle.LicenseExists(*key_set_id)) {
key_set_id->clear();
}
}
return key_set_id;
return true;
}
bool CdmSession::LoadDeviceCertificate(std::string* certificate,
std::string* wrapped_key) {
return DeviceFiles::RetrieveCertificate(certificate,
wrapped_key);
File file;
DeviceFiles handle;
if (!handle.Init(&file)) return false;
return handle.RetrieveCertificate(certificate, wrapped_key);
}
bool CdmSession::StoreLicense(bool active) {
DeviceFiles::LicenseState state = DeviceFiles::kLicenseStateReleasing;
if (active)
state = DeviceFiles::kLicenseStateActive;
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
File file;
DeviceFiles handle;
if (!handle.Init(&file)) return false;
return DeviceFiles::StoreLicense(key_set_id_, state, offline_pssh_data_,
offline_key_request_, offline_key_response_,
offline_key_renewal_request_,
offline_key_renewal_response_,
offline_release_server_url_);
return handle.StoreLicense(
key_set_id_, state, offline_pssh_data_, offline_key_request_,
offline_key_response_, offline_key_renewal_request_,
offline_key_renewal_response_, offline_release_server_url_);
}
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
@@ -407,22 +383,16 @@ void CdmSession::OnTimerEvent() {
if (event_occurred) {
for (CdmEventListenerIter iter = listeners_.begin();
iter != listeners_.end(); ++iter) {
CdmSessionId id = (*iter)->session_id();
if (id.empty() || (id.compare(session_id_) == 0)) {
(*iter)->onEvent(session_id_, event);
}
(*iter)->onEvent(session_id_, event);
}
}
}
void CdmSession::OnKeyReleaseEvent(CdmKeySetId key_set_id) {
if (key_set_id_.compare(key_set_id) == 0) {
void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
if (key_set_id_ == key_set_id) {
for (CdmEventListenerIter iter = listeners_.begin();
iter != listeners_.end(); ++iter) {
CdmSessionId id = (*iter)->session_id();
if (id.empty() || (id.compare(session_id_) == 0)) {
(*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT);
}
iter != listeners_.end(); ++iter) {
(*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT);
}
}
}

View File

@@ -0,0 +1,229 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "certificate_provisioning.h"
#include "device_files.h"
#include "file_store.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "string_conversions.h"
namespace {
const std::string kDefaultProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
}
namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::ClientIdentification;
using video_widevine_server::sdk::ProvisioningRequest;
using video_widevine_server::sdk::ProvisioningResponse;
using video_widevine_server::sdk::SignedProvisioningMessage;
/*
* This function converts SignedProvisioningRequest into base64 string.
* It then wraps it in JSON format expected by the Apiary frontend.
* Apiary requires the base64 encoding to replace '+' with minus '-',
* and '/' with underscore '_'; opposite to stubby's.
*
* Returns the JSON formated string in *request. The JSON string will be
* appended as a query parameter, i.e. signedRequest=<base 64 encoded
* SignedProvisioningRequest>. All base64 '=' padding chars must be removed.
*
* The JSON formated request takes the following format:
*
* base64 encoded message
*/
void CertificateProvisioning::ComposeJsonRequestAsQueryString(
const std::string& message,
CdmProvisioningRequest* request) {
// Performs base64 encoding for message
std::vector<uint8_t> message_vector(message.begin(), message.end());
std::string message_b64 = Base64SafeEncodeNoPad(message_vector);
request->assign(message_b64);
}
/*
* Composes a device provisioning request and output the request in JSON format
* in *request. It also returns the default url for the provisioning server
* in *default_url.
*
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
CdmProvisioningRequest* request,
std::string* default_url) {
default_url->assign(kDefaultProvisioningServerUrl);
if (!crypto_session_.Open()) {
LOGE("GetProvisioningRequest: fails to create a crypto session");
return UNKNOWN_ERROR;
}
// Prepares device provisioning request.
ProvisioningRequest provisioning_request;
ClientIdentification* client_id = provisioning_request.mutable_client_id();
client_id->set_type(ClientIdentification::KEYBOX);
std::string token;
if (!crypto_session_.GetToken(&token)) {
LOGE("GetProvisioningRequest: fails to get token");
return UNKNOWN_ERROR;
}
client_id->set_token(token);
uint32_t nonce;
if (!crypto_session_.GenerateNonce(&nonce)) {
LOGE("GetProvisioningRequest: fails to generate a nonce");
return UNKNOWN_ERROR;
}
// The provisioning server does not convert the nonce to uint32_t, it just
// passes the binary data to the response message.
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
provisioning_request.set_nonce(the_nonce);
std::string serialized_message;
provisioning_request.SerializeToString(&serialized_message);
// Derives signing and encryption keys and constructs signature.
std::string request_signature;
if (!crypto_session_.PrepareRequest(serialized_message, true,
&request_signature)) {
LOGE("GetProvisioningRequest: fails to prepare request");
return UNKNOWN_ERROR;
}
if (request_signature.empty()) {
LOGE("GetProvisioningRequest: request signature is empty");
return UNKNOWN_ERROR;
}
SignedProvisioningMessage signed_provisioning_msg;
signed_provisioning_msg.set_message(serialized_message);
signed_provisioning_msg.set_signature(request_signature);
std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request);
// Converts request into JSON string
ComposeJsonRequestAsQueryString(serialized_request, request);
return NO_ERROR;
}
/*
* Parses the input json_str and locates substring using start_substr and
* end_stubstr. The found base64 substring is then decoded and returns
* in *result.
*
* Returns true for success and false if fails.
*/
bool CertificateProvisioning::ParseJsonResponse(
const CdmProvisioningResponse& json_str,
const std::string& start_substr,
const std::string& end_substr,
std::string* result) {
std::string b64_string;
size_t start = json_str.find(start_substr);
if (start == json_str.npos) {
LOGE("ParseJsonResponse: cannot find start substring");
return false;
}
size_t end = json_str.find(end_substr, start + start_substr.length());
if (end == json_str.npos) {
LOGE("ParseJsonResponse cannot locate end substring");
return false;
}
size_t b64_string_size = end - start - start_substr.length();
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
// Decodes base64 substring and returns it in *result
std::vector<uint8_t> result_vector = Base64SafeDecode(b64_string);
result->assign(result_vector.begin(), result_vector.end());
return true;
}
/*
* The response message consists of a device certificate and the device RSA key.
* The device RSA key is stored in the T.E.E. The device certificate is stored
* in the device.
*
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
CdmProvisioningResponse& response) {
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string serialized_signed_response;
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
&serialized_signed_response)) {
LOGE("Fails to extract signed serialized response from JSON response");
return UNKNOWN_ERROR;
}
// Authenticates provisioning response using D1s (server key derived from
// the provisioing request's input). Validate provisioning response and
// stores private device RSA key and certificate.
SignedProvisioningMessage signed_response;
if (!signed_response.ParseFromString(serialized_signed_response)) {
LOGE("HandleProvisioningResponse: fails to parse signed response");
return UNKNOWN_ERROR;
}
if (!signed_response.has_signature() || !signed_response.has_message()) {
LOGE("HandleProvisioningResponse: signature or message not found");
return UNKNOWN_ERROR;
}
const std::string& signed_message = signed_response.message();
ProvisioningResponse provisioning_response;
if (!provisioning_response.ParseFromString(signed_message)) {
LOGE("HandleProvisioningResponse: Fails to parse signed message");
return UNKNOWN_ERROR;
}
if (!provisioning_response.has_device_rsa_key()) {
LOGE("HandleProvisioningResponse: key not found");
return UNKNOWN_ERROR;
}
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
const std::string& nonce = provisioning_response.nonce();
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
const std::string& signature = signed_response.signature();
std::string wrapped_rsa_key;
if (!crypto_session_.RewrapDeviceRSAKey(signed_message,
signature,
nonce,
enc_rsa_key,
rsa_key_iv,
&wrapped_rsa_key)){
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
return UNKNOWN_ERROR;
}
crypto_session_.Close();
const std::string& device_certificate =
provisioning_response.device_certificate();
File file;
DeviceFiles handle;
if (!handle.Init(&file)) {
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
return UNKNOWN_ERROR;
}
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
} // namespace wvcdm

View File

@@ -1,286 +0,0 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Crypto - wrapper classes for OEMCrypto interface
//
#include "crypto_engine.h"
#include <arpa/inet.h> // TODO(fredgc): Add ntoh to wv_cdm_utilities.h
#include <iostream>
#include <vector>
#include "log.h"
#include "OEMCryptoCENC.h"
#include "properties.h"
#include "wv_cdm_constants.h"
namespace wvcdm {
CryptoEngine* CryptoEngine::crypto_engine_ = NULL;
Lock CryptoEngine::crypto_engine_lock_;
// wrapper classes for OEMCrypto interface
// CryptoEngine -- top-level interface
// CryptoSession -- session-specific interface
// CryptoKey -- key interface
// CryptoEngine methods
CryptoEngine::CryptoEngine() : initialized_(false) {}
CryptoEngine::~CryptoEngine() {
if (initialized_) {
Terminate();
}
CryptoSessionMap::iterator i(sessions_.begin());
for (; i != sessions_.end(); ++i)
delete i->second;
sessions_.clear();
}
// get the instance of OEMCrypto Client
CryptoEngine* CryptoEngine::GetInstance() {
if (NULL == crypto_engine_) {
crypto_engine_ = CreateSingleton();
}
return crypto_engine_;
}
CryptoEngine* CryptoEngine::CreateSingleton() {
AutoLock auto_lock(crypto_engine_lock_);
if (NULL == crypto_engine_) {
crypto_engine_ = new CryptoEngine;
}
return crypto_engine_;
}
void CryptoEngine::DeleteInstance() {
if (NULL != crypto_engine_) {
delete crypto_engine_;
LOGV("CryptoEngine::DeleteInstance");
crypto_engine_ = NULL;
}
}
bool CryptoEngine::Init() {
LOGV("CryptoEngine::Init: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
OEMCryptoResult result = OEMCrypto_Initialize();
initialized_ = (OEMCrypto_SUCCESS == result);
}
return initialized_;
}
bool CryptoEngine::Terminate() {
DestroySessions();
LOGV("CryptoEngine::Terminate: Lock");
AutoLock auto_lock(crypto_lock_);
OEMCryptoResult result = OEMCrypto_Terminate();
if (OEMCrypto_SUCCESS == result) {
initialized_ = false;
}
return !initialized_;
}
bool CryptoEngine::ValidateKeybox() {
LOGV("CryptoEngine::ValidateKeybox: Lock");
AutoLock auto_lock(crypto_lock_);
OEMCryptoResult result = OEMCrypto_IsKeyboxValid();
return (OEMCrypto_SUCCESS == result);
}
CryptoSession* CryptoEngine::CreateSession(const CdmSessionId& session_id) {
LOGV("CryptoEngine::CreateSession: SLock");
AutoLock auto_lock(sessions_lock_);
if (0 == sessions_.size()) {
if (!Init()) {
return NULL;
}
}
CryptoSessionMap::iterator it = sessions_.find(session_id);
if (it != sessions_.end()) {
LOGE("CryptoEngine::CreateSession : Duplicate session ID.");
return NULL;
}
CryptoSession* new_session = new CryptoSession(session_id);
if (!new_session) {
return NULL;
}
if (!new_session->Open()) {
delete new_session;
return NULL;
}
sessions_[session_id] = new_session;
return new_session;
}
CryptoSession* CryptoEngine::FindSessionInternal(
const CdmSessionId& session_id) {
// must hold sessions_lock_
CryptoSessionMap::iterator it = sessions_.find(session_id);
if (it != sessions_.end()) {
return it->second;
}
return NULL;
}
CryptoSession* CryptoEngine::FindSession(const CdmSessionId& session_id) {
LOGV("CryptoEngine::FindSession: SLock");
AutoLock auto_lock(sessions_lock_);
return FindSessionInternal(session_id);
}
bool CryptoEngine::DestroySession(const CdmSessionId& session_id) {
LOGV("CryptoEngine::DestroySession: SLock");
AutoLock auto_lock(sessions_lock_);
if (0 == sessions_.size()) {
return false;
}
CryptoSession* session = FindSessionInternal(session_id);
if (session) {
delete session;
sessions_.erase(session_id);
return true;
} else {
return false;
}
}
bool CryptoEngine::DestroySessions() {
for (CryptoSessionMap::iterator it = sessions_.begin();
it != sessions_.end(); ++it) {
delete it->second;
}
sessions_.clear();
return true;
}
bool CryptoEngine::GetToken(std::string* token) {
LOGV("CryptoEngine::GetToken: Lock");
AutoLock auto_lock(crypto_lock_);
if (!token) {
LOGE("CryptoEngine::GetToken : No token passed to method.");
return false;
}
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
size_t bufSize = sizeof(buf);
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
token->assign((const char*)buf, bufSize);
return true;
}
CryptoEngine::SecurityLevel CryptoEngine::GetSecurityLevel() {
if (!Init())
return kSecurityLevelUnknown;
LOGV("CryptoEngine::GetSecurityLevel: Lock");
AutoLock auto_lock(crypto_lock_);
std::string security_level = OEMCrypto_SecurityLevel();
if ((security_level.size() != 2) ||
(security_level.at(0) != 'L')) {
return kSecurityLevelUnknown;
}
switch (security_level.at(1)) {
case '1': return kSecurityLevelL1;
case '2': return kSecurityLevelL2;
case '3': return kSecurityLevelL3;
default : return kSecurityLevelUnknown;
}
return kSecurityLevelUnknown;
}
bool CryptoEngine::GetDeviceUniqueId(std::string* deviceId) {
if (!Init())
return false;
if (!deviceId) {
LOGE("CryptoEngine::GetDeviceUniqueId : No buffer passed to method.");
return false;
}
LOGV("CryptoEngine::GetDeviceUniqueId: Lock");
AutoLock auto_lock(crypto_lock_);
std::vector<uint8_t> id;
size_t idLength = 32;
id.resize(idLength);
OEMCryptoResult sts = OEMCrypto_GetDeviceID(&id[0], &idLength);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
*deviceId = reinterpret_cast<const char*>(&id[0]);
return true;
}
bool CryptoEngine::GetSystemId(uint32_t* systemId) {
if (!Init())
return false;
if (!systemId) {
LOGE("CryptoEngine::GetSystemId : No buffer passed to method.");
return false;
}
LOGV("CryptoEngine::GetSystemId: Lock");
AutoLock auto_lock(crypto_lock_);
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
size_t bufSize = sizeof(buf);
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
// Decode 32-bit int encoded as network-byte-order byte array starting at
// index 4.
uint32_t* id = reinterpret_cast<uint32_t*>(&buf[4]);
*systemId = ntohl(*id);
return true;
}
bool CryptoEngine::GetProvisioningId(std::string* provisioningId) {
if (!Init())
return false;
if (!provisioningId) {
LOGE("CryptoEngine::GetProvisioningId : No buffer passed to method.");
return false;
}
LOGV("CryptoEngine::GetProvisioningId: Lock");
AutoLock auto_lock(crypto_lock_);
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
size_t bufSize = sizeof(buf);
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
provisioningId->assign(reinterpret_cast<char*>(&buf[8]), 16);
return true;
}
}; // namespace wvcdm

View File

@@ -5,9 +5,10 @@
#include "crypto_session.h"
#include <arpa/inet.h> // TODO(fredgc): Add ntoh to wv_cdm_utilities.h
#include <iostream>
#include "crypto_engine.h"
#include "crypto_key.h"
#include "log.h"
// TODO(gmorgan,jtinker): decide if OEMCryptoCENC is needed here.
#include "OEMCryptoCENC.h"
@@ -21,95 +22,226 @@ std::string EncodeUint32(unsigned int u) {
std::string s;
s.append(1, (u >> 24) & 0xFF);
s.append(1, (u >> 16) & 0xFF);
s.append(1, (u >> 8) & 0xFF);
s.append(1, (u >> 0) & 0xFF);
s.append(1, (u >> 8) & 0xFF);
s.append(1, (u >> 0) & 0xFF);
return s;
}
}
namespace wvcdm {
// wrapper classes for OEMCrypto interface
// CryptoEngine -- top-level interface
// CryptoSession -- session-specific interface
// CryptoKey -- key interface
Lock CryptoSession::crypto_lock_;
bool CryptoSession::initialized_ = false;
int CryptoSession::session_count_ = 0;
// CryptoSession methods
CryptoSession::CryptoSession() :
valid_(false),
open_(false),
is_destination_buffer_type_valid_(false) {}
CryptoSession::CryptoSession(const std::string& sname) :
valid_(true),
open_(false),
cdm_session_id_(sname),
is_destination_buffer_type_valid_(false) {}
CryptoSession::CryptoSession()
: open_(false), is_destination_buffer_type_valid_(false) {
Init();
}
CryptoSession::~CryptoSession() {
if (open_) {
Close();
}
LOGV("CryptoSession::dtor: SLock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
crypto_engine->sessions_.erase(cdm_session_id_);
if (0 == crypto_engine->sessions_.size()) {
crypto_engine->DeleteInstance();
Terminate();
}
void CryptoSession::Init() {
LOGV("CryptoSession::Init");
AutoLock auto_lock(crypto_lock_);
session_count_ += 1;
if (initialized_) return;
OEMCryptoResult sts = OEMCrypto_Initialize();
if (OEMCrypto_SUCCESS != sts) {
LOGE("OEMCrypto_Initialize failed: %d", sts);
return;
}
initialized_ = true;
}
void CryptoSession::Terminate() {
LOGV("CryptoSession::Terminate");
AutoLock auto_lock(crypto_lock_);
session_count_ -= 1;
if (session_count_ > 0 || !initialized_) return;
OEMCryptoResult sts = OEMCrypto_Terminate();
if (OEMCrypto_SUCCESS != sts) {
LOGE("OEMCrypto_Terminate failed: %d", sts);
}
initialized_ = false;
}
bool CryptoSession::ValidateKeybox() {
LOGV("CryptoSession::ValidateKeybox: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
}
OEMCryptoResult result = OEMCrypto_IsKeyboxValid();
return (OEMCrypto_SUCCESS == result);
}
bool CryptoSession::GetToken(std::string* token) {
if (!token) {
LOGE("CryptoSession::GetToken : No token passed to method.");
return false;
}
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
size_t bufSize = sizeof(buf);
LOGV("CryptoSession::GetToken: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
}
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
token->assign((const char*)buf, (size_t)bufSize);
return true;
}
CryptoSession::SecurityLevel CryptoSession::GetSecurityLevel() {
LOGV("CryptoSession::GetSecurityLevel: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return kSecurityLevelUninitialized;
}
std::string security_level = OEMCrypto_SecurityLevel();
if ((security_level.size() != 2) || (security_level.at(0) != 'L')) {
return kSecurityLevelUnknown;
}
CryptoKeyMap::iterator i(keys_.begin());
for (; i != keys_.end(); ++i)
delete i->second;
keys_.clear();
switch (security_level.at(1)) {
case '1':
return kSecurityLevelL1;
case '2':
return kSecurityLevelL2;
case '3':
return kSecurityLevelL3;
default:
return kSecurityLevelUnknown;
}
return kSecurityLevelUnknown;
}
bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
if (!device_id) {
LOGE("CryptoSession::GetDeviceUniqueId : No buffer passed to method.");
return false;
}
std::vector<uint8_t> id;
size_t id_length = 32;
id.resize(id_length);
LOGV("CryptoSession::GetDeviceUniqueId: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
}
OEMCryptoResult sts = OEMCrypto_GetDeviceID(&id[0], &id_length);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
*device_id = reinterpret_cast<const char*>(&id[0]);
return true;
}
bool CryptoSession::GetSystemId(uint32_t* system_id) {
if (!system_id) {
LOGE("CryptoSession::GetSystemId : No buffer passed to method.");
return false;
}
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
size_t buf_size = sizeof(buf);
LOGV("CryptoSession::GetSystemId: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
}
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
// Decode 32-bit int encoded as network-byte-order byte array starting at
// index 4.
uint32_t* id = reinterpret_cast<uint32_t*>(&buf[4]);
*system_id = ntohl(*id);
return true;
}
bool CryptoSession::GetProvisioningId(std::string* provisioning_id) {
if (!provisioning_id) {
LOGE("CryptoSession::GetProvisioningId : No buffer passed to method.");
return false;
}
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
size_t buf_size = sizeof(buf);
LOGV("CryptoSession::GetProvisioningId: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
}
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
provisioning_id->assign(reinterpret_cast<char*>(&buf[8]), 16);
return true;
}
bool CryptoSession::Open() {
LOGV("CryptoSession::Open: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
AutoLock auto_lock(crypto_lock_);
if (!initialized_) return false;
if (open_) return true;
OEMCrypto_SESSION sid;
OEMCryptoResult sts;
if (open_)
return false;
sts = OEMCrypto_OpenSession(&sid);
if (OEMCrypto_SUCCESS != sts) {
open_ = false;
} else {
if (OEMCrypto_SUCCESS == OEMCrypto_OpenSession(&sid)) {
oec_session_id_ = static_cast<CryptoSessionId>(sid);
LOGV("OpenSession: id= %ld", (uint32_t) oec_session_id_);
LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_);
open_ = true;
}
return open_;
}
void CryptoSession::Close() {
LOGV("CryptoSession::Close: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
LOGV("CloseSession: id=%ld open=%s", (uint32_t) oec_session_id_, open_? "true" : "false") ;
if (open_) {
OEMCryptoResult sts = OEMCrypto_CloseSession(oec_session_id_);
if (OEMCrypto_SUCCESS == sts) {
open_ = false;
}
LOGV("CloseSession: id=%ld open=%s", (uint32_t)oec_session_id_,
open_ ? "true" : "false");
AutoLock auto_lock(crypto_lock_);
if (!open_) return;
if (OEMCrypto_SUCCESS == OEMCrypto_CloseSession(oec_session_id_)) {
open_ = false;
}
}
void CryptoSession::GenerateRequestId(std::string& req_id_str) {
LOGV("CryptoSession::GenerateRequestId: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
AutoLock auto_lock(crypto_lock_);
// TODO(gmorgan): Get unique ID from OEMCrypto
req_id_str.assign("987654321");
}
bool CryptoSession::PrepareRequest(const std::string& message,
std::string* signature,
bool is_provisioning) {
bool is_provisioning,
std::string* signature) {
LOGV("CryptoSession::PrepareRequest: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
AutoLock auto_lock(crypto_lock_);
if (!signature) {
LOGE("CryptoSession::PrepareRequest : No output destination provided.");
@@ -117,15 +249,11 @@ bool CryptoSession::PrepareRequest(const std::string& message,
}
if (!Properties::use_certificates_as_identification() || is_provisioning) {
if (!GenerateDerivedKeys(message))
return false;
if (!GenerateDerivedKeys(message)) return false;
if (!GenerateSignature(message, signature, false))
return false;
}
else {
if (!GenerateSignature(message, signature, true))
return false;
if (!GenerateSignature(message, false, signature)) return false;
} else {
if (!GenerateSignature(message, true, signature)) return false;
}
return true;
@@ -134,15 +262,15 @@ bool CryptoSession::PrepareRequest(const std::string& message,
bool CryptoSession::PrepareRenewalRequest(const std::string& message,
std::string* signature) {
LOGV("CryptoSession::PrepareRenewalRequest: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
AutoLock auto_lock(crypto_lock_);
if (!signature) {
LOGE("CryptoSession::PrepareRenewalRequest : No output destination provided.");
LOGE("CryptoSession::PrepareRenewalRequest : No output destination "
"provided.");
return false;
}
if (!GenerateSignature(message, signature, false)) {
if (!GenerateSignature(message, false, signature)) {
return false;
}
@@ -162,13 +290,14 @@ void CryptoSession::GenerateMacContext(const std::string& input_context,
deriv_context->assign(kSigningKeyLabel);
deriv_context->append(1, '\0');
deriv_context->append(input_context);
deriv_context->append(EncodeUint32(kSigningKeySizeBits*2));
deriv_context->append(EncodeUint32(kSigningKeySizeBits * 2));
}
void CryptoSession::GenerateEncryptContext(const std::string& input_context,
std::string* deriv_context) {
if (!deriv_context) {
LOGE("CryptoSession::GenerateEncryptContext : No output destination provided.");
LOGE("CryptoSession::GenerateEncryptContext : No output destination "
"provided.");
return;
}
@@ -193,12 +322,10 @@ size_t CryptoSession::GetOffset(std::string message, std::string field) {
bool CryptoSession::LoadKeys(const std::string& message,
const std::string& signature,
const std::string& mac_key_iv,
const std::string& mac_key,
int num_keys,
const std::string& mac_key, int num_keys,
const CryptoKey* key_array) {
LOGV("CryptoSession::LoadKeys: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
AutoLock auto_lock(crypto_lock_);
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* enc_mac_key = NULL;
@@ -208,7 +335,7 @@ bool CryptoSession::LoadKeys(const std::string& message,
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
}
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) {
const CryptoKey* ki = &key_array[i];
OEMCrypto_KeyObject* ko = &load_key_array[i];
ko->key_id = msg + GetOffset(message, ki->key_id());
@@ -219,30 +346,28 @@ bool CryptoSession::LoadKeys(const std::string& message,
if (ki->HasKeyControl()) {
ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv());
ko->key_control = msg + GetOffset(message, ki->key_control());
}
else {
LOGE("For key %d: XXX key has no control block. size=%d", i, ki->key_control().size());
} else {
LOGE("For key %d: XXX key has no control block. size=%d", i,
ki->key_control().size());
ko->key_control_iv = NULL;
ko->key_control = NULL;
}
}
LOGV("LoadKeys: id=%ld", (uint32_t) oec_session_id_);
return (OEMCrypto_SUCCESS == OEMCrypto_LoadKeys(
oec_session_id_, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(), enc_mac_key_iv, enc_mac_key,
num_keys, &load_key_array[0]));
LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_);
return (OEMCrypto_SUCCESS ==
OEMCrypto_LoadKeys(oec_session_id_, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(), enc_mac_key_iv, enc_mac_key,
num_keys, &load_key_array[0]));
}
bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
LOGV("CryptoSession::LoadKeys: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->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_);
OEMCryptoResult sts = OEMCrypto_LoadDeviceRSAKey(
oec_session_id_,
reinterpret_cast<const uint8_t*>(wrapped_key.data()),
oec_session_id_, reinterpret_cast<const uint8_t*>(wrapped_key.data()),
wrapped_key.size());
if (OEMCrypto_SUCCESS != sts) {
@@ -254,16 +379,14 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
}
bool CryptoSession::RefreshKeys(const std::string& message,
const std::string& signature,
int num_keys,
const std::string& signature, int num_keys,
const CryptoKey* key_array) {
LOGV("CryptoSession::RefreshKeys: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
AutoLock auto_lock(crypto_lock_);
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
std::vector<OEMCrypto_KeyRefreshObject> load_key_array(num_keys);
for (int i=0; i<num_keys; ++i) {
for (int i = 0; i < num_keys; ++i) {
const CryptoKey* ki = &key_array[i];
OEMCrypto_KeyRefreshObject* ko = &load_key_array[i];
if (ki->key_id().empty()) {
@@ -278,30 +401,28 @@ bool CryptoSession::RefreshKeys(const std::string& message,
ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv());
}
ko->key_control = msg + GetOffset(message, ki->key_control());
}
else {
} else {
ko->key_control_iv = NULL;
ko->key_control = NULL;
}
}
LOGV("RefreshKeys: id=%ld", static_cast<uint32_t>(oec_session_id_));
return (OEMCrypto_SUCCESS == OEMCrypto_RefreshKeys(
oec_session_id_, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(),
num_keys, &load_key_array[0]));
return (
OEMCrypto_SUCCESS ==
OEMCrypto_RefreshKeys(oec_session_id_, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(), num_keys, &load_key_array[0]));
}
bool CryptoSession::SelectKey(const std::string& key_id) {
LOGV("CryptoSession::SelectKey: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
AutoLock auto_lock(crypto_lock_);
const uint8_t* key_id_string =
reinterpret_cast<const uint8_t*>(key_id.data());
LOGV("SelectKey: id=%ld", static_cast<uint32_t>(oec_session_id_));
OEMCryptoResult sts = OEMCrypto_SelectKey(oec_session_id_, key_id_string,
key_id.size());
OEMCryptoResult sts =
OEMCrypto_SelectKey(oec_session_id_, key_id_string, key_id.size());
if (OEMCrypto_SUCCESS != sts) {
return false;
}
@@ -314,7 +435,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message) {
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t) oec_session_id_);
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts = OEMCrypto_GenerateDerivedKeys(
oec_session_id_,
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
@@ -337,10 +458,9 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t) oec_session_id_);
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts = OEMCrypto_DeriveKeysFromSessionKey(
oec_session_id_,
reinterpret_cast<const uint8_t*>(session_key.data()),
oec_session_id_, reinterpret_cast<const uint8_t*>(session_key.data()),
session_key.size(),
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
@@ -355,51 +475,43 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
return true;
}
bool CryptoSession::GenerateSignature(const std::string& message,
std::string* signature,
bool use_rsa) {
LOGV("GenerateSignature: id=%ld", (uint32_t) oec_session_id_);
if (!signature)
return false;
bool CryptoSession::GenerateSignature(const std::string& message, bool use_rsa,
std::string* signature) {
LOGV("GenerateSignature: id=%ld", (uint32_t)oec_session_id_);
if (!signature) return false;
size_t length = 0;
OEMCryptoResult sts;
OEMCryptoResult sts = OEMCrypto_SUCCESS;
if (use_rsa) {
sts = OEMCrypto_GenerateRSASignature(
oec_session_id_,
reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
NULL,
&length);
}
else {
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(), NULL, &length);
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
LOGD("GenerateSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
return false;
}
} else {
length = kSignatureSize;
// TODO(gmorgan,kqyang): Use OEMCrypto_GenerateSignature to determine
// length after marvell fixes their implementation.
/*
sts = OEMCrypto_GenerateSignature(
oec_session_id_,
reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
NULL,
&length);
}
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
return false;
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(), NULL, &length);
*/
}
signature->resize(length);
if (use_rsa) {
sts = OEMCrypto_GenerateRSASignature(
oec_session_id_,
reinterpret_cast<const uint8_t*>(message.data()),
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length);
}
else {
} else {
sts = OEMCrypto_GenerateSignature(
oec_session_id_,
reinterpret_cast<const uint8_t*>(message.data()),
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length);
@@ -410,29 +522,24 @@ bool CryptoSession::GenerateSignature(const std::string& message,
return false;
}
// TODO(fredgc): remove in K, when L1 library reports correct length.
// TODO(fredgc): b/8878371
// remove in K, when L1 library reports correct length.
signature->resize(length);
return true;
}
CdmResponseType CryptoSession::Decrypt(bool is_encrypted,
bool is_secure,
const uint8_t* encrypt_buffer,
size_t encrypt_length,
const std::vector<uint8_t>& iv,
size_t block_offset,
void* decrypt_buffer,
size_t decrypt_buffer_offset,
bool is_video) {
CdmResponseType CryptoSession::Decrypt(
bool is_encrypted, bool is_secure, const uint8_t* encrypt_buffer,
size_t encrypt_length, const std::vector<uint8_t>& iv, size_t block_offset,
void* decrypt_buffer, size_t decrypt_buffer_offset, bool is_video) {
if (!is_destination_buffer_type_valid_) {
if (!SetDestinationBufferType())
return UNKNOWN_ERROR;
if (!SetDestinationBufferType()) return UNKNOWN_ERROR;
}
OEMCrypto_DestBufferDesc buffer_descriptor;
buffer_descriptor.type =
is_secure ? destination_buffer_type_: OEMCrypto_BufferType_Clear;
is_secure ? destination_buffer_type_ : OEMCrypto_BufferType_Clear;
switch (buffer_descriptor.type) {
case OEMCrypto_BufferType_Clear:
@@ -452,13 +559,8 @@ CdmResponseType CryptoSession::Decrypt(bool is_encrypted,
}
OEMCryptoResult sts = OEMCrypto_DecryptCTR(
oec_session_id_,
encrypt_buffer,
encrypt_length,
is_encrypted,
&iv[0],
block_offset,
&buffer_descriptor,
oec_session_id_, encrypt_buffer, encrypt_length, is_encrypted, &iv[0],
block_offset, &buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
if (OEMCrypto_SUCCESS != sts) {
@@ -474,29 +576,23 @@ bool CryptoSession::GenerateNonce(uint32_t* nonce) {
}
LOGV("CryptoSession::GenerateNonce: Lock");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
return(OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(oec_session_id_, nonce));
AutoLock auto_lock(crypto_lock_);
return (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(oec_session_id_, nonce));
}
bool CryptoSession::SetDestinationBufferType() {
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (Properties::oem_crypto_use_secure_buffers()) {
if (crypto_engine->GetSecurityLevel() == CryptoEngine::kSecurityLevelL1) {
if (GetSecurityLevel() == CryptoSession::kSecurityLevelL1) {
destination_buffer_type_ = OEMCrypto_BufferType_Secure;
}
else {
} else {
destination_buffer_type_ = OEMCrypto_BufferType_Clear;
}
}
else if (Properties::oem_crypto_use_fifo()) {
} else if (Properties::oem_crypto_use_fifo()) {
destination_buffer_type_ = OEMCrypto_BufferType_Direct;
}
else if (Properties::oem_crypto_use_userspace_buffers()) {
} else if (Properties::oem_crypto_use_userspace_buffers()) {
destination_buffer_type_ = OEMCrypto_BufferType_Clear;
}
else {
} else {
return false;
}
@@ -508,14 +604,10 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
const std::string& signature,
const std::string& nonce,
const std::string& enc_rsa_key,
size_t enc_rsa_key_length,
const std::string& rsa_key_iv,
std::string* wrapped_rsa_key) {
LOGV("CryptoSession::RewrapDeviceRSAKey: Lock+++");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
AutoLock auto_lock(crypto_engine->crypto_lock_);
LOGV("crypto session id=%ld", static_cast<uint32_t>(oec_session_id_));
LOGD("CryptoSession::RewrapDeviceRSAKey, session id=%ld",
static_cast<uint32_t>(oec_session_id_));
const uint8_t* signed_msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* msg_rsa_key = NULL;
@@ -524,21 +616,17 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
if (enc_rsa_key.size() >= MAC_KEY_SIZE && rsa_key_iv.size() >= KEY_IV_SIZE) {
msg_rsa_key = signed_msg + GetOffset(message, enc_rsa_key);
msg_rsa_key_iv = signed_msg + GetOffset(message, rsa_key_iv);
msg_nonce = reinterpret_cast<const uint32_t*>(
signed_msg + GetOffset(message, nonce));
msg_nonce = reinterpret_cast<const uint32_t*>(signed_msg +
GetOffset(message, nonce));
}
// Gets wrapped_rsa_key_length by passing NULL as uint8_t* wrapped_rsa_key
// and 0 as wrapped_rsa_key_length.
size_t wrapped_rsa_key_length = 0;
OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey(
oec_session_id_,
signed_msg, message.size(),
oec_session_id_, signed_msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
msg_nonce,
msg_rsa_key, enc_rsa_key_length,
msg_rsa_key_iv,
NULL,
msg_nonce, msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv, NULL,
&wrapped_rsa_key_length);
if (status != OEMCrypto_ERROR_SHORT_BUFFER) {
LOGE("OEMCrypto_RewrapDeviceRSAKey fails to get wrapped_rsa_key_length");
@@ -547,20 +635,16 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
wrapped_rsa_key->resize(wrapped_rsa_key_length);
status = OEMCrypto_RewrapDeviceRSAKey(
oec_session_id_,
signed_msg,
message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(),
msg_nonce,
msg_rsa_key,
enc_rsa_key_length,
msg_rsa_key_iv,
reinterpret_cast<uint8_t*>(const_cast<char*>(wrapped_rsa_key->data())),
oec_session_id_, signed_msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
msg_nonce, msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv,
reinterpret_cast<uint8_t*>(&(*wrapped_rsa_key)[0]),
&wrapped_rsa_key_length);
// TODO(fredgc): remove in K, when L1 library reports correct length.
// TODO(fredgc): b/8878371
// remove in K, when L1 library reports correct length.
wrapped_rsa_key->resize(wrapped_rsa_key_length);
if (OEMCrypto_SUCCESS != status) {
LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status);
return false;
@@ -569,4 +653,15 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
return true;
}
bool CryptoSession::GetRandom(uint8_t* random_data, size_t data_length) {
OEMCryptoResult sts = OEMCrypto_GetRandom(random_data, data_length);
if (sts != OEMCrypto_SUCCESS) {
LOGE("OEMCrypto_GetRandom fails with %d", sts);
return false;
}
return true;
}
}; // namespace wvcdm

View File

@@ -4,21 +4,12 @@
#include <cstring>
#include <string>
#include <sstream>
#include <unistd.h>
#include "device_files.pb.h"
#include "file_store.h"
#include "log.h"
#include "openssl/sha.h"
namespace wvcdm {
// TODO(rfrias): Make this work for non-unix paths
const char* DeviceFiles::kBasePath = "/data/mediadrm/IDM";
const char* DeviceFiles::kPathDelimiter = "/";
const char* DeviceFiles::kDeviceCertificateFileName = "cert.bin";
const char* DeviceFiles::kLicenseFileNameExt = ".lic";
#include "properties.h"
// Protobuf generated classes.
using video_widevine_client::sdk::DeviceCertificate;
@@ -27,6 +18,23 @@ using video_widevine_client::sdk::License;
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
using video_widevine_client::sdk::License_LicenseState_RELEASING;
namespace {
const char kCertificateFileName[] = "cert.bin";
const char kLicenseFileNameExt[] = ".lic";
} // namespace
namespace wvcdm {
bool DeviceFiles::Init(File* handle) {
file_ = handle;
if (handle == NULL) {
LOGW("DeviceFiles::Init: Invalid file handle parameter");
return false;
}
return true;
}
bool DeviceFiles::StoreCertificate(const std::string& certificate,
const std::string& wrapped_private_key) {
// Fill in file information
@@ -35,7 +43,7 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
file.set_type(video_widevine_client::sdk::File::DEVICE_CERTIFICATE);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
DeviceCertificate *device_certificate = file.mutable_device_certificate();
DeviceCertificate* device_certificate = file.mutable_device_certificate();
device_certificate->set_certificate(certificate);
device_certificate->set_wrapped_private_key(wrapped_private_key);
@@ -49,21 +57,20 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
return false;
}
// File in hashed file data
// Fill in hashed file data
HashedFile hashed_file;
hashed_file.set_file(serialized_string);
hashed_file.set_hash(hash);
hashed_file.SerializeToString(&serialized_string);
return StoreFile(kDeviceCertificateFileName, serialized_string);
return StoreFile(kCertificateFileName, serialized_string);
}
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
std::string* wrapped_private_key) {
std::string serialized_hashed_file;
if (!RetrieveFile(kDeviceCertificateFileName, &serialized_hashed_file))
if (!RetrieveFile(kCertificateFileName, &serialized_hashed_file))
return false;
HashedFile hashed_file;
@@ -244,49 +251,80 @@ bool DeviceFiles::RetrieveLicense(
}
bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt;
return File::Remove(path);
if (!file_) {
LOGW("DeviceFiles::DeleteLicense: Invalid file handle");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(&path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path");
return false;
}
path.append(key_set_id);
path.append(kLicenseFileNameExt);
return file_->Remove(path);
}
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt;
return File::Exists(path);
if (!file_) {
LOGW("DeviceFiles::LicenseExists: Invalid file handle");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(&path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path");
return false;
}
path.append(key_set_id);
path.append(kLicenseFileNameExt);
return file_->Exists(path);
}
bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
if (!hash)
return false;
if (!hash) return false;
hash->resize(SHA256_DIGEST_LENGTH);
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, data.data(), data.size());
SHA256_Final(reinterpret_cast<unsigned char*>(const_cast<char*>(hash->data())), &sha256);
SHA256_Final(reinterpret_cast<unsigned char*>(&(*hash)[0]), &sha256);
return true;
}
bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
if (!name)
if (!file_) {
LOGW("DeviceFiles::StoreFile: Invalid file handle");
return false;
}
std::string path = GetBasePath(kBasePath);
if (!name) {
LOGW("DeviceFiles::StoreFile: Unspecified file name parameter");
return false;
}
if (!File::IsDirectory(path)) {
if (!File::CreateDirectory(path))
return false;
std::string path;
if (!Properties::GetDeviceFilesBasePath(&path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path");
return false;
}
if (!file_->IsDirectory(path)) {
if (!file_->CreateDirectory(path)) return false;
}
path += name;
File file(path, File::kCreate | File::kTruncate | File::kBinary);
if (file.IsBad()) {
if (!file_->Open(path, File::kCreate | File::kTruncate | File::kBinary)) {
LOGW("DeviceFiles::StoreFile: File open failed: %s", path.c_str());
return false;
}
ssize_t bytes = file.Write(data.data(), data.size());
file.Close();
ssize_t bytes = file_->Write(data.data(), data.size());
file_->Close();
if (bytes != static_cast<ssize_t>(data.size())) {
LOGW("DeviceFiles::StoreFile: write failed: %d %d", data.size(), bytes);
@@ -298,36 +336,51 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
}
bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
if (!data)
if (!file_) {
LOGW("DeviceFiles::RetrieveFile: Invalid file handle");
return false;
}
std::string path = GetBasePath(kBasePath) + name;
if (!name) {
LOGW("DeviceFiles::RetrieveFile: Unspecified file name parameter");
return false;
}
if (!File::Exists(path)) {
if (!data) {
LOGW("DeviceFiles::RetrieveFile: Unspecified data parameter");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(&path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path");
return false;
}
path += name;
if (!file_->Exists(path)) {
LOGW("DeviceFiles::RetrieveFile: %s does not exist", path.c_str());
return false;
}
ssize_t bytes = File::FileSize(path);
ssize_t bytes = file_->FileSize(path);
if (bytes <= 0) {
LOGW("DeviceFiles::RetrieveFile: File size invalid: %d", path.c_str());
return false;
}
File file(path, File::kReadOnly | File::kBinary);
if (file.IsBad()) {
if (!file_->Open(path, File::kReadOnly | File::kBinary)) {
LOGW("DeviceFiles::RetrieveFile: File open failed: %s", path.c_str());
return false;
}
data->resize(bytes);
bytes = file.Read(reinterpret_cast<void*>(const_cast<char*>(data->data())),
data->size());
bytes = file_->Read(&(*data)[0], data->size());
file_->Close();
if (bytes != static_cast<ssize_t>(data->size())) {
LOGW("DeviceFiles::StoreFile: write failed: %d %d", data->size(), bytes);
LOGW("DeviceFiles::RetrieveFile: read failed");
return false;
}
@@ -336,11 +389,12 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
return true;
}
std::string DeviceFiles::GetBasePath(const char* dir) {
// TODO(rfrias): Make this work for non-unix paths
std::stringstream ss;
ss << dir << getuid() << kPathDelimiter;
return ss.str();
std::string DeviceFiles::GetCertificateFileName() {
return kCertificateFileName;
}
std::string DeviceFiles::GetLicenseFileNameExtension() {
return kLicenseFileNameExt;
}
} // namespace wvcdm

View File

@@ -4,7 +4,7 @@
#include <vector>
#include "crypto_engine.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "log.h"
#include "policy_engine.h"
@@ -13,13 +13,13 @@
#include "wv_cdm_constants.h"
namespace {
std::string kCompanyNameKey = "company_name";
std::string kModelNameKey = "model_name";
std::string kArchitectureNameKey = "architecture_name";
std::string kDeviceNameKey = "device_name";
std::string kProductNameKey = "product_name";
std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id";
std::string kCompanyNameKey = "company_name";
std::string kModelNameKey = "model_name";
std::string kArchitectureNameKey = "architecture_name";
std::string kDeviceNameKey = "device_name";
std::string kProductNameKey = "product_name";
std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id";
}
namespace wvcdm {
@@ -30,13 +30,13 @@ using video_widevine_server::sdk::ClientIdentification_NameValue;
using video_widevine_server::sdk::LicenseRequest;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_ExistingLicense;
using video_widevine_server::sdk::
LicenseRequest_ContentIdentification_ExistingLicense;
using video_widevine_server::sdk::License;
using video_widevine_server::sdk::License_KeyContainer;
using video_widevine_server::sdk::LicenseError;
using video_widevine_server::sdk::SignedMessage;
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
std::vector<CryptoKey> key_array;
@@ -50,7 +50,11 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
key.set_key_id(license.key(i).id());
// Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes,
// the padding will always be 16 bytes.
length = license.key(i).key().size() - 16;
if (license.key(i).key().size() > 16) {
length = license.key(i).key().size() - 16;
} else {
length = 0;
}
key.set_key_data(license.key(i).key().substr(0, length));
key.set_key_data_iv(license.key(i).iv());
if (license.key(i).has_key_control()) {
@@ -62,7 +66,9 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
case License_KeyContainer::KEY_CONTROL:
if (license.key(i).has_key_control()) {
key.set_key_control(license.key(i).key_control().key_control_block());
key.set_key_control_iv(license.key(i).key_control().iv());
if (license.key(i).key_control().has_iv()) {
key.set_key_control_iv(license.key(i).key_control().iv());
}
key_array.push_back(key);
}
break;
@@ -75,17 +81,14 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
return key_array;
}
CdmLicense::CdmLicense(): session_(NULL) {}
CdmLicense::CdmLicense() : session_(NULL) {}
CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(const std::string& token,
CryptoSession* session,
bool CdmLicense::Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine) {
if (token.size() == 0)
return false;
if (session == NULL || !session->IsValid() || !session->IsOpen())
return false;
if (token.size() == 0) return false;
if (session == NULL || !session->IsOpen()) return false;
token_ = token;
session_ = session;
policy_engine_ = policy_engine;
@@ -97,8 +100,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
const CdmAppParameterMap& app_parameters,
CdmKeyMessage* signed_request,
std::string* server_url) {
if (!session_ ||
token_.empty()) {
if (!session_ || token_.empty()) {
return false;
}
if (init_data.empty()) {
@@ -109,10 +111,6 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
LOGE("CdmLicense::PrepareKeyRequest : No signed request provided.");
return false;
}
if (!server_url) {
LOGE("CdmLicense::PrepareKeyRequest : No server url provided.");
return false;
}
// TODO(gmorgan): Request ID owned by session?
std::string request_id;
@@ -135,38 +133,38 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
client_info->set_value(iter->second);
}
std::string value;
if (Properties::GetCompanyName(value)) {
if (Properties::GetCompanyName(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kCompanyNameKey);
client_info->set_value(value);
}
if (Properties::GetModelName(value)) {
if (Properties::GetModelName(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kModelNameKey);
client_info->set_value(value);
}
if (Properties::GetArchitectureName(value)) {
if (Properties::GetArchitectureName(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kArchitectureNameKey);
client_info->set_value(value);
}
if (Properties::GetDeviceName(value)) {
if (Properties::GetDeviceName(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceNameKey);
client_info->set_value(value);
}
if (Properties::GetProductName(value)) {
if (Properties::GetProductName(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kProductNameKey);
client_info->set_value(value);
}
if (Properties::GetBuildInfo(value)) {
if (Properties::GetBuildInfo(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kBuildInfoKey);
client_info->set_value(value);
}
if (CryptoEngine::GetInstance()->GetDeviceUniqueId(&value)) {
if (session_->GetDeviceUniqueId(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
client_info->set_value(value);
@@ -188,8 +186,8 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
cenc_content_id->set_license_type(video_widevine_server::sdk::STREAMING);
break;
default:
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %u",
(int)license_type);
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
license_type);
return false;
break;
}
@@ -220,8 +218,8 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
// Derive signing and encryption keys and construct signature.
std::string license_request_signature;
if (!session_->PrepareRequest(serialized_license_req,
&license_request_signature, false)) {
if (!session_->PrepareRequest(serialized_license_req, false,
&license_request_signature)) {
signed_request->clear();
return false;
}
@@ -268,7 +266,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
license_request.mutable_content_id()->mutable_license();
current_license->mutable_license_id()->CopyFrom(license_id_);
current_license->mutable_license_id()->CopyFrom(policy_engine_->license_id());
// Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response.
@@ -318,23 +316,19 @@ CdmResponseType CdmLicense::HandleKeyResponse(
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response))
return KEY_ERROR;
if (!signed_response.ParseFromString(license_response)) return KEY_ERROR;
if (signed_response.type() == SignedMessage::ERROR) {
return HandleKeyErrorResponse(signed_response);
}
if (!signed_response.has_signature())
return KEY_ERROR;
if (!signed_response.has_signature()) return KEY_ERROR;
License license;
if (!license.ParseFromString(signed_response.msg()))
return KEY_ERROR;
if (!license.ParseFromString(signed_response.msg())) return KEY_ERROR;
if (Properties::use_certificates_as_identification()) {
if (!signed_response.has_session_key())
return KEY_ERROR;
if (!signed_response.has_session_key()) return KEY_ERROR;
if (!session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
@@ -344,8 +338,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
// Extract mac key
std::string mac_key_iv;
std::string mac_key;
if (license.policy().can_renew())
{
if (license.policy().can_renew()) {
for (int i = 0; i < license.key_size(); ++i) {
if (license.key(i).type() == License_KeyContainer::SIGNING) {
mac_key_iv.assign(license.key(i).iv());
@@ -355,15 +348,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
}
}
if (mac_key_iv.size() != KEY_IV_SIZE ||
mac_key.size() != MAC_KEY_SIZE) {
if (mac_key_iv.size() != KEY_IV_SIZE || mac_key.size() != MAC_KEY_SIZE) {
return KEY_ERROR;
}
// License Id should not be empty for renewable license
if (!license.has_id()) return KEY_ERROR;
license_id_.CopyFrom(license.id());
}
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
@@ -376,17 +363,16 @@ CdmResponseType CdmLicense::HandleKeyResponse(
server_url_ = license.policy().renewal_server_url();
}
// TODO(kqyang, jfore, gmorgan): change SetLicense function signature to
// be able to return true/false to accept/reject the license. (Pending code
// merge from Eureka)
policy_engine_->SetLicense(license);
if (session_->LoadKeys(signed_response.msg(),
signed_response.signature(),
mac_key_iv,
mac_key,
key_array.size(),
if (session_->LoadKeys(signed_response.msg(), signed_response.signature(),
mac_key_iv, mac_key, key_array.size(),
&key_array[0])) {
return KEY_ADDED;
}
else {
} else {
return KEY_ERROR;
}
}
@@ -429,41 +415,31 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
return KEY_ERROR;
}
if (license.id().version() > license_id_.version()) {
// This is the normal case.
license_id_.CopyFrom(license.id());
if (is_renewal) {
if (license.policy().has_renewal_server_url() &&
license.policy().renewal_server_url().size() > 0) {
server_url_ = license.policy().renewal_server_url();
}
}
policy_engine_->UpdateLicense(license);
if (!is_renewal)
return KEY_ADDED;
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
if (session_->RefreshKeys(signed_response.msg(),
signed_response.signature(),
key_array.size(),
&key_array[0])) {
return KEY_ADDED;
}
else {
return KEY_ERROR;
if (is_renewal) {
if (license.policy().has_renewal_server_url() &&
license.policy().renewal_server_url().size() > 0) {
server_url_ = license.policy().renewal_server_url();
}
}
// This isn't supposed to happen.
// TODO(jfore): Handle wrap? We can miss responses and that should be
// considered normal until retries are exhausted.
LOGE("CdmLicense::HandleKeyUpdateResponse: license version: expected > %u,"
" actual = %u", license_id_.version(), license.id().version());
return KEY_ERROR;
// TODO(kqyang, jfore, gmorgan): change UpdateLicense function signature to
// be able to return true/false to accept/reject the license. (Pending code
// merge from Eureka)
policy_engine_->UpdateLicense(license);
if (!is_renewal)
return KEY_ADDED;
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
if (session_->RefreshKeys(signed_response.msg(),
signed_response.signature(),
key_array.size(),
&key_array[0])) {
return KEY_ADDED;
} else {
return KEY_ERROR;
}
}
bool CdmLicense::RestoreOfflineLicense(

View File

@@ -12,6 +12,7 @@ bool Properties::oem_crypto_use_secure_buffers_;
bool Properties::oem_crypto_use_fifo_;
bool Properties::oem_crypto_use_userspace_buffers_;
bool Properties::use_certificates_as_identification_;
bool Properties::extract_pssh_data_;
void Properties::Init() {
begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived;
@@ -21,6 +22,7 @@ void Properties::Init() {
oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers;
use_certificates_as_identification_ =
kPropertyUseCertificatesAsIdentification;
extract_pssh_data_ = kExtractPsshData;
}
} // namespace wvcdm

View File

@@ -10,45 +10,7 @@
#include <vector>
#include "log.h"
namespace {
/*
* Returns a 8-bit char that is mapped to the 6-bit base64 in_ch.
*
* Extracted from http://www.ietf.org/rfc/rfc3548.txt.
*
The "URL and Filename safe" Base 64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 - (minus)
12 M 29 d 46 u 63 _
13 N 30 e 47 v (underline)
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y (pad) =
*/
char B64ToBin(char in_ch) {
if (in_ch >= 'A' && in_ch <= 'Z') return in_ch - 'A';
if (in_ch >= 'a' && in_ch <= 'z') return in_ch - 'a' + 26;
if (in_ch >= '0' && in_ch <= '9') return in_ch - '0' + 52;
if (in_ch == '-') return 62;
if (in_ch == '_') return 63;
// arbitrary delimiter not in Base64 encoded alphabet, do not pick 0
return '?';
}
}
#include "modp_b64w.h"
namespace wvcdm {
@@ -104,181 +66,56 @@ std::string b2a_hex(const std::string& byte) {
// Filename-friendly base64 encoding (RFC4648), commonly referred as
// Base64WebSafeEncode.
// This is the encoding required by GooglePlay for certain
// license server transactions. It is also used for logging
// certain strings.
// This is the encoding required by GooglePlay to interface with the
// provisioning server's Apiary interface as well as for certain license server
// transactions. It is also used for logging certain strings.
// The difference between web safe encoding vs regular encoding is that
// the web safe version replaces '+' with '-' and '/' with '_'.
std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
static const char kBase64Chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
if (bin_input.empty()) {
return std::string();
}
int in_size = bin_input.size();
int final_quantum_in_bytes = in_size % 3;
int full_in_chunks = in_size / 3;
int out_size = full_in_chunks * 4;
if (final_quantum_in_bytes) out_size += 4;
std::string b64_output(modp_b64w_encode_len(in_size), 0);
std::string b64_output(out_size, '\0');
int in_index = 0;
int out_index = 0;
unsigned long buffer;
unsigned char out_cc;
static const unsigned long kInMask = 0xff;
static const unsigned long kOutMask = 0x3f;
for (int i = 0; i < full_in_chunks; ++i) {
// up to 3 bytes (0..255) in
buffer = (bin_input.at(in_index) & kInMask);
buffer <<= 8;
buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask);
buffer <<= 8;
buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask);
++in_index;
// up to 4 bytes (0..63) out
out_cc = (buffer >> 18) & kOutMask;
b64_output.at(out_index) = kBase64Chars[out_cc];
if (++out_index >= out_size)
break;
out_cc = (buffer >> 12) & kOutMask;
b64_output.at(out_index) = kBase64Chars[out_cc];
if (++out_index >= out_size)
break;
out_cc = (buffer >> 6) & kOutMask;
b64_output.at(out_index) = kBase64Chars[out_cc];
if (++out_index >= out_size)
break;
out_cc = buffer & kOutMask;
b64_output.at(out_index) = kBase64Chars[out_cc];
++out_index;
int out_size = modp_b64w_encode(&b64_output[0],
reinterpret_cast<const char*>(&bin_input[0]),
in_size);
if (out_size == -1) {
LOGE("Base64SafeEncode failed");
return std::string();
}
if (final_quantum_in_bytes) {
switch(final_quantum_in_bytes) {
case 1: {
// reads 24-bits data, which is made up of one 8-bits char
buffer = (bin_input.at(in_index++) & kInMask);
buffer <<= 16;
b64_output.resize(out_size);
return b64_output;
}
// writes two 6-bits chars followed by two '=' padding char
out_cc = (buffer >> 18) & kOutMask;
b64_output.at(out_index++) = kBase64Chars[out_cc];
out_cc = (buffer >> 12) & kOutMask;
b64_output.at(out_index++) = kBase64Chars[out_cc];
b64_output.at(out_index++) = '=';
b64_output.at(out_index) = '=';
break;
}
case 2: {
// reads 24-bits data, which is made up of two 8-bits chars
buffer = (bin_input.at(in_index++) & kInMask);
buffer <<= 8;
buffer |= (bin_input.at(in_index++) & kInMask);
buffer <<= 8;
// writes three 6-bits chars followed by one '=' padding char
out_cc = (buffer >> 18) & kOutMask;
b64_output.at(out_index++) = kBase64Chars[out_cc];
out_cc = (buffer >> 12) & kOutMask;
b64_output.at(out_index++) = kBase64Chars[out_cc];
out_cc = (buffer >> 6) & kOutMask;
b64_output.at(out_index++) = kBase64Chars[out_cc];
b64_output.at(out_index) = '=';
break;
}
default:
break;
}
}
std::string Base64SafeEncodeNoPad(const std::vector<uint8_t>& bin_input) {
std::string b64_output = Base64SafeEncode(bin_input);
// Output size: ceiling [ bin_input.size() * 4 / 3 ].
b64_output.resize((bin_input.size() * 4 + 2) / 3);
return b64_output;
}
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
// as Base64WebSafeDecode.
// This is the encoding required by GooglePlay for certain
// license server transactions. It is also used for logging
// certain strings.
std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
if (b64_input.empty()) {
return std::vector<uint8_t>();
}
int in_size = b64_input.size();
int out_size = in_size;
std::vector<uint8_t> bin_output(out_size, '\0');
int in_index = 0;
int out_index = 0;
unsigned long buffer;
unsigned char out_cc;
static const unsigned long kOutMask = 0xff;
int counter = 0;
size_t delimiter_pos = b64_input.rfind('=');
if (delimiter_pos != std::string::npos) {
// Special case for partial last quantum indicated by '='
// at the end of encoded input.
counter = 1;
}
for (; counter < (in_size / 4); ++counter) {
// up to 4 bytes (0..63) in
buffer = B64ToBin(b64_input.at(in_index));
buffer <<= 6;
buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index));
buffer <<= 6;
buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index));
buffer <<= 6;
buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index));
++in_index;
// up to 3 bytes (0..255) out
out_cc = (buffer >> 16) & kOutMask;
bin_output.at(out_index) = out_cc;
if (++out_index >= out_size)
break;
out_cc = (buffer >> 8) & kOutMask;
bin_output.at(out_index) = out_cc;
if (++out_index >= out_size)
break;
out_cc = buffer & kOutMask;
bin_output.at(out_index) = out_cc;
++out_index;
std::vector<uint8_t> bin_output(modp_b64w_decode_len(in_size), 0);
int out_size = modp_b64w_decode(reinterpret_cast<char*>(&bin_output[0]),
b64_input.data(),
in_size);
if (out_size == -1) {
LOGE("Base64SafeDecode failed");
return std::vector<uint8_t>(0);
}
if (delimiter_pos != std::string::npos) {
// it is either 2 chars plus 2 '=' or 3 chars plus one '='
buffer = B64ToBin(b64_input.at(in_index++));
buffer <<= 6;
buffer |= B64ToBin(b64_input.at(in_index++));
buffer <<= 6;
char special_char = b64_input.at(in_index++);
if ('=' == special_char) {
// we have 2 chars and 2 '='
buffer <<= 6;
out_cc = (buffer >> 16) & kOutMask;
bin_output.at(out_index++) = out_cc;
out_cc = (buffer >> 8) & kOutMask;
bin_output.at(out_index) = out_cc;
} else {
// we have 3 chars and 1 '='
buffer |= B64ToBin(special_char);
buffer <<= 6;
buffer |= B64ToBin(b64_input.at(in_index));
out_cc = (buffer >> 16) & kOutMask;
bin_output.at(out_index++) = out_cc;
out_cc = (buffer >> 8) & kOutMask;
bin_output.at(out_index++) = out_cc;
out_cc = buffer & kOutMask;
bin_output.at(out_index) = out_cc;
}
}
// adjust vector to reflect true size
bin_output.resize(out_index);
bin_output.resize(out_size);
return bin_output;
}

View File

@@ -1,193 +1,73 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include <utility>
#include "gtest/gtest.h"
#include "log.h"
#include "string_conversions.h"
namespace {
std::string kMultipleOf24BitsData("Good day!");
std::string kOneByteOverData("Hello Googler");
std::string kTwoBytesOverData("Hello Googlers");
std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh");
std::string kOneByteOverB64Data("SGVsbG8gR29vZ2xlcg==");
std::string kTwoBytesOverB64Data("SGVsbG8gR29vZ2xlcnM=");
std::string kTestData =
"\030\361\\\366\267> \331\210\360\\-\311:\324\256\376"
"\261\234\241\326d\326\177\346\346\223\333Y\305\214\330";
std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg=";
std::string kB64ShortString("r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=");
std::string kB64LongString =
"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
"TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg"
"yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp"
"NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe"
"WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R"
"lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP"
"9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T"
"jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s"
"OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c"
"4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm"
"1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB"
"efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA"
"jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0"
"LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn"
"bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E"
"L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb"
"jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b"
"rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH"
"yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F"
"sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb"
"MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S"
"dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4"
"CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF"
"E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH"
"JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI"
"O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu"
"vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g"
"k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug"
"_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9"
"G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp"
"-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc"
"4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G"
"rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU"
"WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF"
"7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem"
"et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr"
"gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe"
"G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT"
"UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN"
"1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT"
"sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw"
"qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO"
"t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF"
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=";
}
// Test vectors taken from http://tools.ietf.org/html/rfc4648#section-10
const std::string kNullString("");
const std::string kf("f");
const std::string kfo("fo");
const std::string kfoo("foo");
const std::string kfoob("foob");
const std::string kfooba("fooba");
const std::string kfoobar("foobar");
const std::string kfB64("Zg==");
const std::string kfoB64("Zm8=");
const std::string kfooB64("Zm9v");
const std::string kfoobB64("Zm9vYg==");
const std::string kfoobaB64("Zm9vYmE=");
const std::string kfoobarB64("Zm9vYmFy");
// Arbitrary clear test vectors
const std::string kMultipleOf24BitsData("Good day!");
const std::string kOneByteOverData("Hello Googler");
const std::string kTwoBytesOverData("Hello Googlers");
const std::string kTestData =
"\030\361\\\366\267> \331\210\360\\-\311:\324\256\376"
"\261\234\241\326d\326\177\346\346\223\333Y\305\214\330";
// Arbitrary encoded test vectors
const std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh");
const std::string kOneByteOverB64Data("SGVsbG8gR29vZ2xlcg==");
const std::string kTwoBytesOverB64Data("SGVsbG8gR29vZ2xlcnM=");
const std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg=";
const std::pair<const std::string*, const std::string*> kBase64TestVectors[] = {
make_pair(&kNullString, &kNullString),
make_pair(&kf, &kfB64),
make_pair(&kfo, &kfoB64),
make_pair(&kfoo, &kfooB64),
make_pair(&kfoob, &kfoobB64),
make_pair(&kfooba, &kfoobaB64),
make_pair(&kfoobar, &kfoobarB64),
make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data),
make_pair(&kOneByteOverData, &kOneByteOverB64Data),
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
make_pair(&kTestData, &kB64TestData),
};
} // unnamed namespace
namespace wvcdm {
class Base64Test : public testing::Test {
public:
Base64Test() {}
~Base64Test() {}
class Base64EncodeDecodeTest : public ::testing::TestWithParam<
std::pair<const std::string*, const std::string*> > {};
};
TEST_F(Base64Test, Base64MultipleOf24BitsTest)
{
// encodes string
std::vector<uint8_t> message_vector(kMultipleOf24BitsData.begin(),
kMultipleOf24BitsData.end());
std::string message_b64 = Base64SafeEncode(message_vector);
// decodes string
std::vector<uint8_t> result_vector = Base64SafeDecode(message_b64);
std::string result;
result.assign(result_vector.begin(), result_vector.end());
EXPECT_STREQ(kMultipleOf24BitsData.data(), result.data());
}
TEST_F(Base64Test, Base64OneByteOverTest)
{
// encodes string
std::vector<uint8_t> message_vector(kOneByteOverData.begin(),
kOneByteOverData.end());
std::string message_b64 = Base64SafeEncode(message_vector);
// decodes string
std::vector<uint8_t> result_vector = Base64SafeDecode(message_b64);
std::string result;
result.assign(result_vector.begin(), result_vector.end());
EXPECT_STREQ(kOneByteOverData.data(), result.data());
}
TEST_F(Base64Test, Base64TwoBytesOverTest)
{
// encodes string
std::vector<uint8_t> message_vector(kTwoBytesOverData.begin(),
kTwoBytesOverData.end());
std::string message_b64 = Base64SafeEncode(message_vector);
// decodes string
std::vector<uint8_t> result_vector = Base64SafeDecode(message_b64);
std::string result;
result.assign(result_vector.begin(), result_vector.end());
EXPECT_STREQ(kTwoBytesOverData.data(), result.data());
}
TEST_F(Base64Test, Base64EncodeTest)
{
// encodes string
std::vector<uint8_t> message_vector(kTestData.begin(), kTestData.end());
std::string message_b64 = Base64SafeEncode(message_vector);
std::string result;
result.assign(message_b64.begin(), message_b64.end());
EXPECT_STREQ(kB64TestData.data(), result.data());
// decodes string
std::vector<uint8_t> result_vector = Base64SafeDecode(message_b64);
result.clear();
result.assign(result_vector.begin(), result_vector.end());
EXPECT_STREQ(kTestData.data(), result.data());
}
TEST_F(Base64Test, Base64MultipleOf24BitsDecodeTest)
{
// decodes string
std::vector<uint8_t> decoded_vector = Base64SafeDecode(kMultipleOf24BitsB64Data);
std::string result;
result.assign(decoded_vector.begin(), decoded_vector.end());
EXPECT_STREQ(kMultipleOf24BitsData.data(), result.data());
// encodes string
TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) {
std::pair<const std::string*, const std::string*> values = GetParam();
std::vector<uint8_t> decoded_vector = Base64SafeDecode(values.second->data());
std::string decoded_string(decoded_vector.begin(), decoded_vector.end());
EXPECT_STREQ(values.first->data(), decoded_string.data());
std::string b64_string = Base64SafeEncode(decoded_vector);
EXPECT_STREQ(kMultipleOf24BitsB64Data.data(), b64_string.data());
EXPECT_STREQ(values.second->data(), b64_string.data());
}
TEST_F(Base64Test, Base64OneByteOverDecodeTest)
{
// decodes string
std::vector<uint8_t> decoded_vector = Base64SafeDecode(kOneByteOverB64Data);
std::string result;
result.assign(decoded_vector.begin(), decoded_vector.end());
EXPECT_STREQ(kOneByteOverData.data(), result.data());
// encodes string
std::string b64_string = Base64SafeEncode(decoded_vector);
EXPECT_STREQ(kOneByteOverB64Data.data(), b64_string.data());
}
TEST_F(Base64Test, Base64TwoBytesOverDecodeTest)
{
// decodes string
std::vector<uint8_t> decoded_vector = Base64SafeDecode(kTwoBytesOverB64Data);
std::string result;
result.assign(decoded_vector.begin(), decoded_vector.end());
EXPECT_STREQ(kTwoBytesOverData.data(), result.data());
// encodes string
std::string b64_string = Base64SafeEncode(decoded_vector);
EXPECT_STREQ(kTwoBytesOverB64Data.data(), b64_string.data());
}
TEST_F(Base64Test, Base64ShortDecodeTest)
{
// decodes string
std::vector<uint8_t> decoded_vector = Base64SafeDecode(kB64ShortString);
// encodes string
std::string b64_string = Base64SafeEncode(decoded_vector);
EXPECT_STREQ(kB64ShortString.data(), b64_string.data());
}
TEST_F(Base64Test, Base64LongDecodeTest)
{
// decodes string
std::vector<uint8_t> decoded_vector = Base64SafeDecode(kB64LongString);
// encodes string
std::string b64_string = Base64SafeEncode(decoded_vector);
EXPECT_STREQ(kB64LongString.data(), b64_string.data());
}
INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64EncodeDecodeTest,
::testing::ValuesIn(kBase64TestVectors));
} // namespace wvcdm

View File

@@ -3,11 +3,17 @@
#include <errno.h>
#include <getopt.h>
#if defined(CHROMIUM_BUILD)
#include "base/at_exit.h"
#include "base/message_loop.h"
#endif
#include "cdm_engine.h"
#include "config_test_env.h"
#include "gtest/gtest.h"
#include "license_request.h"
#include "log.h"
#include "properties.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "url_request.h"
#include "wv_cdm_types.h"
@@ -22,197 +28,208 @@ std::string g_license_server;
std::string g_port;
wvcdm::KeyId g_wrong_key_id;
int g_use_full_path = 0; // cannot use boolean in getopt_long
// This is the RSA certificate from the provisioning server. The client
// sends this certificate to a license server as verification in the
// provisioning test case.
static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse =
"{\"signedResponse\": {"
"\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
"TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg"
"yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp"
"NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe"
"WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R"
"lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP"
"9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T"
"jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s"
"OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c"
"4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm"
"1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB"
"efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA"
"jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0"
"LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn"
"bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E"
"L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb"
"jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b"
"rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH"
"yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F"
"sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb"
"MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S"
"dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4"
"CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF"
"E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH"
"JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI"
"O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu"
"vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g"
"k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug"
"_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9"
"G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp"
"-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc"
"4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G"
"rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU"
"WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF"
"7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem"
"et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr"
"gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe"
"G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT"
"UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN"
"1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT"
"sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw"
"qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO"
"t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF"
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=\","
"\"signature\": \"r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=\"}}";
} // namespace
namespace wvcdm {
class WvCdmEngineTest : public testing::Test {
public:
WvCdmEngineTest() {}
~WvCdmEngineTest() {}
virtual void SetUp() {
cdm_engine_.reset(new CdmEngine());
cdm_engine_->OpenSession(g_key_system, &session_id_);
}
virtual void TearDown() {
cdm_engine_->CloseSession(session_id_);
}
protected:
void GenerateKeyRequest(const std::string& key_system,
const std::string& init_data) {
wvcdm::CdmAppParameterMap app_parameters;
const std::string& key_id) {
CdmAppParameterMap app_parameters;
std::string server_url;
std::string key_set_id;
EXPECT_EQ(cdm_engine_.GenerateKeyRequest(session_id_,
key_set_id,
init_data,
kLicenseTypeStreaming,
app_parameters,
&key_msg_,
&server_url), wvcdm::KEY_MESSAGE);
std::string init_data = key_id;
CdmKeySetId key_set_id;
// TODO(rfrias): Temporary change till b/9465346 is addressed
if (!Properties::extract_pssh_data()) {
EXPECT_TRUE(CdmEngine::ExtractWidevinePssh(key_id, &init_data));
}
EXPECT_EQ(KEY_MESSAGE,
cdm_engine_->GenerateKeyRequest(session_id_,
key_set_id,
init_data,
kLicenseTypeStreaming,
app_parameters,
&key_msg_,
&server_url));
}
void GenerateRenewalRequest(const std::string& key_system,
const std::string& init_data) {
std::string server_url;
EXPECT_EQ(cdm_engine_.GenerateRenewalRequest(session_id_,
&key_msg_,
&server_url),
wvcdm::KEY_MESSAGE);
}
// concatinates all chunks into one blob
// TODO (edwinwong) move this function to url_request class as GetMessageBody
void ConcatenateChunkedResponse(const std::string http_response,
std::string* message_body) {
if (http_response.empty())
return;
message_body->clear();
const std::string kChunkedTag = "Transfer-Encoding: chunked\r\n\r\n";
size_t chunked_tag_pos = http_response.find(kChunkedTag);
if (std::string::npos != chunked_tag_pos) {
// processes chunked encoding
size_t chunk_size = 0;
size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size();
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);
return;
}
/*
* searches for chunks
*
* header
* chunk size\r\n <-- chunk_size_pos @ beginning of chunk size
* chunk data\r\n <-- chunk_pos @ beginning of chunk data
* chunk size\r\n
* chunk data\r\n
* 0\r\n
*/
const std::string kCrLf = "\r\n";
size_t chunk_pos = http_response.find(kCrLf, chunk_size_pos) +
kCrLf.size();
message_body->assign(&http_response[0], chunk_size_pos);
while ((chunk_size > 0) && (std::string::npos != chunk_pos)) {
message_body->append(&http_response[chunk_pos], chunk_size);
// searches for next chunk
chunk_size_pos = chunk_pos + chunk_size + kCrLf.size();
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);
break;
}
chunk_pos = http_response.find(kCrLf, chunk_size_pos) + kCrLf.size();
}
} else {
// response is not chunked encoded
message_body->assign(http_response);
}
EXPECT_EQ(KEY_MESSAGE,
cdm_engine_->GenerateRenewalRequest(session_id_,
&key_msg_,
&server_url_));
}
// posts a request and extracts the drm message from the response
std::string GetKeyRequestResponse(const std::string& server_url,
const std::string& client_auth,
int expected_response) {
std::string port;
if (server_url.find("https") != std::string::npos) {
port.assign("443");
} else {
port.assign(g_port);
}
UrlRequest url_request(server_url + client_auth, port);
// Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth, g_port, true, true);
if (!url_request.is_connected()) {
return "";
}
url_request.PostRequestChunk(key_msg_);
std::string http_response;
std::string message_body;
int resp_bytes = url_request.GetResponse(http_response);
if (resp_bytes) {
ConcatenateChunkedResponse(http_response, &message_body);
}
LOGD("response:\r\n%s", message_body.c_str());
url_request.PostRequest(key_msg_);
std::string response;
int resp_bytes = url_request.GetResponse(&response);
LOGD("response:\r\n%s", response.c_str());
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_body);
if (expected_response == 200) {
EXPECT_EQ(200, status_code);
int status_code = url_request.GetStatusCode(response);
int kHttpOk = 200;
if (expected_response == kHttpOk) {
EXPECT_EQ(kHttpOk, status_code);
} else {
EXPECT_NE(200, status_code);
EXPECT_NE(kHttpOk, status_code);
}
std::string drm_msg;
if (200 == status_code) {
if (status_code != kHttpOk) {
return "";
} else {
std::string drm_msg;
LicenseRequest lic_request;
lic_request.GetDrmMessage(message_body, drm_msg);
lic_request.GetDrmMessage(response, drm_msg);
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(),
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
drm_msg.size()).c_str());
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
drm_msg.size()).c_str());
return drm_msg;
}
return drm_msg;
}
void VerifyKeyRequestResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data,
bool is_renewal) {
void VerifyNewKeyResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data){
std::string resp = GetKeyRequestResponse(server_url,
client_auth,
200);
if (is_renewal) {
EXPECT_EQ(cdm_engine_.RenewKey(session_id_, resp), wvcdm::KEY_ADDED);
}
else {
std::string key_set_id;
EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp, key_set_id),
wvcdm::KEY_ADDED);
}
CdmKeySetId key_set_id;
EXPECT_EQ(cdm_engine_->AddKey(session_id_, resp, &key_set_id), KEY_ADDED);
}
wvcdm::CdmEngine cdm_engine_;
void VerifyRenewalKeyResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data){
std::string resp = GetKeyRequestResponse(server_url,
client_auth,
200);
EXPECT_EQ(cdm_engine_->RenewKey(session_id_, resp), wvcdm::KEY_ADDED);
}
scoped_ptr<CdmEngine> cdm_engine_;
std::string key_msg_;
std::string session_id_;
std::string server_url_;
};
TEST(WvCdmProvisioningTest, ProvisioningTest) {
CdmEngine cdm_engine;
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
cdm_engine.GetProvisioningRequest(&prov_request, &provisioning_server_url);
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse);
}
TEST_F(WvCdmEngineTest, BaseMessageTest) {
cdm_engine_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id);
GetKeyRequestResponse(g_license_server, g_client_auth, 200);
cdm_engine_.CloseSession(session_id_);
}
TEST_F(WvCdmEngineTest, WrongMessageTest) {
cdm_engine_.OpenSession(g_key_system, &session_id_);
std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id);
std::string wrong_message = a2bs_hex(g_wrong_key_id);
GenerateKeyRequest(g_key_system, wrong_message);
GetKeyRequestResponse(g_license_server, g_client_auth, 500);
cdm_engine_.CloseSession(session_id_);
}
TEST_F(WvCdmEngineTest, NormalDecryption) {
cdm_engine_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
cdm_engine_.CloseSession(session_id_);
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
}
TEST_F(WvCdmEngineTest, LicenseRenewal) {
cdm_engine_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
GenerateRenewalRequest(g_key_system, g_key_id);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, true);
cdm_engine_.CloseSession(session_id_);
VerifyRenewalKeyResponse(server_url_.empty() ? g_license_server : server_url_,
g_client_auth,
g_key_id);
}
} // namespace wvcdm
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
wvcdm::InitLogging(argc, argv);
wvcdm::ConfigTestEnv config;
g_client_auth.assign(config.client_auth());
@@ -231,6 +248,8 @@ int main(int argc, char **argv) {
{ "keyid", required_argument, NULL, 'k' },
{ "port", required_argument, NULL, 'p' },
{ "server", required_argument, NULL, 's' },
{ "vmodule", required_argument, NULL, 0 },
{ "v", required_argument, NULL, 0 },
{ NULL, 0, NULL, '\0' }
};
@@ -301,5 +320,9 @@ int main(int argc, char **argv) {
config.set_port(g_port);
config.set_key_id(g_key_id);
#if defined(CHROMIUM_BUILD)
base::AtExitManager exit;
MessageLoop ttr(MessageLoop::TYPE_IO);
#endif
return RUN_ALL_TESTS();
}

View File

@@ -18,18 +18,19 @@ static const std::string kLicenseServer =
"http://hamid.kir.corp.google.com:8888/drm";
static const std::string kClientAuth = "";
static const std::string kKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0801121030313233343536373839616263646566"; // key - for gHali
#elif (USE_SERVER == USE_SERVER_YT)
static const std::string kLicenseServer =
"https://www.youtube.com/api/drm/widevine?video_id=03681262dc412c06&source=YOUTUBE";
"https://www.youtube.com/api/drm/"
"widevine?video_id=03681262dc412c06&source=YOUTUBE";
static const std::string kClientAuth = "";
static const std::string kKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0801121093789920E8D6520098577DF8F2DD5546"; // pssh data
#elif (USE_SERVER == USE_SERVER_GP)
@@ -45,23 +46,36 @@ static const std::string kClientAuth =
"?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine";
static const std::string kKeyId =
"000000347073736800000000" // blob size and pssh
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
"000000347073736800000000" // blob size and pssh
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
"08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data
#else
#error "Must define USE_SERVER"
#endif
//static const char kWidevineKeySystem[] = "com.widevine.alpha";
// An invalid key id, expected to fail
static const std::string kWrongKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0901121094889920E8D6520098577DF8F2DD5546"; // pssh data
} // namespace
// Url returned by GetProvisioningRequest()
const std::string kProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// Return production-rooted certificates that have test bit set,
// request_license_test uses this url.
const std::string kProductionTestProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1exttest/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const std::string kServerSdkLicenseServer =
"http://kir03fcpg174.widevine.net/widevine/cgi-bin/drm.cgi";
} // namespace
namespace wvcdm {
@@ -70,8 +84,10 @@ ConfigTestEnv::ConfigTestEnv()
key_id_(kKeyId),
key_system_("com.widevine.alpha"),
license_server_(kLicenseServer),
port_("80"),
wrong_key_id_(kWrongKeyId) {
}
port_(kDefaultHttpsPort),
provisioning_server_url_(kProductionProvisioningServerUrl),
provisioning_test_server_url_(kProductionTestProvisioningServerUrl),
server_sdk_license_server_(kServerSdkLicenseServer),
wrong_key_id_(kWrongKeyId) {}
} // namespace wvcdm
} // namespace wvcdm

View File

@@ -6,6 +6,10 @@
#include <string>
#include "wv_cdm_types.h"
namespace {
const std::string kDefaultHttpsPort = "443";
}
namespace wvcdm {
// Configures default test environment.
@@ -19,6 +23,15 @@ class ConfigTestEnv {
const CdmKeySystem& key_system() const { return key_system_; }
const std::string& license_server() const { return license_server_; }
const std::string& port() const { return port_; }
const std::string& provisioning_server_url() const {
return provisioning_server_url_;
}
const std::string& provisioning_test_server_url() const {
return provisioning_test_server_url_;
}
const std::string& server_sdk_license_server() const {
return server_sdk_license_server_;
}
const KeyId& wrong_key_id() const { return wrong_key_id_; }
void set_key_id(KeyId& key_id) { key_id_.assign(key_id); }
@@ -36,6 +49,9 @@ class ConfigTestEnv {
CdmKeySystem key_system_;
std::string license_server_;
std::string port_;
std::string provisioning_server_url_;
std::string provisioning_test_server_url_;
std::string server_sdk_license_server_;
KeyId wrong_key_id_;
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);

File diff suppressed because it is too large Load Diff

View File

@@ -3,187 +3,179 @@
#include "device_files.h"
#include "file_store.h"
#include "gtest/gtest.h"
#include "properties.h"
#include "test_vectors.h"
namespace {
// TODO(rfrias): Make this work for non-unix paths
const std::string kFileExists = "/system/bin/sh";
const std::string kDirExists = "/system/bin";
const std::string kFileDoesNotExist = "/system/bin/shxyxyxy";
const std::string kDirDoesNotExist = "/system/binxyxyxy";
const std::string kTestFileName = "test.txt";
} // namespace
namespace wvcdm {
TEST(FileTest, FileExists) {
EXPECT_TRUE(File::Exists(kFileExists));
EXPECT_TRUE(File::Exists(kDirExists));
EXPECT_FALSE(File::Exists(kFileDoesNotExist));
EXPECT_FALSE(File::Exists(kDirDoesNotExist));
}
class FileTest : public testing::Test {
protected:
virtual void SetUp() { CreateTestDir(); }
virtual void TearDown() { RemoveTestDir(); }
TEST(FileTest, CreateDirectory) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
std::string dirWoDelimiter = dir.substr(0, dir.size()-1);
if (File::Exists(dirWoDelimiter))
EXPECT_TRUE(File::Remove(dirWoDelimiter));
EXPECT_FALSE(File::Exists(dirWoDelimiter));
EXPECT_TRUE(File::CreateDirectory(dirWoDelimiter));
EXPECT_TRUE(File::Exists(dirWoDelimiter));
EXPECT_TRUE(File::Remove(dirWoDelimiter));
EXPECT_TRUE(File::CreateDirectory(dir));
EXPECT_TRUE(File::Exists(dir));
EXPECT_TRUE(File::Remove(dir));
}
void CreateTestDir() {
File file;
if (!file.Exists(test_vectors::kTestDir)) {
EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir));
}
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
}
TEST(FileTest, RemoveDir) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
if (!File::Exists(dir))
EXPECT_TRUE(File::CreateDirectory(dir));
EXPECT_TRUE(File::Exists(dir));
EXPECT_TRUE(File::Remove(dir));
EXPECT_FALSE(File::Exists(dir));
}
void RemoveTestDir() {
File file;
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
}
TEST(FileTest, OpenFileUsingConstructor) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
std::string path = dir + DeviceFiles::kDeviceCertificateFileName;
if (!File::Exists(dir))
EXPECT_TRUE(File::CreateDirectory(dir));
EXPECT_TRUE(File::Exists(dir));
File::Remove(path);
File file(path, File::kCreate);
EXPECT_TRUE(file.IsOpen());
file.Close();
EXPECT_TRUE(File::Exists(path));
}
std::string GenerateRandomData(uint32_t len) {
std::string data(len, 0);
for (size_t i = 0; i < len; i++) {
data[i] = rand() % 256;
}
return data;
}
};
TEST(FileTest, OpenFile) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
std::string path = dir + DeviceFiles::kDeviceCertificateFileName;
if (!File::Exists(dir))
EXPECT_TRUE(File::CreateDirectory(dir));
EXPECT_TRUE(File::Exists(dir));
File::Remove(path);
TEST_F(FileTest, FileExists) {
File file;
file.Open(path, File::kCreate);
EXPECT_TRUE(file.IsOpen());
EXPECT_TRUE(file.Exists(test_vectors::kFileExists));
EXPECT_TRUE(file.Exists(test_vectors::kDirExists));
EXPECT_FALSE(file.Exists(test_vectors::kFileDoesNotExist));
EXPECT_FALSE(file.Exists(test_vectors::kDirDoesNotExist));
}
TEST_F(FileTest, CreateDirectory) {
File file;
std::string dir_wo_delimiter =
test_vectors::kTestDir.substr(0, test_vectors::kTestDir.size() - 1);
if (file.Exists(dir_wo_delimiter)) EXPECT_TRUE(file.Remove(dir_wo_delimiter));
EXPECT_FALSE(file.Exists(dir_wo_delimiter));
EXPECT_TRUE(file.CreateDirectory(dir_wo_delimiter));
EXPECT_TRUE(file.Exists(dir_wo_delimiter));
EXPECT_TRUE(file.Remove(dir_wo_delimiter));
EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir));
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
}
TEST_F(FileTest, RemoveDir) {
File file;
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
EXPECT_FALSE(file.Exists(test_vectors::kTestDir));
}
TEST_F(FileTest, OpenFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File handle;
EXPECT_TRUE(handle.Remove(path));
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(File::Exists(path));
EXPECT_TRUE(handle.Exists(path));
}
TEST(FileTest, RemoveDirAndFile) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
std::string path = dir + DeviceFiles::kDeviceCertificateFileName;
if (!File::Exists(dir))
EXPECT_TRUE(File::CreateDirectory(dir));
EXPECT_TRUE(File::Exists(dir));
File file(path, File::kCreate);
EXPECT_TRUE(file.IsOpen());
TEST_F(FileTest, RemoveDirAndFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(File::Remove(path));
EXPECT_TRUE(File::Remove(dir));
EXPECT_FALSE(File::Exists(path));
EXPECT_FALSE(File::Exists(dir));
}
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file.Remove(path));
EXPECT_FALSE(file.Exists(path));
TEST(FileTest, IsDir) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
std::string path = dir + DeviceFiles::kDeviceCertificateFileName;
if (!File::Exists(dir))
EXPECT_TRUE(File::CreateDirectory(dir));
EXPECT_TRUE(File::Exists(dir));
File file(path, File::kCreate);
EXPECT_TRUE(file.IsOpen());
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(File::Exists(path));
EXPECT_TRUE(File::Exists(dir));
EXPECT_FALSE(File::IsDirectory(path));
EXPECT_TRUE(File::IsDirectory(dir));
EXPECT_TRUE(file.Exists(path));
RemoveTestDir();
EXPECT_FALSE(file.Exists(test_vectors::kTestDir));
EXPECT_FALSE(file.Exists(path));
}
TEST(FileTest, IsRegularFile) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
std::string path = dir + DeviceFiles::kDeviceCertificateFileName;
if (!File::Exists(dir))
EXPECT_TRUE(File::CreateDirectory(dir));
EXPECT_TRUE(File::Exists(dir));
File file(path, File::kCreate);
EXPECT_TRUE(file.IsOpen());
TEST_F(FileTest, IsDir) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(File::Exists(path));
EXPECT_TRUE(File::Exists(dir));
EXPECT_TRUE(File::IsRegularFile(path));
EXPECT_FALSE(File::IsRegularFile(dir));
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
EXPECT_FALSE(file.IsDirectory(path));
EXPECT_TRUE(file.IsDirectory(test_vectors::kTestDir));
}
TEST(FileTest, WriteReadTextFile) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
std::string path = dir + DeviceFiles::kDeviceCertificateFileName;
if (!File::Exists(dir))
EXPECT_TRUE(File::CreateDirectory(dir));
EXPECT_TRUE(File::Exists(dir));
File::Remove(path);
TEST_F(FileTest, IsRegularFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
const char* test_string = "This is a test";
File file1(path, File::kCreate);
EXPECT_TRUE(file1.IsOpen());
EXPECT_TRUE(file1.Write(test_string, strlen(test_string)+1));
file1.Close();
EXPECT_TRUE(File::Exists(path));
char buf[100];
File file2(path, File::kReadOnly);
EXPECT_TRUE(file2.IsOpen());
EXPECT_EQ((ssize_t)strlen(test_string)+1, file2.Read(buf, sizeof(buf)));
file2.Close();
EXPECT_STREQ(test_string, buf);
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
EXPECT_TRUE(file.IsRegularFile(path));
EXPECT_FALSE(file.IsRegularFile(test_vectors::kTestDir));
}
TEST(FileTest, WriteReadBinaryFile) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
std::string path = dir + DeviceFiles::kDeviceCertificateFileName;
if (!File::Exists(dir))
EXPECT_TRUE(File::CreateDirectory(dir));
EXPECT_TRUE(File::Exists(dir));
File::Remove(path);
TEST_F(FileTest, FileSize) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
unsigned char test_buf[600];
for (size_t i = 0; i < sizeof(test_buf); i++) {
test_buf[i] = i % 128;
}
File file1(path, File::kCreate | File::kBinary);
EXPECT_TRUE(file1.IsOpen());
EXPECT_TRUE(file1.Write(test_buf, sizeof(test_buf)));
file1.Close();
EXPECT_TRUE(File::Exists(path));
std::string write_data = GenerateRandomData(600);
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
char buf[1000];
File file2(path, File::kReadOnly);
EXPECT_TRUE(file2.IsOpen());
EXPECT_EQ((ssize_t)sizeof(test_buf), file2.Read(buf, sizeof(buf)));
file2.Close();
EXPECT_TRUE(memcmp(test_buf, buf, sizeof(test_buf)) == 0);
EXPECT_EQ(static_cast<ssize_t>(write_data.size()), file.FileSize(path));
}
TEST(FileTest, FileSize) {
std::string dir = DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
std::string path = dir + DeviceFiles::kDeviceCertificateFileName;
if (!File::Exists(dir))
EXPECT_TRUE(File::CreateDirectory(dir));
File::Remove(path);
TEST_F(FileTest, WriteReadTextFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
unsigned char test_buf[600];
for (size_t i = 0; i < sizeof(test_buf); i++) {
test_buf[i] = i % 128;
}
File file1(path, File::kCreate | File::kBinary);
EXPECT_TRUE(file1.IsOpen());
EXPECT_TRUE(file1.Write(test_buf, sizeof(test_buf)));
file1.Close();
EXPECT_TRUE(File::Exists(path));
std::string write_data = "This is a test";
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
EXPECT_EQ((ssize_t)sizeof(test_buf), File::FileSize(path));
EXPECT_TRUE(File::Remove(dir));
std::string read_data;
read_data.resize(file.FileSize(path));
File rd_file;
EXPECT_TRUE(rd_file.Open(path, File::kReadOnly));
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
rd_file.Close();
EXPECT_EQ(write_data, read_data);
}
TEST_F(FileTest, WriteReadBinaryFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
std::string write_data = GenerateRandomData(600);
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
std::string read_data;
read_data.resize(file.FileSize(path));
File rd_file;
EXPECT_TRUE(rd_file.Open(path, File::kReadOnly));
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
rd_file.Close();
EXPECT_EQ(write_data, read_data);
}
} // namespace wvcdm

View File

@@ -5,13 +5,14 @@
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include "openssl/bio.h"
#include "openssl/err.h"
#include "openssl/x509.h"
#include <string.h>
#include <sys/socket.h>
#include "log.h"
#include "openssl/bio.h"
#include "openssl/err.h"
#include "openssl/x509.h"
namespace wvcdm {
SSL_CTX* HttpSocket::InitSslContext(void) {
@@ -22,8 +23,7 @@ SSL_CTX* HttpSocket::InitSslContext(void) {
SSL_load_error_strings();
method = SSLv3_client_method();
ctx = SSL_CTX_new(method);
if (NULL == ctx)
{
if (NULL == ctx) {
LOGE("failed to create SSL context");
}
return ctx;
@@ -35,8 +35,7 @@ void HttpSocket::ShowServerCertificate(const SSL* ssl) {
// gets the server certificate
cert = SSL_get_peer_certificate(ssl);
if (cert != NULL)
{
if (cert != NULL) {
LOGV("server certificate:");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
LOGV("subject: %s", line);
@@ -45,31 +44,26 @@ void HttpSocket::ShowServerCertificate(const SSL* ssl) {
LOGV("issuer: %s", line);
free(line);
X509_free(cert);
}
else
} else {
LOGE("Failed to get server certificate");
}
}
HttpSocket::HttpSocket() :
secure_connect_(true),
socket_fd_(-1),
ssl_(NULL),
ssl_ctx_(NULL),
timeout_enabled_(false) {
HttpSocket::HttpSocket()
: secure_connect_(true),
socket_fd_(-1),
ssl_(NULL),
ssl_ctx_(NULL),
timeout_enabled_(false) {
SSL_library_init();
}
HttpSocket::~HttpSocket()
{
CloseSocket();
}
HttpSocket::~HttpSocket() { CloseSocket(); }
void HttpSocket::CloseSocket()
{
void HttpSocket::CloseSocket() {
if (socket_fd_ != -1) {
close(socket_fd_);
socket_fd_ = -1;
close(socket_fd_);
socket_fd_ = -1;
}
if (secure_connect_) {
if (ssl_) {
@@ -120,11 +114,10 @@ void HttpSocket::GetDomainNameAndPathFromUrl(const std::string& url,
}
}
bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_timeout)
{
secure_connect_ = (strstr(url, "https") != NULL) ? true : false;
if (secure_connect_)
ssl_ctx_ = InitSslContext();
bool HttpSocket::Connect(const char* url, const std::string& port,
bool enable_timeout, bool secure_connection) {
secure_connect_ = secure_connection;
if (secure_connect_) ssl_ctx_ = InitSslContext();
GetDomainNameAndPathFromUrl(url, domain_name_, resource_path_);
@@ -135,7 +128,8 @@ bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_t
}
int reuse = 1;
if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) ==
-1) {
CloseSocket();
LOGE("setsockopt error %d", errno);
return false;
@@ -143,7 +137,7 @@ bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_t
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo* addr_info = NULL;
bool status = true;
@@ -155,7 +149,8 @@ bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_t
} else {
if (connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen) == -1) {
CloseSocket();
LOGE("cannot connect socket to %s, error=%d", domain_name_.c_str(), errno);
LOGE("cannot connect socket to %s, error=%d", domain_name_.c_str(),
errno);
status = false;
}
}
@@ -164,36 +159,38 @@ bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_t
freeaddrinfo(addr_info);
}
if (!status) return false;
// secures connection
if (secure_connect_ && ssl_ctx_) {
ssl_ = SSL_new(ssl_ctx_);
if (ssl_) {
BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE);
if (a_bio) {
SSL_set_bio(ssl_, a_bio, a_bio);
int ret = SSL_connect(ssl_);
if (1 != ret) {
char buf[256];
LOGE("SSL_connect error:%s", ERR_error_string(ERR_get_error(), buf));
}
} else {
LOGE("BIO_new_socket error");
}
} else {
if (!ssl_) {
LOGE("failed SSL_new");
return false;
}
BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE);
if (!a_bio) {
LOGE("BIO_new_socket error");
return false;
}
SSL_set_bio(ssl_, a_bio, a_bio);
int ret = SSL_connect(ssl_);
if (1 != ret) {
char buf[256];
LOGE("SSL_connect error:%s", ERR_error_string(ERR_get_error(), buf));
return false;
}
}
return status;
return true;
}
int HttpSocket::Read(char* data, int len) {
return(Read(data, len, 0));
}
int HttpSocket::Read(char* data, int len) { return (Read(data, len, 0)); }
// makes non-blocking mode only during read, it supports timeout for read
// returns -1 for error, number of bytes read for success
int HttpSocket::Read(char* data, int len, int timeout_in_ms)
{
int HttpSocket::Read(char* data, int len, int timeout_in_ms) {
bool use_timeout = (timeout_enabled_ && (timeout_in_ms > 0));
int original_flags = 0;
if (use_timeout) {
@@ -216,7 +213,7 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms)
fd_set read_fds;
struct timeval tv;
tv.tv_sec = timeout_in_ms / 1000;
tv.tv_usec = (timeout_in_ms % 1000) * 1000;
tv.tv_usec = (timeout_in_ms % 1000) * 1000;
FD_ZERO(&read_fds);
FD_SET(socket_fd_, &read_fds);
if (select(socket_fd_ + 1, &read_fds, NULL, NULL, &tv) == -1) {
@@ -224,7 +221,7 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms)
break;
}
if (!FD_ISSET(socket_fd_, &read_fds)) {
LOGE("socket read timeout");
LOGD("socket read timeout");
break;
}
}
@@ -240,7 +237,8 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms)
total_read += read;
} else if (read == 0) {
// in blocking mode, zero read mean's peer closed.
// in non-blocking mode, select said that there is data. so it should not happen
// in non-blocking mode, select said that there is data. so it should not
// happen
break;
} else {
LOGE("recv returned %d, error = %d", read, errno);
@@ -249,13 +247,12 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms)
}
if (use_timeout) {
fcntl(socket_fd_, F_SETFL, original_flags); // now blocking again
fcntl(socket_fd_, F_SETFL, original_flags); // now blocking again
}
return total_read;
}
int HttpSocket::Write(const char* data, int len)
{
int HttpSocket::Write(const char* data, int len) {
int total_sent = 0;
int sent = 0;
int to_send = len;
@@ -270,7 +267,7 @@ int HttpSocket::Write(const char* data, int len)
data += sent;
total_sent += sent;
} else if (sent == 0) {
usleep(10); // retry later
usleep(10); // retry later
} else {
LOGE("send returned error %d", errno);
}
@@ -278,4 +275,4 @@ int HttpSocket::Write(const char* data, int len)
return total_sent;
}
} // namespace wvcdm
} // namespace wvcdm

View File

@@ -16,7 +16,8 @@ class HttpSocket {
~HttpSocket();
void CloseSocket();
bool Connect(const char* url, const std::string& port, bool enable_timeout);
bool Connect(const char* url, const std::string& port, bool enable_timeout,
bool secure_connection);
void GetDomainNameAndPathFromUrl(const std::string& url,
std::string& domain_name,
std::string& resource_path);
@@ -27,7 +28,9 @@ class HttpSocket {
int Write(const char* data, int len);
private:
void CloseSslContext(SSL_CTX* ctx) const { if (ctx) SSL_CTX_free(ctx); }
void CloseSslContext(SSL_CTX* ctx) const {
if (ctx) SSL_CTX_free(ctx);
}
SSL_CTX* InitSslContext(void);
void ShowServerCertificate(const SSL* ssl);

View File

@@ -5,12 +5,14 @@
#include "http_socket.h"
#include "log.h"
#include "string_conversions.h"
#include "url_request.h"
namespace {
std::string gTestServer("https://www.google.com");
std::string gTestData("Hello");
const int kHttpBufferSize = 4096;
char gBuffer[kHttpBufferSize];
const std::string kHttpsTestServer("https://www.google.com");
std::string gTestServer(kHttpsTestServer);
std::string gTestData("Hello");
const int kHttpBufferSize = 4096;
char gBuffer[kHttpBufferSize];
}
namespace wvcdm {
@@ -21,9 +23,10 @@ class HttpSocketTest : public testing::Test {
~HttpSocketTest() { socket_.CloseSocket(); }
protected:
bool Connect(const std::string& server_url) {
bool Connect(const std::string& server_url, bool secure_connection) {
if (socket_.Connect(server_url.c_str(), "80", true)) {
std::string port = secure_connection ? "443" : "80";
if (socket_.Connect(server_url.c_str(), port, true, secure_connection)) {
LOGD("connected to %s", socket_.domain_name().c_str());
} else {
LOGE("failed to connect to %s", socket_.domain_name().c_str());
@@ -44,7 +47,7 @@ class HttpSocketTest : public testing::Test {
request.append("\r\nUser-Agent: httpSocketTest/1.0\r\n");
request.append("Content-Length: ");
memset(gBuffer, 0, kHttpBufferSize);
snprintf(gBuffer, kHttpBufferSize, "%d\r\n", static_cast<int>(data.size()));
snprintf(gBuffer, kHttpBufferSize, "%d\r\n", static_cast<int>(data.size()));
request.append(gBuffer);
request.append("Content-Type: multipart/form-data\r\n");
@@ -77,80 +80,89 @@ class HttpSocketTest : public testing::Test {
std::string resource_path_;
};
TEST_F(HttpSocketTest, GetDomainNameAndPathFromUrlTest)
{
socket_.GetDomainNameAndPathFromUrl("http://code.google.com/p/googletest/wiki/Primer",
domain_name_,
resource_path_);
TEST_F(HttpSocketTest, GetDomainNameAndPathFromUrlTest) {
socket_.GetDomainNameAndPathFromUrl(
"https://code.google.com/p/googletest/wiki/Primer", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("http://code.google.com/p/googletest/wiki/Primer/",
domain_name_,
resource_path_);
socket_.GetDomainNameAndPathFromUrl(
"http://code.google.com/p/googletest/wiki/Primer/", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("p/googletest/wiki/Primer/", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("http://code.google.com/",
domain_name_,
socket_.GetDomainNameAndPathFromUrl("http://code.google.com/", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("http://code.google.com",
domain_name_,
socket_.GetDomainNameAndPathFromUrl("http://code.google.com", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("code.google.com/p/googletest/wiki/Primer",
domain_name_,
resource_path_);
socket_.GetDomainNameAndPathFromUrl(
"code.google.com/p/googletest/wiki/Primer", domain_name_, resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("code.google.com",
domain_name_,
socket_.GetDomainNameAndPathFromUrl("code.google.com", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("code.google.com/",
domain_name_,
socket_.GetDomainNameAndPathFromUrl("code.google.com/", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("",
domain_name_,
resource_path_);
socket_.GetDomainNameAndPathFromUrl("", domain_name_, resource_path_);
EXPECT_TRUE(domain_name_.empty());
EXPECT_TRUE(resource_path_.empty());
socket_.GetDomainNameAndPathFromUrl("http://10.21.200.68:8888/drm",
domain_name_,
resource_path_);
domain_name_, resource_path_);
EXPECT_STREQ("10.21.200.68", domain_name_.c_str());
EXPECT_STREQ("drm", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("http://10.21.200.68:8888",
domain_name_,
socket_.GetDomainNameAndPathFromUrl("http://10.21.200.68:8888", domain_name_,
resource_path_);
EXPECT_STREQ("10.21.200.68", domain_name_.c_str());
EXPECT_TRUE(resource_path_.empty());
}
TEST_F(HttpSocketTest, ConnectTest)
{
EXPECT_TRUE(Connect(gTestServer));
TEST_F(HttpSocketTest, ConnectTest) {
const bool kUseSecureConnection = true;
if (gTestServer.find("https") != std::string::npos) {
EXPECT_TRUE(Connect(gTestServer, kUseSecureConnection));
socket_.CloseSocket();
// https connection allows insecure connection through port 80 as well
EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection));
socket_.CloseSocket();
} else {
EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection));
socket_.CloseSocket();
// Test for the case that non-https connection must not use port 443
EXPECT_FALSE(Connect(gTestServer, kUseSecureConnection));
socket_.CloseSocket();
}
EXPECT_FALSE(Connect("ww.g.c", kUseSecureConnection));
socket_.CloseSocket();
EXPECT_FALSE(Connect("ww.g.c"));
EXPECT_FALSE(Connect("ww.g.c", !kUseSecureConnection));
socket_.CloseSocket();
}
TEST_F(HttpSocketTest, RoundTripTest)
{
ASSERT_TRUE(Connect(gTestServer));
TEST_F(HttpSocketTest, RoundTripTest) {
int secure_connection =
(gTestServer.find("https") != std::string::npos) ? true : false;
ASSERT_TRUE(Connect(gTestServer, secure_connection));
EXPECT_TRUE(PostRequest(gTestData));
GetResponse();
socket_.CloseSocket();
@@ -158,28 +170,31 @@ TEST_F(HttpSocketTest, RoundTripTest)
} // namespace wvcdm
int main(int argc, char **argv) {
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
std::string temp;
std::string test_server(gTestServer);
std::string test_server(kHttpsTestServer);
std::string test_data(gTestData);
for (int i=1; i<argc; i++) {
for (int i = 1; i < argc; i++) {
temp.assign(argv[i]);
if (temp.find("--server=") == 0) {
gTestServer.assign(temp.substr(strlen("--server=")));
} else if (temp.find("--data=") == 0) {
gTestData.assign(temp.substr(strlen("--data=")));
}
else {
} else {
std::cout << "error: unknown option '" << argv[i] << "'" << std::endl;
std::cout << "usage: http_socket_test [options]" << std::endl << std::endl;
std::cout << "usage: http_socket_test [options]" << std::endl
<< std::endl;
std::cout << std::setw(30) << std::left << " --server=<server_url>";
std::cout << "configure the test server url, please include http[s] in the url" << std::endl;
std::cout
<< "configure the test server url, please include http[s] in the url"
<< std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << test_server << std::endl;
std::cout << std::setw(30) << std::left << " --data=<data>";
std::cout << "configure data to send, in ascii string format" << std::endl;
std::cout << "configure data to send, in ascii string format"
<< std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << test_data << std::endl << std::endl;
return 0;

View File

@@ -1,6 +1,5 @@
// Copyright 2012 Google Inc. All Rights Reserved.
#include "crypto_engine.h"
#include "crypto_session.h"
#include "license.h"
#include "gtest/gtest.h"
@@ -49,15 +48,13 @@ namespace wvcdm {
class LicenseTest : public ::testing::Test {
protected:
virtual void SetUp() {
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
EXPECT_TRUE(crypto_engine != NULL);
session_ = crypto_engine->CreateSession("Dummy");
session_ = new CryptoSession();
EXPECT_TRUE(session_ != NULL);
std::string token;
EXPECT_TRUE(crypto_engine->GetToken(&token));
EXPECT_TRUE(session_->GetToken(&token));
EXPECT_TRUE(session_->IsOpen());
EXPECT_TRUE(session_->Open());
EXPECT_TRUE(license_.Init(token, session_, &policy_engine_));
}
@@ -76,7 +73,8 @@ TEST(LicenseTestSession, InitNullSession) {
EXPECT_FALSE(license.Init("Dummy", NULL, NULL));
}
TEST_F(LicenseTest, PrepareKeyRequest) {
// TODO(rfrias): Fix or remove test.
TEST_F(LicenseTest, DISABLED_PrepareKeyRequest) {
std::string signed_request;
CdmAppParameterMap app_parameters;
std::string server_url;
@@ -88,7 +86,8 @@ TEST_F(LicenseTest, PrepareKeyRequest) {
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
}
TEST_F(LicenseTest, HandleKeyResponseValid) {
// TODO(rfrias): Fix or remove test.
TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
std::string signed_request;
CdmAppParameterMap app_parameters;
std::string server_url;
@@ -101,7 +100,8 @@ TEST_F(LicenseTest, HandleKeyResponseValid) {
EXPECT_TRUE(license_.HandleKeyResponse(a2bs_hex(kValidResponse)));
}
TEST_F(LicenseTest, HandleKeyResponseInvalid) {
// TODO(rfrias): Fix or remove test.
TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) {
std::string signed_request;
CdmAppParameterMap app_parameters;
std::string server_url;

View File

@@ -65,6 +65,10 @@ class PolicyEngineTest : public ::testing::Test {
}
virtual void TearDown() {
delete policy_engine_;
// Done by policy engine: delete mock_clock_;
policy_engine_ = NULL;
mock_clock_ = NULL;
}
MockClock* mock_clock_;
@@ -85,7 +89,6 @@ TEST_F(PolicyEngineTest, NoLicense) {
TEST_F(PolicyEngineTest, PlaybackSuccess) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(3)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 10));
@@ -103,10 +106,7 @@ TEST_F(PolicyEngineTest, PlaybackSuccess) {
TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(3)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 10));
.WillOnce(Return(license_start_time_ + 5));
License_Policy* policy = license_.mutable_policy();
policy->set_can_play(false);
@@ -126,7 +126,6 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) {
TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(4)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 3600))
@@ -157,7 +156,6 @@ TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) {
TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(4)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 10000))
.WillOnce(Return(license_start_time_ + 13598))
@@ -185,7 +183,6 @@ TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) {
TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(4)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 3600))
@@ -213,7 +210,6 @@ TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) {
TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(4)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 3600))
@@ -242,7 +238,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) {
TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(4)
.WillOnce(Return(license_start_time_ + 10000))
.WillOnce(Return(license_start_time_ + 10005))
.WillOnce(Return(license_start_time_ + 13598))
@@ -271,7 +266,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) {
TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(4)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 3600))
@@ -300,7 +294,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) {
TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(4)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 604800))
@@ -333,7 +326,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(5)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
@@ -366,7 +358,6 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) {
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(6)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
@@ -406,7 +397,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) {
TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(6)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
@@ -450,7 +440,6 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(10)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
@@ -509,7 +498,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
TEST_F(PolicyEngineTest, PlaybackOk_RenewedSuccessAfterExpiry) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(10)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
@@ -569,7 +557,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedSuccessAfterExpiry) {
TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(6)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 10))
@@ -608,7 +595,6 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) {
TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(1)
.WillOnce(Return(license_start_time_));
CdmQueryMap query_info;
@@ -617,7 +603,6 @@ TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) {
TEST_F(PolicyEngineTest, QuerySuccess) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(2)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 100));
@@ -647,10 +632,7 @@ TEST_F(PolicyEngineTest, QuerySuccess) {
TEST_F(PolicyEngineTest, QuerySuccess_Offline) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(4)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 10))
.WillOnce(Return(license_start_time_ + 100));
LicenseIdentification* id = license_.mutable_id();
@@ -693,7 +675,6 @@ TEST_F(PolicyEngineTest, QuerySuccess_Offline) {
TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.Times(4)
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 10))

View File

@@ -14,7 +14,7 @@ class TestTimerHandler : public TimerHandler {
timer_events_++;
}
uint32_t GetTimerEvents() { return timer_events_; }
uint32_t timer_events() { return timer_events_; }
void ResetTimerEvents() { timer_events_ = 0; }
private:
@@ -34,21 +34,21 @@ TEST(TimerTest, TimerCheck) {
Timer timer;
uint32_t duration = 10;
EXPECT_EQ(static_cast<uint32_t>(0), handler.GetTimerEvents());
EXPECT_EQ(0u, handler.timer_events());
EXPECT_FALSE(timer.IsRunning());
EXPECT_TRUE(timer.Start(&handler, 1));
EXPECT_TRUE(timer.IsRunning());
sleep(duration);
EXPECT_TRUE(duration-1 <= handler.GetTimerEvents());
EXPECT_TRUE(handler.GetTimerEvents() <= duration+1);
EXPECT_LE(duration-1, handler.timer_events());
EXPECT_LE(handler.timer_events(), duration+1);
timer.Stop();
EXPECT_FALSE(timer.IsRunning());
sleep(duration);
EXPECT_TRUE(duration-1 <= handler.GetTimerEvents());
EXPECT_TRUE(handler.GetTimerEvents() <= duration+1);
EXPECT_LE(duration-1, handler.timer_events());
EXPECT_LE(handler.timer_events(), duration+1);
}
}

View File

@@ -2,6 +2,7 @@
#include "url_request.h"
#include <errno.h>
#include <sstream>
#include "http_socket.h"
@@ -10,28 +11,26 @@
namespace wvcdm {
UrlRequest::UrlRequest(const std::string& url, const std::string& port)
: is_connected_(false),
UrlRequest::UrlRequest(const std::string& url, const std::string& port,
bool secure_connection, bool chunk_transfer_mode)
: chunk_transfer_mode_(chunk_transfer_mode),
is_connected_(false),
port_("80"),
request_(""),
server_url_(url)
{
server_url_(url) {
if (!port.empty()) {
port_.assign(port);
}
if (socket_.Connect((server_url_).c_str(), port_, true)) {
if (socket_.Connect((server_url_).c_str(), port_, true, secure_connection)) {
LOGD("connected to %s", socket_.domain_name().c_str());
is_connected_ = true;
} else {
LOGE("failed to connect to %s, port=%s",
socket_.domain_name().c_str(), port.c_str());
LOGE("failed to connect to %s, port=%s", socket_.domain_name().c_str(),
port.c_str());
}
}
UrlRequest::~UrlRequest()
{
socket_.CloseSocket();
}
UrlRequest::~UrlRequest() { socket_.CloseSocket(); }
void UrlRequest::AppendChunkToUpload(const std::string& data) {
// format of chunk:
@@ -49,24 +48,97 @@ void UrlRequest::AppendChunkToUpload(const std::string& data) {
request_.append("\r\n"); // marks end of data
}
int UrlRequest::GetResponse(std::string& response) {
response.clear();
// Concatenate all chunks into one blob and returns the response with
// header information.
void UrlRequest::ConcatenateChunkedResponse(const std::string http_response,
std::string* modified_response) {
if (http_response.empty()) return;
const int kTimeoutInMs = 1500 * 2;
modified_response->clear();
const std::string kChunkedTag = "Transfer-Encoding: chunked\r\n\r\n";
size_t chunked_tag_pos = http_response.find(kChunkedTag);
if (std::string::npos != chunked_tag_pos) {
// processes chunked encoding
size_t chunk_size = 0;
size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size();
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);
return;
}
// Search for chunks in the following format:
// header
// chunk size\r\n <-- chunk_size_pos @ beginning of chunk size
// chunk data\r\n <-- chunk_pos @ beginning of chunk data
// chunk size\r\n
// chunk data\r\n
// 0\r\n
const std::string kCrLf = "\r\n";
size_t chunk_pos = http_response.find(kCrLf, chunk_size_pos);
modified_response->assign(http_response, 0, chunk_size_pos);
while ((chunk_size > 0) && (std::string::npos != chunk_pos)) {
chunk_pos += kCrLf.size();
modified_response->append(http_response, chunk_pos, chunk_size);
// Search for next chunk
chunk_size_pos = chunk_pos + chunk_size + kCrLf.size();
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);
break;
}
chunk_pos = http_response.find(kCrLf, chunk_size_pos);
}
} else {
// Response is not chunked encoded
modified_response->assign(http_response);
}
}
void UrlRequest::DumpMessage(const std::string& description,
const std::string& message) {
if (description.empty()) return;
LOGD("%s (%d bytes):", description.c_str(), message.size());
size_t remaining = message.size();
size_t portion = 0;
size_t start = 0;
while (remaining > 0) {
// LOGX may not get to empty its buffer if it is too large,
// pick an arbitrary small size to be safe
portion = (remaining < 512) ? remaining : 512;
LOGD("%s", message.substr(start, portion).c_str());
start += portion;
remaining -= portion;
}
LOGD("total bytes dumped(%d)", start);
}
int UrlRequest::GetResponse(std::string* message) {
message->clear();
std::string response;
const int kTimeoutInMs = 3000;
int bytes = 0;
int total_bytes = 0;
do {
memset(buffer_, 0, kHttpBufferSize);
bytes = socket_.Read(buffer_, kHttpBufferSize, kTimeoutInMs);
if (bytes > 0) {
response.append(buffer_, bytes);
total_bytes += bytes;
} else {
if (bytes < 0) LOGE("read error = ", errno);
// bytes == 0 indicates nothing to read
}
} while (bytes > 0);
return total_bytes;
ConcatenateChunkedResponse(response, message);
LOGD("%d bytes returned", message->size());
return message->size();
}
int UrlRequest::GetStatusCode(const std::string& response) {
@@ -106,6 +178,9 @@ bool UrlRequest::PostRequestChunk(const std::string& data) {
}
bool UrlRequest::PostRequest(const std::string& data) {
if (chunk_transfer_mode_) {
return PostRequestChunk(data);
}
request_.assign("POST /");
request_.append(socket_.resource_path());
request_.append(" HTTP/1.1\r\n");
@@ -149,4 +224,4 @@ bool UrlRequest::PostCertRequestInQueryString(const std::string& data) {
return true;
}
} // namespace wvcdm
} // namespace wvcdm

View File

@@ -13,12 +13,16 @@ namespace wvcdm {
// Only POST request method is implemented.
class UrlRequest {
public:
UrlRequest(const std::string& url, const std::string& port);
UrlRequest(const std::string& url, const std::string& port,
bool secure_connect, bool chunk_transfer_mode);
~UrlRequest();
void AppendChunkToUpload(const std::string& data);
int GetResponse(std::string& response);
int GetStatusCode(const std::string& response);
void ConcatenateChunkedResponse(const std::string http_response,
std::string* modified_response);
void DumpMessage(const std::string& description, const std::string& message);
int GetResponse(std::string* message);
int GetStatusCode(const std::string& response);
bool is_connected() const { return is_connected_; }
bool PostRequest(const std::string& data);
bool PostRequestChunk(const std::string& data);
@@ -27,6 +31,7 @@ class UrlRequest {
private:
static const unsigned int kHttpBufferSize = 4096;
char buffer_[kHttpBufferSize];
bool chunk_transfer_mode_;
bool is_connected_;
std::string port_;
std::string request_;

View File

@@ -1,201 +0,0 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/at_exit.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "string_conversions.h"
#include "wv_content_decryption_module.h"
namespace {
// Default license server to play server, can be configured using --server command line option
std::string gGpLicenseServer =
"https://jmt17.google.com/video-dev/license/GetCencLicense";
std::string gYtLicenseServer =
"https://www.youtube.com/api/drm/widevine?video_id=03681262dc412c06&source=YOUTUBE";
std::string gLicenseServer(gYtLicenseServer);
// Default key id (pssh), can be configured using --keyid command line option
std::string gKeyID = "000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data
// An invalid key id, expected to fail
std::string kWrongKeyID = "000000347073736800000000" // blob size and psshb
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0901121094889920E8D6520098577DF8F2DD5546"; // pssh data
static const char kWidevineKeySystem[] = "com.widevine.alpha";
} // namespace
namespace wvcdm_test {
class WvCdmDecryptorTest : public testing::Test {
public:
WvCdmDecryptorTest() {}
~WvCdmDecryptorTest() {}
protected:
void GenerateKeyRequest(const std::string& key_system,
const std::string& init_data) {
EXPECT_EQ(decryptor_.GenerateKeyRequest(key_system,
init_data,
&key_msg_,
&session_id_), wvcdm::KEY_MESSAGE);
}
void GenerateRenewalRequest(const std::string& key_system,
const std::string& init_data) {
EXPECT_EQ(decryptor_.GenerateRenewalRequest(key_system,
init_data,
session_id_, &key_msg_),
wvcdm::KEY_MESSAGE);
}
std::string GetKeyRequestResponse(const std::string& server_url,
int expected_response) {
net::TestDelegate d;
net::TestNetworkDelegate network_delegate;
net::TestURLRequestContext context(true);
context.set_network_delegate(&network_delegate);
scoped_ptr<net::HostResolver> resolver(
net::HostResolver::CreateDefaultResolver(NULL));
context.set_host_resolver(resolver.get());
context.Init();
net::URLRequest r(GURL(server_url), &d, &context);
r.EnableChunkedUpload();
r.set_method("POST");
r.AppendChunkToUpload(key_msg_.data(), key_msg_.size(), true);
r.Start();
EXPECT_TRUE(r.is_pending());
MessageLoop::current()->Run();
std::string data = d.data_received();
// Youtube server returns 400 for invalid message while play server returns
// 500, so just test inequity here for invalid message
if (expected_response == 200) {
EXPECT_EQ(200, r.GetResponseCode()) << data;
} else {
EXPECT_NE(200, r.GetResponseCode()) << data;
}
EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status());
EXPECT_TRUE(d.bytes_received() > 0);
// Extract DRM message:
// https://docs.google.com/a/google.com/document/d/1Xue3bgwv2qIAnuFIZ-HCcix43dvH2UxsOEA_8FCBO3I/edit#
if (r.status().status() == net::URLRequestStatus::SUCCESS) {
size_t pos = data.find("\r\n");
if (pos != data.npos) data = data.substr(pos+2);
}
return data;
}
void VerifyKeyRequestResponse(const std::string& server_url,
std::string& init_data,
bool is_renewal) {
std::string resp = GetKeyRequestResponse(server_url, 200);
std::cout << "Message: " << wvcdm::b2a_hex(key_msg_) << std::endl;
std::cout << "Response: " << wvcdm::b2a_hex(resp) << std::endl;
if (is_renewal) {
EXPECT_EQ(decryptor_.RenewKey(kWidevineKeySystem,
init_data, resp,
session_id_), wvcdm::KEY_ADDED);
}
else {
EXPECT_EQ(decryptor_.AddKey(kWidevineKeySystem,
init_data, resp,
session_id_), wvcdm::KEY_ADDED);
}
std::cout << "back from AddKey" << std::endl;
}
wvcdm::WvContentDecryptionModule decryptor_;
std::string key_msg_;
std::string session_id_;
};
TEST_F(WvCdmDecryptorTest, BaseMessageTest)
{
GenerateKeyRequest(kWidevineKeySystem, gKeyID);
GetKeyRequestResponse(gLicenseServer, 200);
}
TEST_F(WvCdmDecryptorTest, WrongMessageTest)
{
std::string wrong_message = wvcdm::a2b_hex(kWrongKeyID);
GenerateKeyRequest(kWidevineKeySystem, wrong_message);
GetKeyRequestResponse(gLicenseServer, 500);
}
TEST_F(WvCdmDecryptorTest, NormalWebMDecryption) {
GenerateKeyRequest(kWidevineKeySystem, gKeyID);
VerifyKeyRequestResponse(gLicenseServer, gKeyID, false);
}
TEST_F(WvCdmDecryptorTest, LicenseRenewal) {
GenerateKeyRequest(kWidevineKeySystem, gKeyID);
VerifyKeyRequestResponse(gLicenseServer, gKeyID, false);
GenerateRenewalRequest(kWidevineKeySystem, gKeyID);
VerifyKeyRequestResponse(gLicenseServer, gKeyID, true);
}
} // namespace wvcdm_test
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
std::string temp;
std::string license_server(gLicenseServer);
std::string key_id(gKeyID);
for (int i=1; i<argc; i++) {
temp.assign(argv[i]);
if (temp.find("--server=") == 0) {
gLicenseServer.assign(temp.substr(strlen("--server=")));
}
else if (temp.find("--keyid=") == 0) {
gKeyID.assign(temp.substr(strlen("--keyid=")));
}
else {
std::cout << "error: unknown option '" << argv[i] << "'" << std::endl;
std::cout << "usage: wvcdm_test [options]" << std::endl << std::endl;
std::cout << std::setw(30) << std::left << " --server=<server_url>";
std::cout << "configure the license server url, please include http[s] in the url" << std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << license_server << std::endl;
std::cout << std::setw(30) << std::left << " --keyid=<key_id>";
std::cout << "configure the key id or pssh, in hex format" << std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << key_id << std::endl << std::endl;
return 0;
}
}
std::cout << std::endl;
std::cout << "Server: " << gLicenseServer << std::endl;
std::cout << "KeyID: " << gKeyID << std::endl << std::endl;
gKeyID = wvcdm::a2b_hex(gKeyID);
base::AtExitManager exit;
MessageLoop ttr(MessageLoop::TYPE_IO);
return RUN_ALL_TESTS();
}

View File

@@ -26,6 +26,11 @@ const bool kPropertyOemCryptoUseUserSpaceBuffers = false;
// and passed as the token in the license request
const bool kPropertyUseCertificatesAsIdentification = true;
// If false, extraction of widevine PSSH information from the PSSH box
// takes place external to the CDM. This will become the default behaviour
// once all platforms support it (b/9465346)
const bool kExtractPsshData = true;
} // namespace wvcdm
#endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_

View File

@@ -34,7 +34,7 @@ class WvContentDecryptionModule {
// Accept license response and extract key info.
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data,
CdmKeySetId& key_set_id);
CdmKeySetId* key_set_id);
// Setup keys for offline usage which were retrived in an earlier key request
virtual CdmResponseType RestoreKey(const CdmSessionId& session_id,

View File

@@ -14,31 +14,31 @@
#include "log.h"
namespace {
const char* kCurrentDirectory = ".";
const char* kParentDirectory = "..";
}
const char* kCurrentDirectory = ".";
const char* kParentDirectory = "..";
} // namespace
namespace wvcdm {
class File::Impl {
public:
Impl() : file_(NULL) {}
Impl(const std::string& file_path) : file_(NULL), file_path_(file_path) {}
virtual ~Impl() {}
FILE* file_;
std::string file_path_;
};
File::File() : impl_(new File::Impl()) {}
File::File(const std::string& file_path, int flags) :
impl_(new File::Impl()) {
Open(file_path, flags);
}
File::~File() {
Close();
delete impl_;
}
bool File::Open(const std::string& name, int flags) {
std::string openFlags = "";
std::string open_flags;
if (((flags & File::kTruncate) && Exists(name)) ||
((flags & File::kCreate) && !Exists(name))) {
@@ -49,39 +49,29 @@ bool File::Open(const std::string& name, int flags) {
}
if (flags & File::kBinary) {
openFlags = (flags & File::kReadOnly)? "rb" : "rb+";
open_flags = (flags & File::kReadOnly)? "rb" : "rb+";
} else {
openFlags = (flags & File::kReadOnly)? "r" : "r+";
open_flags = (flags & File::kReadOnly)? "r" : "r+";
}
impl_->file_ = fopen(name.c_str(), openFlags.c_str());
impl_->file_ = fopen(name.c_str(), open_flags.c_str());
if (!impl_->file_) {
LOGW("File::Open: fopen failed: %d", errno);
}
return IsOpen();
impl_->file_path_ = name;
return impl_->file_ != NULL;
}
void File::Close() {
if (impl_->file_) {
fclose(impl_->file_);
impl_->file_ = NULL;
}
}
}
bool File::IsOpen() {
return impl_->file_ != NULL;
}
bool File::IsBad() {
if (impl_->file_)
return ferror(impl_->file_) != 0;
else
return true;
}
ssize_t File::Read(void* buffer, size_t bytes) {
ssize_t File::Read(char* buffer, size_t bytes) {
if (impl_->file_) {
size_t len = fread(buffer, 1, bytes, impl_->file_);
size_t len = fread(buffer, sizeof(char), bytes, impl_->file_);
if (len == 0) {
LOGW("File::Read: fread failed: %d", errno);
}
@@ -91,9 +81,9 @@ ssize_t File::Read(void* buffer, size_t bytes) {
return -1;
}
ssize_t File::Write(const void* buffer, size_t bytes) {
ssize_t File::Write(const char* buffer, size_t bytes) {
if (impl_->file_) {
size_t len = fwrite(buffer, 1, bytes, impl_->file_);
size_t len = fwrite(buffer, sizeof(char), bytes, impl_->file_);
if (len == 0) {
LOGW("File::Write: fwrite failed: %d", errno);
}
@@ -103,27 +93,27 @@ ssize_t File::Write(const void* buffer, size_t bytes) {
return -1;
}
bool File::Exists(const std::string& file) {
bool File::Exists(const std::string& path) {
struct stat buf;
int res = stat(file.c_str(), &buf) == 0;
int res = stat(path.c_str(), &buf) == 0;
if (!res) {
LOGV("File::Exists: stat failed: %d", errno);
}
return res;
}
bool File::Remove(const std::string& file_path) {
if (IsDirectory(file_path)) {
bool File::Remove(const std::string& path) {
if (IsDirectory(path)) {
DIR* dir;
if ((dir = opendir(file_path.c_str())) != NULL) {
if ((dir = opendir(path.c_str())) != NULL) {
// first remove files and dir within it
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, kCurrentDirectory) &&
(strcmp(entry->d_name, kParentDirectory))) {
std::string file_path_to_remove = file_path + '/';
file_path_to_remove += entry->d_name;
if (!Remove(file_path_to_remove)) {
std::string path_to_remove = path + '/';
path_to_remove += entry->d_name;
if (!Remove(path_to_remove)) {
closedir(dir);
return false;
}
@@ -131,13 +121,13 @@ bool File::Remove(const std::string& file_path) {
}
closedir(dir);
}
if (rmdir(file_path.c_str())) {
if (rmdir(path.c_str())) {
LOGW("File::Remove: rmdir failed: %d", errno);
return false;
}
return true;
} else {
if (unlink(file_path.c_str())) {
if (unlink(path.c_str()) && (errno != ENOENT)) {
LOGW("File::Remove: unlink failed: %d", errno);
return false;
}
@@ -195,9 +185,9 @@ bool File::IsRegularFile(const std::string& path) {
return false;
}
ssize_t File::FileSize(const std::string& file_path) {
ssize_t File::FileSize(const std::string& path) {
struct stat buf;
if (stat(file_path.c_str(), &buf) == 0)
if (stat(path.c_str(), &buf) == 0)
return buf.st_size;
else
return -1;

View File

@@ -10,7 +10,9 @@
namespace wvcdm {
void log_write(LogPriority level, const char *fmt, ...) {
void InitLogging(int argc, const char* const* argv) {}
void Log(const char* file, int line, LogPriority level, const char* fmt, ...) {
va_list ap;
char buf[LOG_BUF_SIZE];
va_start(ap, fmt);

View File

@@ -1,46 +0,0 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include <string>
#include "cutils/properties.h"
#include "properties.h"
namespace {
bool GetAndroidProperty(const char* key, std::string& value) {
char val[PROPERTY_VALUE_MAX];
if (property_get(key, val, "Unknown") <= 0)
return false;
value = val;
return true;
}
}
namespace wvcdm {
bool Properties::GetCompanyName(std::string& company_name) {
return GetAndroidProperty("ro.product.manufacturer", company_name);
}
bool Properties::GetModelName(std::string& model_name) {
return GetAndroidProperty("ro.product.model", model_name);
}
bool Properties::GetArchitectureName(std::string& arch_name) {
return GetAndroidProperty("ro.product.cpu.abi", arch_name);
}
bool Properties::GetDeviceName(std::string& device_name) {
return GetAndroidProperty("ro.product.device", device_name);
}
bool Properties::GetProductName(std::string& product_name) {
return GetAndroidProperty("ro.product.name", product_name);
}
bool Properties::GetBuildInfo(std::string& build_info) {
return GetAndroidProperty("ro.build.fingerprint", build_info);
}
} // namespace wvcdm

View File

@@ -0,0 +1,95 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "properties.h"
#include <sstream>
#include <string>
#include <unistd.h>
#include "cutils/properties.h"
#include "log.h"
namespace {
bool GetAndroidProperty(const char* key, std::string* value) {
char val[PROPERTY_VALUE_MAX];
if (!key) {
LOGW("GetAndroidProperty: Invalid property key parameter");
return false;
}
if (!value) {
LOGW("GetAndroidProperty: Invalid property value parameter");
return false;
}
if (property_get(key, val, "Unknown") <= 0) return false;
*value = val;
return true;
}
} // namespace
namespace wvcdm {
bool Properties::GetCompanyName(std::string* company_name) {
if (!company_name) {
LOGW("Properties::GetCompanyName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.manufacturer", company_name);
}
bool Properties::GetModelName(std::string* model_name) {
if (!model_name) {
LOGW("Properties::GetModelName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.model", model_name);
}
bool Properties::GetArchitectureName(std::string* arch_name) {
if (!arch_name) {
LOGW("Properties::GetArchitectureName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.cpu.abi", arch_name);
}
bool Properties::GetDeviceName(std::string* device_name) {
if (!device_name) {
LOGW("Properties::GetDeviceName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.device", device_name);
}
bool Properties::GetProductName(std::string* product_name) {
if (!product_name) {
LOGW("Properties::GetProductName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.name", product_name);
}
bool Properties::GetBuildInfo(std::string* build_info) {
if (!build_info) {
LOGW("Properties::GetBuildInfo: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.build.fingerprint", build_info);
}
bool Properties::GetDeviceFilesBasePath(std::string* base_path) {
if (!base_path) {
LOGW("Properties::GetDeviceFilesBasePath: Invalid parameter");
return false;
}
std::stringstream ss;
ss << "/data/mediadrm/IDM" << getuid() << "/";
*base_path = ss.str();
return true;
}
} // namespace wvcdm

View File

@@ -26,7 +26,10 @@ class Timer::Impl : virtual public android::RefBase {
private:
virtual bool threadLoop() {
sleep(period_);
struct timeval timeout;
timeout.tv_sec = period_;
timeout.tv_usec = 0;
TEMP_FAILURE_RETRY(select(0, NULL, NULL, NULL, &timeout));
handler_->OnTimerEvent();
return true;
}

View File

@@ -48,7 +48,7 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
app_parameters, key_request,
server_url);
if ((license_type == kLicenseTypeRelease) && (sts != KEY_MESSAGE)) {
if (license_type == kLicenseTypeRelease && sts != KEY_MESSAGE) {
cdm_engine_->CloseKeySetSession(key_set_id);
}
return sts;
@@ -57,10 +57,10 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
CdmResponseType WvContentDecryptionModule::AddKey(
const CdmSessionId& session_id,
const CdmKeyResponse& key_data,
CdmKeySetId& key_set_id) {
CdmKeySetId* key_set_id) {
CdmResponseType sts = cdm_engine_->AddKey(session_id, key_data, key_set_id);
if ((sts == KEY_ADDED) && session_id.empty()) // license type release
cdm_engine_->CloseKeySetSession(key_set_id);
if (sts == KEY_ADDED && session_id.empty()) // license type release
cdm_engine_->CloseKeySetSession(*key_set_id);
return sts;
}

View File

@@ -24,29 +24,6 @@ std::string g_license_server;
std::string g_port;
wvcdm::KeyId g_wrong_key_id;
int g_use_full_path = 0; // cannot use boolean in getopt_long
// returns production-rooted certificates that have test bit set, this test
// uses this url
const std::string kProductionTestProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1exttest/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// url returned by GetProvisioningRequest()
const std::string kProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// TODO(edwinwong, rfrias): refactor to set these parameters though config
std::string kServerSdkClientAuth = "";
wvcdm::KeyId kServerSdkKeyId = wvcdm::a2bs_hex(
"000000347073736800000000"
"edef8ba979d64acea3c827dcd51d21ed00000014"
"0801121030313233343536373839414243444546");
std::string kServerSdkLicenseServer = "http://kir03fcpg174.widevine.net/"
"widevine/cgi-bin/drm.cgi";
} // namespace
namespace wvcdm {
@@ -66,10 +43,11 @@ class TestWvCdmEventListener : public WvCdmEventListener {
TestWvCdmEventListener() : WvCdmEventListener() {}
virtual void onEvent(const CdmSessionId& id, CdmEventType event) {
session_id_ = id;
event_type_ = event;
event_type_ = event;
}
CdmSessionId session_id() { return session_id_; };
CdmEventType event_type() { return event_type_; };
CdmSessionId session_id() { return session_id_; }
CdmEventType event_type() { return event_type_; }
private:
CdmSessionId session_id_;
CdmEventType event_type_;
@@ -88,10 +66,10 @@ class WvCdmRequestLicenseTest : public testing::Test {
std::string server_url;
std::string key_set_id;
EXPECT_EQ(wvcdm::KEY_MESSAGE,
decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data,
license_type, app_parameters,
&key_msg_, &server_url));
EXPECT_EQ(0, static_cast<int>(server_url.size()));
decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data,
license_type, app_parameters,
&key_msg_, &server_url));
EXPECT_EQ(0u, server_url.size());
}
void GenerateRenewalRequest(const std::string& key_system,
@@ -101,14 +79,13 @@ class WvCdmRequestLicenseTest : public testing::Test {
std::string init_data;
wvcdm::CdmAppParameterMap app_parameters;
std::string server_url;
std::string key_set_id;
EXPECT_EQ(wvcdm::KEY_MESSAGE,
decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data,
license_type, app_parameters,
&key_msg_, &server_url));
decryptor_.GenerateKeyRequest(session_id_, key_set_id_, init_data,
license_type, app_parameters,
&key_msg_, &server_url));
// TODO(edwinwong, rfrias): Add tests cases for when license server url
// is empty on renewal. Need appropriate key id at the server.
EXPECT_NE(0, static_cast<int>(server_url.size()));
EXPECT_NE(0u, server_url.size());
}
void GenerateKeyRelease(CdmKeySetId key_set_id) {
@@ -117,120 +94,29 @@ class WvCdmRequestLicenseTest : public testing::Test {
wvcdm::CdmAppParameterMap app_parameters;
std::string server_url;
EXPECT_EQ(wvcdm::KEY_MESSAGE,
decryptor_.GenerateKeyRequest(session_id, key_set_id, init_data,
kLicenseTypeRelease, app_parameters,
&key_msg_, &server_url));
decryptor_.GenerateKeyRequest(session_id, key_set_id, init_data,
kLicenseTypeRelease, app_parameters,
&key_msg_, &server_url));
}
void DumpResponse(const std::string& description,
const std::string& response) {
if (description.empty())
return;
LOGD("%s (%d bytes):", description.c_str(), response.size());
size_t remaining = response.size();
size_t portion = 0;
size_t start = 0;
while (remaining > 0) {
// LOGX may not get to empty its buffer if it is too large,
// pick an arbitrary small size to be safe
portion = (remaining < 512) ? remaining : 512;
LOGD("%s", response.substr(start, portion).c_str());
start += portion;
remaining -= portion;
}
LOGD("total bytes dumped(%d)", start);
}
// concatinates all chunks into one blob
// TODO (edwinwong) move this function to url_request class as GetMessageBody
void ConcatenateChunkedResponse(const std::string http_response,
std::string* message_body) {
if (http_response.empty())
return;
message_body->clear();
const std::string kChunkedTag = "Transfer-Encoding: chunked\r\n\r\n";
size_t chunked_tag_pos = http_response.find(kChunkedTag);
if (std::string::npos != chunked_tag_pos) {
// processes chunked encoding
size_t chunk_size = 0;
size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size();
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);
return;
}
/*
* searches for chunks
*
* header
* chunk size\r\n <-- chunk_size_pos @ beginning of chunk size
* chunk data\r\n <-- chunk_pos @ beginning of chunk data
* chunk size\r\n
* chunk data\r\n
* 0\r\n
*/
const std::string kCrLf = "\r\n";
size_t chunk_pos = http_response.find(kCrLf, chunk_size_pos) +
kCrLf.size();
message_body->assign(&http_response[0], chunk_size_pos);
while ((chunk_size > 0) && (std::string::npos != chunk_pos)) {
message_body->append(&http_response[chunk_pos], chunk_size);
// searches for next chunk
chunk_size_pos = chunk_pos + chunk_size + kCrLf.size();
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);
break;
}
chunk_pos = http_response.find(kCrLf, chunk_size_pos) + kCrLf.size();
}
} else {
// response is not chunked encoded
message_body->assign(http_response);
}
}
// posts a request and extracts 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,
const std::string& client_auth,
int expected_response) {
std::string port;
if (server_url.find("https") != std::string::npos) {
port.assign("443");
} else {
port.assign(g_port);
}
UrlRequest url_request(server_url + client_auth, port);
// Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth, g_port, true, true);
if (!url_request.is_connected()) {
return "";
}
// TODO(edwinwong, rfrias): need a cleaner solution to handle
// HTTP servers that use chunking vs those that do not
if (server_url.compare(kServerSdkLicenseServer) == 0)
url_request.PostRequest(key_msg_);
else
url_request.PostRequestChunk(key_msg_);
std::string http_response;
std::string message_body;
int resp_bytes = url_request.GetResponse(http_response);
if (resp_bytes) {
ConcatenateChunkedResponse(http_response, &message_body);
}
url_request.PostRequest(key_msg_);
std::string message;
int resp_bytes = url_request.GetResponse(&message);
LOGD("end %d bytes response dump", resp_bytes);
LOGD("end %s ", message.c_str());
// 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_body);
int status_code = url_request.GetStatusCode(message);
if (expected_response == 200) {
EXPECT_EQ(200, status_code);
} else {
@@ -240,7 +126,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
std::string drm_msg;
if (200 == status_code) {
LicenseRequest lic_request;
lic_request.GetDrmMessage(message_body, drm_msg);
lic_request.GetDrmMessage(message, drm_msg);
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(),
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
drm_msg.size()).c_str());
@@ -248,61 +134,49 @@ class WvCdmRequestLicenseTest : public testing::Test {
return drm_msg;
}
// Posts a request and extracts the signed provisioning message from
// Post a request and extract the signed provisioning message from
// the HTTP response.
std::string GetCertRequestResponse(const std::string& server_url,
int expected_response) {
std::string port;
if (server_url.find("https") != std::string::npos) {
port.assign("443");
} else {
port.assign(g_port);
}
UrlRequest url_request(server_url, port);
// Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url, g_port, true, true);
if (!url_request.is_connected()) {
return "";
}
url_request.PostCertRequestInQueryString(key_msg_);
std::string http_response;
std::string message_body;
int resp_bytes = url_request.GetResponse(http_response);
if (resp_bytes) {
ConcatenateChunkedResponse(http_response, &message_body);
}
std::string message;
int resp_bytes = url_request.GetResponse(&message);
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_body);
int status_code = url_request.GetStatusCode(message);
if (expected_response == 200) {
EXPECT_EQ(200, status_code);
} else {
EXPECT_NE(200, status_code);
}
return message_body;
return message;
}
void VerifyKeyRequestResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data,
bool is_renewal) {
std::string resp = GetKeyRequestResponse(server_url,
client_auth,
200);
std::string& init_data, bool is_renewal) {
std::string resp = GetKeyRequestResponse(server_url, client_auth, 200);
if (is_renewal) {
// TODO application makes a license request, CDM will renew the license
// when appropriate
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, key_set_id_),
wvcdm::KEY_ADDED);
}
else {
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, key_set_id_),
wvcdm::KEY_ADDED);
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_),
wvcdm::KEY_ADDED);
} else {
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_),
wvcdm::KEY_ADDED);
}
}
wvcdm::ConfigTestEnv config_;
wvcdm::WvContentDecryptionModule decryptor_;
CdmKeyMessage key_msg_;
CdmSessionId session_id_;
@@ -311,15 +185,14 @@ class WvCdmRequestLicenseTest : public testing::Test {
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
std::string provisioning_server_url = "";
std::string provisioning_server_url;
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(&key_msg_,
&provisioning_server_url));
EXPECT_STREQ(provisioning_server_url.data(),
kProductionProvisioningServerUrl.data());
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
&key_msg_, &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url());
std::string response = GetCertRequestResponse(
kProductionTestProvisioningServerUrl, 200);
std::string response =
GetCertRequestResponse(config_.provisioning_test_server_url(), 200);
EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
decryptor_.CloseSession(session_id_);
@@ -327,23 +200,26 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
std::string provisioning_server_url = "";
std::string provisioning_server_url;
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(&key_msg_,
&provisioning_server_url));
EXPECT_STREQ(provisioning_server_url.data(),
kProductionProvisioningServerUrl.data());
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
&key_msg_, &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url());
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_.GetProvisioningRequest(
&key_msg_, &provisioning_server_url));
EXPECT_STREQ(provisioning_server_url.data(),
kProductionProvisioningServerUrl.data());
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
&key_msg_, &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url());
std::string response = GetCertRequestResponse(
kProductionTestProvisioningServerUrl, 200);
std::string response =
GetCertRequestResponse(config_.provisioning_test_server_url(), 200);
EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
response =
GetCertRequestResponse(config_.provisioning_test_server_url(), 200);
EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::UNKNOWN_ERROR,
decryptor_.HandleProvisioningResponse(response));
decryptor_.CloseSession(session_id_);
}
@@ -372,17 +248,16 @@ TEST_F(WvCdmRequestLicenseTest, AddSteamingKeyTest) {
TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, false);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, false);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
@@ -395,9 +270,9 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, false);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
@@ -412,15 +287,14 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
key_set_id_.clear();
GenerateKeyRelease(key_set_id);
key_set_id_ = key_set_id;
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, false);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
}
TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, false);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
@@ -431,8 +305,8 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
CdmSessionId restore_session_id = session_id_;
TestWvCdmEventListener listener;
EXPECT_TRUE(decryptor_.AttachEventListener(restore_session_id, &listener));
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(restore_session_id,
key_set_id));
EXPECT_EQ(wvcdm::KEY_ADDED,
decryptor_.RestoreKey(restore_session_id, key_set_id));
session_id_.clear();
key_set_id_.clear();
@@ -442,8 +316,7 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
EXPECT_TRUE(listener.session_id().size() != 0);
EXPECT_TRUE(listener.session_id().compare(restore_session_id) == 0);
EXPECT_TRUE(listener.event_type() == LICENSE_EXPIRED_EVENT);
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, false);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(restore_session_id);
}
@@ -459,13 +332,11 @@ TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) {
TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, false);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
GenerateRenewalRequest(g_key_system, kLicenseTypeOffline);
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, true);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, true);
decryptor_.CloseSession(session_id_);
}
@@ -476,7 +347,8 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) {
CdmQueryMap query_info;
CdmQueryMap::iterator itr;
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryKeyStatus(session_id_, &query_info));
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_.QueryKeyStatus(session_id_, &query_info));
itr = query_info.find(wvcdm::QUERY_KEY_LICENSE_TYPE);
ASSERT_TRUE(itr != query_info.end());
@@ -507,7 +379,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) {
itr = query_info.find(wvcdm::QUERY_KEY_RENEWAL_SERVER_URL);
ASSERT_TRUE(itr != query_info.end());
EXPECT_LT(0, (int)itr->second.size());
EXPECT_LT(0u, itr->second.size());
decryptor_.CloseSession(session_id_);
}
@@ -519,13 +391,12 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) {
itr = query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL);
ASSERT_TRUE(itr != query_info.end());
EXPECT_EQ(2, (int)itr->second.size());
EXPECT_EQ(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3.at(0),
itr->second.at(0));
EXPECT_EQ(2u, itr->second.size());
EXPECT_EQ(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3.at(0), itr->second.at(0));
itr = query_info.find(wvcdm::QUERY_KEY_DEVICE_ID);
ASSERT_TRUE(itr != query_info.end());
EXPECT_GT((int)itr->second.size(), 0);
EXPECT_GT(itr->second.size(), 0u);
itr = query_info.find(wvcdm::QUERY_KEY_SYSTEM_ID);
ASSERT_TRUE(itr != query_info.end());
@@ -536,9 +407,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) {
itr = query_info.find(wvcdm::QUERY_KEY_PROVISIONING_ID);
ASSERT_TRUE(itr != query_info.end());
EXPECT_EQ(16, (int)itr->second.size());
decryptor_.CloseSession(session_id_);
EXPECT_EQ(16u, itr->second.size());
}
TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) {
@@ -548,8 +417,8 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) {
CdmQueryMap query_info;
CdmQueryMap::iterator itr;
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryKeyControlInfo(session_id_,
&query_info));
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_.QueryKeyControlInfo(session_id_, &query_info));
uint32_t oem_crypto_session_id;
itr = query_info.find(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID);
@@ -596,19 +465,14 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionTest) {
size_t encrypt_length = data.encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data.is_encrypted,
data.is_secure,
data.key_id,
&data.encrypt_data.front(),
encrypt_length,
data.iv,
data.block_offset,
&decrypt_buffer.front(),
0));
EXPECT_EQ(NO_ERROR,
decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure,
data.key_id, &data.encrypt_data.front(),
encrypt_length, data.iv, data.block_offset,
&decrypt_buffer.front(), 0));
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
decrypt_buffer.begin()));
decryptor_.CloseSession(session_id_);
}
@@ -645,19 +509,14 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionNoKeyTest) {
size_t encrypt_length = data.encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data.is_encrypted,
data.is_secure,
data.key_id,
&data.encrypt_data.front(),
encrypt_length,
data.iv,
data.block_offset,
&decrypt_buffer.front(),
0));
EXPECT_EQ(NO_ERROR,
decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure,
data.key_id, &data.encrypt_data.front(),
encrypt_length, data.iv, data.block_offset,
&decrypt_buffer.front(), 0));
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
decrypt_buffer.begin()));
decryptor_.CloseSession(session_id_);
}
@@ -696,131 +555,14 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionTest) {
size_t encrypt_length = data.encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data.is_encrypted,
data.is_secure,
data.key_id,
&data.encrypt_data.front(),
encrypt_length,
data.iv,
data.block_offset,
&decrypt_buffer.front(),
0));
EXPECT_EQ(NO_ERROR,
decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure,
data.key_id, &data.encrypt_data.front(),
encrypt_length, data.iv, data.block_offset,
&decrypt_buffer.front(), 0));
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, OfflineLicenseDecryptionTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, false);
// key 1, encrypted, 256b
DecryptionData data;
data.is_encrypted = true;
data.is_secure = false;
data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546");
data.encrypt_data = wvcdm::a2b_hex(
"b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561"
"8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771"
"a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93"
"c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe"
"4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785"
"7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412"
"9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff"
"3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c");
data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6");
data.block_offset = 0;
data.decrypt_data = wvcdm::a2b_hex(
"cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a"
"d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a"
"e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d"
"8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0"
"d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c"
"a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b"
"5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c"
"e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf");
std::vector<uint8_t> decrypt_buffer;
size_t encrypt_length = data.encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data.is_encrypted,
data.is_secure,
data.key_id,
&data.encrypt_data.front(),
encrypt_length,
data.iv,
data.block_offset,
&decrypt_buffer.front(),
0));
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, RestoreOfflineLicenseDecryptionTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
kServerSdkKeyId, false);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
session_id_.clear();
decryptor_.OpenSession(g_key_system, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
// key 1, encrypted, 256b
DecryptionData data;
data.is_encrypted = true;
data.is_secure = false;
data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546");
data.encrypt_data = wvcdm::a2b_hex(
"b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561"
"8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771"
"a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93"
"c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe"
"4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785"
"7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412"
"9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff"
"3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c");
data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6");
data.block_offset = 0;
data.decrypt_data = wvcdm::a2b_hex(
"cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a"
"d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a"
"e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d"
"8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0"
"d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c"
"a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b"
"5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c"
"e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf");
std::vector<uint8_t> decrypt_buffer;
size_t encrypt_length = data.encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data.is_encrypted,
data.is_secure,
data.key_id,
&data.encrypt_data.front(),
encrypt_length,
data.iv,
data.block_offset,
&decrypt_buffer.front(),
0));
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
decrypt_buffer.begin()));
decryptor_.CloseSession(session_id_);
}
@@ -887,20 +629,15 @@ TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) {
size_t encrypt_length = data[i].encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data[i].is_encrypted,
data[i].is_secure,
data[i].key_id,
&data[i].encrypt_data.front(),
encrypt_length,
data[i].iv,
data[i].block_offset,
&decrypt_buffer.front(),
0));
EXPECT_EQ(
NO_ERROR,
decryptor_.Decrypt(session_id_, data[i].is_encrypted, data[i].is_secure,
data[i].key_id, &data[i].encrypt_data.front(),
encrypt_length, data[i].iv, data[i].block_offset,
&decrypt_buffer.front(), 0));
EXPECT_TRUE(std::equal(data[i].decrypt_data.begin(),
data[i].decrypt_data.end(),
decrypt_buffer.begin()));
data[i].decrypt_data.end(), decrypt_buffer.begin()));
}
decryptor_.CloseSession(session_id_);
}
@@ -932,19 +669,14 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockDecryptionTest) {
size_t encrypt_length = data.encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data.is_encrypted,
data.is_secure,
data.key_id,
&data.encrypt_data.front(),
encrypt_length,
data.iv,
data.block_offset,
&decrypt_buffer.front(),
0));
EXPECT_EQ(NO_ERROR,
decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure,
data.key_id, &data.encrypt_data.front(),
encrypt_length, data.iv, data.block_offset,
&decrypt_buffer.front(), 0));
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
decrypt_buffer.begin()));
decryptor_.CloseSession(session_id_);
}
@@ -975,16 +707,11 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) {
size_t encrypt_length = data.encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data.is_encrypted,
data.is_secure,
data.key_id,
&data.encrypt_data.front(),
encrypt_length,
data.iv,
data.block_offset,
&decrypt_buffer.front(),
0));
EXPECT_EQ(NO_ERROR,
decryptor_.Decrypt(session_id_, data.is_encrypted, data.is_secure,
data.key_id, &data.encrypt_data.front(),
encrypt_length, data.iv, data.block_offset,
&decrypt_buffer.front(), 0));
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
@@ -992,6 +719,118 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) {
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
/*
// key 1, encrypted, 256b
DecryptionData data;
data.is_encrypted = true;
data.is_secure = false;
data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546");
data.encrypt_data = wvcdm::a2b_hex(
"b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561"
"8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771"
"a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93"
"c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe"
"4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785"
"7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412"
"9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff"
"3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c");
data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6");
data.block_offset = 0;
data.decrypt_data = wvcdm::a2b_hex(
"cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a"
"d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a"
"e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d"
"8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0"
"d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c"
"a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b"
"5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c"
"e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf");
std::vector<uint8_t> decrypt_buffer;
size_t encrypt_length = data.encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data.is_encrypted,
data.is_secure,
data.key_id,
&data.encrypt_data.front(),
encrypt_length,
data.iv,
data.block_offset,
&decrypt_buffer.front(),
0));
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
*/
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, DISABLED_RestoreOfflineLicenseDecryptionTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
session_id_.clear();
decryptor_.OpenSession(g_key_system, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
/*
// key 1, encrypted, 256b
DecryptionData data;
data.is_encrypted = true;
data.is_secure = false;
data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546");
data.encrypt_data = wvcdm::a2b_hex(
"b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561"
"8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771"
"a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93"
"c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe"
"4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785"
"7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412"
"9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff"
"3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c");
data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6");
data.block_offset = 0;
data.decrypt_data = wvcdm::a2b_hex(
"cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a"
"d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a"
"e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d"
"8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0"
"d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c"
"a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b"
"5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c"
"e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf");
std::vector<uint8_t> decrypt_buffer;
size_t encrypt_length = data.encrypt_data.size();
decrypt_buffer.resize(encrypt_length);
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_,
data.is_encrypted,
data.is_secure,
data.key_id,
&data.encrypt_data.front(),
encrypt_length,
data.iv,
data.block_offset,
&decrypt_buffer.front(),
0));
EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
*/
decryptor_.CloseSession(session_id_);
}
// TODO(rfrias, edwinwong): pending L1 OEMCrypto due to key block handling
/*
TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) {
@@ -1049,7 +888,7 @@ TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) {
*/
} // namespace wvcdm
int main(int argc, char **argv) {
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
wvcdm::ConfigTestEnv config;
@@ -1065,16 +904,15 @@ int main(int argc, char **argv) {
int show_usage = 0;
static const struct option long_options[] = {
{ "use_full_path", no_argument, &g_use_full_path, 0 },
{ "keyid", required_argument, NULL, 'k' },
{ "port", required_argument, NULL, 'p' },
{ "server", required_argument, NULL, 's' },
{ NULL, 0, NULL, '\0' }
};
{"use_full_path", no_argument, &g_use_full_path, 0},
{"keyid", required_argument, NULL, 'k'},
{"port", required_argument, NULL, 'p'},
{"server", required_argument, NULL, 's'}, {NULL, 0, NULL, '\0'}};
int option_index = 0;
int opt = 0;
while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) {
while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options,
&option_index)) != -1) {
switch (opt) {
case 'k': {
g_key_id.clear();
@@ -1105,8 +943,10 @@ int main(int argc, char **argv) {
if (show_usage) {
std::cout << std::endl;
std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl;
std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl;
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl;
std::cout << " enclose multiple arguments in '' when using adb shell"
<< std::endl;
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'"
<< std::endl << std::endl;
std::cout << std::setw(30) << std::left << " --port=<connection port>";
std::cout << "specifies the port number, in decimal format" << std::endl;
@@ -1114,7 +954,9 @@ int main(int argc, char **argv) {
std::cout << "default: " << g_port << std::endl;
std::cout << std::setw(30) << std::left << " --server=<server_url>";
std::cout << "configure the license server url, please include http[s] in the url" << std::endl;
std::cout
<< "configure the license server url, please include http[s] in the url"
<< std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << license_server << std::endl;

View File

@@ -0,0 +1,18 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// For platform specific test vectors
#include <string>
namespace wvcdm {
namespace test_vectors {
// for FileStore unit tests
static const std::string kFileExists = "/system/bin/sh";
static const std::string kDirExists = "/system/bin";
static const std::string kFileDoesNotExist = "/system/bin/enoext";
static const std::string kDirDoesNotExist = "/system/bin_enoext";
static const std::string kTestDir = "/data/mediadrm/IDM0/";
} // namespace test_vectors
} // namespace wvcdm

View File

@@ -21,12 +21,12 @@ LOCAL_C_INCLUDES := \
external/gtest/include \
external/openssl/include \
external/stlport/stlport \
$(LOCAL_PATH)/core/test/include \
vendor/widevine/libwvdrmengine/test/gmock/include \
vendor/widevine/libwvdrmengine/android/cdm/test \
vendor/widevine/libwvdrmengine/cdm/core/include \
vendor/widevine/libwvdrmengine/cdm/core/test \
vendor/widevine/libwvdrmengine/cdm/include \
vendor/widevine/libwvdrmengine/oemcrypto/include
vendor/widevine/libwvdrmengine/oemcrypto/include \
vendor/widevine/libwvdrmengine/test/gmock/include
LOCAL_C_INCLUDES += external/protobuf/src