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

@@ -3,6 +3,22 @@
#
LOCAL_PATH := $(call my-dir)
# -----------------------------------------------------------------------------
# Builds libmodp_b64.a
#
include $(CLEAR_VARS)
LOCAL_MODULE := libmodp_b64
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_C_INCLUDES := \
bionic \
external/stlport/stlport
LOCAL_SRC_FILES := third_party/stringencoders/src/modp_b64w.cpp
include $(BUILD_STATIC_LIBRARY)
# -----------------------------------------------------------------------------
# Builds libcdm_protos.a
# Generates *.a, *.pb.h and *.pb.cc for *.proto files.
@@ -18,10 +34,12 @@ LOCAL_C_INCLUDES := \
LOCAL_SRC_FILES := $(call all-proto-files-under, cdm/core/src)
# $(call local-intermediates-dir)/proto/$(LOCAL_PATH)/cdm/core/src is used 21
# $(call local-intermediates-dir)/proto/$(LOCAL_PATH)/cdm/core/src is used
# to locate *.pb.h by cdm source
# $(call local-intermediates-dir)/proto is used to locate *.pb.h included 23
# $(call local-intermediates-dir)/proto is used to locate *.pb.h included
# by *.pb.cc
# The module that depends on this prebuilt will have LOCAL_C_INCLUDES prepended
# with this path.
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(call local-intermediates-dir)/proto \
$(call local-intermediates-dir)/proto/$(LOCAL_PATH)/cdm/core/src

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,18 +19,19 @@ typedef std::map<CdmKeySetId, CdmSessionId> CdmReleaseKeySetMap;
class CdmEngine : public TimerHandler {
public:
CdmEngine();
~CdmEngine();
virtual ~CdmEngine();
// Session related methods
CdmResponseType OpenSession(const CdmKeySystem& key_system,
virtual 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 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,
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id,
const CdmInitData& init_data,
const CdmLicenseType license_type,
@@ -39,49 +40,51 @@ class CdmEngine : public TimerHandler {
std::string* server_url);
// Accept license response and extract key info.
CdmResponseType AddKey(const CdmSessionId& session_id,
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data,
CdmKeySetId& key_set_id);
CdmKeySetId* key_set_id);
CdmResponseType RestoreKey(const CdmSessionId& session_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,
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,
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,
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info);
// Query seesion control information
CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
CdmQueryMap* key_info);
// Provisioning related methods
CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request,
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,
virtual CdmResponseType Decrypt(const CdmSessionId& session_id,
bool is_encrypted,
bool is_secure,
const KeyId& key_id,
@@ -94,42 +97,35 @@ class CdmEngine : public TimerHandler {
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,
virtual bool AttachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener);
bool DetachEventListener(const CdmSessionId& session_id,
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,30 +78,31 @@ 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);
};

View File

