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:
Jeff Tinker
2013-04-22 20:05:55 -07:00
parent 3a28eeeb68
commit 958bbe6d05
30 changed files with 1497 additions and 290 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View 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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

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