Certificate provisioning verification
bug: 8620943 This is a merge of changes made to the Widevine CDM repository during certificate provisioning verification. The following changes are included: Fixes for certificate based licensing https://widevine-internal-review.googlesource.com/#/c/5162/ Base64 encode and decode now handles non-multiple of 24-bits input https://widevine-internal-review.googlesource.com/#/c/4981/ Fixed issues with device provisioning response handling https://widevine-internal-review.googlesource.com/#/c/5153/ Persistent storage to support device certificates https://widevine-internal-review.googlesource.com/#/c/5161/ Enable loading of certificates https://widevine-internal-review.googlesource.com/#/c/5172/ Provide license server url https://widevine-internal-review.googlesource.com/#/c/5173/ Change-Id: I0c032c1ae0055dcc1a7a77ad4b0ea0898030dc7d
This commit is contained in:
@@ -11,11 +11,11 @@ cd $ANDROID_BUILD_TOP/external/gtest/
|
|||||||
pwd
|
pwd
|
||||||
mm
|
mm
|
||||||
|
|
||||||
cd $ANDROID_BUILD_TOP/vendor/widevine/libwvdrmengine/test/gmock
|
cd $ANDROID_BUILD_TOP/vendor/widevine/libwvdrmengine
|
||||||
pwd
|
pwd
|
||||||
mm
|
mm
|
||||||
|
|
||||||
cd $ANDROID_BUILD_TOP/vendor/widevine/libwvdrmengine/cdm/core/test
|
cd $ANDROID_BUILD_TOP/vendor/widevine/libwvdrmengine/test/gmock
|
||||||
pwd
|
pwd
|
||||||
mm
|
mm
|
||||||
|
|
||||||
@@ -43,18 +43,5 @@ cd $ANDROID_BUILD_TOP/vendor/widevine/libwvdrmengine/test/java/src/com/widevine/
|
|||||||
pwd
|
pwd
|
||||||
mm
|
mm
|
||||||
|
|
||||||
echo "waiting for device"
|
cd $ANDROID_BUILD_TOP/vendor/widevine/libwvdrmengine
|
||||||
adb root && adb wait-for-device remount && adb sync
|
./run_all_unit_tests.sh
|
||||||
|
|
||||||
adb shell /system/bin/request_license_test
|
|
||||||
adb shell /system/bin/policy_engine_unittest
|
|
||||||
adb shell /system/bin/libwvdrmmediacrypto_test
|
|
||||||
adb shell /system/bin/libwvdrmdrmplugin_test
|
|
||||||
adb shell /system/bin/cdm_engine_test
|
|
||||||
adb shell /system/bin/oemcrypto_test
|
|
||||||
adb shell LD_LIBRARY_PATH=/system/vendor/lib/mediadrm/ /system/bin/libwvdrmengine_test
|
|
||||||
|
|
||||||
adb shell am start com.widevine.test/com.widevine.test.MediaDrmAPITest
|
|
||||||
|
|
||||||
# TODO: make this test more command line friendly
|
|
||||||
echo "check logcat output for MediaDrmAPITest"
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ LOCAL_C_INCLUDES := \
|
|||||||
vendor/widevine/libwvdrmengine/oemcrypto/include
|
vendor/widevine/libwvdrmengine/oemcrypto/include
|
||||||
|
|
||||||
LOCAL_C_INCLUDES += \
|
LOCAL_C_INCLUDES += \
|
||||||
|
external/openssl/include \
|
||||||
external/protobuf/src \
|
external/protobuf/src \
|
||||||
../oemcrypto/include
|
../oemcrypto/include
|
||||||
|
|
||||||
@@ -26,11 +27,13 @@ LOCAL_SRC_FILES := \
|
|||||||
$(CORE_SRC_DIR)/cdm_session.cpp \
|
$(CORE_SRC_DIR)/cdm_session.cpp \
|
||||||
$(CORE_SRC_DIR)/crypto_engine.cpp \
|
$(CORE_SRC_DIR)/crypto_engine.cpp \
|
||||||
$(CORE_SRC_DIR)/crypto_session.cpp \
|
$(CORE_SRC_DIR)/crypto_session.cpp \
|
||||||
|
$(CORE_SRC_DIR)/device_files.cpp \
|
||||||
$(CORE_SRC_DIR)/license.cpp \
|
$(CORE_SRC_DIR)/license.cpp \
|
||||||
$(CORE_SRC_DIR)/policy_engine.cpp \
|
$(CORE_SRC_DIR)/policy_engine.cpp \
|
||||||
$(CORE_SRC_DIR)/properties.cpp \
|
$(CORE_SRC_DIR)/properties.cpp \
|
||||||
$(CORE_SRC_DIR)/string_conversions.cpp \
|
$(CORE_SRC_DIR)/string_conversions.cpp \
|
||||||
$(SRC_DIR)/clock.cpp \
|
$(SRC_DIR)/clock.cpp \
|
||||||
|
$(SRC_DIR)/file_store.cpp \
|
||||||
$(SRC_DIR)/lock.cpp \
|
$(SRC_DIR)/lock.cpp \
|
||||||
$(SRC_DIR)/log.cpp \
|
$(SRC_DIR)/log.cpp \
|
||||||
$(SRC_DIR)/properties.cpp \
|
$(SRC_DIR)/properties.cpp \
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
#ifndef CDM_BASE_CDM_ENGINE_H_
|
#ifndef CDM_BASE_CDM_ENGINE_H_
|
||||||
#define CDM_BASE_CDM_ENGINE_H_
|
#define CDM_BASE_CDM_ENGINE_H_
|
||||||
|
|
||||||
#include "crypto_engine.h"
|
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
class CdmSession;
|
class CdmSession;
|
||||||
|
class CryptoEngine;
|
||||||
class WvCdmEventListener;
|
class WvCdmEventListener;
|
||||||
|
|
||||||
typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap;
|
typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap;
|
||||||
@@ -32,13 +32,11 @@ class CdmEngine : public TimerHandler {
|
|||||||
const CdmInitData& init_data,
|
const CdmInitData& init_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
CdmAppParameterMap& app_parameters,
|
CdmAppParameterMap& app_parameters,
|
||||||
CdmKeyMessage* key_request);
|
CdmKeyMessage* key_request,
|
||||||
|
std::string* server_url);
|
||||||
|
|
||||||
// Accept license response and extract key info.
|
// Accept license response and extract key info.
|
||||||
CdmResponseType AddKey(const CdmSessionId& session_id,
|
CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||||
bool is_key_system_init_data_present,
|
|
||||||
const CdmKeySystem& key_system,
|
|
||||||
const CdmInitData& init_data,
|
|
||||||
const CdmKeyResponse& key_data);
|
const CdmKeyResponse& key_data);
|
||||||
|
|
||||||
// Cancel session and unload keys.
|
// Cancel session and unload keys.
|
||||||
@@ -48,16 +46,11 @@ class CdmEngine : public TimerHandler {
|
|||||||
|
|
||||||
// Construct valid renewal request for the current session keys.
|
// Construct valid renewal request for the current session keys.
|
||||||
CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
|
CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
|
||||||
bool is_key_system_init_data_present,
|
CdmKeyMessage* key_request,
|
||||||
const CdmKeySystem& key_system,
|
std::string* server_url);
|
||||||
const CdmInitData& init_data,
|
|
||||||
CdmKeyMessage* key_request);
|
|
||||||
|
|
||||||
// Accept renewal response and update key info.
|
// Accept renewal response and update key info.
|
||||||
CdmResponseType RenewKey(const CdmSessionId& session_id,
|
CdmResponseType RenewKey(const CdmSessionId& session_id,
|
||||||
bool is_key_system_init_data_present,
|
|
||||||
const CdmKeySystem& key_system,
|
|
||||||
const CdmInitData& init_data,
|
|
||||||
const CdmKeyResponse& key_data);
|
const CdmKeyResponse& key_data);
|
||||||
|
|
||||||
// Query system information
|
// Query system information
|
||||||
@@ -107,9 +100,7 @@ class CdmEngine : public TimerHandler {
|
|||||||
// private methods
|
// private methods
|
||||||
// Cancel all sessions
|
// Cancel all sessions
|
||||||
bool CancelSessions();
|
bool CancelSessions();
|
||||||
void CleanupProvisioingSessions(CdmSession* cdm_session,
|
void CleanupProvisioningSession(const CdmSessionId& cdm_session_id);
|
||||||
CryptoEngine* crypto_engine,
|
|
||||||
const CdmSessionId& cdm_session_id);
|
|
||||||
void ComposeJsonRequest(const std::string& message,
|
void ComposeJsonRequest(const std::string& message,
|
||||||
const std::string& signature,
|
const std::string& signature,
|
||||||
CdmProvisioningRequest* request);
|
CdmProvisioningRequest* request);
|
||||||
@@ -131,6 +122,7 @@ class CdmEngine : public TimerHandler {
|
|||||||
virtual void OnTimerEvent();
|
virtual void OnTimerEvent();
|
||||||
|
|
||||||
// instance variables
|
// instance variables
|
||||||
|
CdmSession* provisioning_session_;
|
||||||
CdmSessionMap sessions_;
|
CdmSessionMap sessions_;
|
||||||
|
|
||||||
// policy timer
|
// policy timer
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ class CdmSession {
|
|||||||
CdmResponseType GenerateKeyRequest(const CdmInitData& pssh_data,
|
CdmResponseType GenerateKeyRequest(const CdmInitData& pssh_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
CdmAppParameterMap& app_parameters,
|
CdmAppParameterMap& app_parameters,
|
||||||
CdmKeyMessage* key_request);
|
CdmKeyMessage* key_request,
|
||||||
|
std::string* server_url);
|
||||||
|
|
||||||
// AddKey() - Accept license response and extract key info.
|
// AddKey() - Accept license response and extract key info.
|
||||||
CdmResponseType AddKey(const CdmKeyResponse& key_response);
|
CdmResponseType AddKey(const CdmKeyResponse& key_response);
|
||||||
@@ -65,7 +66,8 @@ class CdmSession {
|
|||||||
// License renewal
|
// License renewal
|
||||||
// GenerateRenewalRequest() - Construct valid renewal request for the current
|
// GenerateRenewalRequest() - Construct valid renewal request for the current
|
||||||
// session keys.
|
// session keys.
|
||||||
CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request);
|
CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||||
|
std::string* server_url);
|
||||||
|
|
||||||
// RenewKey() - Accept renewal response and update key info.
|
// RenewKey() - Accept renewal response and update key info.
|
||||||
CdmResponseType RenewKey(const CdmKeyResponse& key_response);
|
CdmResponseType RenewKey(const CdmKeyResponse& key_response);
|
||||||
|
|||||||
@@ -56,10 +56,11 @@ class CryptoSession {
|
|||||||
bool GenerateSignature(const std::string& message,
|
bool GenerateSignature(const std::string& message,
|
||||||
std::string* signature);
|
std::string* signature);
|
||||||
bool RewrapDeviceRSAKey(const std::string& message,
|
bool RewrapDeviceRSAKey(const std::string& message,
|
||||||
const uint32_t* nonce,
|
const std::string& signature,
|
||||||
const uint8_t* enc_rsa_key,
|
const std::string& nonce,
|
||||||
|
const std::string& enc_rsa_key,
|
||||||
size_t enc_rsa_key_length,
|
size_t enc_rsa_key_length,
|
||||||
const uint8_t* enc_rsa_key_iv,
|
const std::string& rsa_key_iv,
|
||||||
uint8_t* wrapped_rsa_key,
|
uint8_t* wrapped_rsa_key,
|
||||||
size_t* wrapped_rsa_key_length);
|
size_t* wrapped_rsa_key_length);
|
||||||
|
|
||||||
|
|||||||
33
libwvdrmengine/cdm/core/include/device_files.h
Normal file
33
libwvdrmengine/cdm/core/include/device_files.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
#ifndef CDM_BASE_DEVICE_FILES_H_
|
||||||
|
#define CDM_BASE_DEVICE_FILES_H_
|
||||||
|
|
||||||
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
|
||||||
|
class DeviceFiles {
|
||||||
|
public:
|
||||||
|
static bool StoreCertificate(const std::string& certificate,
|
||||||
|
const std::string& wrapped_private_key);
|
||||||
|
static bool RetrieveCertificate(std::string* certificate,
|
||||||
|
std::string* wrapped_private_key);
|
||||||
|
|
||||||
|
static std::string GetPath(const char* dir, const char * filename);
|
||||||
|
static const char* kBasePath;
|
||||||
|
static const char* kIdmPath;
|
||||||
|
static const char* kCencPath;
|
||||||
|
static const char* kDeviceCertificateFileName;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
CORE_DISALLOW_COPY_AND_ASSIGN(DeviceFiles);
|
||||||
|
}; // namespace wvcdm
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CDM_BASE_DEVICE_FILES_H_
|
||||||
52
libwvdrmengine/cdm/core/include/file_store.h
Normal file
52
libwvdrmengine/cdm/core/include/file_store.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// File - Platform independent interface for a File class
|
||||||
|
//
|
||||||
|
#ifndef CDM_BASE_FILE_STORE_H_
|
||||||
|
#define CDM_BASE_FILE_STORE_H_
|
||||||
|
|
||||||
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
|
||||||
|
// File class. The implementation is platform dependent.
|
||||||
|
class File {
|
||||||
|
public:
|
||||||
|
// defines as bit flag
|
||||||
|
enum OpenFlags {
|
||||||
|
kNoFlags = 0,
|
||||||
|
kBinary = 1,
|
||||||
|
kCreate = 2,
|
||||||
|
kReadOnly = 4, // defauts 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();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
Impl *impl_;
|
||||||
|
|
||||||
|
CORE_DISALLOW_COPY_AND_ASSIGN(File);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace wvcdm
|
||||||
|
|
||||||
|
#endif // CDM_BASE_FILE_STORE_H_
|
||||||
@@ -27,8 +27,10 @@ class CdmLicense {
|
|||||||
bool PrepareKeyRequest(const CdmInitData& pssh_data,
|
bool PrepareKeyRequest(const CdmInitData& pssh_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
CdmAppParameterMap& app_parameters,
|
CdmAppParameterMap& app_parameters,
|
||||||
CdmKeyMessage* signed_request);
|
CdmKeyMessage* signed_request,
|
||||||
bool PrepareKeyRenewalRequest(CdmKeyMessage* signed_request);
|
std::string* server_url);
|
||||||
|
bool PrepareKeyRenewalRequest(CdmKeyMessage* signed_request,
|
||||||
|
std::string* server_url);
|
||||||
CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
|
CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
|
||||||
CdmResponseType HandleKeyRenewalResponse(
|
CdmResponseType HandleKeyRenewalResponse(
|
||||||
const CdmKeyResponse& license_response);
|
const CdmKeyResponse& license_response);
|
||||||
@@ -39,6 +41,7 @@ private:
|
|||||||
LicenseIdentification license_id_;
|
LicenseIdentification license_id_;
|
||||||
CryptoSession* session_;
|
CryptoSession* session_;
|
||||||
PolicyEngine* policy_engine_;
|
PolicyEngine* policy_engine_;
|
||||||
|
std::string server_url_;
|
||||||
std::string token_;
|
std::string token_;
|
||||||
|
|
||||||
// Used for certificate based licensing
|
// Used for certificate based licensing
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "buffer_reader.h"
|
#include "buffer_reader.h"
|
||||||
#include "cdm_session.h"
|
#include "cdm_session.h"
|
||||||
#include "crypto_engine.h"
|
#include "crypto_engine.h"
|
||||||
|
#include "device_files.h"
|
||||||
#include "license_protocol.pb.h"
|
#include "license_protocol.pb.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "properties.h"
|
#include "properties.h"
|
||||||
@@ -29,7 +30,7 @@ using video_widevine_server::sdk::SignedProvisioningMessage;
|
|||||||
|
|
||||||
typedef std::map<CdmSessionId,CdmSession*>::const_iterator CdmSessionIter;
|
typedef std::map<CdmSessionId,CdmSession*>::const_iterator CdmSessionIter;
|
||||||
|
|
||||||
CdmEngine::CdmEngine() {
|
CdmEngine::CdmEngine() : provisioning_session_(NULL) {
|
||||||
Properties::Init();
|
Properties::Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +106,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
|||||||
const CdmInitData& init_data,
|
const CdmInitData& init_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
CdmAppParameterMap& app_parameters,
|
CdmAppParameterMap& app_parameters,
|
||||||
CdmKeyMessage* key_request) {
|
CdmKeyMessage* key_request,
|
||||||
|
std::string* server_url) {
|
||||||
LOGI("CdmEngine::GenerateKeyRequest");
|
LOGI("CdmEngine::GenerateKeyRequest");
|
||||||
|
|
||||||
CdmSessionIter iter = sessions_.find(session_id);
|
CdmSessionIter iter = sessions_.find(session_id);
|
||||||
@@ -140,7 +142,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
|||||||
CdmResponseType sts = iter->second->GenerateKeyRequest(extracted_pssh,
|
CdmResponseType sts = iter->second->GenerateKeyRequest(extracted_pssh,
|
||||||
license_type,
|
license_type,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
key_request);
|
key_request,
|
||||||
|
server_url);
|
||||||
|
|
||||||
if (KEY_MESSAGE != sts) {
|
if (KEY_MESSAGE != sts) {
|
||||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d",
|
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d",
|
||||||
@@ -156,9 +159,6 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
|||||||
|
|
||||||
CdmResponseType CdmEngine::AddKey(
|
CdmResponseType CdmEngine::AddKey(
|
||||||
const CdmSessionId& session_id,
|
const CdmSessionId& session_id,
|
||||||
bool is_key_system_init_data_present,
|
|
||||||
const CdmKeySystem& key_system,
|
|
||||||
const CdmInitData& init_data,
|
|
||||||
const CdmKeyResponse& key_data) {
|
const CdmKeyResponse& key_data) {
|
||||||
LOGI("CdmEngine::AddKey");
|
LOGI("CdmEngine::AddKey");
|
||||||
|
|
||||||
@@ -168,14 +168,6 @@ CdmResponseType CdmEngine::AddKey(
|
|||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_key_system_init_data_present) {
|
|
||||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_key_system_init_data_present) {
|
|
||||||
// TODO(edwinwong, rfrias): validate init_data has not changed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key_data.empty()) {
|
if (key_data.empty()) {
|
||||||
LOGE("CdmEngine::AddKey: no key_data");
|
LOGE("CdmEngine::AddKey: no key_data");
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
@@ -218,10 +210,8 @@ CdmResponseType CdmEngine::CancelKeyRequest(
|
|||||||
|
|
||||||
CdmResponseType CdmEngine::GenerateRenewalRequest(
|
CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||||
const CdmSessionId& session_id,
|
const CdmSessionId& session_id,
|
||||||
bool is_key_system_init_data_present,
|
CdmKeyMessage* key_request,
|
||||||
const CdmKeySystem& key_system,
|
std::string* server_url) {
|
||||||
const CdmInitData& init_data,
|
|
||||||
CdmKeyMessage* key_request) {
|
|
||||||
LOGI("CdmEngine::GenerateRenewalRequest");
|
LOGI("CdmEngine::GenerateRenewalRequest");
|
||||||
|
|
||||||
CdmSessionIter iter = sessions_.find(session_id);
|
CdmSessionIter iter = sessions_.find(session_id);
|
||||||
@@ -230,14 +220,6 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
|||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_key_system_init_data_present) {
|
|
||||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_key_system_init_data_present) {
|
|
||||||
// TODO(edwinwong, rfrias): validate init_data has not changed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!key_request) {
|
if (!key_request) {
|
||||||
LOGE("CdmEngine::GenerateRenewalRequest: no key request destination provided");
|
LOGE("CdmEngine::GenerateRenewalRequest: no key request destination provided");
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
@@ -245,7 +227,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
|||||||
|
|
||||||
key_request->clear();
|
key_request->clear();
|
||||||
|
|
||||||
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request);
|
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request,
|
||||||
|
server_url);
|
||||||
|
|
||||||
if (KEY_MESSAGE != sts) {
|
if (KEY_MESSAGE != sts) {
|
||||||
LOGE("CdmEngine::GenerateRenewalRequest: key request generation failed, sts=%d",
|
LOGE("CdmEngine::GenerateRenewalRequest: key request generation failed, sts=%d",
|
||||||
@@ -258,9 +241,6 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
|||||||
|
|
||||||
CdmResponseType CdmEngine::RenewKey(
|
CdmResponseType CdmEngine::RenewKey(
|
||||||
const CdmSessionId& session_id,
|
const CdmSessionId& session_id,
|
||||||
bool is_key_system_init_data_present,
|
|
||||||
const CdmKeySystem& key_system,
|
|
||||||
const CdmInitData& init_data,
|
|
||||||
const CdmKeyResponse& key_data) {
|
const CdmKeyResponse& key_data) {
|
||||||
LOGI("CdmEngine::RenewKey");
|
LOGI("CdmEngine::RenewKey");
|
||||||
|
|
||||||
@@ -270,14 +250,6 @@ CdmResponseType CdmEngine::RenewKey(
|
|||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_key_system_init_data_present) {
|
|
||||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_key_system_init_data_present) {
|
|
||||||
// TODO(edwinwong, rfrias): validate init_data has not changed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key_data.empty()) {
|
if (key_data.empty()) {
|
||||||
LOGE("CdmEngine::RenewKey: no key_data");
|
LOGE("CdmEngine::RenewKey: no key_data");
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
@@ -357,15 +329,9 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
|
|||||||
return iter->second->QueryKeyControlInfo(key_info);
|
return iter->second->QueryKeyControlInfo(key_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CdmEngine::CleanupProvisioingSessions(
|
void CdmEngine::CleanupProvisioningSession(const CdmSessionId& cdm_session_id) {
|
||||||
CdmSession* cdm_session,
|
CloseSession(cdm_session_id);
|
||||||
CryptoEngine* crypto_engine,
|
provisioning_session_ = NULL;
|
||||||
const CdmSessionId& cdm_session_id) {
|
|
||||||
if (NULL == cdm_session) return;
|
|
||||||
|
|
||||||
cdm_session->DestroySession();
|
|
||||||
if (crypto_engine) crypto_engine->DestroySession(cdm_session_id);
|
|
||||||
delete cdm_session;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -391,21 +357,16 @@ void CdmEngine::ComposeJsonRequest(
|
|||||||
// performs base64 encoding for message
|
// performs base64 encoding for message
|
||||||
std::vector<uint8_t> message_vector(message.begin(), message.end());
|
std::vector<uint8_t> message_vector(message.begin(), message.end());
|
||||||
std::string message_b64 = Base64SafeEncode(message_vector);
|
std::string message_b64 = Base64SafeEncode(message_vector);
|
||||||
LOGD("b64 serialized req:\r\n%s", message_b64.data());
|
|
||||||
|
|
||||||
// performs base64 encoding for signature
|
// performs base64 encoding for signature
|
||||||
std::vector<uint8_t> signature_vector(signature.begin(), signature.end());
|
std::vector<uint8_t> signature_vector(signature.begin(), signature.end());
|
||||||
std::string signature_b64 = Base64SafeEncode(signature_vector);
|
std::string signature_b64 = Base64SafeEncode(signature_vector);
|
||||||
LOGD("b64 signature:\r\n%s", signature_b64.data());
|
|
||||||
|
|
||||||
// TODO(edwinwong): write a function to escape JSON output
|
|
||||||
request->assign("{'signedRequest':{'message':'");
|
request->assign("{'signedRequest':{'message':'");
|
||||||
request->append(message_b64);
|
request->append(message_b64);
|
||||||
request->append("','signature':'");
|
request->append("','signature':'");
|
||||||
request->append(signature_b64);
|
request->append(signature_b64);
|
||||||
request->append("'}}");
|
request->append("'}}");
|
||||||
LOGD("json str:\r\n%s", request->c_str());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -419,7 +380,12 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
CdmProvisioningRequest* request,
|
CdmProvisioningRequest* request,
|
||||||
std::string* default_url) {
|
std::string* default_url) {
|
||||||
if (!request || !default_url) {
|
if (!request || !default_url) {
|
||||||
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
|
LOGE("GetProvisioningRequest: invalid input parameters");
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provisioning_session_) {
|
||||||
|
LOGE("GetProvisioningRequest: duplicate provisioning request?");
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,19 +396,19 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
//
|
//
|
||||||
CdmSession* cdm_session = new CdmSession();
|
CdmSession* cdm_session = new CdmSession();
|
||||||
if (!cdm_session) {
|
if (!cdm_session) {
|
||||||
LOGE("CdmEngine::GetProvisioningRequest: fails to create a cdm session");
|
LOGE("GetProvisioningRequest: fails to create a cdm session");
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cdm_session->session_id().empty()) {
|
if (cdm_session->session_id().empty()) {
|
||||||
LOGE("CdmEngine::GetProvisioningRequest: fails to generate session ID");
|
LOGE("GetProvisioningRequest: fails to generate session ID");
|
||||||
delete cdm_session;
|
delete cdm_session;
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||||
if (!crypto_engine) {
|
if (!crypto_engine) {
|
||||||
LOGE("CdmEngine::GetProvisioningRequest: fails to create a crypto engine");
|
LOGE("GetProvisioningRequest: fails to create a crypto engine");
|
||||||
delete cdm_session;
|
delete cdm_session;
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
@@ -450,11 +416,13 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
CdmSessionId cdm_session_id = cdm_session->session_id();
|
CdmSessionId cdm_session_id = cdm_session->session_id();
|
||||||
CryptoSession* crypto_session = crypto_engine->CreateSession(cdm_session_id);
|
CryptoSession* crypto_session = crypto_engine->CreateSession(cdm_session_id);
|
||||||
if (!crypto_session) {
|
if (!crypto_session) {
|
||||||
LOGE("CdmEngine::GetProvisioningRequest: fails to create a crypto session");
|
LOGE("GetProvisioningRequest: fails to create a crypto session");
|
||||||
delete cdm_session;
|
delete cdm_session;
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provisioning_session_ = cdm_session;
|
||||||
|
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Prepares device provisioning request.
|
// Prepares device provisioning request.
|
||||||
@@ -464,20 +432,24 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
client_id->set_type(ClientIdentification::KEYBOX);
|
client_id->set_type(ClientIdentification::KEYBOX);
|
||||||
std::string token;
|
std::string token;
|
||||||
if (!crypto_engine->GetToken(&token)) {
|
if (!crypto_engine->GetToken(&token)) {
|
||||||
LOGE("CdmEngine::GetProvisioningRequest: fails to get token");
|
LOGE("GetProvisioningRequest: fails to get token");
|
||||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
client_id->set_token(token);
|
client_id->set_token(token);
|
||||||
|
|
||||||
uint32_t nonce;
|
uint32_t nonce;
|
||||||
if (!crypto_session->GenerateNonce(&nonce)) {
|
if (!crypto_session->GenerateNonce(&nonce)) {
|
||||||
LOGE("CdmEngine::GetProvisioningRequest: fails to generate a nonce");
|
LOGE("GetProvisioningRequest: fails to generate a nonce");
|
||||||
crypto_engine->DestroySession(cdm_session_id);
|
crypto_engine->DestroySession(cdm_session_id);
|
||||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
provisioning_request.set_nonce(UintToString(nonce));
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
// Serializes the provisioning request.
|
// Serializes the provisioning request.
|
||||||
std::string serialized_request;
|
std::string serialized_request;
|
||||||
@@ -487,27 +459,23 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
std::string request_signature;
|
std::string request_signature;
|
||||||
if (!crypto_session->PrepareRequest(serialized_request, &request_signature)) {
|
if (!crypto_session->PrepareRequest(serialized_request, &request_signature)) {
|
||||||
request->clear();
|
request->clear();
|
||||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request_signature.empty()) {
|
if (request_signature.empty()) {
|
||||||
request->clear();
|
request->clear();
|
||||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts request into JSON string
|
// converts request into JSON string
|
||||||
ComposeJsonRequest(serialized_request, request_signature, request);
|
ComposeJsonRequest(serialized_request, request_signature, request);
|
||||||
|
|
||||||
// TODO(edwinwong): returns default provisioning server url
|
static const std::string kDefaultProvisioningServerUrl =
|
||||||
default_url->clear();
|
"http://www-googleapis-test.sandbox.google.com/certificateprovisioning/v1/devicecertificates/create";
|
||||||
|
default_url->assign(kDefaultProvisioningServerUrl);
|
||||||
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
// Closes the cdm session.
|
|
||||||
//
|
|
||||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,12 +505,6 @@ bool CdmEngine::ParseJsonResponse(
|
|||||||
|
|
||||||
size_t b64_string_size = end - start - start_substr.length();
|
size_t b64_string_size = end - start - start_substr.length();
|
||||||
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
|
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
|
||||||
|
|
||||||
// Due to the size of the message, debug string cannot dump out the
|
|
||||||
// entire string. Dump the beginning and end to verify instead.
|
|
||||||
LOGD("size=%u, b64_string start=%s, end=%s", b64_string.length(),
|
|
||||||
b64_string.substr(0, 16).c_str(),
|
|
||||||
b64_string.substr(b64_string_size - 16).c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decodes base64 substring and returns it in *result
|
// Decodes base64 substring and returns it in *result
|
||||||
@@ -587,32 +549,23 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
|||||||
|
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// First creates a cdm session, then creates a crypto session.
|
// Creates a crypto session using provisioning_session_.
|
||||||
//
|
//
|
||||||
CdmSession* cdm_session = new CdmSession();
|
if (!provisioning_session_) {
|
||||||
if (!cdm_session) {
|
LOGE("HandleProvisioningResponse: invalid provisioning session");
|
||||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to create a cdm session");
|
|
||||||
return UNKNOWN_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cdm_session->session_id().empty()) {
|
|
||||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to generate session ID");
|
|
||||||
delete cdm_session;
|
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||||
if (!crypto_engine) {
|
if (!crypto_engine) {
|
||||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to create a crypto engine");
|
LOGE("HandleProvisioningResponse: fails to create a crypto engine");
|
||||||
delete cdm_session;
|
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmSessionId cdm_session_id = cdm_session->session_id();
|
CdmSessionId cdm_session_id = provisioning_session_->session_id();
|
||||||
CryptoSession* crypto_session = crypto_engine->CreateSession(cdm_session_id);
|
CryptoSession* crypto_session = crypto_engine->FindSession(cdm_session_id);
|
||||||
if (!crypto_session) {
|
if (!crypto_session) {
|
||||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to create a crypto session");
|
LOGE("HandleProvisioningResponse: fails to find %s", cdm_session_id.c_str());
|
||||||
delete cdm_session;
|
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,36 +574,50 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
|||||||
// the provisioing request's input). Validate provisioning response and
|
// the provisioing request's input). Validate provisioning response and
|
||||||
// stores private device RSA key and certificate.
|
// stores private device RSA key and certificate.
|
||||||
ProvisioningResponse provisioning_response;
|
ProvisioningResponse provisioning_response;
|
||||||
|
|
||||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to parse signed message");
|
LOGE("HandleProvisioningResponse: fails to parse signed message");
|
||||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
|
||||||
const std::string& enc_rsa_key_iv = provisioning_response.device_rsa_key_iv();
|
if (!provisioning_response.has_device_rsa_key()) {
|
||||||
uint32_t nonce = strtoul(provisioning_response.nonce().data(), NULL, 10);
|
LOGE("HandleProvisioningResponse: invalid response - key not found");
|
||||||
std::vector<uint8_t> wrapped_rsa_key;
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
size_t wrapped_rsa_key_length = 0;
|
return UNKNOWN_ERROR;
|
||||||
if (!crypto_session->RewrapDeviceRSAKey(response,
|
}
|
||||||
&nonce,
|
|
||||||
reinterpret_cast<const uint8_t*>(enc_rsa_key.data()),
|
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||||
enc_rsa_key.length(),
|
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
|
||||||
reinterpret_cast<const uint8_t*>(enc_rsa_key_iv.data()),
|
const std::string& nonce = provisioning_response.nonce();
|
||||||
&wrapped_rsa_key[0],
|
|
||||||
&wrapped_rsa_key_length)) {
|
const int kRsaKeySize = 256;
|
||||||
LOGE("CdmEngine::HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
size_t wrapped_rsa_key_length = kRsaKeySize + enc_rsa_key.length();
|
||||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
std::vector<uint8_t> wrapped_rsa_key;
|
||||||
|
wrapped_rsa_key.resize(wrapped_rsa_key_length);
|
||||||
|
|
||||||
|
if (!crypto_session->RewrapDeviceRSAKey(signed_message,
|
||||||
|
signature,
|
||||||
|
nonce.data(),
|
||||||
|
enc_rsa_key,
|
||||||
|
enc_rsa_key.size(),
|
||||||
|
rsa_key_iv,
|
||||||
|
&wrapped_rsa_key[0],
|
||||||
|
&wrapped_rsa_key_length)) {
|
||||||
|
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||||
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(edwinwong): stores private device RSA key and cert
|
|
||||||
const std::string& device_certificate = provisioning_response.device_certificate();
|
const std::string& device_certificate = provisioning_response.device_certificate();
|
||||||
|
std::string the_wrapped_rsa_key(wrapped_rsa_key.begin(), wrapped_rsa_key.end());
|
||||||
|
DeviceFiles::StoreCertificate(device_certificate, the_wrapped_rsa_key);
|
||||||
|
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Closes the cdm session.
|
// Closes the cdm session.
|
||||||
//
|
//
|
||||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
|
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "clock.h"
|
#include "clock.h"
|
||||||
#include "crypto_engine.h"
|
#include "crypto_engine.h"
|
||||||
|
#include "device_files.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "properties.h"
|
#include "properties.h"
|
||||||
#include "string_conversions.h"
|
#include "string_conversions.h"
|
||||||
@@ -64,7 +65,8 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
|||||||
const CdmInitData& pssh_data,
|
const CdmInitData& pssh_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
CdmAppParameterMap& app_parameters,
|
CdmAppParameterMap& app_parameters,
|
||||||
CdmKeyMessage* key_request) {
|
CdmKeyMessage* key_request,
|
||||||
|
std::string* server_url) {
|
||||||
if (!crypto_session_) {
|
if (!crypto_session_) {
|
||||||
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
@@ -77,7 +79,8 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
|||||||
|
|
||||||
if (license_received_) {
|
if (license_received_) {
|
||||||
return Properties::require_explicit_renew_request() ?
|
return Properties::require_explicit_renew_request() ?
|
||||||
UNKNOWN_ERROR : GenerateRenewalRequest(key_request);
|
UNKNOWN_ERROR : GenerateRenewalRequest(key_request,
|
||||||
|
server_url);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (Properties::use_certificates_as_identification()) {
|
if (Properties::use_certificates_as_identification()) {
|
||||||
@@ -88,7 +91,8 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
|||||||
if (!license_parser_.PrepareKeyRequest(pssh_data,
|
if (!license_parser_.PrepareKeyRequest(pssh_data,
|
||||||
license_type,
|
license_type,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
key_request)) {
|
key_request,
|
||||||
|
server_url)) {
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
} else {
|
} else {
|
||||||
return KEY_MESSAGE;
|
return KEY_MESSAGE;
|
||||||
@@ -178,8 +182,10 @@ CdmResponseType CdmSession::Decrypt(bool is_encrypted,
|
|||||||
// License renewal
|
// License renewal
|
||||||
// GenerateRenewalRequest() - Construct valid renewal request for the current
|
// GenerateRenewalRequest() - Construct valid renewal request for the current
|
||||||
// session keys.
|
// session keys.
|
||||||
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request) {
|
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||||
if (!license_parser_.PrepareKeyRenewalRequest(key_request)) {
|
std::string* server_url) {
|
||||||
|
if (!license_parser_.PrepareKeyRenewalRequest(key_request,
|
||||||
|
server_url)) {
|
||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
} else {
|
} else {
|
||||||
return KEY_MESSAGE;
|
return KEY_MESSAGE;
|
||||||
@@ -207,8 +213,8 @@ CdmSessionId CdmSession::GenerateSessionId() {
|
|||||||
|
|
||||||
bool CdmSession::LoadDeviceCertificate(std::string* certificate,
|
bool CdmSession::LoadDeviceCertificate(std::string* certificate,
|
||||||
std::string* wrapped_key) {
|
std::string* wrapped_key) {
|
||||||
// TODO(edwingwong,rfrias): Need to read in the private key
|
return DeviceFiles::RetrieveCertificate(certificate,
|
||||||
return false;
|
wrapped_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
const uint32_t kMaxSignatureBufLength = 256;
|
||||||
// Encode unsigned integer into a big endian formatted string
|
// Encode unsigned integer into a big endian formatted string
|
||||||
std::string EncodeUint32(unsigned int u) {
|
std::string EncodeUint32(unsigned int u) {
|
||||||
std::string s;
|
std::string s;
|
||||||
@@ -357,8 +358,8 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
|
|||||||
bool CryptoSession::GenerateSignature(const std::string& message,
|
bool CryptoSession::GenerateSignature(const std::string& message,
|
||||||
std::string* signature) {
|
std::string* signature) {
|
||||||
LOGV("GenerateSignature: id=%ld", (uint32_t) oec_session_id_);
|
LOGV("GenerateSignature: id=%ld", (uint32_t) oec_session_id_);
|
||||||
uint8_t signature_buf[32];
|
uint8_t signature_buf[kMaxSignatureBufLength];
|
||||||
uint32_t length = 32;
|
size_t length = kMaxSignatureBufLength;
|
||||||
OEMCryptoResult sts;
|
OEMCryptoResult sts;
|
||||||
if (Properties::use_certificates_as_identification()) {
|
if (Properties::use_certificates_as_identification()) {
|
||||||
sts = OEMCrypto_GenerateRSASignature(
|
sts = OEMCrypto_GenerateRSASignature(
|
||||||
@@ -475,10 +476,11 @@ bool CryptoSession::SetDestinationBufferType() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||||
const uint32_t* nonce,
|
const std::string& signature,
|
||||||
const uint8_t* enc_rsa_key,
|
const std::string& nonce,
|
||||||
|
const std::string& enc_rsa_key,
|
||||||
size_t enc_rsa_key_length,
|
size_t enc_rsa_key_length,
|
||||||
const uint8_t* enc_rsa_key_iv,
|
const std::string& rsa_key_iv,
|
||||||
uint8_t* wrapped_rsa_key,
|
uint8_t* wrapped_rsa_key,
|
||||||
size_t* wrapped_rsa_key_length) {
|
size_t* wrapped_rsa_key_length) {
|
||||||
LOGV("CryptoSession::RewrapDeviceRSAKey: Lock+++");
|
LOGV("CryptoSession::RewrapDeviceRSAKey: Lock+++");
|
||||||
@@ -487,31 +489,28 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
|||||||
|
|
||||||
LOGV("crypto session id=%ld", static_cast<uint32_t>(oec_session_id_));
|
LOGV("crypto session id=%ld", static_cast<uint32_t>(oec_session_id_));
|
||||||
|
|
||||||
// HMAC-SHA256 signature
|
const uint8_t* signed_msg = reinterpret_cast<const uint8_t*>(message.data());
|
||||||
uint8_t signature[kSignatureSize];
|
const uint8_t* msg_rsa_key = NULL;
|
||||||
size_t signature_length = kSignatureSize;
|
const uint8_t* msg_rsa_key_iv = NULL;
|
||||||
OEMCryptoResult status = OEMCrypto_GenerateSignature(
|
const uint32_t* msg_nonce = NULL;
|
||||||
oec_session_id_,
|
if (enc_rsa_key.size() >= MAC_KEY_SIZE && rsa_key_iv.size() >= KEY_IV_SIZE) {
|
||||||
reinterpret_cast<const uint8_t*>(message.data()),
|
msg_rsa_key = signed_msg + GetOffset(message, enc_rsa_key);
|
||||||
message.size(),
|
msg_rsa_key_iv = signed_msg + GetOffset(message, rsa_key_iv);
|
||||||
signature,
|
msg_nonce = reinterpret_cast<const uint32_t*>(signed_msg + GetOffset(message, nonce));
|
||||||
&signature_length);
|
|
||||||
if (OEMCrypto_SUCCESS != status) {
|
|
||||||
LOGE("CryptoSession::RewrapDeviceRSAKey: GenerateSiganture failed");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status = OEMCrypto_RewrapDeviceRSAKey(
|
OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey(
|
||||||
oec_session_id_,
|
oec_session_id_,
|
||||||
reinterpret_cast<const uint8_t*>(message.data()), message.length(),
|
signed_msg, message.size(),
|
||||||
signature, signature_length,
|
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
|
||||||
nonce,
|
msg_nonce,
|
||||||
enc_rsa_key, enc_rsa_key_length,
|
msg_rsa_key, enc_rsa_key_length,
|
||||||
enc_rsa_key_iv,
|
msg_rsa_key_iv,
|
||||||
wrapped_rsa_key,
|
wrapped_rsa_key,
|
||||||
wrapped_rsa_key_length);
|
wrapped_rsa_key_length);
|
||||||
|
|
||||||
if (OEMCrypto_SUCCESS != status) {
|
if (OEMCrypto_SUCCESS != status) {
|
||||||
|
LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
196
libwvdrmengine/cdm/core/src/device_files.cpp
Normal file
196
libwvdrmengine/cdm/core/src/device_files.cpp
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "device_files.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <string.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/drm";
|
||||||
|
const char* DeviceFiles::kIdmPath = "/data/drm/IDM";
|
||||||
|
const char* DeviceFiles::kCencPath = "/data/drm/IDM/CENC";
|
||||||
|
const char* DeviceFiles::kDeviceCertificateFileName = "cert.bin";
|
||||||
|
|
||||||
|
// Protobuf generated classes.
|
||||||
|
using video_widevine_client::sdk::DeviceCertificate;
|
||||||
|
using video_widevine_client::sdk::HashedFile;
|
||||||
|
|
||||||
|
bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||||
|
const std::string& wrapped_private_key) {
|
||||||
|
// Fill in file information
|
||||||
|
video_widevine_client::sdk::File file;
|
||||||
|
|
||||||
|
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();
|
||||||
|
device_certificate->set_certificate(certificate);
|
||||||
|
device_certificate->set_wrapped_private_key(wrapped_private_key);
|
||||||
|
|
||||||
|
std::string serialized_string;
|
||||||
|
file.SerializeToString(&serialized_string);
|
||||||
|
|
||||||
|
// calculate SHA hash
|
||||||
|
std::string hash;
|
||||||
|
if (!Hash(serialized_string, &hash)) {
|
||||||
|
LOGW("DeviceFiles::StoreCertificate: Hash computation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||||
|
std::string* wrapped_private_key) {
|
||||||
|
|
||||||
|
std::string serialized_hashed_file;
|
||||||
|
if (!RetrieveFile(kDeviceCertificateFileName, &serialized_hashed_file))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
HashedFile hashed_file;
|
||||||
|
if (!hashed_file.ParseFromString(serialized_hashed_file)) {
|
||||||
|
LOGW("DeviceFiles::RetrieveCertificate: Unable to parse hash file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hash;
|
||||||
|
if (!Hash(hashed_file.file(), &hash)) {
|
||||||
|
LOGW("DeviceFiles::RetrieveCertificate: Hash computation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash.compare(hashed_file.hash())) {
|
||||||
|
LOGW("DeviceFiles::RetrieveCertificate: Hash mismatch");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
video_widevine_client::sdk::File file;
|
||||||
|
if (!file.ParseFromString(hashed_file.file())) {
|
||||||
|
LOGW("DeviceFiles::RetrieveCertificate: Unable to parse file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.type() != video_widevine_client::sdk::File::DEVICE_CERTIFICATE) {
|
||||||
|
LOGW("DeviceFiles::RetrieveCertificate: Incorrect file type");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
||||||
|
LOGW("DeviceFiles::RetrieveCertificate: Incorrect file version");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.has_device_certificate()) {
|
||||||
|
LOGW("DeviceFiles::RetrieveCertificate: Certificate not present");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceCertificate device_certificate = file.device_certificate();
|
||||||
|
|
||||||
|
*certificate = device_certificate.certificate();
|
||||||
|
*wrapped_private_key = device_certificate.wrapped_private_key();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
|
||||||
|
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);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
|
||||||
|
if (!name)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!File::IsDirectory(kCencPath)) {
|
||||||
|
if (!File::CreateDirectory(kCencPath))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path = GetPath(kCencPath, name);
|
||||||
|
|
||||||
|
File file(path, File::kCreate | File::kTruncate | File::kBinary);
|
||||||
|
if (file.IsBad()) {
|
||||||
|
LOGW("DeviceFiles::StoreFile: File open failed: %s", path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||||
|
if (!data)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string path = GetPath(kCencPath, name);
|
||||||
|
|
||||||
|
if (!File::Exists(path)) {
|
||||||
|
LOGW("DeviceFiles::RetrieveFile: %s does not exist", path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
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());
|
||||||
|
|
||||||
|
if (bytes != static_cast<ssize_t>(data->size())) {
|
||||||
|
LOGW("DeviceFiles::StoreFile: write failed: %d %d", data->size(), bytes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DeviceFiles::GetPath(const char* dir, const char * filename) {
|
||||||
|
// TODO(rfrias): Make this work for non-unix paths
|
||||||
|
std::string path = dir;
|
||||||
|
path += "/";
|
||||||
|
path += filename;
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
47
libwvdrmengine/cdm/core/src/device_files.proto
Normal file
47
libwvdrmengine/cdm/core/src/device_files.proto
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// device_files.proto
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
// Format of various files stored at the device.
|
||||||
|
//
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package video_widevine_client.sdk;
|
||||||
|
|
||||||
|
// need this if we are using libprotobuf-cpp-2.3.0-lite
|
||||||
|
option optimize_for = LITE_RUNTIME;
|
||||||
|
|
||||||
|
message DeviceCertificate {
|
||||||
|
optional bytes certificate = 1;
|
||||||
|
optional bytes wrapped_private_key = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message License {
|
||||||
|
optional bytes key_set_id = 1;
|
||||||
|
optional bytes pssh_data = 2;
|
||||||
|
optional bytes license_request = 3;
|
||||||
|
optional bytes license = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message File {
|
||||||
|
enum FileType {
|
||||||
|
DEVICE_CERTIFICATE = 1;
|
||||||
|
LICENSE = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FileVersion {
|
||||||
|
VERSION_1 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional FileType type = 1;
|
||||||
|
optional FileVersion version = 2 [default = VERSION_1];
|
||||||
|
optional DeviceCertificate device_certificate = 3;
|
||||||
|
repeated License licenses = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HashedFile {
|
||||||
|
optional bytes file = 1;
|
||||||
|
optional bytes hash = 2;
|
||||||
|
}
|
||||||
@@ -91,7 +91,8 @@ bool CdmLicense::Init(const std::string& token,
|
|||||||
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
CdmAppParameterMap& app_parameters,
|
CdmAppParameterMap& app_parameters,
|
||||||
CdmKeyMessage* signed_request) {
|
CdmKeyMessage* signed_request,
|
||||||
|
std::string* server_url) {
|
||||||
if (!session_ ||
|
if (!session_ ||
|
||||||
token_.empty()) {
|
token_.empty()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -104,6 +105,10 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
|||||||
LOGE("CdmLicense::PrepareKeyRequest : No signed request provided.");
|
LOGE("CdmLicense::PrepareKeyRequest : No signed request provided.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!server_url) {
|
||||||
|
LOGE("CdmLicense::PrepareKeyRequest : No server url provided.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(gmorgan): Request ID owned by session?
|
// TODO(gmorgan): Request ID owned by session?
|
||||||
std::string request_id;
|
std::string request_id;
|
||||||
@@ -191,6 +196,9 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
|||||||
std::string serialized_license_req;
|
std::string serialized_license_req;
|
||||||
license_request.SerializeToString(&serialized_license_req);
|
license_request.SerializeToString(&serialized_license_req);
|
||||||
|
|
||||||
|
if (Properties::use_certificates_as_identification())
|
||||||
|
key_request_ = serialized_license_req;
|
||||||
|
|
||||||
// Derive signing and encryption keys and construct signature.
|
// Derive signing and encryption keys and construct signature.
|
||||||
std::string license_request_signature;
|
std::string license_request_signature;
|
||||||
if (!session_->PrepareRequest(serialized_license_req,
|
if (!session_->PrepareRequest(serialized_license_req,
|
||||||
@@ -212,13 +220,12 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
|||||||
|
|
||||||
signed_message.SerializeToString(signed_request);
|
signed_message.SerializeToString(signed_request);
|
||||||
|
|
||||||
if (Properties::use_certificates_as_identification())
|
*server_url = server_url_;
|
||||||
key_request_ = *signed_request;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request) {
|
bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request,
|
||||||
|
std::string* server_url) {
|
||||||
if (!session_) {
|
if (!session_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -226,6 +233,10 @@ bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request) {
|
|||||||
LOGE("CdmLicense::PrepareKeyRenewalRequest : No signed request provided.");
|
LOGE("CdmLicense::PrepareKeyRenewalRequest : No signed request provided.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!server_url) {
|
||||||
|
LOGE("CdmLicense::PrepareKeyRenewalRequest : No server url provided.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
LicenseRequest license_request;
|
LicenseRequest license_request;
|
||||||
license_request.set_type(LicenseRequest::RENEWAL);
|
license_request.set_type(LicenseRequest::RENEWAL);
|
||||||
@@ -263,7 +274,7 @@ bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request) {
|
|||||||
signed_message.set_msg(serialized_license_req);
|
signed_message.set_msg(serialized_license_req);
|
||||||
|
|
||||||
signed_message.SerializeToString(signed_request);
|
signed_message.SerializeToString(signed_request);
|
||||||
|
*server_url = server_url_;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,6 +343,10 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
|||||||
return KEY_ERROR;
|
return KEY_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (license.policy().has_renewal_server_url()) {
|
||||||
|
server_url_ = license.policy().renewal_server_url();
|
||||||
|
}
|
||||||
|
|
||||||
policy_engine_->SetLicense(license);
|
policy_engine_->SetLicense(license);
|
||||||
|
|
||||||
if (session_->LoadKeys(signed_response.msg(),
|
if (session_->LoadKeys(signed_response.msg(),
|
||||||
@@ -378,6 +393,11 @@ CdmResponseType CdmLicense::HandleKeyRenewalResponse(
|
|||||||
// This is the normal case.
|
// This is the normal case.
|
||||||
license_id_.CopyFrom(license.id());
|
license_id_.CopyFrom(license.id());
|
||||||
|
|
||||||
|
if (license.policy().has_renewal_server_url() &&
|
||||||
|
license.policy().renewal_server_url().size() > 0) {
|
||||||
|
server_url_ = license.policy().renewal_server_url();
|
||||||
|
}
|
||||||
|
|
||||||
policy_engine_->UpdateLicense(license);
|
policy_engine_->UpdateLicense(license);
|
||||||
|
|
||||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
#include "string_conversions.h"
|
#include "string_conversions.h"
|
||||||
|
|
||||||
@@ -12,14 +12,41 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Helper for Base64SafeDecode()
|
/*
|
||||||
char B64ToBin(char inch) {
|
* Returns a 8-bit char that is mapped to the 6-bit base64 in_ch.
|
||||||
if (inch >= 'A' && inch <= 'Z') return inch - 'A';
|
*
|
||||||
if (inch >= 'a' && inch <= 'z') return inch - 'a' + 26;
|
* Extracted from http://www.ietf.org/rfc/rfc3548.txt.
|
||||||
if (inch >= '0' && inch <= '9') return inch - '0' + 52;
|
*
|
||||||
if (inch == '-') return 62;
|
The "URL and Filename safe" Base 64 Alphabet
|
||||||
// if (inch == '_')
|
|
||||||
return 63;
|
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 '?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,10 +102,13 @@ std::string b2a_hex(const std::string& byte) {
|
|||||||
byte.length());
|
byte.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filename-friendly base64 encoding (RFC4648).
|
// Filename-friendly base64 encoding (RFC4648), commonly referred as
|
||||||
|
// Base64WebSafeEncode.
|
||||||
// This is the encoding required by GooglePlay for certain
|
// This is the encoding required by GooglePlay for certain
|
||||||
// license server transactions. It is also used for logging
|
// license server transactions. It is also used for logging
|
||||||
// certain strings.
|
// 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) {
|
std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
|
||||||
static const char kBase64Chars[] =
|
static const char kBase64Chars[] =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
@@ -87,9 +117,13 @@ std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
|
|||||||
if (bin_input.empty()) {
|
if (bin_input.empty()) {
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
int in_size = bin_input.size();
|
int in_size = bin_input.size();
|
||||||
int rup = ((in_size % 3) != 0) ? 1 : 0;
|
int final_quantum_in_bytes = in_size % 3;
|
||||||
int out_size = ((in_size * 4) / 3) + rup;
|
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(out_size, '\0');
|
std::string b64_output(out_size, '\0');
|
||||||
int in_index = 0;
|
int in_index = 0;
|
||||||
int out_index = 0;
|
int out_index = 0;
|
||||||
@@ -98,7 +132,7 @@ std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
|
|||||||
static const unsigned long kInMask = 0xff;
|
static const unsigned long kInMask = 0xff;
|
||||||
static const unsigned long kOutMask = 0x3f;
|
static const unsigned long kOutMask = 0x3f;
|
||||||
|
|
||||||
while (in_index < in_size) {
|
for (int i = 0; i < full_in_chunks; ++i) {
|
||||||
// up to 3 bytes (0..255) in
|
// up to 3 bytes (0..255) in
|
||||||
buffer = (bin_input.at(in_index) & kInMask);
|
buffer = (bin_input.at(in_index) & kInMask);
|
||||||
buffer <<= 8;
|
buffer <<= 8;
|
||||||
@@ -106,6 +140,7 @@ std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
|
|||||||
buffer <<= 8;
|
buffer <<= 8;
|
||||||
buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask);
|
buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask);
|
||||||
++in_index;
|
++in_index;
|
||||||
|
|
||||||
// up to 4 bytes (0..63) out
|
// up to 4 bytes (0..63) out
|
||||||
out_cc = (buffer >> 18) & kOutMask;
|
out_cc = (buffer >> 18) & kOutMask;
|
||||||
b64_output.at(out_index) = kBase64Chars[out_cc];
|
b64_output.at(out_index) = kBase64Chars[out_cc];
|
||||||
@@ -123,10 +158,49 @@ std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
|
|||||||
b64_output.at(out_index) = kBase64Chars[out_cc];
|
b64_output.at(out_index) = kBase64Chars[out_cc];
|
||||||
++out_index;
|
++out_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
return b64_output;
|
return b64_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode for Filename-friendly base64 encoding (RFC4648).
|
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
|
||||||
|
// as Base64WebSafeDecode.
|
||||||
// This is the encoding required by GooglePlay for certain
|
// This is the encoding required by GooglePlay for certain
|
||||||
// license server transactions. It is also used for logging
|
// license server transactions. It is also used for logging
|
||||||
// certain strings.
|
// certain strings.
|
||||||
@@ -134,9 +208,9 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
|
|||||||
if (b64_input.empty()) {
|
if (b64_input.empty()) {
|
||||||
return std::vector<uint8_t>();
|
return std::vector<uint8_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
int in_size = b64_input.size();
|
int in_size = b64_input.size();
|
||||||
// out_size should be an integral number of bytes, assuming correct encode
|
int out_size = in_size;
|
||||||
int out_size = ((in_size * 3) / 4);
|
|
||||||
std::vector<uint8_t> bin_output(out_size, '\0');
|
std::vector<uint8_t> bin_output(out_size, '\0');
|
||||||
int in_index = 0;
|
int in_index = 0;
|
||||||
int out_index = 0;
|
int out_index = 0;
|
||||||
@@ -144,7 +218,14 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
|
|||||||
unsigned char out_cc;
|
unsigned char out_cc;
|
||||||
static const unsigned long kOutMask = 0xff;
|
static const unsigned long kOutMask = 0xff;
|
||||||
|
|
||||||
while (in_index < in_size) {
|
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
|
// up to 4 bytes (0..63) in
|
||||||
buffer = B64ToBin(b64_input.at(in_index));
|
buffer = B64ToBin(b64_input.at(in_index));
|
||||||
buffer <<= 6;
|
buffer <<= 6;
|
||||||
@@ -167,6 +248,37 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
|
|||||||
bin_output.at(out_index) = out_cc;
|
bin_output.at(out_index) = out_cc;
|
||||||
++out_index;
|
++out_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
return bin_output;
|
return bin_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
193
libwvdrmengine/cdm/core/test/base64_test.cpp
Normal file
193
libwvdrmengine/cdm/core/test/base64_test.cpp
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "string_conversions.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string kMultipleOf24BitsData("Good day!");
|
||||||
|
std::string kOneByteOverData("Hello Googler");
|
||||||
|
std::string kTwoBytesOverData("Hello Googlers");
|
||||||
|
std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh");
|
||||||
|
std::string kOneByteOverB64Data("SGVsbG8gR29vZ2xlcg==");
|
||||||
|
std::string kTwoBytesOverB64Data("SGVsbG8gR29vZ2xlcnM=");
|
||||||
|
std::string kTestData =
|
||||||
|
"\030\361\\\366\267> \331\210\360\\-\311:\324\256\376"
|
||||||
|
"\261\234\241\326d\326\177\346\346\223\333Y\305\214\330";
|
||||||
|
std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg=";
|
||||||
|
std::string kB64ShortString("r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=");
|
||||||
|
std::string kB64LongString =
|
||||||
|
"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
|
||||||
|
"TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg"
|
||||||
|
"yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp"
|
||||||
|
"NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe"
|
||||||
|
"WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R"
|
||||||
|
"lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP"
|
||||||
|
"9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T"
|
||||||
|
"jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s"
|
||||||
|
"OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c"
|
||||||
|
"4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm"
|
||||||
|
"1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB"
|
||||||
|
"efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA"
|
||||||
|
"jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0"
|
||||||
|
"LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn"
|
||||||
|
"bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E"
|
||||||
|
"L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb"
|
||||||
|
"jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b"
|
||||||
|
"rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH"
|
||||||
|
"yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F"
|
||||||
|
"sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb"
|
||||||
|
"MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S"
|
||||||
|
"dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4"
|
||||||
|
"CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF"
|
||||||
|
"E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH"
|
||||||
|
"JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI"
|
||||||
|
"O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu"
|
||||||
|
"vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g"
|
||||||
|
"k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug"
|
||||||
|
"_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9"
|
||||||
|
"G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp"
|
||||||
|
"-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc"
|
||||||
|
"4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G"
|
||||||
|
"rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU"
|
||||||
|
"WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF"
|
||||||
|
"7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem"
|
||||||
|
"et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr"
|
||||||
|
"gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe"
|
||||||
|
"G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT"
|
||||||
|
"UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN"
|
||||||
|
"1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT"
|
||||||
|
"sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw"
|
||||||
|
"qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO"
|
||||||
|
"t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF"
|
||||||
|
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
|
||||||
|
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
|
||||||
|
class Base64Test : public testing::Test {
|
||||||
|
public:
|
||||||
|
Base64Test() {}
|
||||||
|
~Base64Test() {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
std::string b64_string = Base64SafeEncode(decoded_vector);
|
||||||
|
EXPECT_STREQ(kMultipleOf24BitsB64Data.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());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wvcdm
|
||||||
@@ -24,53 +24,55 @@ wvcdm::KeyId g_wrong_key_id;
|
|||||||
int g_use_full_path = 0; // cannot use boolean in getopt_long
|
int g_use_full_path = 0; // cannot use boolean in getopt_long
|
||||||
|
|
||||||
static wvcdm::CdmProvisioningResponse kJsonResponse =
|
static wvcdm::CdmProvisioningResponse kJsonResponse =
|
||||||
"{\"signedResponse\": {"
|
"{\"kind\": \"certificateprovisioning#certificateProvisioningResponse\","
|
||||||
"\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
|
"\"signedResponse\": {"
|
||||||
"TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg"
|
"\"message\": \"CtAJiVocnKtls7HO9SZtMg7-aEZosRT-qAjLnKt4FZ_5jvW-BEVBPNj1yeXh"
|
||||||
"yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp"
|
"o_wla-VdgYQBRsnXuONH4Rh7Kg0T1mv3ybc2VIU4imZ46nW7FsZYRxz3EwWkFelIav9JDeHBlat"
|
||||||
"NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe"
|
"qOKJEXtCiaySgdzNyZDDsZHxQu8Yh4ZeiHTVNHotoAiFy9qoUO0oaHpZ8xJxDkuN0u9-yauLdGy"
|
||||||
"WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R"
|
"F8B3u2CJjO7ztbYC1a5b8NTTDzN3hKdXy9FcR0a5R5YDzvlCWj-TeEIFSop0t4QI7bGLaubXJ_K"
|
||||||
"lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP"
|
"y7ppcLwM4LAOs12G4TyXa3R3A64rjF7tBdnbsA2S1ELpePWPK2jtvUu1TBWxS2bToU1PQrY8C9E"
|
||||||
"9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T"
|
"bDaN7TpE92sOyrW1sIGkit03thAaRjHUl_JtLyqKCFUamjsQqCi7pk_4IvoFJiYKAfE5Wf5cbP0"
|
||||||
"jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s"
|
"IrFW3CSkgYtO_d7jnyTUaweh9eP_8udIoUNkJjHMpglR_VJcWyIqw-hg8PqWm7AcYBUNDF0sYzM"
|
||||||
"OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c"
|
"uMiw2uq4RSBq8vklZ_nJjVc2GZ1pSqpaLLghDpviGUbEoGGJP4I15pveCKbyOj6TfKbeiN2jgit"
|
||||||
"4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm"
|
"1CaHMOmirOeSpD0gODGHQaTuVFbWQg-jgsbhht0NDHyqwmPDrCe78h2gXS6k0qDb3HL9M4QT5E4"
|
||||||
"1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB"
|
"-E2cZLfmvtrDokugAwf0eZM14464Oi_o6NK-Gbic8q1VnI1Y7uDctVHPAlV2NjsCVoaALtpNQZ0"
|
||||||
"efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA"
|
"HmaOF_jJ2RxCSyQCX1sxkRR_jeKyH10_i-GkXPokB3Z23LvHOheEVNmJJNvM9YBSw318aAiOIAo"
|
||||||
"jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0"
|
"Wdffrfo9j4xDs34W-ndKn344BNUq8tGmo3EVhSg6pdLPcCWSpjbgQxnBe_cwmKJVKU39GPMFV0P"
|
||||||
"LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn"
|
"vEeYKWljptsdUc9MH4XLomyalSPGCOkoVIwdPd1qXLhh1mmdNRm_7X-7zBezltjw42XKjtOKwBi"
|
||||||
"bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E"
|
"_UFmgY6i4Mjbk9ShExjWpvgKNntWepIZGZhYVP1tt4H-wqsXMfupTviHf_FaQX3cadUbO_6imfa"
|
||||||
"L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb"
|
"91MvnFe5v0iIi1HxsQzl0NmIByHFNUfHpzrZ0RzkmxsRY5lMLpOfdue7NJ5IZN1iVgiSS3llwa3"
|
||||||
"jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b"
|
"2s2Yq-gCzqv7vwdIwAxZ__gOr42LR__KCB51g9xwXdVLH71Dv_WDgDv5LJas9MVz5vv_TDo9Gqf"
|
||||||
"rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH"
|
"ZdEI8M2h0LbhdCVGxyYoeWVl6iHrPoeP6mJ5uaDZAAn_IdoVVPuCA8Wqmi_kbRdduwkuyasbIVx"
|
||||||
"yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F"
|
"dMwR7cRJz5Rl5kw36kjJtOv4wy9NU41PgNNuW1_2dWtIeKOcBJH2-5KbQbnntbKNdcPqZhTaoci"
|
||||||
"sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb"
|
"_3qDZtdlSqIJbDh7_4ZBkbcOHJ73jPKNOiLeDGD_rXFuPy4mAuxkZ8shXcw57xsIjz_OkB_Vr_2"
|
||||||
"MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S"
|
"2JhNMicnjITZDTGZzN4xyPldtOINCQGBBpUBLOaiWEFePqG9m5uWv6_NtpPt3I_YQnfUzv2RLFe"
|
||||||
"dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4"
|
"1pOLktoorTPWi8TH8hXnnmhHLMvd9r1Z1nISnZGmn_EhEFt-N-CCpiWAo5yQWIUA0iMqCdLHTub"
|
||||||
"CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF"
|
"l-tuhHyLqmiBUU9O7Ynwl12k2nqJTbbeSCt1mueee8j5bITUGZ8KAAtbIe_k0XMl5mRZWVzoBw0"
|
||||||
"E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH"
|
"RKmvBiWSRnOESEOdEAZw1ALRVjzEhSkDqGoIa7wkKsAIIAhIQB9n_3hOqlcEiZ4BTNiE2vRjgzd"
|
||||||
"JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI"
|
"aLBSKOAjCCAQoCggEBAKfruetSk8ULthZLZCoqaViWPEdlO50Bi1ChIjXCiuGNp566o74R4r_ou"
|
||||||
"O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu"
|
"8V7VX-Ze6p2Zpbm3iTPbB-vORaINQECi8TOA1xmxH0u7jrqHwfYX2a1EdCzp3DX5_lF2Rb44FI7"
|
||||||
"vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g"
|
"gKW1BwIeUDOdaMKApxZOp-jt1MBsMaMZDWgvQ5EAULzESq04zsynylaScm-IaZrY0mC3ynGz2YW"
|
||||||
"k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug"
|
"6TxbQKBUJsOUnpEEIDK2qlyEEESp0UFcTn2Lakda7MFI9wMAlH_qrDRdlDcLwGeD2qT8Lm4CFhV"
|
||||||
"_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9"
|
"cNDm3nqpONmBCJlebqSvavf5tALlIdR5CdfFV64beT8zWG1J-Rz6AGmHYgCucCAwEAASiZIDABE"
|
||||||
"G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp"
|
"oACFJ8UtXGiM__6SQFHXjN69lqzWfJKhplriW7m9wdCO-LEcCNFpUmzWBd1JppY11hL9AYO8OTI"
|
||||||
"-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc"
|
"Habeb65R51q_SjulaxlJwureJAYe-cO7RTvQvCddK9SJaDxeBe367ot_pnXvh66tksCkfvakY7W"
|
||||||
"4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G"
|
"igaDB8WVIaml6hmDADZgGAVfrPVWVPXMaY9LYg0r3r5PllfrmG7uXU0JcIa_0N-3MgT4QV4qIG2"
|
||||||
"rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU"
|
"9K9givfjELYidRpDDXu8KKcw0NYu-bTUNRaNrx6yaIpSbozen5HHBVdb5gJFB-922gr9mqY1PF_"
|
||||||
"WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF"
|
"-ulwvcXLn-Oa17QJ0YL1LkswDjVSC89MpJWkjW_qVlF8Rq2BQqwAggBEhCr7oe2E5WSaDltfP2r"
|
||||||
"7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem"
|
"q_jyGK_qu4sFIo4CMIIBCgKCAQEAmtIaWtGjInLYjdQic5DPUGUgnZah-FZVFt0SwcH90u1UhQW"
|
||||||
"et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr"
|
"eRgBH1psYrYqaaS8zu6vclTCT17E0WsDJlS8P8dBDlSMlyrNBZjCTDyczkI68tM5v0HXnpDCb5s"
|
||||||
"gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe"
|
"RaCSlP7Dl9BV_67GZPFoVuw53QCaI8cQ4RHcNVupExNRcFiCc-HSb3tZO7yyZyoB1Yx6GJnlts3"
|
||||||
"G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT"
|
"WA3A0DqVQIY79IGqdtTkKwydiNXVqR5ksMecqALwh9vKx6oSkxNat3I1BKQnzTY0pYChDjprabV"
|
||||||
"UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN"
|
"O9dAM2fYwGELJYel9qQV6LT6ar0PbKCMGa9qR-YwYiR8-SN-jpvmKL0h98bhTsC65DUQswIDAQA"
|
||||||
"1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT"
|
"BKJkgMAESgANZMzj1jPLFR8wnWKrTwL7dLF8qNJOPf7KDdJeqioqEkAe7mZiH3ZlQlE5EjPFGn5"
|
||||||
"sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw"
|
"IytduT2SdPGZoAsMEavJb_nETlELNXW16bQSPKJXr1gYrzoJ86t2ePrbXOTDQUV9pIi9yCLgWYp"
|
||||||
"qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO"
|
"r9PVx9osIRfd4qHg_MPEITQhlxvMvX51-_NxiQpdDf3HH-x2tMu6pELU7eruIPhBPxhrOJ9eWTK"
|
||||||
"t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF"
|
"54w6ygZ0-RdEgJKRSCCtTcjjom1TJ_GimvqVJZ449LeVycc23BsJDkBNE9XOARSCHK0i3VDWTg-"
|
||||||
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
|
"autjKClFWaVcyoNDqW6cfdODF7YyEYP8sBbYenMoJn5pizXaYvl3qc4LY8a-8HVFU6akgCvcj2w"
|
||||||
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=\","
|
"vaAMOdQvh5f5m6X33lLfE0w8R4JFkqxB7UC5Ev_nPDkyX1urg76t31_9qTmQAlxdoIhmBsPziSW"
|
||||||
"\"signature\": \"r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=\"}}";
|
"jE3gII_qetwT7z1cPX8bGGPELwFiG5sQ6jQbHlGV-gaL2v-Peh6HZbGltsoI0wm7_N1i-BwiyUi"
|
||||||
|
"BPHgGJ8=\","
|
||||||
|
"\"signature\": \"fRiAeyze4AtvYXpxWImduHrJ1lZ4K0bRT1lk2jVIpcs=\"}}";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
@@ -84,22 +86,23 @@ class WvCdmEngineTest : public testing::Test {
|
|||||||
void GenerateKeyRequest(const std::string& key_system,
|
void GenerateKeyRequest(const std::string& key_system,
|
||||||
const std::string& init_data) {
|
const std::string& init_data) {
|
||||||
wvcdm::CdmAppParameterMap app_parameters;
|
wvcdm::CdmAppParameterMap app_parameters;
|
||||||
|
std::string server_url;
|
||||||
EXPECT_EQ(cdm_engine_.GenerateKeyRequest(session_id_,
|
EXPECT_EQ(cdm_engine_.GenerateKeyRequest(session_id_,
|
||||||
true, // is_key_system_present
|
true, // is_key_system_present
|
||||||
key_system,
|
key_system,
|
||||||
init_data,
|
init_data,
|
||||||
kLicenseTypeStreaming,
|
kLicenseTypeStreaming,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
&key_msg_), wvcdm::KEY_MESSAGE);
|
&key_msg_,
|
||||||
|
&server_url), wvcdm::KEY_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateRenewalRequest(const std::string& key_system,
|
void GenerateRenewalRequest(const std::string& key_system,
|
||||||
const std::string& init_data) {
|
const std::string& init_data) {
|
||||||
|
std::string server_url;
|
||||||
EXPECT_EQ(cdm_engine_.GenerateRenewalRequest(session_id_,
|
EXPECT_EQ(cdm_engine_.GenerateRenewalRequest(session_id_,
|
||||||
true, // is_key_system_init_data_present,
|
&key_msg_,
|
||||||
key_system,
|
&server_url),
|
||||||
init_data,
|
|
||||||
&key_msg_),
|
|
||||||
wvcdm::KEY_MESSAGE);
|
wvcdm::KEY_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,18 +150,10 @@ class WvCdmEngineTest : public testing::Test {
|
|||||||
client_auth,
|
client_auth,
|
||||||
200);
|
200);
|
||||||
if (is_renewal) {
|
if (is_renewal) {
|
||||||
EXPECT_EQ(cdm_engine_.RenewKey(session_id_,
|
EXPECT_EQ(cdm_engine_.RenewKey(session_id_, resp), wvcdm::KEY_ADDED);
|
||||||
true, // is_key_system_init_data_present
|
|
||||||
g_key_system,
|
|
||||||
init_data,
|
|
||||||
resp), wvcdm::KEY_ADDED);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
EXPECT_EQ(cdm_engine_.AddKey(session_id_,
|
EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp), wvcdm::KEY_ADDED);
|
||||||
true, // is_key_system_init_data_present
|
|
||||||
g_key_system,
|
|
||||||
init_data,
|
|
||||||
resp), wvcdm::KEY_ADDED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
92
libwvdrmengine/cdm/core/test/device_files_unittest.cpp
Normal file
92
libwvdrmengine/cdm/core/test/device_files_unittest.cpp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "device_files.h"
|
||||||
|
#include "file_store.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
|
||||||
|
TEST(DeviceFilesTest, StoreCertificate) {
|
||||||
|
std::string device_certificate_path =
|
||||||
|
DeviceFiles::GetPath(DeviceFiles::kCencPath,
|
||||||
|
DeviceFiles::kDeviceCertificateFileName);
|
||||||
|
|
||||||
|
if (!File::Exists(DeviceFiles::kCencPath))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(DeviceFiles::kCencPath));
|
||||||
|
if (File::Exists(device_certificate_path))
|
||||||
|
EXPECT_TRUE(File::Remove(device_certificate_path));
|
||||||
|
|
||||||
|
char test_buf[1200];
|
||||||
|
for (size_t i = 0; i < sizeof(test_buf); i++) {
|
||||||
|
test_buf[i] = i % 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cert_len = 500;
|
||||||
|
std::string certificate(&test_buf[0], cert_len);
|
||||||
|
std::string wrapped_private_key(&test_buf[cert_len],
|
||||||
|
sizeof(test_buf) - cert_len - 1);
|
||||||
|
|
||||||
|
EXPECT_TRUE(DeviceFiles::StoreCertificate(certificate, wrapped_private_key));
|
||||||
|
EXPECT_TRUE(File::Exists(device_certificate_path));
|
||||||
|
EXPECT_GT(File::FileSize(device_certificate_path),
|
||||||
|
(ssize_t)sizeof(test_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DeviceFilesTest, StoreCertificateInitial) {
|
||||||
|
std::string device_certificate_path =
|
||||||
|
DeviceFiles::GetPath(DeviceFiles::kCencPath,
|
||||||
|
DeviceFiles::kDeviceCertificateFileName);
|
||||||
|
|
||||||
|
if (File::Exists(DeviceFiles::kCencPath))
|
||||||
|
EXPECT_TRUE(File::Remove(DeviceFiles::kIdmPath));
|
||||||
|
|
||||||
|
char test_buf[1200];
|
||||||
|
for (size_t i = 0; i < sizeof(test_buf); i++) {
|
||||||
|
test_buf[i] = i % 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cert_len = 500;
|
||||||
|
std::string certificate(&test_buf[0], cert_len);
|
||||||
|
std::string wrapped_private_key(&test_buf[cert_len],
|
||||||
|
sizeof(test_buf) - cert_len - 1);
|
||||||
|
|
||||||
|
EXPECT_TRUE(DeviceFiles::StoreCertificate(certificate, wrapped_private_key));
|
||||||
|
EXPECT_TRUE(File::Exists(device_certificate_path));
|
||||||
|
EXPECT_GT(File::FileSize(device_certificate_path),
|
||||||
|
(ssize_t)sizeof(test_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DeviceFilesTest, RetrieveCertificate) {
|
||||||
|
std::string device_certificate_path =
|
||||||
|
DeviceFiles::GetPath(DeviceFiles::kCencPath,
|
||||||
|
DeviceFiles::kDeviceCertificateFileName);
|
||||||
|
|
||||||
|
if (File::Exists(DeviceFiles::kCencPath))
|
||||||
|
EXPECT_TRUE(File::Remove(DeviceFiles::kIdmPath));
|
||||||
|
|
||||||
|
char test_buf[1200];
|
||||||
|
for (size_t i = 0; i < sizeof(test_buf); i++) {
|
||||||
|
test_buf[i] = i % 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cert_len = 500;
|
||||||
|
std::string certificate(&test_buf[0], cert_len);
|
||||||
|
std::string wrapped_private_key(&test_buf[cert_len],
|
||||||
|
sizeof(test_buf) - cert_len - 1);
|
||||||
|
|
||||||
|
EXPECT_TRUE(DeviceFiles::StoreCertificate(certificate, wrapped_private_key));
|
||||||
|
EXPECT_TRUE(File::Exists(device_certificate_path));
|
||||||
|
EXPECT_GT(File::FileSize(device_certificate_path),
|
||||||
|
(ssize_t)sizeof(test_buf));
|
||||||
|
|
||||||
|
std::string in_certificate;
|
||||||
|
std::string in_wrapped_private_key;
|
||||||
|
EXPECT_TRUE(DeviceFiles::RetrieveCertificate(&in_certificate,
|
||||||
|
&in_wrapped_private_key));
|
||||||
|
EXPECT_TRUE(memcmp(certificate.data(), in_certificate.data(),
|
||||||
|
certificate.size()) == 0);
|
||||||
|
EXPECT_TRUE(memcmp(wrapped_private_key.data(), in_wrapped_private_key.data(),
|
||||||
|
wrapped_private_key.size()) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
174
libwvdrmengine/cdm/core/test/file_store_unittest.cpp
Normal file
174
libwvdrmengine/cdm/core/test/file_store_unittest.cpp
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "file_store.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// TODO(rfrias): Make this work for non-unix paths
|
||||||
|
const std::string kDataDrmDir = "/data/drm";
|
||||||
|
const std::string kIdmTestDir = "/data/drm/IDMtest";
|
||||||
|
const std::string kCencTestDir = "/data/drm/IDMtest/CENCtest";
|
||||||
|
const std::string kCencTestDirWithSlash = "/data/drm/IDMtest/CENCtest/";
|
||||||
|
const std::string kTestFile01 = "/data/drm/IDMtest/CENCtest/file01.txt";
|
||||||
|
|
||||||
|
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";
|
||||||
|
} // 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileTest, CreateDirectory) {
|
||||||
|
if (File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::Remove(kIdmTestDir));
|
||||||
|
EXPECT_FALSE(File::Exists(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Remove(kIdmTestDir));
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDirWithSlash));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Remove(kIdmTestDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileTest, RemoveDir) {
|
||||||
|
if (!File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Remove(kCencTestDir));
|
||||||
|
EXPECT_FALSE(File::Exists(kCencTestDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileTest, OpenFileUsingConstructor) {
|
||||||
|
if (!File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
File::Remove(kTestFile01);
|
||||||
|
File file(kTestFile01, File::kCreate);
|
||||||
|
EXPECT_TRUE(file.IsOpen());
|
||||||
|
file.Close();
|
||||||
|
EXPECT_TRUE(File::Exists(kTestFile01));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileTest, OpenFile) {
|
||||||
|
if (!File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
File::Remove(kTestFile01);
|
||||||
|
File file;
|
||||||
|
file.Open(kTestFile01, File::kCreate);
|
||||||
|
EXPECT_TRUE(file.IsOpen());
|
||||||
|
file.Close();
|
||||||
|
EXPECT_TRUE(File::Exists(kTestFile01));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileTest, RemoveDirAndFile) {
|
||||||
|
if (!File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
File file(kTestFile01, File::kCreate);
|
||||||
|
EXPECT_TRUE(file.IsOpen());
|
||||||
|
file.Close();
|
||||||
|
EXPECT_TRUE(File::Remove(kTestFile01));
|
||||||
|
EXPECT_TRUE(File::Remove(kCencTestDir));
|
||||||
|
EXPECT_FALSE(File::Exists(kTestFile01));
|
||||||
|
EXPECT_FALSE(File::Exists(kCencTestDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileTest, IsDir) {
|
||||||
|
if (!File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
File file(kTestFile01, File::kCreate);
|
||||||
|
EXPECT_TRUE(file.IsOpen());
|
||||||
|
file.Close();
|
||||||
|
EXPECT_TRUE(File::Exists(kTestFile01));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
EXPECT_FALSE(File::IsDirectory(kTestFile01));
|
||||||
|
EXPECT_TRUE(File::IsDirectory(kCencTestDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileTest, IsRegularFile) {
|
||||||
|
if (!File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
File file(kTestFile01, File::kCreate);
|
||||||
|
EXPECT_TRUE(file.IsOpen());
|
||||||
|
file.Close();
|
||||||
|
EXPECT_TRUE(File::Exists(kTestFile01));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::IsRegularFile(kTestFile01));
|
||||||
|
EXPECT_FALSE(File::IsRegularFile(kCencTestDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileTest, WriteReadTextFile) {
|
||||||
|
if (!File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
File::Remove(kTestFile01);
|
||||||
|
|
||||||
|
const char* test_string = "This is a test";
|
||||||
|
File file1(kTestFile01, File::kCreate);
|
||||||
|
EXPECT_TRUE(file1.IsOpen());
|
||||||
|
EXPECT_TRUE(file1.Write(test_string, strlen(test_string)+1));
|
||||||
|
file1.Close();
|
||||||
|
EXPECT_TRUE(File::Exists(kTestFile01));
|
||||||
|
|
||||||
|
char buf[100];
|
||||||
|
File file2(kTestFile01, 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) {
|
||||||
|
if (!File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
EXPECT_TRUE(File::Exists(kCencTestDir));
|
||||||
|
File::Remove(kTestFile01);
|
||||||
|
|
||||||
|
unsigned char test_buf[600];
|
||||||
|
for (size_t i = 0; i < sizeof(test_buf); i++) {
|
||||||
|
test_buf[i] = i % 128;
|
||||||
|
}
|
||||||
|
File file1(kTestFile01, File::kCreate | File::kBinary);
|
||||||
|
EXPECT_TRUE(file1.IsOpen());
|
||||||
|
EXPECT_TRUE(file1.Write(test_buf, sizeof(test_buf)));
|
||||||
|
file1.Close();
|
||||||
|
EXPECT_TRUE(File::Exists(kTestFile01));
|
||||||
|
|
||||||
|
char buf[1000];
|
||||||
|
File file2(kTestFile01, 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) {
|
||||||
|
if (!File::Exists(kCencTestDir))
|
||||||
|
EXPECT_TRUE(File::CreateDirectory(kCencTestDir));
|
||||||
|
File::Remove(kTestFile01);
|
||||||
|
|
||||||
|
unsigned char test_buf[600];
|
||||||
|
for (size_t i = 0; i < sizeof(test_buf); i++) {
|
||||||
|
test_buf[i] = i % 128;
|
||||||
|
}
|
||||||
|
File file1(kTestFile01, File::kCreate | File::kBinary);
|
||||||
|
EXPECT_TRUE(file1.IsOpen());
|
||||||
|
EXPECT_TRUE(file1.Write(test_buf, sizeof(test_buf)));
|
||||||
|
file1.Close();
|
||||||
|
EXPECT_TRUE(File::Exists(kTestFile01));
|
||||||
|
|
||||||
|
EXPECT_EQ((ssize_t)sizeof(test_buf), File::FileSize(kTestFile01));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -79,20 +79,24 @@ TEST(LicenseTestSession, InitNullSession) {
|
|||||||
TEST_F(LicenseTest, PrepareKeyRequest) {
|
TEST_F(LicenseTest, PrepareKeyRequest) {
|
||||||
std::string signed_request;
|
std::string signed_request;
|
||||||
CdmAppParameterMap app_parameters;
|
CdmAppParameterMap app_parameters;
|
||||||
|
std::string server_url;
|
||||||
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
||||||
kLicenseTypeStreaming,
|
kLicenseTypeStreaming,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
&signed_request);
|
&signed_request,
|
||||||
|
&server_url);
|
||||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseTest, HandleKeyResponseValid) {
|
TEST_F(LicenseTest, HandleKeyResponseValid) {
|
||||||
std::string signed_request;
|
std::string signed_request;
|
||||||
CdmAppParameterMap app_parameters;
|
CdmAppParameterMap app_parameters;
|
||||||
|
std::string server_url;
|
||||||
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
||||||
kLicenseTypeStreaming,
|
kLicenseTypeStreaming,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
&signed_request);
|
&signed_request,
|
||||||
|
&server_url);
|
||||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||||
EXPECT_TRUE(license_.HandleKeyResponse(a2bs_hex(kValidResponse)));
|
EXPECT_TRUE(license_.HandleKeyResponse(a2bs_hex(kValidResponse)));
|
||||||
}
|
}
|
||||||
@@ -100,10 +104,12 @@ TEST_F(LicenseTest, HandleKeyResponseValid) {
|
|||||||
TEST_F(LicenseTest, HandleKeyResponseInvalid) {
|
TEST_F(LicenseTest, HandleKeyResponseInvalid) {
|
||||||
std::string signed_request;
|
std::string signed_request;
|
||||||
CdmAppParameterMap app_parameters;
|
CdmAppParameterMap app_parameters;
|
||||||
|
std::string server_url;
|
||||||
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
license_.PrepareKeyRequest(a2bs_hex(kInitData),
|
||||||
kLicenseTypeStreaming,
|
kLicenseTypeStreaming,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
&signed_request);
|
&signed_request,
|
||||||
|
&server_url);
|
||||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||||
EXPECT_FALSE(license_.HandleKeyResponse(a2bs_hex(kInvalidResponse)));
|
EXPECT_FALSE(license_.HandleKeyResponse(a2bs_hex(kInvalidResponse)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "http_socket.h"
|
#include "http_socket.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "string_conversions.h"
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ void UrlRequest::AppendChunkToUpload(const std::string& data) {
|
|||||||
int UrlRequest::GetResponse(std::string& response) {
|
int UrlRequest::GetResponse(std::string& response) {
|
||||||
response.clear();
|
response.clear();
|
||||||
|
|
||||||
const int kTimeoutInMs = 1500;
|
const int kTimeoutInMs = 1500 * 2;
|
||||||
int bytes = 0;
|
int bytes = 0;
|
||||||
int total_bytes = 0;
|
int total_bytes = 0;
|
||||||
do {
|
do {
|
||||||
@@ -102,5 +103,29 @@ bool UrlRequest::PostRequest(const std::string& data) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UrlRequest::AppendData(const std::string& data) {
|
||||||
|
request_.append(data);
|
||||||
|
request_.append("\r\n"); // marks end of data
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UrlRequest::PostCertRequest(const std::string& data) {
|
||||||
|
request_.assign("POST /");
|
||||||
|
request_.append(socket_.resource_path());
|
||||||
|
request_.append(" HTTP/1.1\r\n");
|
||||||
|
request_.append("User-Agent: Widevine CDM v1.0\r\n");
|
||||||
|
request_.append("Host: ");
|
||||||
|
request_.append(socket_.domain_name());
|
||||||
|
request_.append("\r\nAccept: */*");
|
||||||
|
request_.append("\r\nContent-Type: application/json");
|
||||||
|
request_.append("\r\nContent-Length: ");
|
||||||
|
request_.append(UintToString(data.size()));
|
||||||
|
request_.append("\r\n"); // empty line to terminate header
|
||||||
|
request_.append("\r\n"); // terminates the request
|
||||||
|
|
||||||
|
AppendData(data);
|
||||||
|
|
||||||
|
socket_.Write(request_.c_str(), request_.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ class UrlRequest {
|
|||||||
~UrlRequest();
|
~UrlRequest();
|
||||||
|
|
||||||
void AppendChunkToUpload(const std::string& data);
|
void AppendChunkToUpload(const std::string& data);
|
||||||
|
void AppendData(const std::string& data);
|
||||||
int GetResponse(std::string& response);
|
int GetResponse(std::string& response);
|
||||||
int GetStatusCode(const std::string& response);
|
int GetStatusCode(const std::string& response);
|
||||||
bool is_connected() const { return is_connected_; }
|
bool is_connected() const { return is_connected_; }
|
||||||
bool PostRequest(const std::string& data);
|
bool PostRequest(const std::string& data);
|
||||||
|
bool PostCertRequest(const std::string& data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const unsigned int kHttpBufferSize = 4096;
|
static const unsigned int kHttpBufferSize = 4096;
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class WvContentDecryptionModule {
|
|||||||
const CdmInitData& init_data,
|
const CdmInitData& init_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
CdmAppParameterMap& app_parameters,
|
CdmAppParameterMap& app_parameters,
|
||||||
CdmKeyMessage* key_request);
|
CdmKeyMessage* key_request,
|
||||||
|
std::string* server_url);
|
||||||
|
|
||||||
// Accept license response and extract key info.
|
// Accept license response and extract key info.
|
||||||
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||||
|
|||||||
206
libwvdrmengine/cdm/src/file_store.cpp
Normal file
206
libwvdrmengine/cdm/src/file_store.cpp
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// File class - provides a simple android specific file implementation
|
||||||
|
|
||||||
|
#include "file_store.h"
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const char* kCurrentDirectory = ".";
|
||||||
|
const char* kParentDirectory = "..";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
|
||||||
|
class File::Impl {
|
||||||
|
public:
|
||||||
|
FILE* file_;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = "";
|
||||||
|
|
||||||
|
if (((flags & File::kTruncate) && Exists(name)) ||
|
||||||
|
((flags & File::kCreate) && !Exists(name))) {
|
||||||
|
FILE* fp = fopen(name.c_str(), "w+");
|
||||||
|
if (fp) {
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & File::kBinary) {
|
||||||
|
openFlags = (flags & File::kReadOnly)? "rb" : "rb+";
|
||||||
|
} else {
|
||||||
|
openFlags = (flags & File::kReadOnly)? "r" : "r+";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_->file_ = fopen(name.c_str(), openFlags.c_str());
|
||||||
|
if (!impl_->file_) {
|
||||||
|
LOGW("File::Open: fopen failed: %d", errno);
|
||||||
|
}
|
||||||
|
return IsOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void File::Close() {
|
||||||
|
if (impl_->file_) {
|
||||||
|
fclose(impl_->file_);
|
||||||
|
impl_->file_ = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::IsOpen() {
|
||||||
|
return impl_->file_ != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::IsBad() {
|
||||||
|
if (impl_->file_)
|
||||||
|
return ferror(impl_->file_) != 0;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t File::Read(void* buffer, size_t bytes) {
|
||||||
|
if (impl_->file_) {
|
||||||
|
size_t len = fread(buffer, 1, bytes, impl_->file_);
|
||||||
|
if (len == 0) {
|
||||||
|
LOGW("File::Read: fread failed: %d", errno);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
LOGW("File::Read: file not open");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t File::Write(const void* buffer, size_t bytes) {
|
||||||
|
if (impl_->file_) {
|
||||||
|
size_t len = fwrite(buffer, 1, bytes, impl_->file_);
|
||||||
|
if (len == 0) {
|
||||||
|
LOGW("File::Write: fwrite failed: %d", errno);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
LOGW("File::Write: file not open");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::Exists(const std::string& file) {
|
||||||
|
struct stat buf;
|
||||||
|
int res = stat(file.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)) {
|
||||||
|
DIR* dir;
|
||||||
|
if ((dir = opendir(file_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)) {
|
||||||
|
closedir(dir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
if (rmdir(file_path.c_str())) {
|
||||||
|
LOGW("File::Remove: rmdir failed: %d", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (unlink(file_path.c_str())) {
|
||||||
|
LOGW("File::Remove: unlink failed: %d", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::CreateDirectory(std::string path) {
|
||||||
|
size_t size = path.size();
|
||||||
|
if ((size == 1) && (path[0] == '/'))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (size <= 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (path.at(size-1) == '/') {
|
||||||
|
--size;
|
||||||
|
path.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = path.find('/', 1);
|
||||||
|
while (pos < size) {
|
||||||
|
path.at(pos) = '\0';
|
||||||
|
if (mkdir(path.c_str(), 0775) != 0) {
|
||||||
|
if (errno != EEXIST) {
|
||||||
|
LOGW("File::CreateDirectory: mkdir failed: %d\n", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path.at(pos) = '/';
|
||||||
|
pos = path.find('/', pos+1);
|
||||||
|
}
|
||||||
|
if (mkdir(path.c_str(), 0775) != 0) {
|
||||||
|
if (errno != EEXIST) {
|
||||||
|
LOGW("File::CreateDirectory: mkdir failed: %d\n", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::IsDirectory(const std::string& path) {
|
||||||
|
struct stat buf;
|
||||||
|
if (stat(path.c_str(), &buf) == 0)
|
||||||
|
return buf.st_mode & S_IFDIR;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::IsRegularFile(const std::string& path) {
|
||||||
|
struct stat buf;
|
||||||
|
if (stat(path.c_str(), &buf) == 0)
|
||||||
|
return buf.st_mode & S_IFREG;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t File::FileSize(const std::string& file_path) {
|
||||||
|
struct stat buf;
|
||||||
|
if (stat(file_path.c_str(), &buf) == 0)
|
||||||
|
return buf.st_size;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wvcdm
|
||||||
@@ -34,20 +34,19 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
|
|||||||
const CdmInitData& init_data,
|
const CdmInitData& init_data,
|
||||||
const CdmLicenseType license_type,
|
const CdmLicenseType license_type,
|
||||||
CdmAppParameterMap& app_parameters,
|
CdmAppParameterMap& app_parameters,
|
||||||
CdmKeyMessage* key_request) {
|
CdmKeyMessage* key_request,
|
||||||
|
std::string* server_url) {
|
||||||
CdmKeySystem key_system;
|
CdmKeySystem key_system;
|
||||||
return cdm_engine_->GenerateKeyRequest(session_id, false, key_system,
|
return cdm_engine_->GenerateKeyRequest(session_id, false, key_system,
|
||||||
init_data, license_type,
|
init_data, license_type,
|
||||||
app_parameters, key_request);
|
app_parameters, key_request,
|
||||||
|
server_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType WvContentDecryptionModule::AddKey(
|
CdmResponseType WvContentDecryptionModule::AddKey(
|
||||||
const CdmSessionId& session_id,
|
const CdmSessionId& session_id,
|
||||||
const CdmKeyResponse& key_data) {
|
const CdmKeyResponse& key_data) {
|
||||||
CdmKeySystem key_system;
|
return cdm_engine_->AddKey(session_id, key_data);
|
||||||
CdmInitData init_data;
|
|
||||||
return cdm_engine_->AddKey(session_id, false, key_system,
|
|
||||||
init_data, key_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType WvContentDecryptionModule::CancelKeyRequest(
|
CdmResponseType WvContentDecryptionModule::CancelKeyRequest(
|
||||||
|
|||||||
@@ -3,10 +3,22 @@
|
|||||||
#
|
#
|
||||||
LOCAL_PATH := $(call my-dir)
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
test_name := base64_test
|
||||||
|
test_src_dir := ../core/test
|
||||||
|
include $(LOCAL_PATH)/unit-test.mk
|
||||||
|
|
||||||
test_name := cdm_engine_test
|
test_name := cdm_engine_test
|
||||||
test_src_dir := ../core/test
|
test_src_dir := ../core/test
|
||||||
include $(LOCAL_PATH)/unit-test.mk
|
include $(LOCAL_PATH)/unit-test.mk
|
||||||
|
|
||||||
|
test_name := device_files_unittest
|
||||||
|
test_src_dir := ../core/test
|
||||||
|
include $(LOCAL_PATH)/unit-test.mk
|
||||||
|
|
||||||
|
test_name := file_store_unittest
|
||||||
|
test_src_dir := ../core/test
|
||||||
|
include $(LOCAL_PATH)/unit-test.mk
|
||||||
|
|
||||||
test_name := http_socket_test
|
test_name := http_socket_test
|
||||||
test_src_dir := ../core/test
|
test_src_dir := ../core/test
|
||||||
include $(LOCAL_PATH)/unit-test.mk
|
include $(LOCAL_PATH)/unit-test.mk
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ std::string g_license_server;
|
|||||||
std::string g_port;
|
std::string g_port;
|
||||||
wvcdm::KeyId g_wrong_key_id;
|
wvcdm::KeyId g_wrong_key_id;
|
||||||
int g_use_full_path = 0; // cannot use boolean in getopt_long
|
int g_use_full_path = 0; // cannot use boolean in getopt_long
|
||||||
|
|
||||||
|
static const std::string kDefaultProvisioningServerUrl =
|
||||||
|
"http://www-googleapis-test.sandbox.google.com/certificateprovisioning/v1/devicecertificates/create";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
@@ -46,11 +49,14 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
void GenerateKeyRequest(const std::string& key_system,
|
void GenerateKeyRequest(const std::string& key_system,
|
||||||
const std::string& init_data) {
|
const std::string& init_data) {
|
||||||
wvcdm::CdmAppParameterMap app_parameters;
|
wvcdm::CdmAppParameterMap app_parameters;
|
||||||
|
std::string server_url;
|
||||||
EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_,
|
EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_,
|
||||||
init_data,
|
init_data,
|
||||||
kLicenseTypeStreaming,
|
kLicenseTypeStreaming,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
&key_msg_), wvcdm::KEY_MESSAGE);
|
&key_msg_,
|
||||||
|
&server_url), wvcdm::KEY_MESSAGE);
|
||||||
|
EXPECT_EQ(0, server_url.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateRenewalRequest(const std::string& key_system,
|
void GenerateRenewalRequest(const std::string& key_system,
|
||||||
@@ -58,11 +64,14 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
// TODO application makes a license request, CDM will renew the license
|
// TODO application makes a license request, CDM will renew the license
|
||||||
// when appropriate.
|
// when appropriate.
|
||||||
wvcdm::CdmAppParameterMap app_parameters;
|
wvcdm::CdmAppParameterMap app_parameters;
|
||||||
|
std::string server_url;
|
||||||
EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_,
|
EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_,
|
||||||
init_data,
|
init_data,
|
||||||
kLicenseTypeStreaming,
|
kLicenseTypeStreaming,
|
||||||
app_parameters,
|
app_parameters,
|
||||||
&key_msg_), wvcdm::KEY_MESSAGE);
|
&key_msg_,
|
||||||
|
&server_url), wvcdm::KEY_MESSAGE);
|
||||||
|
EXPECT_NE(0, server_url.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// posts a request and extracts the drm message from the response
|
// posts a request and extracts the drm message from the response
|
||||||
@@ -101,6 +110,34 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
return drm_msg;
|
return drm_msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// posts a request and extracts the drm message from the response
|
||||||
|
std::string GetCertRequestResponse(const std::string& server_url,
|
||||||
|
int expected_response) {
|
||||||
|
UrlRequest url_request(server_url, g_port);
|
||||||
|
|
||||||
|
if (!url_request.is_connected()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
url_request.PostCertRequest(key_msg_);
|
||||||
|
std::string response;
|
||||||
|
int resp_bytes = url_request.GetResponse(response);
|
||||||
|
LOGD("size=%u, response start: %s", response.size(),
|
||||||
|
response.substr(0, 1024).c_str());
|
||||||
|
LOGD("end: %s", response.substr(response.size() - 256).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(response);
|
||||||
|
if (expected_response == 200) {
|
||||||
|
EXPECT_EQ(200, status_code);
|
||||||
|
} else {
|
||||||
|
EXPECT_NE(200, status_code);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
void VerifyKeyRequestResponse(const std::string& server_url,
|
void VerifyKeyRequestResponse(const std::string& server_url,
|
||||||
const std::string& client_auth,
|
const std::string& client_auth,
|
||||||
std::string& init_data,
|
std::string& init_data,
|
||||||
@@ -124,6 +161,19 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
std::string session_id_;
|
std::string session_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
||||||
|
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||||
|
std::string provisioning_server_url = "";
|
||||||
|
|
||||||
|
decryptor_.GetProvisioningRequest(&key_msg_, &provisioning_server_url);
|
||||||
|
EXPECT_STREQ(provisioning_server_url.data(), kDefaultProvisioningServerUrl.data());
|
||||||
|
|
||||||
|
std::string response = GetCertRequestResponse(kDefaultProvisioningServerUrl, 200);
|
||||||
|
if (!response.empty())
|
||||||
|
decryptor_.HandleProvisioningResponse(response);
|
||||||
|
decryptor_.CloseSession(session_id_);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) {
|
TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) {
|
||||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||||
GenerateKeyRequest(g_key_system, g_key_id);
|
GenerateKeyRequest(g_key_system, g_key_id);
|
||||||
|
|||||||
@@ -145,13 +145,16 @@ status_t WVDrmPlugin::getKeyRequest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
CdmKeyMessage keyRequest;
|
CdmKeyMessage keyRequest;
|
||||||
|
string cdmDefaultUrl;
|
||||||
|
|
||||||
CdmResponseType res = mCDM->GenerateKeyRequest(cdmSessionId, cdmInitData,
|
CdmResponseType res = mCDM->GenerateKeyRequest(cdmSessionId, cdmInitData,
|
||||||
cdmLicenseType,
|
cdmLicenseType,
|
||||||
cdmParameters, &keyRequest);
|
cdmParameters, &keyRequest,
|
||||||
|
&cdmDefaultUrl);
|
||||||
|
|
||||||
if (isCdmResponseTypeSuccess(res)) {
|
if (isCdmResponseTypeSuccess(res)) {
|
||||||
defaultUrl.clear();
|
defaultUrl.clear();
|
||||||
|
defaultUrl.setTo(cdmDefaultUrl.data(), cdmDefaultUrl.size());
|
||||||
|
|
||||||
request.clear();
|
request.clear();
|
||||||
request.appendArray(reinterpret_cast<const uint8_t*>(keyRequest.data()),
|
request.appendArray(reinterpret_cast<const uint8_t*>(keyRequest.data()),
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ class MockCDM : public WvContentDecryptionModule {
|
|||||||
|
|
||||||
MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&));
|
MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&));
|
||||||
|
|
||||||
MOCK_METHOD5(GenerateKeyRequest, CdmResponseType(const CdmSessionId&,
|
MOCK_METHOD6(GenerateKeyRequest, CdmResponseType(const CdmSessionId&,
|
||||||
const CdmInitData&,
|
const CdmInitData&,
|
||||||
const CdmLicenseType,
|
const CdmLicenseType,
|
||||||
CdmAppParameterMap&,
|
CdmAppParameterMap&,
|
||||||
CdmKeyMessage*));
|
CdmKeyMessage*, string*));
|
||||||
|
|
||||||
MOCK_METHOD2(AddKey, CdmResponseType(const CdmSessionId&,
|
MOCK_METHOD2(AddKey, CdmResponseType(const CdmSessionId&,
|
||||||
const CdmKeyResponse&));
|
const CdmKeyResponse&));
|
||||||
@@ -179,7 +179,6 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
|||||||
initData.appendArray(initDataRaw, kInitDataSize);
|
initData.appendArray(initDataRaw, kInitDataSize);
|
||||||
|
|
||||||
CdmKeyMessage cdmRequest(requestRaw, requestRaw + kRequestSize);
|
CdmKeyMessage cdmRequest(requestRaw, requestRaw + kRequestSize);
|
||||||
Vector<uint8_t> request;
|
|
||||||
|
|
||||||
KeyedVector<String8, String8> parameters;
|
KeyedVector<String8, String8> parameters;
|
||||||
CdmAppParameterMap cdmParameters;
|
CdmAppParameterMap cdmParameters;
|
||||||
@@ -191,7 +190,7 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
|||||||
parameters.add(String8("answer"), String8("42"));
|
parameters.add(String8("answer"), String8("42"));
|
||||||
cdmParameters["answer"] = "42";
|
cdmParameters["answer"] = "42";
|
||||||
|
|
||||||
String8 defaultUrl;
|
static const char* kDefaultUrl = "http://google.com/";
|
||||||
|
|
||||||
{
|
{
|
||||||
InSequence calls;
|
InSequence calls;
|
||||||
@@ -199,19 +198,25 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
|||||||
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId,
|
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId,
|
||||||
ElementsAreArray(initDataRaw,
|
ElementsAreArray(initDataRaw,
|
||||||
kInitDataSize),
|
kInitDataSize),
|
||||||
kLicenseTypeOffline, cdmParameters, _))
|
kLicenseTypeOffline, cdmParameters, _,
|
||||||
|
_))
|
||||||
.WillOnce(DoAll(SetArgPointee<4>(cdmRequest),
|
.WillOnce(DoAll(SetArgPointee<4>(cdmRequest),
|
||||||
|
SetArgPointee<5>(kDefaultUrl),
|
||||||
Return(wvcdm::KEY_MESSAGE)));
|
Return(wvcdm::KEY_MESSAGE)));
|
||||||
|
|
||||||
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId,
|
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId,
|
||||||
ElementsAreArray(initDataRaw,
|
ElementsAreArray(initDataRaw,
|
||||||
kInitDataSize),
|
kInitDataSize),
|
||||||
kLicenseTypeStreaming, cdmParameters,
|
kLicenseTypeStreaming, cdmParameters,
|
||||||
_))
|
_, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<4>(cdmRequest),
|
.WillOnce(DoAll(SetArgPointee<4>(cdmRequest),
|
||||||
|
SetArgPointee<5>(kDefaultUrl),
|
||||||
Return(wvcdm::KEY_MESSAGE)));
|
Return(wvcdm::KEY_MESSAGE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<uint8_t> request;
|
||||||
|
String8 defaultUrl;
|
||||||
|
|
||||||
status_t res = plugin.getKeyRequest(sessionId, initData,
|
status_t res = plugin.getKeyRequest(sessionId, initData,
|
||||||
String8("video/h264"),
|
String8("video/h264"),
|
||||||
DrmPlugin::kKeyType_Offline,
|
DrmPlugin::kKeyType_Offline,
|
||||||
@@ -219,7 +224,7 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
|||||||
|
|
||||||
ASSERT_EQ(OK, res);
|
ASSERT_EQ(OK, res);
|
||||||
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
||||||
EXPECT_TRUE(defaultUrl.isEmpty());
|
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
||||||
|
|
||||||
res = plugin.getKeyRequest(sessionId, initData, String8("video/h264"),
|
res = plugin.getKeyRequest(sessionId, initData, String8("video/h264"),
|
||||||
DrmPlugin::kKeyType_Streaming, parameters,
|
DrmPlugin::kKeyType_Streaming, parameters,
|
||||||
@@ -227,7 +232,7 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
|||||||
|
|
||||||
ASSERT_EQ(OK, res);
|
ASSERT_EQ(OK, res);
|
||||||
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
||||||
EXPECT_TRUE(defaultUrl.isEmpty());
|
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WVDrmPluginTest, AddsKeys) {
|
TEST_F(WVDrmPluginTest, AddsKeys) {
|
||||||
@@ -319,7 +324,7 @@ TEST_F(WVDrmPluginTest, GetsProvisioningRequests) {
|
|||||||
|
|
||||||
ASSERT_EQ(OK, res);
|
ASSERT_EQ(OK, res);
|
||||||
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
||||||
EXPECT_EQ(String8(kDefaultUrl), defaultUrl);
|
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) {
|
TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) {
|
||||||
|
|||||||
24
libwvdrmengine/run_all_unit_tests.sh
Executable file
24
libwvdrmengine/run_all_unit_tests.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$ANDROID_BUILD_TOP" ]; then
|
||||||
|
echo "Android build environment not set"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "waiting for device"
|
||||||
|
adb root && adb wait-for-device remount && adb sync
|
||||||
|
|
||||||
|
adb shell /system/bin/request_license_test
|
||||||
|
adb shell /system/bin/policy_engine_unittest
|
||||||
|
adb shell /system/bin/libwvdrmmediacrypto_test
|
||||||
|
adb shell /system/bin/libwvdrmdrmplugin_test
|
||||||
|
adb shell /system/bin/cdm_engine_test
|
||||||
|
adb shell /system/bin/oemcrypto_test
|
||||||
|
adb shell /system/bin/file_store_unittest
|
||||||
|
adb shell /system/bin/device_files_unittest
|
||||||
|
adb shell LD_LIBRARY_PATH=/system/vendor/lib/mediadrm/ /system/bin/libwvdrmengine_test
|
||||||
|
|
||||||
|
adb shell am start com.widevine.test/com.widevine.test.MediaDrmAPITest
|
||||||
|
# TODO: make this test more command line friendly
|
||||||
|
echo "check logcat output for MediaDrmAPITest"
|
||||||
|
|
||||||
Reference in New Issue
Block a user