@@ -7,6 +7,8 @@
namespace wvcdm {
class File;
class DeviceFiles {
public:
typedef enum {
@@ -15,12 +17,17 @@ class DeviceFiles {
kLicenseStateUnknown,
} LicenseState;
static bool StoreCertificate(const std::string& certificate,
DeviceFiles() {}
virtual ~DeviceFiles() {}
virtual bool Init(File* handle);
virtual bool StoreCertificate(const std::string& certificate,
const std::string& wrapped_private_key);
static bool RetrieveCertificate(std::string* certificate,
virtual bool RetrieveCertificate(std::string* certificate,
std::string* wrapped_private_key);
static bool StoreLicense(const std::string& key_set_id,
virtual bool StoreLicense(const std::string& key_set_id,
const LicenseState state,
const CdmInitData& pssh_data,
const CdmKeyMessage& key_request,
@@ -28,7 +35,7 @@ class DeviceFiles {
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,
virtual bool RetrieveLicense(const std::string& key_set_id,
LicenseState* state,
CdmInitData* pssh_data,
CdmKeyMessage* key_request,
@@ -36,23 +43,24 @@ class DeviceFiles {
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 DeleteLicense(const std::string& key_set_id);
virtual bool LicenseExists(const std::string& key_set_id);
static std::string GetBasePath(const char* dir);
static const char* kBasePath;
static const char* kPathDelimiter;
static const char* kDeviceCertificateFileName;
static const char* kLicenseFileNameExt;
// 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,78 +7,58 @@
#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_))
if (!license_parser_.Init(token, session.get(), &policy_engine_))
return UNKNOWN_ERROR;
crypto_session_.reset(session.release());
license_received_ = false;
reinitialize_session_ = false;
return NO_ERROR;
else
return UNKNOWN_ERROR;
}
CdmResponseType CdmSession::ReInit() {
DestroySession();
return Init();
}
bool CdmSession::DestroySession() {
if (crypto_session_) {
delete crypto_session_;
crypto_session_ = NULL;
}
return true;
}
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_,
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_)) {
@@ -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,16 +122,22 @@ 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 {
if (init_data.empty()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return KEY_ERROR;
}
else if (license_received_) { // renewal
return Properties::require_explicit_renew_request() ?
UNKNOWN_ERROR : GenerateRenewalRequest(key_request, server_url);
}
else {
CdmInitData pssh_data;
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_)) {
@@ -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,
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
CdmKeySetId* key_set_id) {
if (!crypto_session_) {
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);
}
CdmSessionId CdmSession::GenerateKeySetId(CdmInitData& pssh_data) {
Clock clock;
int64_t current_time = clock.GetCurrentTime();
std::string key_set_id;
while (key_set_id.empty()) {
int random = rand();
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];
bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
if (!key_set_id) {
LOGW("CdmSession::GenerateKeySetId: key set id destination not provided");
return false;
}
hash.resize(SHA256_DIGEST_LENGTH/4);
key_set_id = KEY_SET_ID_PREFIX + b2a_hex(hash);
if (DeviceFiles::LicenseExists(key_set_id)) { // key set collision
key_set_id.clear();
std::vector<uint8_t> random_data(
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
File file;
DeviceFiles handle;
if (!handle.Init(&file)) return false;
while (key_set_id->empty()) {
if (!crypto_session_->GetRandom(&random_data[0], random_data.size()))
return false;
*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,24 +383,18 @@ 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);
}
}
}
}
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);
}
}
}
}
} // namespace wvcdm

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"
@@ -29,87 +30,218 @@ std::string EncodeUint32(unsigned int u) {
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) {
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,16 +346,16 @@ 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(),
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]));
@@ -236,13 +363,11 @@ bool CryptoSession::LoadKeys(const std::string& message,
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(),
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]));
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 {
sts = OEMCrypto_GenerateSignature(
oec_session_id_,
reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
NULL,
&length);
}
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);
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);
*/
}
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 (!File::IsDirectory(path)) {
if (!File::CreateDirectory(path))
if (!name) {
LOGW("DeviceFiles::StoreFile: Unspecified file name parameter");
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.
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());
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,10 +415,6 @@ 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) {
@@ -440,6 +422,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
}
}
// 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)
@@ -452,18 +437,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
key_array.size(),
&key_array[0])) {
return KEY_ADDED;
}
else {
} else {
return KEY_ERROR;
}
}
// 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;
}
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 =
// 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";
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=";
}
// 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_,
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), wvcdm::KEY_MESSAGE);
&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_,
EXPECT_EQ(KEY_MESSAGE,
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);
}
&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);
}
if (status_code != kHttpOk) {
return "";
} else {
std::string drm_msg;
if (200 == status_code) {
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());
}
return drm_msg;
}
}
void VerifyKeyRequestResponse(const std::string& server_url,
void VerifyNewKeyResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data,
bool is_renewal) {
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

@@ -25,7 +25,8 @@ static const std::string kKeyId =
#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
@@ -53,14 +54,27 @@ static const std::string kKeyId =
#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
"0901121094889920E8D6520098577DF8F2DD5546"; // pssh data
// 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

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));
}
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));
}
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));
}
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);
void CreateTestDir() {
File file;
file.Open(path, File::kCreate);
EXPECT_TRUE(file.IsOpen());
file.Close();
EXPECT_TRUE(File::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());
file.Close();
EXPECT_TRUE(File::Remove(path));
EXPECT_TRUE(File::Remove(dir));
EXPECT_FALSE(File::Exists(path));
EXPECT_FALSE(File::Exists(dir));
}
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());
file.Close();
EXPECT_TRUE(File::Exists(path));
EXPECT_TRUE(File::Exists(dir));
EXPECT_FALSE(File::IsDirectory(path));
EXPECT_TRUE(File::IsDirectory(dir));
}
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());
file.Close();
EXPECT_TRUE(File::Exists(path));
EXPECT_TRUE(File::Exists(dir));
EXPECT_TRUE(File::IsRegularFile(path));
EXPECT_FALSE(File::IsRegularFile(dir));
}
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);
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);
}
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);
unsigned char test_buf[600];
for (size_t i = 0; i < sizeof(test_buf); i++) {
test_buf[i] = i % 128;
if (!file.Exists(test_vectors::kTestDir)) {
EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir));
}
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));
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);
}
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);
unsigned char test_buf[600];
for (size_t i = 0; i < sizeof(test_buf); i++) {
test_buf[i] = i % 128;
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
}
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));
EXPECT_EQ((ssize_t)sizeof(test_buf), File::FileSize(path));
EXPECT_TRUE(File::Remove(dir));
void RemoveTestDir() {
File file;
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
}
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_F(FileTest, FileExists) {
File file;
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(handle.Exists(path));
}
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.Exists(path));
EXPECT_TRUE(file.Remove(path));
EXPECT_FALSE(file.Exists(path));
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path));
RemoveTestDir();
EXPECT_FALSE(file.Exists(test_vectors::kTestDir));
EXPECT_FALSE(file.Exists(path));
}
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(test_vectors::kTestDir));
EXPECT_FALSE(file.IsDirectory(path));
EXPECT_TRUE(file.IsDirectory(test_vectors::kTestDir));
}
TEST_F(FileTest, IsRegularFile) {
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(test_vectors::kTestDir));
EXPECT_TRUE(file.IsRegularFile(path));
EXPECT_FALSE(file.IsRegularFile(test_vectors::kTestDir));
}
TEST_F(FileTest, FileSize) {
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));
EXPECT_EQ(static_cast<ssize_t>(write_data.size()), file.FileSize(path));
}
TEST_F(FileTest, WriteReadTextFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(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));
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,28 +44,23 @@ 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),
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;
@@ -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;
@@ -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_) {
if (!ssl_) {
LOGE("failed SSL_new");
return false;
}
BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE);
if (a_bio) {
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));
}
} else {
LOGE("BIO_new_socket error");
}
} else {
LOGE("failed SSL_new");
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) {
@@ -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);
@@ -254,8 +252,7 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms)
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;

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());
@@ -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_,
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_,
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();
EXPECT_FALSE(Connect("ww.g.c"));
// 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", !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");

View File

@@ -13,11 +13,15 @@ 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);
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);
@@ -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,16 +49,17 @@ 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() {
@@ -68,20 +69,9 @@ void File::Close() {
}
}
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 {
@@ -68,8 +45,9 @@ class TestWvCdmEventListener : public WvCdmEventListener {
session_id_ = id;
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_;
@@ -91,7 +69,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
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()));
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,
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) {
@@ -122,115 +99,24 @@ class WvCdmRequestLicenseTest : public testing::Test {
&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);
}
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_),
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_),
wvcdm::KEY_ADDED);
}
else {
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, key_set_id_),
} 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(
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
&key_msg_, &provisioning_server_url));
EXPECT_STREQ(provisioning_server_url.data(),
kProductionProvisioningServerUrl.data());
EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url());
std::string response = GetCertRequestResponse(
kProductionTestProvisioningServerUrl, 200);
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(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,16 +465,11 @@ 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()));
@@ -645,16 +509,11 @@ 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()));
@@ -696,134 +555,17 @@ 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()));
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
@@ -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,16 +669,11 @@ 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()));
@@ -975,6 +707,54 @@ 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_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(),
decrypt_buffer.begin()));
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,
@@ -988,7 +768,66 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) {
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_);
}
@@ -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

View File

@@ -17,11 +17,15 @@ enum {
kErrorCDMGeneric = ERROR_DRM_VENDOR_MIN + 1,
kErrorUnsupportedCrypto = ERROR_DRM_VENDOR_MIN + 2,
kErrorExpectedUnencrypted = ERROR_DRM_VENDOR_MIN + 3,
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 3,
// Used by crypto test mode
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
};
_STLP_STATIC_ASSERT(static_cast<uint32_t>(kErrorWVDrmMaxErrorUsed) <=
static_cast<uint32_t>(ERROR_DRM_VENDOR_MAX));
} // namespace wvdrm
#endif // WV_ERRORS_H_

View File

@@ -206,10 +206,10 @@ status_t WVDrmPlugin::provideKeyResponse(
CdmKeyResponse cdmResponse(response.begin(), response.end());
CdmKeySetId cdmKeySetId;
bool isRequest = (memcmp(scope.array(), SESSION_ID_PREFIX.data(),
SESSION_ID_PREFIX.size()) == 0);
bool isRelease = (memcmp(scope.array(), KEY_SET_ID_PREFIX.data(),
KEY_SET_ID_PREFIX.size()) == 0);
bool isRequest = (memcmp(scope.array(), SESSION_ID_PREFIX,
sizeof(SESSION_ID_PREFIX) - 1) == 0);
bool isRelease = (memcmp(scope.array(), KEY_SET_ID_PREFIX,
sizeof(KEY_SET_ID_PREFIX) - 1) == 0);
if (isRequest) {
cdmSessionId.assign(scope.begin(), scope.end());
@@ -219,7 +219,7 @@ status_t WVDrmPlugin::provideKeyResponse(
return android::ERROR_DRM_CANNOT_HANDLE;
}
CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, cdmKeySetId);
CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, &cdmKeySetId);
if (isRequest && isCdmResponseTypeSuccess(res)) {
keySetId.clear();

View File

@@ -37,7 +37,7 @@ class MockCDM : public WvContentDecryptionModule {
MOCK_METHOD3(AddKey, CdmResponseType(const CdmSessionId&,
const CdmKeyResponse&,
CdmKeySetId&));
CdmKeySetId*));
MOCK_METHOD1(CancelKeyRequest, CdmResponseType(const CdmSessionId&));
@@ -117,7 +117,7 @@ class WVDrmPluginTest : public Test {
fread(sessionIdRaw, sizeof(uint8_t), kSessionIdSize, fp);
fclose(fp);
memcpy(sessionIdRaw, SESSION_ID_PREFIX.data(), SESSION_ID_PREFIX.size());
memcpy(sessionIdRaw, SESSION_ID_PREFIX, sizeof(SESSION_ID_PREFIX) - 1);
sessionId.appendArray(sessionIdRaw, kSessionIdSize);
cdmSessionId.assign(sessionId.begin(), sessionId.end());
@@ -149,6 +149,9 @@ TEST_F(WVDrmPluginTest, OpensSessions) {
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
status_t res = plugin.openSession(sessionId);
ASSERT_EQ(OK, res);
@@ -185,7 +188,7 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
fclose(fp);
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX.data(), KEY_SET_ID_PREFIX.size());
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX, sizeof(KEY_SET_ID_PREFIX) - 1);
CdmKeySetId cdmKeySetId(reinterpret_cast<char *>(keySetIdRaw), kKeySetIdSize);
Vector<uint8_t> keySetId;
keySetId.appendArray(keySetIdRaw, kKeySetIdSize);
@@ -292,7 +295,7 @@ TEST_F(WVDrmPluginTest, AddsKeys) {
Vector<uint8_t> response;
response.appendArray(responseRaw, kResponseSize);
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX.data(), KEY_SET_ID_PREFIX.size());
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX, sizeof(KEY_SET_ID_PREFIX) - 1);
CdmKeySetId cdmKeySetId(reinterpret_cast<char *>(keySetIdRaw), kKeySetIdSize);
Vector<uint8_t> keySetId;
@@ -300,11 +303,11 @@ TEST_F(WVDrmPluginTest, AddsKeys) {
EXPECT_CALL(cdm, AddKey(cdmSessionId,
ElementsAreArray(responseRaw, kResponseSize), _))
.WillOnce(DoAll(SetArgReferee<2>(cdmKeySetId),
.WillOnce(DoAll(SetArgPointee<2>(cdmKeySetId),
Return(wvcdm::KEY_ADDED)));
EXPECT_CALL(cdm, AddKey("", ElementsAreArray(responseRaw, kResponseSize),
cdmKeySetId))
Pointee(cdmKeySetId)))
.Times(1);
status_t res = plugin.provideKeyResponse(sessionId, response, keySetId);
@@ -646,6 +649,9 @@ TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) {
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
status_t res = plugin.openSession(sessionId);
ASSERT_EQ(OK, res);
@@ -729,6 +735,9 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) {
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
status_t res = plugin.openSession(sessionId);
ASSERT_EQ(OK, res);
@@ -793,6 +802,9 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) {
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
status_t res = plugin.openSession(sessionId);
ASSERT_EQ(OK, res);
@@ -859,6 +871,9 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) {
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
status_t res = plugin.openSession(sessionId);
ASSERT_EQ(OK, res);
@@ -935,6 +950,9 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) {
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
status_t res = plugin.openSession(sessionId);
ASSERT_EQ(OK, res);
@@ -972,6 +990,9 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) {
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
status_t res = plugin.openSession(sessionId);
ASSERT_EQ(OK, res);
}
@@ -1015,6 +1036,9 @@ TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) {
EXPECT_CALL(cdm, AttachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
status_t res = plugin.openSession(sessionId);
ASSERT_EQ(OK, res);
@@ -1063,6 +1087,9 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) {
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
status_t res = plugin.setListener(listener);
ASSERT_EQ(OK, res);

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

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

@@ -16,7 +16,6 @@
namespace wvoec_mock {
bool KeyControlBlock::Validate() {
valid_ = false;
if (0x6b63746c != verification_) { // kctl.
LOGE("KCB: BAD verification string: %08X (not %08X)", verification_,
@@ -35,7 +34,6 @@ bool KeyControlBlock::Validate() {
LOGW("KCB: CGMS setting set for refresh.");
}
}
valid_ = true;
return valid_;
}

View File

@@ -135,7 +135,7 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateNonce(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_GenerateNonce(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -172,7 +172,7 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -227,7 +227,7 @@ OEMCryptoResult OEMCrypto_GenerateSignature(
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateSignature(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_GenerateSignature(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -292,7 +292,7 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_LoadKeys(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_LoadKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -393,7 +393,7 @@ OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
size_t num_keys,
const OEMCrypto_KeyRefreshObject* key_array) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%d)\n", num_keys);
printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%zu)\n", num_keys);
}
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
@@ -403,7 +403,7 @@ OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_RefreshKeys(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_RefreshKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -491,7 +491,7 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_SelectKey(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_SelectKey(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -527,8 +527,9 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
break;
case OEMCrypto_BufferType_Secure:
buffer_type = kBufferTypeSecure;
destination = ((uint8_t*)out_buffer->buffer.secure.handle
+ out_buffer->buffer.secure.offset);
destination =
reinterpret_cast<uint8_t*>(out_buffer->buffer.secure.handle)
+ out_buffer->buffer.secure.offset;
max_length = out_buffer->buffer.secure.max_length;
break;
default:
@@ -552,7 +553,7 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_DecryptCTR(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_DecryptCTR(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -562,7 +563,7 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (!crypto_engine->DecryptCTR(session_ctx, iv, (int)block_offset,
if (!crypto_engine->DecryptCTR(session_ctx, iv, block_offset,
data_addr, data_length, is_encrypted,
destination, buffer_type)) {
LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_DECRYPT_FAILED]");
@@ -718,7 +719,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (message == NULL || message_length == 0 || signature == NULL
@@ -753,7 +754,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
}
size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1];
if( result == OEMCrypto_SUCCESS) {
if (result == OEMCrypto_SUCCESS) {
if (padding > 16) {
LOGE("[RewrapRSAKey(): Encrypted RSA has bad padding: %d]", padding);
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
@@ -761,7 +762,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
}
size_t rsa_key_length = enc_rsa_key_length - padding;
// verify signature, verify RSA key, and load it.
if( result == OEMCrypto_SUCCESS) {
if (result == OEMCrypto_SUCCESS) {
if (!session_ctx->LoadRSAKey(pkcs8_rsa_key, rsa_key_length,
message, message_length,
signature, signature_length)) {
@@ -773,7 +774,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
// Now we generate a wrapped keybox.
WrappedRSAKey* wrapped = reinterpret_cast<WrappedRSAKey*>(wrapped_rsa_key);
// Pick a random context and IV for generating keys.
if( result == OEMCrypto_SUCCESS) {
if (result == OEMCrypto_SUCCESS) {
if (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
@@ -784,7 +785,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
const std::vector<uint8_t> context(wrapped->context,
wrapped->context + sizeof(wrapped->context));
// Generate mac and encryption keys for encrypting the signature.
if( result == OEMCrypto_SUCCESS) {
if (result == OEMCrypto_SUCCESS) {
if (!session_ctx->DeriveKeys(crypto_engine->keybox().device_key().value(),
context, context)) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -792,7 +793,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
}
// Encrypt rsa key with keybox.
if( result == OEMCrypto_SUCCESS) {
if (result == OEMCrypto_SUCCESS) {
if (!session_ctx->EncryptRSAKey(pkcs8_rsa_key, enc_rsa_key_length,
wrapped->iv, wrapped->enc_rsa_key)) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -802,8 +803,8 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
// The wrapped keybox must be signed with the same key we verify with. I'll
// pick the server key, so I don't have to modify LoadRSAKey.
if( result == OEMCrypto_SUCCESS) {
size_t sig_length = sizeof(wrapped->signature);
if (result == OEMCrypto_SUCCESS) {
unsigned int sig_length = sizeof(wrapped->signature);
if (!HMAC(EVP_sha256(), &session_ctx->mac_key_server()[0],
SHA256_DIGEST_LENGTH, wrapped->context,
buffer_size - sizeof(wrapped->signature), wrapped->signature,
@@ -844,7 +845,7 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
const std::vector<uint8_t> context(wrapped->context,
@@ -864,7 +865,7 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
}
size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1];
if( result == OEMCrypto_SUCCESS) {
if (result == OEMCrypto_SUCCESS) {
if (padding > 16) {
LOGE("[LoadDeviceRSAKey(): Encrypted RSA has bad padding: %d]", padding);
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
@@ -872,7 +873,7 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
}
size_t rsa_key_length = enc_rsa_key_length - padding;
// verify signature.
if( result == OEMCrypto_SUCCESS) {
if (result == OEMCrypto_SUCCESS) {
if (!session_ctx->LoadRSAKey(pkcs8_rsa_key, rsa_key_length,
wrapped->context,
wrapped_rsa_key_length - sizeof(wrapped->signature),
@@ -908,7 +909,7 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -958,7 +959,7 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1008,7 +1009,7 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session,
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (in_buffer == NULL || buffer_length == 0 ||
@@ -1037,7 +1038,7 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session,
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (!session_ctx->Generic_Decrypt(in_buffer, buffer_length, iv, algorithm,
@@ -1065,7 +1066,7 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_Generic_Sign(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_Generic_Sign(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (*signature_length < SHA256_DIGEST_LENGTH) {
@@ -1096,7 +1097,7 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session,
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_Generic_Verify(): ERROR_NO_INVALID_SESSION]");
LOGE("[OEMCrypto_Generic_Verify(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (signature_length != SHA256_DIGEST_LENGTH) {

View File

@@ -15,8 +15,6 @@
'dependencies': [
'oec_mrvl',
],
'libraries': [
],
}, {
'dependencies': [
'oec_mock',
@@ -24,59 +22,24 @@
}],
],
},
{
'target_name': 'oec_client',
'type': 'static_library',
'sources': [
'client/oemcrypto_client.h',
'client/oemcrypto_client.cpp',
],
'dependencies': [
'../../../base/base.gyp:base',
],
'include_dirs': [
'client',
'../include/widevine',
'../core/include',
],
},
{
'target_name': 'oec_mock',
'type': 'static_library',
'conditions': [
[ 'use_openssl==1', {
'sources!': [
'mock/src/encryptor_nss.cpp',
],
}, {
'sources!': [
'mock/src/encryptor_openssl.cpp',
],
},],
],
'sources': [
'mock/src/oemcrypto_mock.cpp',
'mock/src/oemcrypto_engine_mock.cpp',
'mock/src/oemcrypto_engine_mock.h',
'mock/src/oemcrypto_key_mock.cpp',
'mock/src/oemcrypto_key_mock.h',
'mock/src/oemcrypto_keybox_mock.cpp',
'mock/src/oemcrypto_keybox_mock.h',
'mock/src/encryptor.h',
'mock/src/encryptor.cpp',
'mock/src/encryptor_nss.cpp',
'mock/src/encryptor_openssl.cpp',
'mock/src/cmac.h',
'mock/src/cmac.c',
],
'dependencies': [
'../../../base/base.gyp:base',
'../../../crypto/crypto.gyp:crypto',
'mock/src/wvcrc.cpp',
],
'include_dirs': [
'mock/src',
'../include',
'../core/include',
'include',
],
'dependencies': [
'../../../crypto/crypto.gyp:crypto',
# TODO(kqyang): make it platform independent.
'../chromium/util.gyp:lock',
'../chromium/util.gyp:string_conversions',
],
},
{
@@ -85,14 +48,14 @@
'sources': [
'eureka/src/oemcrypto_mrvl.cpp',
],
'dependencies': [
'../../../base/base.gyp:base',
'../../../crypto/crypto.gyp:crypto',
],
'include_dirs': [
'../include',
'../core/include',
],
'dependencies': [
'../../../base/base.gyp:base',
'../../../crypto/crypto.gyp:crypto',
],
'cflags': [
'-Wsign-conversion',
],
@@ -115,13 +78,13 @@
'test/oemcrypto_test.cpp',
],
'include_dirs': [
'../include',
'../../../testing/gtest/include',
'include',
'mock/src',
],
'dependencies': [
'oec_lib',
'../../../base/base.gyp:base',
'../../../testing/gtest.gyp:gtest',
'../../../testing/gtest.gyp:gtest_main',
],
},
],

View File

@@ -4,7 +4,8 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
oemcrypto_test.cpp
oemcrypto_test.cpp \
../../cdm/src/log.cpp \
LOCAL_MODULE_TAGS := tests

View File

@@ -30,11 +30,6 @@
using namespace std;
namespace {
// Use random generated or static test vectors
bool g_random = true;
// Enable/disable console output of test vectors
bool g_verbose = false;
const size_t kNumKeys = 4;
const size_t kDuration = 2;
const size_t kLongDuration = 5;
@@ -921,7 +916,6 @@ class Session {
"e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408"
"0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231"
"180120002a0c31383836373837343035000000000080");
OEMCryptoResult sts;
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GenerateDerivedKeys(
session_id(),
@@ -1482,7 +1476,6 @@ Session OEMCryptoClientTest::badSession;
// These two tests are first, becuase it might give an idea why other
// tests are failing when the device has the wrong keybox installed.
TEST_F(OEMCryptoClientTest, VersionNumber) {
OEMCryptoResult sts;
testSetUp();
const char* level = OEMCrypto_SecurityLevel();
@@ -1528,6 +1521,7 @@ TEST_F(OEMCryptoClientTest, DISABLED_CheckSystemID) {
uint32_t req_len = 256;
size_t key_data_len = req_len;
sts = OEMCrypto_GetKeyData(key_data, &key_data_len);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
uint32_t* data = reinterpret_cast<uint32_t*>(key_data);
uint32_t system_id = htonl(data[1]);
@@ -1741,8 +1735,6 @@ TEST_F(OEMCryptoClientTest, GenerateNonce) {
uint32_t nonce;
s.GenerateNonce(&nonce);
// std::cout << "GenerateNonce:: nonce=" << nonce << std::endl;
s.close();
ASSERT_TRUE(s.successStatus());
ASSERT_FALSE(s.isOpen());
@@ -1758,9 +1750,6 @@ TEST_F(OEMCryptoClientTest, GenerateTwoNonces) {
s.GenerateNonce(&nonce1);
s.GenerateNonce(&nonce2);
// std::cout << "GenerateNonce:: nonce1=" << nonce1 << std::endl;
// std::cout << "GenerateNonce:: nonce2=" << nonce2 << std::endl;
ASSERT_TRUE(nonce1 != nonce2);
s.close();
@@ -1784,8 +1773,10 @@ TEST_F(OEMCryptoClientTest, GenerateDerivedKeys) {
// Define CAN_INSTALL_KEYBOX if you are compiling with the reference
// implementation of OEMCrypto, or if your version of OEMCrypto supports
// OEMCrypto_InstallKeybox and OEwith a clear keybox.
// OEMCrypto_InstallKeybox with a clear keybox.
// The Below tests are based on a specific keybox which is installed for testing.
// They are disabled by default. Just because you can install a test keybox,
// does not mean you want to install a test keybox.
#if defined(CAN_INSTALL_KEYBOX)
///////////////////////////////////////////////////
@@ -2822,7 +2813,6 @@ TEST_F(DISABLED_TestKeybox, ValidateRSATestKeys) {
}
TEST_F(DISABLED_TestKeybox, CertificateProvision) {
OEMCryptoResult sts;
testSetUp();
InstallKeybox(kDefaultKeybox, true);
Session& s = createSession("ONE");
@@ -2844,7 +2834,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvision) {
}
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange1) {
OEMCryptoResult sts;
testSetUp();
InstallKeybox(kDefaultKeybox, true);
Session& s = createSession("ONE");
@@ -2882,7 +2871,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange1) {
}
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange2) {
OEMCryptoResult sts;
testSetUp();
InstallKeybox(kDefaultKeybox, true);
Session& s = createSession("ONE");
@@ -2922,7 +2910,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange2) {
}
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange3) {
OEMCryptoResult sts;
testSetUp();
InstallKeybox(kDefaultKeybox, true);
Session& s = createSession("ONE");
@@ -2962,7 +2949,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange3) {
}
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadSignature) {
OEMCryptoResult sts;
testSetUp();
InstallKeybox(kDefaultKeybox, true);
Session& s = createSession("ONE");
@@ -3000,7 +2986,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadSignature) {
}
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadNonce) {
OEMCryptoResult sts;
testSetUp();
InstallKeybox(kDefaultKeybox, true);
Session& s = createSession("ONE");
@@ -3038,7 +3023,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadNonce) {
}
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRSAKey) {
OEMCryptoResult sts;
testSetUp();
InstallKeybox(kDefaultKeybox, true);
Session& s = createSession("ONE");
@@ -3141,7 +3125,6 @@ TEST_F(DISABLED_TestKeybox, RSASignature) {
}
TEST_F(DISABLED_TestKeybox, LoadRSASessionKey) {
OEMCryptoResult sts;
testSetUp();
InstallKeybox(kDefaultKeybox, true);

View File

@@ -0,0 +1,8 @@
# Description:
# Modp base64 encode and decode functions
licenses(['notice'])
exports_files(['LICENSE'])
package(default_visibility = ['//visibility:public'])

View File

@@ -0,0 +1,35 @@
MODP_B64 - High performance base64 encoder/decoder
http://code.google.com/p/stringencoders/
Copyright &copy; 2005, 2006, 2007 Nick Galbreath -- nickg [at] modp [dot] com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the modp.com nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This is the standard "new" BSD license:
http://www.opensource.org/licenses/bsd-license.php

View File

@@ -0,0 +1,4 @@
edwinwong@google.com
jtinker@google.com
rfrias@google.com
widevine-eng@google.com

View File

@@ -0,0 +1,30 @@
URL: https://code.google.com/p/stringencoders/
Version: v3.10.3
License: BSD
License File: LICENSE
Description:
This directory contains open source code for modp base64 encode and decode
functions from https://code.google.com/p/stringencoders/.
Local Modifications:
Comment out #include "config.h" in modep_b64.cpp
Additional Notes:
Note that the directory structure in third_party/stringencoders mirrors that of
stringencoders-v3.10.3, therefore, the include files are placed in ./src
instead of ./include.
The following instructions demonstrate how modp_b64_data.h is generated.
modp_b64_data.h contains conversion tables to generate web safe encoded
base64 strings.
1. navigate to https://code.google.com/p/stringencoders
2. download stringencoders-v3.10.3.tar.gz from the "Downloads" tab
3. extract source to a working folder
4. change into stringencoders-v3.10.3/ directory
5. ./configure --with-b64w-chars='-_='
6. make
7. now copy modp_b64w_data.h to third_party/stringencoders/src/.
8. copy src/modp_b64w.c to third_party/stringencoders/src/*.cpp
9. copy src/modp_b64w.h to third_party/stringencoders/src/.

View File

@@ -0,0 +1,269 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set expandtab shiftwidth=4 tabstop=4: */
/**
* \file modp_b64w.c
* <PRE>
* MODP_B64 - High performance base64 encoder/decoder
* http://code.google.com/p/stringencoders/
*
* Copyright &copy; 2005, 2006, 2007 Nick Galbreath -- nickg [at] modp [dot] com
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the modp.com nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This is the standard "new" BSD license:
* http://www.opensource.org/licenses/bsd-license.php
* </PRE>
*/
/* public header */
#include "modp_b64w.h"
/*
* If you are ripping this out of the library, comment out the next
* line and uncomment the next lines as approrpiate
*/
//#include "config.h"
/* if on motoral, sun, ibm; uncomment this */
/* #define WORDS_BIGENDIAN 1 */
/* else for Intel, Amd; uncomment this */
/* #undef WORDS_BIGENDIAN */
#include "modp_b64w_data.h"
#define BADCHAR 0x01FFFFFF
/**
* you can control if we use padding by commenting out this
* next line. However, I highly recommend you use padding and not
* using it should only be for compatability with a 3rd party.
* Also, 'no padding' is not tested!
*/
#define DOPAD 1
/*
* if we aren't doing padding
* set the pad character to NULL
*/
#ifndef DOPAD
#undef CHARPAD
#define CHARPAD '\0'
#endif
int modp_b64w_encode(char* dest, const char* str, int len)
{
int i;
const uint8_t* s = (const uint8_t*) str;
uint8_t* p = (uint8_t*) dest;
/* unsigned here is important! */
/* uint8_t is fastest on G4, amd */
/* uint32_t is fastest on Intel */
uint32_t t1, t2, t3;
for (i = 0; i < len - 2; i += 3) {
t1 = s[i]; t2 = s[i+1]; t3 = s[i+2];
*p++ = e0[t1];
*p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
*p++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
*p++ = e2[t3];
}
switch (len - i) {
case 0:
break;
case 1:
t1 = s[i];
*p++ = e0[t1];
*p++ = e1[(t1 & 0x03) << 4];
*p++ = CHARPAD;
*p++ = CHARPAD;
break;
default: /* case 2 */
t1 = s[i]; t2 = s[i+1];
*p++ = e0[t1];
*p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
*p++ = e2[(t2 & 0x0F) << 2];
*p++ = CHARPAD;
}
*p = '\0';
return (int)(p - (uint8_t*)dest);
}
#ifdef WORDS_BIGENDIAN /* BIG ENDIAN -- SUN / IBM / MOTOROLA */
int modp_b64w_decode(char* dest, const char* src, int len)
{
int i;
if (len == 0) return 0;
#ifdef DOPAD
/* if padding is used, then the message must be at least
4 chars and be a multiple of 4.
there can be at most 2 pad chars at the end */
if (len < 4 || (len % 4 != 0)) return -1;
if (src[len-1] == CHARPAD) {
len--;
if (src[len -1] == CHARPAD) {
len--;
}
}
#endif /* DOPAD */
int leftover = len % 4;
int chunks = (leftover == 0) ? len / 4 - 1 : len /4;
uint8_t* p = (uint8_t*) dest;
uint32_t x = 0;
uint32_t* destInt = (uint32_t*) p;
uint32_t* srcInt = (uint32_t*) src;
uint32_t y = *srcInt++;
for (i = 0; i < chunks; ++i) {
x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] |
d2[y >> 8 & 0xff] | d3[y & 0xff];
if (x >= BADCHAR) return -1;
*destInt = x << 8;
p += 3;
destInt = (uint32_t*)p;
y = *srcInt++;
}
switch (leftover) {
case 0:
x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] |
d2[y >> 8 & 0xff] | d3[y & 0xff];
if (x >= BADCHAR) return -1;
*p++ = ((uint8_t*)&x)[1];
*p++ = ((uint8_t*)&x)[2];
*p = ((uint8_t*)&x)[3];
return (chunks+1)*3;
#ifndef DOPAD
case 1: /* with padding this is an impossible case */
x = d3[y >> 24];
*p = (uint8_t)x;
break;
#endif
case 2:
x = d3[y >> 24] *64 + d3[(y >> 16) & 0xff];
*p = (uint8_t)(x >> 4);
break;
default: /* case 3 */
x = (d3[y >> 24] *64 + d3[(y >> 16) & 0xff])*64 +
d3[(y >> 8) & 0xff];
*p++ = (uint8_t) (x >> 10);
*p = (uint8_t) (x >> 2);
break;
}
if (x >= BADCHAR) return -1;
return 3*chunks + (6*leftover)/8;
}
#else /* LITTLE ENDIAN -- INTEL AND FRIENDS */
int modp_b64w_decode(char* dest, const char* src, int len)
{
int i;
if (len == 0) return 0;
#ifdef DOPAD
/*
* if padding is used, then the message must be at least
* 4 chars and be a multiple of 4
*/
if (len < 4 || (len % 4 != 0)) return -1; /* error */
/* there can be at most 2 pad chars at the end */
if (src[len-1] == CHARPAD) {
len--;
if (src[len -1] == CHARPAD) {
len--;
}
}
#endif
int leftover = len % 4;
int chunks = (leftover == 0) ? len / 4 - 1 : len /4;
uint8_t* p = (uint8_t*) dest;
uint32_t x = 0;
uint32_t* destInt = (uint32_t*) p;
uint32_t* srcInt = (uint32_t*) src;
uint32_t y = *srcInt++;
for (i = 0; i < chunks; ++i) {
x = d0[y & 0xff] |
d1[(y >> 8) & 0xff] |
d2[(y >> 16) & 0xff] |
d3[(y >> 24) & 0xff];
if (x >= BADCHAR) return -1;
*destInt = x ;
p += 3;
destInt = (uint32_t*)p;
y = *srcInt++;}
switch (leftover) {
case 0:
x = d0[y & 0xff] |
d1[(y >> 8) & 0xff] |
d2[(y >> 16) & 0xff] |
d3[(y >> 24) & 0xff];
if (x >= BADCHAR) return -1;
*p++ = ((uint8_t*)(&x))[0];
*p++ = ((uint8_t*)(&x))[1];
*p = ((uint8_t*)(&x))[2];
return (chunks+1)*3;
break;
#ifndef DOPAD
case 1: /* with padding this is an impossible case */
x = d0[y & 0xff];
*p = *((uint8_t*)(&x)); // i.e. first char/byte in int
break;
#endif
case 2: // * case 2, 1 output byte */
x = d0[y & 0xff] | d1[y >> 8 & 0xff];
*p = *((uint8_t*)(&x)); // i.e. first char
break;
default: /* case 3, 2 output bytes */
x = d0[y & 0xff] |
d1[y >> 8 & 0xff ] |
d2[y >> 16 & 0xff]; /* 0x3c */
*p++ = ((uint8_t*)(&x))[0];
*p = ((uint8_t*)(&x))[1];
break;
}
if (x >= BADCHAR) return -1;
return 3*chunks + (6*leftover)/8;
}
#endif /* if bigendian / else / endif */

View File

@@ -0,0 +1,241 @@
/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set expandtab shiftwidth=4 tabstop=4: */
/**
* \file
* <PRE>
* High performance WEB-SAFE base64 encoder / decoder
*
* Copyright &copy; 2005, 2006, 2007 Nick Galbreath -- nickg [at] modp [dot] com
* All rights reserved.
*
* http://code.google.com/p/stringencoders/
*
* Released under bsd license. See modp_b64w.c for details.
* </pre>
*
* This uses a "URL-safe" or "WEB-safe" encoding. THe standard
* base 64 encoding uses the characters '+', '/' and '=' have special
* restrictions when used inside a URL.
*
* This uses "+" to "-", "/" to "_", and "=" to "." as the replacement
* alphabet.
*
* It's easy to change this to use "URL safe" characters and to remove
* padding. See the modp_b64.c source code for details.
*
*/
#ifndef COM_MODP_STRINGENCODERS_B64W
#define COM_MODP_STRINGENCODERS_B64W
#ifdef __cplusplus
#define BEGIN_C extern "C" {
#define END_C }
#else
#define BEGIN_C
#define END_C
#endif
BEGIN_C
/**
* Encode a raw binary string into web-safe base 64.
* \param[out] dest should be allocated by the caller to contain
* at least modp_b64w_encode_len(len) bytes (see below)
* This will contain the null-terminated b64w encoded result
* \param[in] src contains the bytes
* \param[in] len contains the number of bytes in the src
* \return length of the destination string plus the ending null byte
* i.e. the result will be equal to strlen(dest) + 1
*
* Example
*
* \code
* char* src = ...;
* int srclen = ...; //the length of number of bytes in src
* char* dest = (char*) malloc(modp_b64w_encode_len);
* int len = modp_b64w_encode(dest, src, sourcelen);
* if (len == -1) {
* printf("Error\n");
* } else {
* printf("b64w = %s\n", dest);
* }
* \endcode
*
*/
int modp_b64w_encode(char* dest, const char* src, int len);
/**
* Decode a web-safe base64 encoded string
*
* \param[out] dest should be allocated by the caller to contain at least
* len * 3 / 4 bytes.
* \param[in] src should contain exactly len bytes of b64w characters.
* if src contains -any- non-base characters (such as white
* space, -1 is returned.
* \param[in] len is the length of src
*
* \return the length (strlen) of the output, or -1 if unable to
* decode
*
* \code
* char* src = ...;
* int srclen = ...; // or if you don't know use strlen(src)
* char* dest = (char*) malloc(modp_b64w_decode_len(srclen));
* int len = modp_b64w_decode(dest, src, sourcelen);
* if (len == -1) { error }
* \endcode
*/
int modp_b64w_decode(char* dest, const char* src, int len);
/**
* Given a source string of length len, this returns the amount of
* memory the destination string should have.
*
* remember, this is integer math
* 3 bytes turn into 4 chars
* ceiling[len / 3] * 4 + 1
*
* +1 is for any extra null.
*/
#define modp_b64w_encode_len(A) ((A+2)/3 * 4 + 1)
/**
* Given a base64 string of length len,
* this returns the amount of memory required for output string
* It maybe be more than the actual number of bytes written.
* NOTE: remember this is integer math
* this allocates a bit more memory than traditional versions of b64w
* decode 4 chars turn into 3 bytes
* floor[len * 3/4] + 2
*/
#define modp_b64w_decode_len(A) (A / 4 * 3 + 2)
/**
* Will return the strlen of the output from encoding.
* This may be less than the required number of bytes allocated.
*
* This allows you to 'deserialized' a struct
* \code
* char* b64wencoded = "...";
* int len = strlen(b64wencoded);
*
* struct datastuff foo;
* if (modp_b64w_encode_strlen(sizeof(struct datastuff)) != len) {
* // wrong size
* return false;
* } else {
* // safe to do;
* if (modp_b64w_decode((char*) &foo, b64wencoded, len) == -1) {
* // bad characters
* return false;
* }
* }
* // foo is filled out now
* \endcode
*/
#define modp_b64w_encode_strlen(A) ((A + 2)/ 3 * 4)
END_C
#ifdef __cplusplus
#include <cstring>
#include <string>
namespace modp {
/** \brief b64w encode a cstr with len
*
* \param[in] s the input string to encode
* \param[in] len the length of the input string
* \return a newly allocated b64w string. Empty if failed.
*/
inline std::string b64w_encode(const char* s, size_t len)
{
std::string x(modp_b64w_encode_len(len), '\0');
int d = modp_b64w_encode(const_cast<char*>(x.data()), s,
static_cast<int>(len));
x.erase(d, std::string::npos);
return x;
}
/** \brief b64w encode a cstr
*
* \param[in] s the input string to encode
* \return a newly allocated b64w string. Empty if failed.
*/
inline std::string b64w_encode(const char* s)
{
return b64w_encode(s, static_cast<int>(strlen(s)));
}
/** \brief b64w encode a const std::string
*
* \param[in] s the input string to encode
* \return a newly allocated b64w string. Empty if failed.
*/
inline std::string b64w_encode(const std::string& s)
{
return b64w_encode(s.data(), s.size());
}
/** \brief self-modifing b64w encode
*
* web-safe base 64 decode a string (self-modifing)
* On failure, the string is empty.
*
* \param[in,out] s the string to be decoded
* \return a reference to the input string
*/
inline std::string& b64w_encode(std::string& s)
{
std::string x(b64w_encode(s.data(), s.size()));
s.swap(x);
return s;
}
inline std::string b64w_decode(const char* src, size_t len)
{
std::string x(modp_b64w_decode_len(len)+1, '\0');
int d = modp_b64w_decode(const_cast<char*>(x.data()), src,
static_cast<int>(len));
if (d < 0) {
x.clear();
} else {
x.erase(d, std::string::npos);
}
return x;
}
inline std::string b64w_decode(const char* src)
{
return b64w_decode(src, strlen(src));
}
/**
* base 64 decode a string (self-modifing)
* On failure, the string is empty.
*
* This function is for C++ only (duh)
*
* \param[in,out] s the string to be decoded
* \return a reference to the input string
*/
inline std::string& b64w_decode(std::string& s)
{
std::string x(b64w_decode(s.data(), s.size()));
s.swap(x);
return s;
}
inline std::string b64w_decode(const std::string& s)
{
return b64w_decode(s.data(), s.size());
}
}
#endif /* __cplusplus */
#endif /* MODP_B64W */

View File

@@ -0,0 +1,480 @@
#include <stdint.h>
#define CHAR62 '-'
#define CHAR63 '_'
#define CHARPAD '='
static const unsigned char e0[256] = {
'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C',
'C', 'C', 'D', 'D', 'D', 'D', 'E', 'E', 'E', 'E',
'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H',
'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J',
'K', 'K', 'K', 'K', 'L', 'L', 'L', 'L', 'M', 'M',
'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O',
'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R',
'R', 'R', 'S', 'S', 'S', 'S', 'T', 'T', 'T', 'T',
'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W',
'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y',
'Z', 'Z', 'Z', 'Z', 'a', 'a', 'a', 'a', 'b', 'b',
'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd',
'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g',
'g', 'g', 'h', 'h', 'h', 'h', 'i', 'i', 'i', 'i',
'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l',
'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n',
'o', 'o', 'o', 'o', 'p', 'p', 'p', 'p', 'q', 'q',
'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's',
't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v',
'v', 'v', 'w', 'w', 'w', 'w', 'x', 'x', 'x', 'x',
'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0',
'0', '0', '1', '1', '1', '1', '2', '2', '2', '2',
'3', '3', '3', '3', '4', '4', '4', '4', '5', '5',
'5', '5', '6', '6', '6', '6', '7', '7', '7', '7',
'8', '8', '8', '8', '9', '9', '9', '9', '-', '-',
'-', '-', '_', '_', '_', '_'
};
static const unsigned char e1[256] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B',
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', '-', '_'
};
static const unsigned char e2[256] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B',
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', '-', '_'
};
#ifdef WORDS_BIGENDIAN
/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */
static const uint32_t d0[256] = {
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00f80000, 0x01ffffff, 0x01ffffff,
0x00d00000, 0x00d40000, 0x00d80000, 0x00dc0000, 0x00e00000, 0x00e40000,
0x00e80000, 0x00ec0000, 0x00f00000, 0x00f40000, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
0x00040000, 0x00080000, 0x000c0000, 0x00100000, 0x00140000, 0x00180000,
0x001c0000, 0x00200000, 0x00240000, 0x00280000, 0x002c0000, 0x00300000,
0x00340000, 0x00380000, 0x003c0000, 0x00400000, 0x00440000, 0x00480000,
0x004c0000, 0x00500000, 0x00540000, 0x00580000, 0x005c0000, 0x00600000,
0x00640000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00fc0000,
0x01ffffff, 0x00680000, 0x006c0000, 0x00700000, 0x00740000, 0x00780000,
0x007c0000, 0x00800000, 0x00840000, 0x00880000, 0x008c0000, 0x00900000,
0x00940000, 0x00980000, 0x009c0000, 0x00a00000, 0x00a40000, 0x00a80000,
0x00ac0000, 0x00b00000, 0x00b40000, 0x00b80000, 0x00bc0000, 0x00c00000,
0x00c40000, 0x00c80000, 0x00cc0000, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff
};
static const uint32_t d1[256] = {
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0003e000, 0x01ffffff, 0x01ffffff,
0x00034000, 0x00035000, 0x00036000, 0x00037000, 0x00038000, 0x00039000,
0x0003a000, 0x0003b000, 0x0003c000, 0x0003d000, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,
0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,
0x0000d000, 0x0000e000, 0x0000f000, 0x00010000, 0x00011000, 0x00012000,
0x00013000, 0x00014000, 0x00015000, 0x00016000, 0x00017000, 0x00018000,
0x00019000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0003f000,
0x01ffffff, 0x0001a000, 0x0001b000, 0x0001c000, 0x0001d000, 0x0001e000,
0x0001f000, 0x00020000, 0x00021000, 0x00022000, 0x00023000, 0x00024000,
0x00025000, 0x00026000, 0x00027000, 0x00028000, 0x00029000, 0x0002a000,
0x0002b000, 0x0002c000, 0x0002d000, 0x0002e000, 0x0002f000, 0x00030000,
0x00031000, 0x00032000, 0x00033000, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff
};
static const uint32_t d2[256] = {
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000f80, 0x01ffffff, 0x01ffffff,
0x00000d00, 0x00000d40, 0x00000d80, 0x00000dc0, 0x00000e00, 0x00000e40,
0x00000e80, 0x00000ec0, 0x00000f00, 0x00000f40, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
0x00000040, 0x00000080, 0x000000c0, 0x00000100, 0x00000140, 0x00000180,
0x000001c0, 0x00000200, 0x00000240, 0x00000280, 0x000002c0, 0x00000300,
0x00000340, 0x00000380, 0x000003c0, 0x00000400, 0x00000440, 0x00000480,
0x000004c0, 0x00000500, 0x00000540, 0x00000580, 0x000005c0, 0x00000600,
0x00000640, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000fc0,
0x01ffffff, 0x00000680, 0x000006c0, 0x00000700, 0x00000740, 0x00000780,
0x000007c0, 0x00000800, 0x00000840, 0x00000880, 0x000008c0, 0x00000900,
0x00000940, 0x00000980, 0x000009c0, 0x00000a00, 0x00000a40, 0x00000a80,
0x00000ac0, 0x00000b00, 0x00000b40, 0x00000b80, 0x00000bc0, 0x00000c00,
0x00000c40, 0x00000c80, 0x00000cc0, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff
};
static const uint32_t d3[256] = {
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000003e, 0x01ffffff, 0x01ffffff,
0x00000034, 0x00000035, 0x00000036, 0x00000037, 0x00000038, 0x00000039,
0x0000003a, 0x0000003b, 0x0000003c, 0x0000003d, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006,
0x00000007, 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c,
0x0000000d, 0x0000000e, 0x0000000f, 0x00000010, 0x00000011, 0x00000012,
0x00000013, 0x00000014, 0x00000015, 0x00000016, 0x00000017, 0x00000018,
0x00000019, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000003f,
0x01ffffff, 0x0000001a, 0x0000001b, 0x0000001c, 0x0000001d, 0x0000001e,
0x0000001f, 0x00000020, 0x00000021, 0x00000022, 0x00000023, 0x00000024,
0x00000025, 0x00000026, 0x00000027, 0x00000028, 0x00000029, 0x0000002a,
0x0000002b, 0x0000002c, 0x0000002d, 0x0000002e, 0x0000002f, 0x00000030,
0x00000031, 0x00000032, 0x00000033, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff
};
#else
/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */
static const uint32_t d0[256] = {
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff,
0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4,
0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018,
0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030,
0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,
0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060,
0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc,
0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078,
0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090,
0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8,
0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0,
0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff
};
static const uint32_t d1[256] = {
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff,
0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003,
0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,
0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,
0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,
0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001,
0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003,
0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001,
0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002,
0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002,
0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003,
0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff
};
static const uint32_t d2[256] = {
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff,
0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00,
0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100,
0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300,
0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,
0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600,
0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00,
0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700,
0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900,
0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00,
0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00,
0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff
};
static const uint32_t d3[256] = {
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff,
0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000,
0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000,
0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000,
0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,
0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000,
0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000,
0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000,
0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000,
0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000,
0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000,
0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff
};
#endif