Support Offline Licenses
Bug: 8621588 Merge of the following CLs from the Widevine CDM repository: https://widevine-internal-review.googlesource.com/#/c/5602/ https://widevine-internal-review.googlesource.com/#/c/5431/ https://widevine-internal-review.googlesource.com/#/c/5660/ Change-Id: If37940e2535e1a1eca95e4394d8cf9bf689e9c3a
This commit is contained in:
@@ -14,6 +14,7 @@ class CryptoEngine;
|
||||
class WvCdmEventListener;
|
||||
|
||||
typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap;
|
||||
typedef std::map<CdmKeySetId, CdmSessionId> CdmReleaseKeySetMap;
|
||||
|
||||
class CdmEngine : public TimerHandler {
|
||||
public:
|
||||
@@ -24,12 +25,13 @@ class CdmEngine : public TimerHandler {
|
||||
CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
CdmSessionId* session_id);
|
||||
CdmResponseType CloseSession(const CdmSessionId& session_id);
|
||||
CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id);
|
||||
CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
|
||||
|
||||
// License related methods
|
||||
// Construct a valid license request
|
||||
CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
|
||||
bool is_key_system_present,
|
||||
const CdmKeySystem& key_system,
|
||||
const CdmKeySetId& key_set_id,
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
@@ -38,12 +40,14 @@ class CdmEngine : public TimerHandler {
|
||||
|
||||
// Accept license response and extract key info.
|
||||
CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data);
|
||||
const CdmKeyResponse& key_data,
|
||||
CdmKeySetId& key_set_id);
|
||||
|
||||
CdmResponseType RestoreKey(const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id);
|
||||
|
||||
// Cancel session and unload keys.
|
||||
CdmResponseType CancelKeyRequest(const CdmSessionId& session_id,
|
||||
bool is_key_system_present,
|
||||
const CdmKeySystem& key_system);
|
||||
CdmResponseType CancelKeyRequest(const CdmSessionId& session_id);
|
||||
|
||||
// Construct valid renewal request for the current session keys.
|
||||
CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
|
||||
@@ -121,9 +125,12 @@ class CdmEngine : public TimerHandler {
|
||||
void DisablePolicyTimer();
|
||||
virtual void OnTimerEvent();
|
||||
|
||||
virtual void OnKeyReleaseEvent(CdmKeySetId key_set_id);
|
||||
|
||||
// instance variables
|
||||
CdmSession* provisioning_session_;
|
||||
CdmSessionMap sessions_;
|
||||
CdmReleaseKeySetMap release_key_sets_;
|
||||
|
||||
// policy timer
|
||||
Timer policy_timer_;
|
||||
|
||||
@@ -19,14 +19,16 @@ namespace wvcdm {
|
||||
class CdmSession {
|
||||
public:
|
||||
CdmSession() : session_id_(GenerateSessionId()), license_received_(false),
|
||||
reinitialize_session_(false) {}
|
||||
reinitialize_session_(false), license_type_(kLicenseTypeStreaming) {}
|
||||
~CdmSession() {}
|
||||
|
||||
CdmResponseType Init();
|
||||
CdmResponseType ReInit();
|
||||
|
||||
bool DestroySession();
|
||||
|
||||
CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
const CdmLicenseType license_type);
|
||||
|
||||
void set_key_system(const CdmKeySystem& ksystem) { key_system_ = ksystem; }
|
||||
const CdmKeySystem& key_system() { return key_system_; }
|
||||
|
||||
@@ -37,12 +39,13 @@ class CdmSession {
|
||||
|
||||
CdmResponseType GenerateKeyRequest(const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
CdmResponseType AddKey(const CdmKeyResponse& key_response);
|
||||
CdmResponseType AddKey(const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id);
|
||||
|
||||
// CancelKeyRequest() - Cancel session.
|
||||
CdmResponseType CancelKeyRequest();
|
||||
@@ -74,19 +77,31 @@ class CdmSession {
|
||||
// RenewKey() - Accept renewal response and update key info.
|
||||
CdmResponseType RenewKey(const CdmKeyResponse& key_response);
|
||||
|
||||
// License release
|
||||
// GenerateReleaseRequest() - Construct valid release request for the current
|
||||
// session keys.
|
||||
CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
|
||||
// RenewKey() - Accept renewal response and update key info.
|
||||
CdmResponseType ReleaseKey(const CdmKeyResponse& key_response);
|
||||
|
||||
bool IsKeyValid(const KeyId& key_id);
|
||||
|
||||
bool AttachEventListener(WvCdmEventListener* listener);
|
||||
bool DetachEventListener(WvCdmEventListener* listener);
|
||||
|
||||
void OnTimerEvent();
|
||||
void OnKeyReleaseEvent(CdmKeySetId key_set_id);
|
||||
|
||||
private:
|
||||
|
||||
// Generate unique ID for each new session.
|
||||
CdmSessionId GenerateSessionId();
|
||||
CdmKeySetId GenerateKeySetId(CdmInitData& pssh_data);
|
||||
|
||||
bool LoadDeviceCertificate(std::string* cert, std::string* wrapped_key);
|
||||
bool StoreLicense(bool active);
|
||||
|
||||
// instance variables
|
||||
const CdmSessionId session_id_;
|
||||
@@ -97,6 +112,19 @@ class CdmSession {
|
||||
bool license_received_;
|
||||
bool reinitialize_session_;
|
||||
|
||||
CdmLicenseType license_type_;
|
||||
|
||||
// license type offline related information
|
||||
CdmInitData offline_pssh_data_;
|
||||
CdmKeyMessage offline_key_request_;
|
||||
CdmKeyResponse offline_key_response_;
|
||||
CdmKeyMessage offline_key_renewal_request_;
|
||||
CdmKeyResponse offline_key_renewal_response_;
|
||||
std::string offline_release_server_url_;
|
||||
|
||||
// license type release and offline related information
|
||||
CdmKeySetId key_set_id_;
|
||||
|
||||
KeyId key_id_;
|
||||
|
||||
// Used for certificate based licensing
|
||||
|
||||
@@ -9,15 +9,41 @@ namespace wvcdm {
|
||||
|
||||
class DeviceFiles {
|
||||
public:
|
||||
typedef enum {
|
||||
kLicenseStateActive,
|
||||
kLicenseStateReleasing,
|
||||
kLicenseStateUnknown,
|
||||
} LicenseState;
|
||||
|
||||
static bool StoreCertificate(const std::string& certificate,
|
||||
const std::string& wrapped_private_key);
|
||||
static bool RetrieveCertificate(std::string* certificate,
|
||||
std::string* wrapped_private_key);
|
||||
|
||||
static bool StoreLicense(const std::string& key_set_id,
|
||||
const LicenseState state,
|
||||
const CdmInitData& pssh_data,
|
||||
const CdmKeyMessage& key_request,
|
||||
const CdmKeyResponse& key_response,
|
||||
const CdmKeyMessage& key_renewal_request,
|
||||
const CdmKeyResponse& key_renewal_response,
|
||||
const std::string& release_server_url);
|
||||
static bool RetrieveLicense(const std::string& key_set_id,
|
||||
LicenseState* state,
|
||||
CdmInitData* pssh_data,
|
||||
CdmKeyMessage* key_request,
|
||||
CdmKeyResponse* key_response,
|
||||
CdmKeyMessage* key_renewal_request,
|
||||
CdmKeyResponse* key_renewal_response,
|
||||
std::string* release_server_url);
|
||||
static bool DeleteLicense(const std::string& key_set_id);
|
||||
static bool LicenseExists(const std::string& key_set_id);
|
||||
|
||||
static std::string GetBasePath(const char* dir);
|
||||
static const char* kBasePath;
|
||||
static const char* kPathDelimiter;
|
||||
static const char* kDeviceCertificateFileName;
|
||||
static const char* kLicenseFileNameExt;
|
||||
|
||||
private:
|
||||
static bool Hash(const std::string& data, std::string* hash);
|
||||
|
||||
@@ -26,15 +26,21 @@ class CdmLicense {
|
||||
|
||||
bool PrepareKeyRequest(const CdmInitData& pssh_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
bool PrepareKeyRenewalRequest(CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
bool PrepareKeyUpdateRequest(bool is_renewal,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
|
||||
CdmResponseType HandleKeyRenewalResponse(
|
||||
CdmResponseType HandleKeyUpdateResponse(
|
||||
bool is_renewal,
|
||||
const CdmKeyResponse& license_response);
|
||||
|
||||
bool RestoreOfflineLicense(CdmKeyMessage& license_request,
|
||||
CdmKeyResponse& license_response,
|
||||
CdmKeyResponse& license_renewal_response);
|
||||
|
||||
private:
|
||||
CdmResponseType HandleKeyErrorResponse(const SignedMessage& signed_message);
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
static const size_t KEY_CONTROL_SIZE = 16;
|
||||
// TODO(kqyang): Key ID size is not fixed in spec, but conventionally we
|
||||
@@ -16,6 +18,11 @@ static const size_t KEY_PAD_SIZE = 16;
|
||||
static const size_t KEY_SIZE = 16;
|
||||
static const size_t MAC_KEY_SIZE = 32;
|
||||
|
||||
static const std::string SESSION_ID_PREFIX = "sid";
|
||||
static const std::string KEY_SET_ID_PREFIX = "ksid";
|
||||
|
||||
static const CdmKeySystem KEY_SYSTEM = "com.widevine";
|
||||
|
||||
// define query keys, values here
|
||||
static const std::string QUERY_KEY_LICENSE_TYPE = "LicenseType";
|
||||
// "Streaming", "Offline"
|
||||
|
||||
@@ -11,15 +11,22 @@ namespace wvcdm {
|
||||
// The caller of the CDM API must provide an implementation for onEvent
|
||||
// and signal its intent by using the Attach/DetachEventListener methods
|
||||
// in the WvContentDecryptionModule class.
|
||||
// The listener may also specify, when the instance is created, whether to be
|
||||
// notified about events for a particular session or all sessions.
|
||||
class WvCdmEventListener {
|
||||
public:
|
||||
WvCdmEventListener() {}
|
||||
WvCdmEventListener(CdmSessionId& session_id) : session_id_(session_id) {}
|
||||
virtual ~WvCdmEventListener() {}
|
||||
|
||||
virtual void onEvent(const CdmSessionId& session_id,
|
||||
CdmEventType cdm_event) = 0;
|
||||
|
||||
virtual CdmSessionId session_id() { return session_id_; }
|
||||
|
||||
private:
|
||||
CdmSessionId session_id_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(WvCdmEventListener);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ typedef std::string CdmKeyMessage;
|
||||
typedef std::string CdmKeyResponse;
|
||||
typedef std::string KeyId;
|
||||
typedef std::string CdmSessionId;
|
||||
typedef std::string CdmKeySetId;
|
||||
typedef std::string RequestId;
|
||||
typedef uint32_t CryptoResult;
|
||||
typedef uint32_t CryptoSessionId;
|
||||
@@ -50,7 +51,8 @@ enum CdmEventType {
|
||||
|
||||
enum CdmLicenseType {
|
||||
kLicenseTypeOffline,
|
||||
kLicenseTypeStreaming
|
||||
kLicenseTypeStreaming,
|
||||
kLicenseTypeRelease
|
||||
};
|
||||
|
||||
// forward class references
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "buffer_reader.h"
|
||||
#include "cdm_session.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_engine.h"
|
||||
#include "device_files.h"
|
||||
#include "license_protocol.pb.h"
|
||||
@@ -35,10 +36,13 @@ using video_widevine_server::sdk::ProvisioningRequest;
|
||||
using video_widevine_server::sdk::ProvisioningResponse;
|
||||
using video_widevine_server::sdk::SignedProvisioningMessage;
|
||||
|
||||
typedef std::map<CdmSessionId,CdmSession*>::const_iterator CdmSessionIter;
|
||||
typedef std::map<CdmSessionId, CdmSession*>::const_iterator CdmSessionIter;
|
||||
typedef std::map<CdmKeySetId, CdmSessionId>::iterator CdmReleaseKeySetIter;
|
||||
|
||||
CdmEngine::CdmEngine() : provisioning_session_(NULL) {
|
||||
Properties::Init();
|
||||
Clock clock;
|
||||
srand(static_cast<int>(clock.GetCurrentTime() & 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
CdmEngine::~CdmEngine() {
|
||||
@@ -65,23 +69,22 @@ CdmResponseType CdmEngine::OpenSession(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): Save key_system in session for validation checks
|
||||
CdmSession* new_session = new CdmSession();
|
||||
if (!new_session) {
|
||||
LOGE("CdmEngine::OpenSession: session creation failed");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (new_session->session_id().empty()) {
|
||||
CdmSessionId new_session_id = new_session->session_id();
|
||||
|
||||
if (new_session_id.empty()) {
|
||||
LOGE("CdmEngine::OpenSession: failure to generate session ID");
|
||||
delete(new_session);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionId new_session_id = new_session->session_id();
|
||||
|
||||
CdmResponseType sts = new_session->Init();
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("CdmEngine::OpenSession: bad session init");
|
||||
delete(new_session);
|
||||
return sts;
|
||||
}
|
||||
@@ -91,6 +94,24 @@ CdmResponseType CdmEngine::OpenSession(
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::OpenKeySetSession");
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::OpenKeySetSession: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionId session_id;
|
||||
CdmResponseType sts = OpenSession(KEY_SYSTEM, &session_id);
|
||||
|
||||
if (sts != NO_ERROR)
|
||||
return sts;
|
||||
|
||||
release_key_sets_[key_set_id] = session_id;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::CloseSession");
|
||||
|
||||
@@ -108,10 +129,24 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::CloseKeySetSession");
|
||||
|
||||
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
|
||||
key_set_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType sts = CloseSession(iter->second);
|
||||
release_key_sets_.erase(iter);
|
||||
return sts;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
const CdmSessionId& session_id,
|
||||
bool is_key_system_present,
|
||||
const CdmKeySystem& key_system,
|
||||
const CdmKeySetId& key_set_id,
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
@@ -119,14 +154,36 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
std::string* server_url) {
|
||||
LOGI("CdmEngine::GenerateKeyRequest");
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
CdmSessionId id = session_id;
|
||||
CdmResponseType sts;
|
||||
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
if (key_set_id.empty()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: invalid key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!session_id.empty()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: invalid session ID = %s",
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
}
|
||||
|
||||
if (is_key_system_present) {
|
||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
||||
CdmSessionIter iter = sessions_.find(id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
|
||||
id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
@@ -136,33 +193,60 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
|
||||
key_request->clear();
|
||||
|
||||
// TODO(edwinwong, rfrias): need to pass in license type and app parameters
|
||||
CdmResponseType sts = iter->second->GenerateKeyRequest(init_data,
|
||||
license_type,
|
||||
app_parameters,
|
||||
key_request,
|
||||
server_url);
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
|
||||
if (sts != KEY_ADDED) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
|
||||
"sts = %d", (int)sts);
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
|
||||
sts = iter->second->GenerateKeyRequest(init_data, license_type,
|
||||
app_parameters, key_request,
|
||||
server_url);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d",
|
||||
(int)sts);
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||
"sts = %d", (int)sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): persist init_data, license_type, app_parameters
|
||||
// in session
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
OnKeyReleaseEvent(key_set_id);
|
||||
}
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::AddKey(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data) {
|
||||
const CdmKeyResponse& key_data,
|
||||
CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::AddKey");
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionId id = session_id;
|
||||
bool license_type_release = session_id.empty();
|
||||
|
||||
if (license_type_release) {
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::AddKey: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
}
|
||||
|
||||
CdmSessionIter iter = sessions_.find(id);
|
||||
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::AddKey: session_id not found = %s", session_id.c_str());
|
||||
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -171,18 +255,41 @@ CdmResponseType CdmEngine::AddKey(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->AddKey(key_data);
|
||||
CdmResponseType sts = iter->second->AddKey(key_data, &key_set_id);
|
||||
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts);
|
||||
return sts;
|
||||
}
|
||||
EnablePolicyTimer();
|
||||
return sts;
|
||||
|
||||
if (!license_type_release) {
|
||||
EnablePolicyTimer();
|
||||
}
|
||||
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::CancelKeyRequest(
|
||||
CdmResponseType CdmEngine::RestoreKey(
|
||||
const CdmSessionId& session_id,
|
||||
bool is_key_system_present,
|
||||
const CdmKeySystem& key_system) {
|
||||
const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::RestoreKey");
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::RestoreKey: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
return iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::CancelKeyRequest");
|
||||
|
||||
//TODO(gmorgan): Issue: what is semantics of canceling a key request. Should
|
||||
@@ -197,10 +304,6 @@ CdmResponseType CdmEngine::CancelKeyRequest(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (is_key_system_present) {
|
||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): unload keys here
|
||||
DisablePolicyTimer();
|
||||
return NO_ERROR;
|
||||
@@ -747,37 +850,64 @@ bool CdmEngine::ExtractWidevinePssh(
|
||||
while (1) {
|
||||
// size of PSSH atom, used for skipping
|
||||
uint32_t size;
|
||||
if (!reader.Read4(&size)) return false;
|
||||
if (!reader.Read4(&size)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
|
||||
return false;
|
||||
}
|
||||
|
||||
// "pssh"
|
||||
std::vector<uint8_t> pssh;
|
||||
if (!reader.ReadVec(&pssh, 4)) return false;
|
||||
if (memcmp(&pssh[0], "pssh", 4)) return false;
|
||||
if (!reader.ReadVec(&pssh, 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH literal");
|
||||
return false;
|
||||
}
|
||||
if (memcmp(&pssh[0], "pssh", 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
// flags
|
||||
uint32_t flags;
|
||||
if (!reader.Read4(&flags)) return false;
|
||||
if (flags != 0) return false;
|
||||
if (!reader.Read4(&flags)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH flags");
|
||||
return false;
|
||||
}
|
||||
if (flags != 0) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH flags not zero");
|
||||
return false;
|
||||
}
|
||||
|
||||
// system id
|
||||
std::vector<uint8_t> system_id;
|
||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) return false;
|
||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(&system_id[0], kWidevineSystemId,
|
||||
sizeof(kWidevineSystemId))) {
|
||||
// skip the remaining contents of the atom,
|
||||
// after size field, atom name, flags and system id
|
||||
if (!reader.SkipBytes(
|
||||
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) return false;
|
||||
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// size of PSSH box
|
||||
uint32_t pssh_length;
|
||||
if (!reader.Read4(&pssh_length)) return false;
|
||||
if (!reader.Read4(&pssh_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
|
||||
return false;
|
||||
}
|
||||
|
||||
output->clear();
|
||||
if (!reader.ReadString(output, pssh_length)) return false;
|
||||
if (!reader.ReadString(output, pssh_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -806,4 +936,12 @@ void CdmEngine::OnTimerEvent() {
|
||||
}
|
||||
}
|
||||
|
||||
void CdmEngine::OnKeyReleaseEvent(CdmKeySetId key_set_id) {
|
||||
|
||||
for (CdmSessionIter iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnKeyReleaseEvent(key_set_id);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "clock.h"
|
||||
#include "cdm_engine.h"
|
||||
#include "crypto_engine.h"
|
||||
#include "device_files.h"
|
||||
#include "log.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
@@ -28,17 +30,22 @@ CdmResponseType CdmSession::Init() {
|
||||
|
||||
crypto_session_ = crypto_engine->CreateSession(session_id_);
|
||||
if (!crypto_session_) {
|
||||
LOGE("CdmSession::Init crypto session creation failure");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
std::string token;
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (!LoadDeviceCertificate(&token, &wrapped_key_))
|
||||
if (!LoadDeviceCertificate(&token, &wrapped_key_)) {
|
||||
LOGE("CdmSession::Init provisioning needed");
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!crypto_engine->GetToken(&token))
|
||||
if (!crypto_engine->GetToken(&token)) {
|
||||
LOGE("CdmSession::Init token retrieval failure");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (license_parser_.Init(token, crypto_session_, &policy_engine_))
|
||||
@@ -60,6 +67,49 @@ bool CdmSession::DestroySession() {
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
const CdmKeySetId& key_set_id,
|
||||
const CdmLicenseType license_type) {
|
||||
key_set_id_ = key_set_id;
|
||||
|
||||
// Retrieve license information from persistent store
|
||||
DeviceFiles::LicenseState license_state;
|
||||
|
||||
if (!DeviceFiles::RetrieveLicense(key_set_id, &license_state,
|
||||
&offline_pssh_data_,
|
||||
&offline_key_request_,
|
||||
&offline_key_response_,
|
||||
&offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_,
|
||||
&offline_release_server_url_)) {
|
||||
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (license_state != DeviceFiles::kLicenseStateActive) {
|
||||
LOGE("CdmSession::Init invalid offline license state = %s", license_state);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (!crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
}
|
||||
|
||||
if (license_type == kLicenseTypeOffline) {
|
||||
if (!license_parser_.RestoreOfflineLicense(offline_key_request_,
|
||||
offline_key_response_,
|
||||
offline_key_renewal_response_))
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
license_received_ = true;
|
||||
license_type_ = license_type;
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
bool CdmSession::VerifySession(const CdmKeySystem& key_system,
|
||||
const CdmInitData& init_data) {
|
||||
// TODO(gmorgan): Compare key_system and init_data with value received
|
||||
@@ -70,7 +120,7 @@ bool CdmSession::VerifySession(const CdmKeySystem& key_system,
|
||||
CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
|
||||
@@ -82,7 +132,6 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
reinitialize_session_ = false;
|
||||
}
|
||||
|
||||
|
||||
if (!crypto_session_) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -93,10 +142,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (license_received_) {
|
||||
license_type_ = license_type;
|
||||
|
||||
if (license_type_ == kLicenseTypeRelease) {
|
||||
return GenerateReleaseRequest(key_request, server_url);
|
||||
}
|
||||
else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request() ?
|
||||
UNKNOWN_ERROR : GenerateRenewalRequest(key_request,
|
||||
server_url);
|
||||
UNKNOWN_ERROR : GenerateRenewalRequest(key_request, server_url);
|
||||
}
|
||||
else {
|
||||
CdmInitData pssh_data;
|
||||
@@ -117,14 +170,22 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
key_request,
|
||||
server_url)) {
|
||||
return KEY_ERROR;
|
||||
} else {
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_pssh_data_ = pssh_data;
|
||||
offline_key_request_ = *key_request;
|
||||
offline_release_server_url_ = *server_url;
|
||||
}
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType CdmSession::AddKey(
|
||||
const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id) {
|
||||
if (!crypto_session_) {
|
||||
LOGW("CdmSession::AddKey: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -135,17 +196,34 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (license_received_) {
|
||||
if (license_type_ == kLicenseTypeRelease) {
|
||||
return ReleaseKey(key_response);
|
||||
}
|
||||
else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request() ?
|
||||
UNKNOWN_ERROR : RenewKey(key_response);
|
||||
}
|
||||
else {
|
||||
CdmResponseType sts = license_parser_.HandleKeyResponse(key_response);
|
||||
|
||||
if (sts == KEY_ADDED)
|
||||
license_received_ = true;
|
||||
if (sts != KEY_ADDED)
|
||||
return sts;
|
||||
|
||||
return sts;
|
||||
license_received_ = true;
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_key_response_ = key_response;
|
||||
key_set_id_ = GenerateKeySetId(offline_pssh_data_);
|
||||
if (!StoreLicense(true)) {
|
||||
LOGE("CdmSession::AddKey: Unable to store license");
|
||||
ReInit();
|
||||
key_set_id_.clear();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
*key_set_id = key_set_id_;
|
||||
return KEY_ADDED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,17 +285,49 @@ CdmResponseType CdmSession::Decrypt(bool is_encrypted,
|
||||
// session keys.
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
if (!license_parser_.PrepareKeyRenewalRequest(key_request,
|
||||
server_url)) {
|
||||
if (!license_parser_.PrepareKeyUpdateRequest(true, key_request, server_url))
|
||||
return KEY_ERROR;
|
||||
} else {
|
||||
return KEY_MESSAGE;
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_key_renewal_request_ = *key_request;
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
// RenewKey() - Accept renewal response and update key info.
|
||||
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
return license_parser_.HandleKeyRenewalResponse(key_response);
|
||||
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(true,
|
||||
key_response);
|
||||
if (sts != KEY_ADDED)
|
||||
return sts;
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_key_renewal_response_ = key_response;
|
||||
if (!StoreLicense(true))
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
if (license_parser_.PrepareKeyUpdateRequest(false, key_request,
|
||||
server_url)) {
|
||||
// Mark license as being released
|
||||
if (!StoreLicense(false))
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// ReleaseKey() - Accept release response and release license.
|
||||
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false,
|
||||
key_response);
|
||||
DeviceFiles::DeleteLicense(key_set_id_);
|
||||
return sts;
|
||||
}
|
||||
|
||||
bool CdmSession::IsKeyValid(const KeyId& key_id) {
|
||||
@@ -227,11 +337,39 @@ bool CdmSession::IsKeyValid(const KeyId& key_id) {
|
||||
}
|
||||
|
||||
CdmSessionId CdmSession::GenerateSessionId() {
|
||||
static const std::string kSessionPrefix("Session");
|
||||
static int session_num = 1;
|
||||
// TODO(rkuroiwa): Want this to be unique. Probably doing Hash(time+init_data)
|
||||
// to get something that is reasonably unique.
|
||||
return kSessionPrefix + IntToString(++session_num);
|
||||
return SESSION_ID_PREFIX + IntToString(++session_num);
|
||||
}
|
||||
|
||||
|
||||
CdmSessionId CdmSession::GenerateKeySetId(CdmInitData& pssh_data) {
|
||||
Clock clock;
|
||||
int64_t current_time = clock.GetCurrentTime();
|
||||
std::string key_set_id;
|
||||
|
||||
while (key_set_id.empty()) {
|
||||
int random = rand();
|
||||
|
||||
std::vector<uint8_t> hash(SHA256_DIGEST_LENGTH, 0);
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
SHA256_Update(&sha256, pssh_data.data(), pssh_data.size());
|
||||
SHA256_Update(&sha256, ¤t_time, sizeof(int64_t));
|
||||
SHA256_Update(&sha256, &random, sizeof(random));
|
||||
SHA256_Final(&hash[0], &sha256);
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
|
||||
hash[i%(SHA256_DIGEST_LENGTH/4)] ^= hash[i];
|
||||
}
|
||||
hash.resize(SHA256_DIGEST_LENGTH/4);
|
||||
key_set_id = KEY_SET_ID_PREFIX + b2a_hex(hash);
|
||||
|
||||
if (DeviceFiles::LicenseExists(key_set_id)) { // key set collision
|
||||
key_set_id.clear();
|
||||
}
|
||||
}
|
||||
return key_set_id;
|
||||
}
|
||||
|
||||
bool CdmSession::LoadDeviceCertificate(std::string* certificate,
|
||||
@@ -240,6 +378,18 @@ bool CdmSession::LoadDeviceCertificate(std::string* certificate,
|
||||
wrapped_key);
|
||||
}
|
||||
|
||||
bool CdmSession::StoreLicense(bool active) {
|
||||
DeviceFiles::LicenseState state = DeviceFiles::kLicenseStateReleasing;
|
||||
if (active)
|
||||
state = DeviceFiles::kLicenseStateActive;
|
||||
|
||||
return DeviceFiles::StoreLicense(key_set_id_, state, offline_pssh_data_,
|
||||
offline_key_request_, offline_key_response_,
|
||||
offline_key_renewal_request_,
|
||||
offline_key_renewal_response_,
|
||||
offline_release_server_url_);
|
||||
}
|
||||
|
||||
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
||||
std::pair<CdmEventListenerIter, bool> result = listeners_.insert(listener);
|
||||
return result.second;
|
||||
@@ -258,7 +408,22 @@ void CdmSession::OnTimerEvent() {
|
||||
if (event_occurred) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
(*iter)->onEvent(session_id(), event);
|
||||
CdmSessionId id = (*iter)->session_id();
|
||||
if (id.empty() || (id.compare(session_id_) == 0)) {
|
||||
(*iter)->onEvent(session_id_, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CdmSession::OnKeyReleaseEvent(CdmKeySetId key_set_id) {
|
||||
if (key_set_id_.compare(key_set_id) == 0) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
CdmSessionId id = (*iter)->session_id();
|
||||
if (id.empty() || (id.compare(session_id_) == 0)) {
|
||||
(*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,14 @@ namespace wvcdm {
|
||||
const char* DeviceFiles::kBasePath = "/data/mediadrm/IDM";
|
||||
const char* DeviceFiles::kPathDelimiter = "/";
|
||||
const char* DeviceFiles::kDeviceCertificateFileName = "cert.bin";
|
||||
const char* DeviceFiles::kLicenseFileNameExt = ".lic";
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_client::sdk::DeviceCertificate;
|
||||
using video_widevine_client::sdk::HashedFile;
|
||||
using video_widevine_client::sdk::License;
|
||||
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
|
||||
using video_widevine_client::sdk::License_LicenseState_RELEASING;
|
||||
|
||||
bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
const std::string& wrapped_private_key) {
|
||||
@@ -107,6 +111,148 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreLicense(
|
||||
const std::string& key_set_id,
|
||||
const LicenseState state,
|
||||
const CdmInitData& pssh_data,
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_message,
|
||||
const CdmKeyMessage& license_renewal_request,
|
||||
const CdmKeyResponse& license_renewal,
|
||||
const std::string& release_server_url) {
|
||||
// Fill in file information
|
||||
video_widevine_client::sdk::File file;
|
||||
|
||||
file.set_type(video_widevine_client::sdk::File::LICENSE);
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
|
||||
License* license = file.mutable_license();
|
||||
switch(state) {
|
||||
case kLicenseStateActive:
|
||||
license->set_state(License_LicenseState_ACTIVE);
|
||||
break;
|
||||
case kLicenseStateReleasing:
|
||||
license->set_state(License_LicenseState_RELEASING);
|
||||
break;
|
||||
default:
|
||||
LOGW("DeviceFiles::StoreLicense: Unknown license state: %u", state);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
license->set_pssh_data(pssh_data);
|
||||
license->set_license_request(license_request);
|
||||
license->set_license(license_message);
|
||||
license->set_renewal_request(license_renewal_request);
|
||||
license->set_renewal(license_renewal);
|
||||
license->set_release_server_url(release_server_url);
|
||||
|
||||
std::string serialized_string;
|
||||
file.SerializeToString(&serialized_string);
|
||||
|
||||
// calculate SHA hash
|
||||
std::string hash;
|
||||
if (!Hash(serialized_string, &hash)) {
|
||||
LOGW("DeviceFiles::StoreLicense: 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);
|
||||
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
return StoreFile(file_name.c_str(), serialized_string);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveLicense(
|
||||
const std::string& key_set_id,
|
||||
LicenseState* state,
|
||||
CdmInitData* pssh_data,
|
||||
CdmKeyMessage* license_request,
|
||||
CdmKeyResponse* license_message,
|
||||
CdmKeyMessage* license_renewal_request,
|
||||
CdmKeyResponse* license_renewal,
|
||||
std::string* release_server_url) {
|
||||
std::string serialized_hashed_file;
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
if (!RetrieveFile(file_name.c_str(), &serialized_hashed_file))
|
||||
return false;
|
||||
|
||||
HashedFile hashed_file;
|
||||
if (!hashed_file.ParseFromString(serialized_hashed_file)) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Unable to parse hash file");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash;
|
||||
if (!Hash(hashed_file.file(), &hash)) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash.compare(hashed_file.hash())) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Hash mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(hashed_file.file())) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.type() != video_widevine_client::sdk::File::LICENSE) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Incorrect file type");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Incorrect file version");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file.has_license()) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: License not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
License license = file.license();
|
||||
|
||||
switch(license.state()) {
|
||||
case License_LicenseState_ACTIVE:
|
||||
*state = kLicenseStateActive;
|
||||
break;
|
||||
case License_LicenseState_RELEASING:
|
||||
*state = kLicenseStateReleasing;
|
||||
break;
|
||||
default:
|
||||
LOGW("DeviceFiles::RetrieveLicense: Unrecognized license state: %u",
|
||||
kLicenseStateUnknown);
|
||||
*state = kLicenseStateUnknown;
|
||||
break;
|
||||
}
|
||||
*pssh_data = license.pssh_data();
|
||||
*license_request = license.license_request();
|
||||
*license_message = license.license();
|
||||
*license_renewal_request = license.renewal_request();
|
||||
*license_renewal = license.renewal();
|
||||
*release_server_url = license.release_server_url();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
||||
std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt;
|
||||
return File::Remove(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt;
|
||||
return File::Exists(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash)
|
||||
return false;
|
||||
@@ -147,6 +293,7 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("DeviceFiles::StoreFile: success: %s (%db)", path.c_str(), data.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -184,6 +331,8 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("DeviceFiles::RetrieveFile: success: %s (%db)", path.c_str(),
|
||||
data->size());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,18 @@ message DeviceCertificate {
|
||||
}
|
||||
|
||||
message License {
|
||||
optional bytes key_set_id = 1;
|
||||
enum LicenseState {
|
||||
ACTIVE = 1;
|
||||
RELEASING = 2;
|
||||
}
|
||||
|
||||
optional LicenseState state = 1;
|
||||
optional bytes pssh_data = 2;
|
||||
optional bytes license_request = 3;
|
||||
optional bytes license = 4;
|
||||
optional bytes renewal_request = 5;
|
||||
optional bytes renewal = 6;
|
||||
optional bytes release_server_url = 7;
|
||||
}
|
||||
|
||||
message File {
|
||||
@@ -38,7 +46,7 @@ message File {
|
||||
optional FileType type = 1;
|
||||
optional FileVersion version = 2 [default = VERSION_1];
|
||||
optional DeviceCertificate device_certificate = 3;
|
||||
repeated License licenses = 4;
|
||||
optional License license = 4;
|
||||
}
|
||||
|
||||
message HashedFile {
|
||||
|
||||
@@ -36,8 +36,6 @@ using video_widevine_server::sdk::License;
|
||||
using video_widevine_server::sdk::License_KeyContainer;
|
||||
using video_widevine_server::sdk::LicenseError;
|
||||
using video_widevine_server::sdk::SignedMessage;
|
||||
using video_widevine_server::sdk::STREAMING;
|
||||
using video_widevine_server::sdk::VERSION_2_1;
|
||||
|
||||
|
||||
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
@@ -97,7 +95,7 @@ bool CdmLicense::Init(const std::string& token,
|
||||
|
||||
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
if (!session_ ||
|
||||
@@ -180,7 +178,20 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
|
||||
content_id->mutable_cenc_id();
|
||||
cenc_content_id->add_pssh(init_data);
|
||||
cenc_content_id->set_license_type(STREAMING);
|
||||
|
||||
switch (license_type) {
|
||||
case kLicenseTypeOffline:
|
||||
cenc_content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
|
||||
break;
|
||||
case kLicenseTypeStreaming:
|
||||
cenc_content_id->set_license_type(video_widevine_server::sdk::STREAMING);
|
||||
break;
|
||||
default:
|
||||
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %u",
|
||||
(int)license_type);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
cenc_content_id->set_request_id(request_id);
|
||||
|
||||
// TODO(jfore): The time field will be updated once the cdm wrapper
|
||||
@@ -197,7 +208,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
}
|
||||
license_request.set_key_control_nonce(UintToString(nonce));
|
||||
LOGD("PrepareKeyRequest: nonce=%u", nonce);
|
||||
license_request.set_protocol_version(VERSION_2_1);
|
||||
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
|
||||
|
||||
// License request is complete. Serialize it.
|
||||
std::string serialized_license_req;
|
||||
@@ -215,6 +226,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
}
|
||||
|
||||
if (license_request_signature.empty()) {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: License request signature empty");
|
||||
signed_request->clear();
|
||||
return false;
|
||||
}
|
||||
@@ -231,22 +243,27 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
if (!session_) {
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: Invalid crypto session");
|
||||
return false;
|
||||
}
|
||||
if (!signed_request) {
|
||||
LOGE("CdmLicense::PrepareKeyRenewalRequest : No signed request provided.");
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: No signed request provided");
|
||||
return false;
|
||||
}
|
||||
if (!server_url) {
|
||||
LOGE("CdmLicense::PrepareKeyRenewalRequest : No server url provided.");
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: No server url provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
LicenseRequest license_request;
|
||||
license_request.set_type(LicenseRequest::RENEWAL);
|
||||
if (is_renewal)
|
||||
license_request.set_type(LicenseRequest::RENEWAL);
|
||||
else
|
||||
license_request.set_type(LicenseRequest::RELEASE);
|
||||
|
||||
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
|
||||
license_request.mutable_content_id()->mutable_license();
|
||||
@@ -259,8 +276,8 @@ bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request,
|
||||
return false;
|
||||
}
|
||||
license_request.set_key_control_nonce(UintToString(nonce));
|
||||
LOGD("PrepareKeyRenewalRequest: nonce=%u", nonce);
|
||||
license_request.set_protocol_version(VERSION_2_1);
|
||||
LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce);
|
||||
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
|
||||
|
||||
// License request is complete. Serialize it.
|
||||
std::string serialized_license_req;
|
||||
@@ -272,7 +289,11 @@ bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request,
|
||||
&license_request_signature))
|
||||
return false;
|
||||
|
||||
if (license_request_signature.empty()) return false;
|
||||
if (license_request_signature.empty()) {
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: empty license request"
|
||||
" signature");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Put serialize license request and signature together
|
||||
SignedMessage signed_message;
|
||||
@@ -369,44 +390,60 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyRenewalResponse(
|
||||
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
bool is_renewal,
|
||||
const CdmKeyResponse& license_response) {
|
||||
if (!session_) {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
if (license_response.empty()) {
|
||||
LOGE("CdmLicense::HandleKeyRenewalResponse : Empty license response.");
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse : Empty license response.");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
SignedMessage signed_response;
|
||||
if (!signed_response.ParseFromString(license_response))
|
||||
if (!signed_response.ParseFromString(license_response)) {
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse signed message");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (signed_response.type() == SignedMessage::ERROR) {
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
}
|
||||
|
||||
if (!signed_response.has_signature())
|
||||
if (!signed_response.has_signature()) {
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: signature missing");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
License license;
|
||||
if (!license.ParseFromString(signed_response.msg()))
|
||||
if (!license.ParseFromString(signed_response.msg())) {
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse license"
|
||||
" from signed message");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (!license.has_id()) return KEY_ERROR;
|
||||
if (!license.has_id()) {
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: license id not present");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (license.id().version() > license_id_.version()) {
|
||||
// This is the normal case.
|
||||
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();
|
||||
if (is_renewal) {
|
||||
if (license.policy().has_renewal_server_url() &&
|
||||
license.policy().renewal_server_url().size() > 0) {
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
}
|
||||
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
if (!is_renewal)
|
||||
return KEY_ADDED;
|
||||
|
||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||
|
||||
if (session_->RefreshKeys(signed_response.msg(),
|
||||
@@ -423,15 +460,67 @@ CdmResponseType CdmLicense::HandleKeyRenewalResponse(
|
||||
// This isn't supposed to happen.
|
||||
// TODO(jfore): Handle wrap? We can miss responses and that should be
|
||||
// considered normal until retries are exhausted.
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: license version: expected > %u,"
|
||||
" actual = %u", license_id_.version(), license.id().version());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
bool CdmLicense::RestoreOfflineLicense(
|
||||
CdmKeyMessage& license_request,
|
||||
CdmKeyResponse& license_response,
|
||||
CdmKeyResponse& license_renewal_response) {
|
||||
|
||||
if (license_request.empty() || license_response.empty()) {
|
||||
LOGE("CdmLicense::RestoreOfflineLicense: key_request or response empty: "
|
||||
"%u %u", license_request.size(), license_response.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
SignedMessage signed_request;
|
||||
if (!signed_request.ParseFromString(license_request)) {
|
||||
LOGE("CdmLicense::RestoreOfflineLicense: license_request parse failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
|
||||
LOGE("CdmLicense::RestoreOfflineLicense: license request type: expected = "
|
||||
"%d, actual = %d",
|
||||
SignedMessage::LICENSE_REQUEST,
|
||||
signed_request.type());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
key_request_ = signed_request.msg();
|
||||
}
|
||||
else {
|
||||
if (!session_->GenerateDerivedKeys(signed_request.msg()))
|
||||
return false;
|
||||
}
|
||||
|
||||
CdmResponseType sts = HandleKeyResponse(license_response);
|
||||
|
||||
if (sts != KEY_ADDED)
|
||||
return false;
|
||||
|
||||
if (!license_renewal_response.empty()) {
|
||||
sts = HandleKeyUpdateResponse(true, license_renewal_response);
|
||||
|
||||
if (sts != KEY_ADDED)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
const SignedMessage& signed_message) {
|
||||
|
||||
LicenseError license_error;
|
||||
if (!license_error.ParseFromString(signed_message.msg()))
|
||||
if (!license_error.ParseFromString(signed_message.msg())) {
|
||||
LOGE("CdmLicense::HandleKeyErrorResponse: Unable to parse license error");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
switch (license_error.error_code()) {
|
||||
case LicenseError::INVALID_CREDENTIALS:
|
||||
@@ -440,6 +529,8 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
return DEVICE_REVOKED;
|
||||
case LicenseError::SERVICE_UNAVAILABLE:
|
||||
default:
|
||||
LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d",
|
||||
license_error.error_code());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,14 +36,14 @@ class WvCdmEngineTest : public testing::Test {
|
||||
const std::string& init_data) {
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
std::string server_url;
|
||||
std::string key_set_id;
|
||||
EXPECT_EQ(cdm_engine_.GenerateKeyRequest(session_id_,
|
||||
true, // is_key_system_present
|
||||
key_system,
|
||||
init_data,
|
||||
kLicenseTypeStreaming,
|
||||
app_parameters,
|
||||
&key_msg_,
|
||||
&server_url), wvcdm::KEY_MESSAGE);
|
||||
key_set_id,
|
||||
init_data,
|
||||
kLicenseTypeStreaming,
|
||||
app_parameters,
|
||||
&key_msg_,
|
||||
&server_url), wvcdm::KEY_MESSAGE);
|
||||
}
|
||||
|
||||
void GenerateRenewalRequest(const std::string& key_system,
|
||||
@@ -125,7 +125,7 @@ class WvCdmEngineTest : public testing::Test {
|
||||
return "";
|
||||
}
|
||||
|
||||
url_request.PostRequest(key_msg_);
|
||||
url_request.PostRequestChunk(key_msg_);
|
||||
std::string http_response;
|
||||
std::string message_body;
|
||||
int resp_bytes = url_request.GetResponse(http_response);
|
||||
@@ -166,7 +166,9 @@ class WvCdmEngineTest : public testing::Test {
|
||||
EXPECT_EQ(cdm_engine_.RenewKey(session_id_, resp), wvcdm::KEY_ADDED);
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp), wvcdm::KEY_ADDED);
|
||||
std::string key_set_id;
|
||||
EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp, key_set_id),
|
||||
wvcdm::KEY_ADDED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,136 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
std::string kKeySetId1 = "ksid05e79dab2750370195d1";
|
||||
std::string kKeySetId2 = "ksid17fe1e9005934171bbd1";
|
||||
std::string kKeySetId3 = "ksid2eadb793100566012934";
|
||||
|
||||
std::string kPsshData1 = "0801121093789920E8D6520098577DF8F2DD5546";
|
||||
std::string kPsshData2 = "0801121030313233343536373839616263646566";
|
||||
std::string kPsshData3 = "08011210e02562e04cd55351b14b3d748d36ed8e";
|
||||
|
||||
std::string kKeyRequest1 = "0801128D0C0ACF0B080112EF090AB0020802121007D9FFDE"
|
||||
"13AA95C122678053362136BD18D6DDC78C05228E023082010A0282010100B2873F12C61"
|
||||
"8883986B67A404733168D34676EA3261231B4EB12CB6F862AAE529D21B449D78E7A8FBB"
|
||||
"9CC1A8AE119D89EC571EA23EE92517AFB86EA089E81CF3A773AEDEDFC1979AEE115D657"
|
||||
"FE372CBF4178E57F60BEDD15491EF28891ED8663437019C0EF5F79FD0ADDAAE87281DD9"
|
||||
"10ED10356DD6B606C0DD2452343329151BD23A42060869252892BD2C0368787E880F716"
|
||||
"438E5835BC93FED65D0017D8758E20E5E614B44BAFAF7286484624CC5EB109D85F1C048"
|
||||
"9B25A75528C0CD23961067192EF65766EAB5E97FE7EBE25D63DDE41BB34048B8A378EAE"
|
||||
"AEBC86C44AB4AA757F29B48C9476660DCC4EE656EB35AC309213399CBEF38394FE32B88"
|
||||
"3F9B02030100012899203001128002435CAFA39AD7DC12A40F943FE8DE10C5DD911059D"
|
||||
"7B0990742E11419B2E6E797EAE46B5D017F0EF3F429AA43E4F570BBE46F56BBCEACBDFA"
|
||||
"8495E4451249D39D5FEFC4F5557498B233C4C9C101E2388E107582D3C3FBF266AB9FE96"
|
||||
"A40405A2098583B33D7AD083E105458DF7AE92FD517D538B4671614DA0D167F9661EE0C"
|
||||
"574AB95A5BE87B1BEE27659D875A37A1730F84033ACB6BCA8D3C575A2692E7390F062BC"
|
||||
"E1E58D9C621A1A11CB215615ED0C5C12";
|
||||
std::string kKeyRequest2 = "0801128D0C0ACF0B080112EF090AB0020802121007D9FFDE"
|
||||
"13AA95C122678053362136BD188BDCC78C05228E023082010A0282010100C53F667AAA8"
|
||||
"27758D3ABF81DAFA223373473EE13E4C500A09CD35CE2620D5DA0D987A237EBFCE1F793"
|
||||
"D6F88187749A85B3AF4D737899EF19C3029461E95202E9DF91DD5869FE482FCA17160AF"
|
||||
"7AC380D03880EBE3EA55B7FB3ED5ED6B9FAD19234AA75BB642A9A86AEF7CDA86F7B2EC7"
|
||||
"C40C31C380F95C1F3C1564B012D530A7971AA656BBD27DAB715F0D5771FB7608453AF88"
|
||||
"A331E3E5852F6F9B076A95C807307E541C39216D64B02D180A1009AFB60777EF7C72275"
|
||||
"033ABD6FDF12145FFD379B6CB25F1127A783A40D65BF95AB74A08E39209EF2C0341A92A"
|
||||
"217FD223734C7D1F0DC0D5B50FBA521FF1F841D1E5ED18DFC13A6D28E5A0A4FACBE6C78"
|
||||
"31B5020301000128992030011280022DBC4444671C0C4CF0FBB3BB30C80C78FAFA67223"
|
||||
"C6FE3F0BB495B1959B7472E8A64BC13838B3FB731FAC0609FEAB325FE8B7358C2F63D5E"
|
||||
"E6C0380B85DBA3FE207B9D6AE7C31467A36A9E8D3D786CC4C383BC2774131D6C099D06D"
|
||||
"052ED13A4545A0026A4FC949DE3B79D1C0C6A0D581A3D3598E46D3E4AC827C94045D693"
|
||||
"37CAF103CC8839EA7FF020C0721AB17B140640C5E31C1727073CA48F445DA5EE55521D8"
|
||||
"85732BAA17BD672B62C717ADBC13F235";
|
||||
std::string kKeyRequest3 = "0801128D0C0ACF0B080112EF090AB0020802121007D9FFDE"
|
||||
"13AA95C122678053362136BD18D6DDC78C05228E023082010A0282010100B2873F12C61"
|
||||
"8883986B67A404733168D34676EA3261231B4EB12CB6F862AAE529D21B449D78E7A8FBB"
|
||||
"9CC1A8AE119D89EC571EA23EE92517AFB86EA089E81CF3A773AEDEDFC1979AEE115D657"
|
||||
"FE372CBF4178E57F60BEDD15491EF28891ED8663437019C0EF5F79FD0ADDAAE87281DD9"
|
||||
"10ED10356DD6B606C0DD2452343329151BD23A42060869252892BD2C0368787E880F716"
|
||||
"438E5835BC93FED65D0017D8758E20E5E614B44BAFAF7286484624CC5EB109D85F1C048"
|
||||
"9B25A75528C0CD23961067192EF65766EAB5E97FE7EBE25D63DDE41BB34048B8A378EAE"
|
||||
"AEBC86C44AB4AA757F29B48C9476660DCC4EE656EB35AC309213399CBEF38394FE32B88"
|
||||
"3F9B02030100012899203001128002435CAFA39AD7DC12A40F943FE8DE10C5DD911059D"
|
||||
"7B0990742E11419B2E6E797EAE46B5D017F0EF3F429AA43E4F570BBE46F56BBCEACBDFA"
|
||||
"8495E4451249D39D5FEFC4F5557498B233C4C9C101E2388E107582D3C3FBF266AB9FE96"
|
||||
"A40405A2098583B33D7AD083E105458DF7AE92FD517D538B4671614DA0D167F9661EE0C"
|
||||
"574AB95A5BE87B1BEE27659D875A37A1730F84033ACB6BCA8D3C575A2692E7390F062BC"
|
||||
"E1E58D9C621A1A11CB215615ED0C5C12";
|
||||
|
||||
std::string kKeyResponse1 = "080212C2040A460A0939383736353433323112086D03DC5"
|
||||
"FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E7967396755464B"
|
||||
"6963455954335A34626738366C77200128001283010801180120809A9E0128809A9E013"
|
||||
"0AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F766964656F2D"
|
||||
"6465762F6865617274626561742F63656E633F736F757263653D594F555455424526766"
|
||||
"964656F5F69643D45474843364F484E624F6F266F617574683D79612E67747371617769"
|
||||
"646576696E65483C503C1A661210EBA4B8B97E17DC6C45D359222B9327121A50583647C"
|
||||
"BC8240C2760A79DC72DF7FC0216C0943F12307B9AAED429277A6D0024F93CCA17B7F2BA"
|
||||
"C19875E041F12E6729A98E0DFF2EA9806D6C20AF801A5A3E19DAF918FC9AF230C8765DF"
|
||||
"B5BE869DF7320011A80010A10E02562E04CD55351B14B3D748D36ED8E12107C9AC11A9D"
|
||||
"39304821C47969729A43321A2077E79664AF73B13FC6C55B5B9B2000E174E94F7D9E9BB"
|
||||
"B58A547A82FAAAF46182002280242340A202031C7ADB3C2F80975499E285CEE0AF8343A"
|
||||
"F3DF50A49EEBCCEB3747C955FEFB12107E021073F71CF814A9CCAA7008B38CD81A80010"
|
||||
"A100065901A64A25899A5193664ABF9AF621210874AB758C27A29677D62D2EE092905E2"
|
||||
"1A207C4AD3CB7A80740215598D5E74883";
|
||||
std::string kKeyResponse2 = "080212C2040A460A0939383736353433323112080C1913B"
|
||||
"45E8893481A2B596E4B58753250316A5F53785073494E6B377459354E7967396755464B"
|
||||
"6963455954335A34626738366C77200128001283010801180120809A9E0128809A9E013"
|
||||
"0AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F766964656F2D"
|
||||
"6465762F6865617274626561742F63656E633F736F757263653D594F555455424526766"
|
||||
"964656F5F69643D45474843364F484E624F6F266F617574683D79612E67747371617769"
|
||||
"646576696E65483C503C1A661210A2B662DC43891A32E65E0CC31CD597B21A50046489D"
|
||||
"7EE8A674A4976800FD0553075498F90FD1997A63AECFF3481BF5CA908502676FA7E7E6B"
|
||||
"02179FE6C42FAE7351EF85C8494BBE0782CB4C7A5AA6E439590C2D39DD5DB2B1A3DA91E"
|
||||
"3D43FA5ABDD20011A80010A10E02562E04CD55351B14B3D748D36ED8E1210B0EF7CC3B2"
|
||||
"0E10F4F704DBCF3142AB611A20F684136083E458C6B2977DB4D1FC559F2AFF2C454FEDF"
|
||||
"CD68F29D50D351D58DC2002280242340A20223C0E4281615524569AF90EC3BE98349B27"
|
||||
"0016C5DCC4CFDF11CB55C9DCFB231210888EF5E2311B3B3407549903D8F30D771A80010"
|
||||
"A100065901A64A25899A5193664ABF9AF621210F38F473DC71703C52CC970FCACFA2111"
|
||||
"1A20067E176521B6176A97157EDB9D06C";
|
||||
std::string kKeyResponse3 = "080212C2040A460A0939383736353433323112086D03DC5"
|
||||
"FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E7967396755464B"
|
||||
"6963455954335A34626738366C77200128001283010801180120809A9E0128809A9E013"
|
||||
"0AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F766964656F2D"
|
||||
"6465762F6865617274626561742F63656E633F736F757263653D594F555455424526766"
|
||||
"964656F5F69643D45474843364F484E624F6F266F617574683D79612E67747371617769"
|
||||
"646576696E65483C503C1A661210EBA4B8B97E17DC6C45D359222B9327121A50583647C"
|
||||
"BC8240C2760A79DC72DF7FC0216C0943F12307B9AAED429277A6D0024F93CCA17B7F2BA"
|
||||
"C19875E041F12E6729A98E0DFF2EA9806D6C20AF801A5A3E19DAF918FC9AF230C8765DF"
|
||||
"B5BE869DF7320011A80010A10E02562E04CD55351B14B3D748D36ED8E12107C9AC11A9D"
|
||||
"39304821C47969729A43321A2077E79664AF73B13FC6C55B5B9B2000E174E94F7D9E9BB"
|
||||
"B58A547A82FAAAF46182002280242340A202031C7ADB3C2F80975499E285CEE0AF8343A"
|
||||
"F3DF50A49EEBCCEB3747C955FEFB12107E021073F71CF814A9CCAA7008B38CD81A80010"
|
||||
"A100065901A64A25899A5193664ABF9AF621210874AB758C27A29677D62D2EE092905E2"
|
||||
"1A207C4AD3CB7A80740215598D5E74883";
|
||||
|
||||
std::string kKeyRenewalRequest1 = "";
|
||||
std::string kKeyRenewalRequest2 = "0801125E124A1A480A460A0939383736353433323"
|
||||
"112080C1913B45E8893481A2B596E4B58753250316A5F53785073494E6B377459354E79"
|
||||
"67396755464B6963455954335A34626738366C772001280018022A0C383231353639373"
|
||||
"13400000030151A20789770161DD09DD5210E1A399DCA51C7741D2FC83488B731D22F4C"
|
||||
"B01E74D240";
|
||||
std::string kKeyRenewalRequest3 = "0801125E124A1A480A460A0939383736353433323"
|
||||
"112086D03DC5FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E79"
|
||||
"67396755464B6963455954335A34626738366C772001280018022A0C313736393630363"
|
||||
"23739000030151A209ABDAF1E996DFED08BF64B71356DAD8244BFAD335749206C2A1749"
|
||||
"61CB15B9AD";
|
||||
|
||||
std::string kKeyRenewalResponse1 = "";
|
||||
std::string kKeyRenewalResponse2 = "";
|
||||
std::string kKeyRenewalResponse3 = "080212EC010A460A093938373635343332311208"
|
||||
"6D03DC5FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E7967396"
|
||||
"755464B6963455954335A34626738366C77200128011283010801180120809A9E012880"
|
||||
"9A9E0130AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F76696"
|
||||
"4656F2D6465762F6865617274626561742F63656E633F736F757263653D594F55545542"
|
||||
"4526766964656F5F69643D45474843364F484E624F6F266F617574683D79612E6774737"
|
||||
"1617769646576696E65483C503C1A16200342120A106B63746C0000012C697A0C870000"
|
||||
"000820E3DDC78C051A20466E04142A0F410F5BA032BD8B0B77C800E9972C707E034E2ED"
|
||||
"E83661C6C5386";
|
||||
|
||||
std::string kKeyReleaseServerUrl1 = "http://hamid.kir.corp.google.com:8888/drm";
|
||||
std::string kKeyReleaseServerUrl2 = "https://www.youtube.com/api/drm/widevine?video_id=03681262dc412c06&source=YOUTUBE";
|
||||
std::string kKeyReleaseServerUrl3 = "https://jmt17.google.com/video-dev/license/GetCencLicense";
|
||||
}
|
||||
|
||||
TEST(DeviceFilesTest, StoreCertificate) {
|
||||
std::string device_base_path =
|
||||
DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
|
||||
@@ -93,4 +223,341 @@ TEST(DeviceFilesTest, RetrieveCertificate) {
|
||||
EXPECT_TRUE(File::Remove(device_base_path));
|
||||
}
|
||||
|
||||
TEST(DeviceFilesTest, StoreLicense) {
|
||||
std::string device_base_path =
|
||||
DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
|
||||
std::string license_path_1 = device_base_path + kKeySetId1 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
|
||||
if (!File::Exists(device_base_path))
|
||||
EXPECT_TRUE(File::CreateDirectory(device_base_path));
|
||||
if (File::Exists(license_path_1))
|
||||
EXPECT_TRUE(File::Remove(license_path_1));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1,
|
||||
DeviceFiles::kLicenseStateActive,
|
||||
kPsshData1, kKeyRequest1,
|
||||
kKeyResponse1, kKeyRenewalRequest1,
|
||||
kKeyRenewalResponse1,
|
||||
kKeyReleaseServerUrl1));
|
||||
size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() +
|
||||
kKeyRequest1.size() + kKeyResponse1.size() +
|
||||
kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() +
|
||||
kKeyReleaseServerUrl1.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_1));
|
||||
EXPECT_GT(File::FileSize(license_path_1), static_cast<ssize_t>(size));
|
||||
}
|
||||
|
||||
TEST(DeviceFilesTest, StoreLicenseInitial) {
|
||||
std::string device_base_path =
|
||||
DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
|
||||
std::string license_path_1 = device_base_path + kKeySetId1 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
|
||||
if (!File::Exists(device_base_path))
|
||||
EXPECT_TRUE(File::Remove(device_base_path));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1,
|
||||
DeviceFiles::kLicenseStateActive,
|
||||
kPsshData1, kKeyRequest1,
|
||||
kKeyResponse1, kKeyRenewalRequest1,
|
||||
kKeyRenewalResponse1,
|
||||
kKeyReleaseServerUrl1));
|
||||
size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() +
|
||||
kKeyRequest1.size() + kKeyResponse1.size() +
|
||||
kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() +
|
||||
kKeyReleaseServerUrl1.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_1));
|
||||
EXPECT_GT(File::FileSize(license_path_1), static_cast<ssize_t>(size));
|
||||
}
|
||||
|
||||
TEST(DeviceFilesTest, StoreLicenses) {
|
||||
std::string device_base_path =
|
||||
DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
|
||||
std::string license_path_1 = device_base_path + kKeySetId1 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
std::string license_path_2 = device_base_path + kKeySetId2 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
std::string license_path_3 = device_base_path + kKeySetId3 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
|
||||
if (!File::Exists(device_base_path))
|
||||
EXPECT_TRUE(File::Remove(device_base_path));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1,
|
||||
DeviceFiles::kLicenseStateActive,
|
||||
kPsshData1, kKeyRequest1,
|
||||
kKeyResponse1, kKeyRenewalRequest1,
|
||||
kKeyRenewalResponse1,
|
||||
kKeyReleaseServerUrl1));
|
||||
size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() +
|
||||
kKeyRequest1.size() + kKeyResponse1.size() +
|
||||
kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() +
|
||||
kKeyReleaseServerUrl1.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_1));
|
||||
EXPECT_GT(File::FileSize(license_path_1), static_cast<ssize_t>(size));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId2,
|
||||
DeviceFiles::kLicenseStateReleasing,
|
||||
kPsshData2, kKeyRequest2,
|
||||
kKeyResponse2, kKeyRenewalRequest2,
|
||||
kKeyRenewalResponse2,
|
||||
kKeyReleaseServerUrl2));
|
||||
size = sizeof(DeviceFiles::LicenseState) + kPsshData2.size() +
|
||||
kKeyRequest2.size() + kKeyResponse2.size() +
|
||||
kKeyRenewalRequest2.size() + kKeyRenewalResponse2.size() +
|
||||
kKeyReleaseServerUrl2.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_2));
|
||||
EXPECT_GT(File::FileSize(license_path_2), static_cast<ssize_t>(size));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId3,
|
||||
DeviceFiles::kLicenseStateReleasing,
|
||||
kPsshData3, kKeyRequest3,
|
||||
kKeyResponse3, kKeyRenewalRequest3,
|
||||
kKeyRenewalResponse3,
|
||||
kKeyReleaseServerUrl3));
|
||||
size = sizeof(DeviceFiles::LicenseState) + kPsshData3.size() +
|
||||
kKeyRequest3.size() + kKeyResponse3.size() +
|
||||
kKeyRenewalRequest3.size() + kKeyRenewalResponse3.size() +
|
||||
kKeyReleaseServerUrl3.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_3));
|
||||
EXPECT_GT(File::FileSize(license_path_3), static_cast<ssize_t>(size));
|
||||
}
|
||||
|
||||
TEST(DeviceFilesTest, RetrieveLicenses) {
|
||||
std::string device_base_path =
|
||||
DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
|
||||
std::string license_path_1 = device_base_path + kKeySetId1 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
std::string license_path_2 = device_base_path + kKeySetId2 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
std::string license_path_3 = device_base_path + kKeySetId3 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
|
||||
if (!File::Exists(device_base_path))
|
||||
EXPECT_TRUE(File::Remove(device_base_path));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1,
|
||||
DeviceFiles::kLicenseStateActive,
|
||||
kPsshData1, kKeyRequest1,
|
||||
kKeyResponse1, kKeyRenewalRequest1,
|
||||
kKeyRenewalResponse1,
|
||||
kKeyReleaseServerUrl1));
|
||||
size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() +
|
||||
kKeyRequest1.size() + kKeyResponse1.size() +
|
||||
kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() +
|
||||
kKeyReleaseServerUrl1.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_1));
|
||||
EXPECT_GT(File::FileSize(license_path_1), static_cast<ssize_t>(size));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId2,
|
||||
DeviceFiles::kLicenseStateReleasing,
|
||||
kPsshData2, kKeyRequest2,
|
||||
kKeyResponse2, kKeyRenewalRequest2,
|
||||
kKeyRenewalResponse2,
|
||||
kKeyReleaseServerUrl2));
|
||||
size = sizeof(DeviceFiles::LicenseState) + kPsshData2.size() +
|
||||
kKeyRequest2.size() + kKeyResponse2.size() +
|
||||
kKeyRenewalRequest2.size() + kKeyRenewalResponse2.size() +
|
||||
kKeyReleaseServerUrl2.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_2));
|
||||
EXPECT_GT(File::FileSize(license_path_2), static_cast<ssize_t>(size));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId3,
|
||||
DeviceFiles::kLicenseStateReleasing,
|
||||
kPsshData3, kKeyRequest3,
|
||||
kKeyResponse3, kKeyRenewalRequest3,
|
||||
kKeyRenewalResponse3,
|
||||
kKeyReleaseServerUrl3));
|
||||
size = sizeof(DeviceFiles::LicenseState) + kPsshData3.size() +
|
||||
kKeyRequest3.size() + kKeyResponse3.size() +
|
||||
kKeyRenewalRequest3.size() + kKeyRenewalResponse3.size() +
|
||||
kKeyReleaseServerUrl3.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_3));
|
||||
EXPECT_GT(File::FileSize(license_path_3), static_cast<ssize_t>(size));
|
||||
|
||||
DeviceFiles::LicenseState state;
|
||||
std::string pssh_data, key_request, key_response, key_renewal_request,
|
||||
key_renewal_response, release_server_url;
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data,
|
||||
&key_request, &key_response,
|
||||
&key_renewal_request,
|
||||
&key_renewal_response,
|
||||
&release_server_url));
|
||||
EXPECT_TRUE(state == DeviceFiles::kLicenseStateActive);
|
||||
EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size())
|
||||
== 0);
|
||||
EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(),
|
||||
key_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(),
|
||||
key_response.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(),
|
||||
key_renewal_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(),
|
||||
key_renewal_response.size()) == 0);
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId2, &state, &pssh_data,
|
||||
&key_request, &key_response,
|
||||
&key_renewal_request,
|
||||
&key_renewal_response,
|
||||
&release_server_url));
|
||||
EXPECT_TRUE(state == DeviceFiles::kLicenseStateReleasing);
|
||||
EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData2.data(), pssh_data.size())
|
||||
== 0);
|
||||
EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest2.data(),
|
||||
key_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse2.data(),
|
||||
key_response.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest2.data(),
|
||||
key_renewal_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse2.data(),
|
||||
key_renewal_response.size()) == 0);
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId3, &state, &pssh_data,
|
||||
&key_request, &key_response,
|
||||
&key_renewal_request,
|
||||
&key_renewal_response,
|
||||
&release_server_url));
|
||||
EXPECT_TRUE(state == DeviceFiles::kLicenseStateReleasing);
|
||||
EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData3.data(), pssh_data.size())
|
||||
== 0);
|
||||
EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest3.data(),
|
||||
key_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse3.data(),
|
||||
key_response.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest3.data(),
|
||||
key_renewal_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse3.data(),
|
||||
key_renewal_response.size()) == 0);
|
||||
|
||||
}
|
||||
|
||||
TEST(DeviceFilesTest, UpdateLicenseState) {
|
||||
std::string device_base_path =
|
||||
DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
|
||||
std::string license_path_1 = device_base_path + kKeySetId1 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
|
||||
if (!File::Exists(device_base_path))
|
||||
EXPECT_TRUE(File::Remove(device_base_path));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1,
|
||||
DeviceFiles::kLicenseStateActive,
|
||||
kPsshData1, kKeyRequest1,
|
||||
kKeyResponse1, kKeyRenewalRequest1,
|
||||
kKeyRenewalResponse1,
|
||||
kKeyReleaseServerUrl1));
|
||||
size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() +
|
||||
kKeyRequest1.size() + kKeyResponse1.size() +
|
||||
kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() +
|
||||
kKeyReleaseServerUrl1.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_1));
|
||||
EXPECT_GT(File::FileSize(license_path_1), static_cast<ssize_t>(size));
|
||||
|
||||
DeviceFiles::LicenseState state;
|
||||
std::string pssh_data, key_request, key_response, key_renewal_request,
|
||||
key_renewal_response, release_server_url;
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data,
|
||||
&key_request, &key_response,
|
||||
&key_renewal_request,
|
||||
&key_renewal_response,
|
||||
&release_server_url));
|
||||
EXPECT_TRUE(state == DeviceFiles::kLicenseStateActive);
|
||||
EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size())
|
||||
== 0);
|
||||
EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(),
|
||||
key_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(),
|
||||
key_response.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(),
|
||||
key_renewal_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(),
|
||||
key_renewal_response.size()) == 0);
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1,
|
||||
DeviceFiles::kLicenseStateReleasing,
|
||||
kPsshData1, kKeyRequest1,
|
||||
kKeyResponse1, kKeyRenewalRequest1,
|
||||
kKeyRenewalResponse1,
|
||||
kKeyReleaseServerUrl1));
|
||||
size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() +
|
||||
kKeyRequest1.size() + kKeyResponse1.size() +
|
||||
kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() +
|
||||
kKeyReleaseServerUrl1.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_1));
|
||||
EXPECT_GT(File::FileSize(license_path_1), static_cast<ssize_t>(size));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data,
|
||||
&key_request, &key_response,
|
||||
&key_renewal_request,
|
||||
&key_renewal_response,
|
||||
&release_server_url));
|
||||
EXPECT_TRUE(state == DeviceFiles::kLicenseStateReleasing);
|
||||
EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size())
|
||||
== 0);
|
||||
EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(),
|
||||
key_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(),
|
||||
key_response.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(),
|
||||
key_renewal_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(),
|
||||
key_renewal_response.size()) == 0);
|
||||
}
|
||||
|
||||
TEST(DeviceFilesTest, DeleteLicense) {
|
||||
std::string device_base_path =
|
||||
DeviceFiles::GetBasePath(DeviceFiles::kBasePath);
|
||||
std::string license_path_1 = device_base_path + kKeySetId1 +
|
||||
DeviceFiles::kLicenseFileNameExt;
|
||||
|
||||
if (!File::Exists(device_base_path))
|
||||
EXPECT_TRUE(File::Remove(device_base_path));
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1,
|
||||
DeviceFiles::kLicenseStateActive,
|
||||
kPsshData1, kKeyRequest1,
|
||||
kKeyResponse1, kKeyRenewalRequest1,
|
||||
kKeyRenewalResponse1,
|
||||
kKeyReleaseServerUrl1));
|
||||
size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() +
|
||||
kKeyRequest1.size() + kKeyResponse1.size() +
|
||||
kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() +
|
||||
kKeyReleaseServerUrl1.size();
|
||||
EXPECT_TRUE(File::Exists(license_path_1));
|
||||
EXPECT_GT(File::FileSize(license_path_1), static_cast<ssize_t>(size));
|
||||
|
||||
DeviceFiles::LicenseState state;
|
||||
std::string pssh_data, key_request, key_response, key_renewal_request,
|
||||
key_renewal_response, release_server_url;
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data,
|
||||
&key_request, &key_response,
|
||||
&key_renewal_request,
|
||||
&key_renewal_response,
|
||||
&release_server_url));
|
||||
EXPECT_TRUE(state == DeviceFiles::kLicenseStateActive);
|
||||
EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size())
|
||||
== 0);
|
||||
EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(),
|
||||
key_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(),
|
||||
key_response.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(),
|
||||
key_renewal_request.size()) == 0);
|
||||
EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(),
|
||||
key_renewal_response.size()) == 0);
|
||||
|
||||
EXPECT_TRUE(DeviceFiles::DeleteLicense(kKeySetId1));
|
||||
|
||||
EXPECT_FALSE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data,
|
||||
&key_request, &key_response,
|
||||
&key_renewal_request,
|
||||
&key_renewal_response,
|
||||
&release_server_url));
|
||||
EXPECT_TRUE(File::Remove(device_base_path));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ void LicenseRequest::GetDrmMessage(const std::string& response,
|
||||
if (drm_msg_pos != std::string::npos) {
|
||||
drm_msg = response.substr(drm_msg_pos);
|
||||
} else {
|
||||
// TODO(edwinwong, rfrias): hack to get HTTP message body out for
|
||||
// non-Google Play webservers. Need to clean this up. Possibly test
|
||||
// for GLS and decide which part is the drm message
|
||||
drm_msg = response.substr(header_end_pos);
|
||||
LOGE("drm msg not found");
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "url_request.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "http_socket.h"
|
||||
#include "log.h"
|
||||
#include "string_conversions.h"
|
||||
@@ -79,7 +81,7 @@ int UrlRequest::GetStatusCode(const std::string& response) {
|
||||
return status_code;
|
||||
}
|
||||
|
||||
bool UrlRequest::PostRequest(const std::string& data) {
|
||||
bool UrlRequest::PostRequestChunk(const std::string& data) {
|
||||
request_.assign("POST /");
|
||||
request_.append(socket_.resource_path());
|
||||
request_.append(" HTTP/1.1\r\n");
|
||||
@@ -103,6 +105,31 @@ bool UrlRequest::PostRequest(const std::string& data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UrlRequest::PostRequest(const std::string& data) {
|
||||
request_.assign("POST /");
|
||||
request_.append(socket_.resource_path());
|
||||
request_.append(" HTTP/1.1\r\n");
|
||||
request_.append("Host: ");
|
||||
request_.append(socket_.domain_name());
|
||||
request_.append("\r\nConnection: Keep-Alive\r\n");
|
||||
request_.append("User-Agent: Widevine CDM v1.0\r\n");
|
||||
request_.append("Accept-Encoding: gzip,deflate\r\n");
|
||||
request_.append("Accept-Language: en-us,fr\r\n");
|
||||
request_.append("Accept-Charset: iso-8859-1,*,utf-8\r\n");
|
||||
std::ostringstream ss;
|
||||
ss << data.size();
|
||||
request_.append("Content-Length: ");
|
||||
request_.append(ss.str());
|
||||
request_.append("\r\n\r\n");
|
||||
request_.append(data);
|
||||
|
||||
// terminates with \r\n, then ends with an empty line
|
||||
request_.append("\r\n\r\n");
|
||||
|
||||
socket_.Write(request_.c_str(), request_.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UrlRequest::PostCertRequestInQueryString(const std::string& data) {
|
||||
request_.assign("POST /");
|
||||
request_.append(socket_.resource_path());
|
||||
|
||||
@@ -21,6 +21,7 @@ class UrlRequest {
|
||||
int GetStatusCode(const std::string& response);
|
||||
bool is_connected() const { return is_connected_; }
|
||||
bool PostRequest(const std::string& data);
|
||||
bool PostRequestChunk(const std::string& data);
|
||||
bool PostCertRequestInQueryString(const std::string& data);
|
||||
|
||||
private:
|
||||
|
||||
@@ -24,6 +24,7 @@ class WvContentDecryptionModule {
|
||||
|
||||
// Construct a valid license request.
|
||||
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id,
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
@@ -32,7 +33,12 @@ class WvContentDecryptionModule {
|
||||
|
||||
// Accept license response and extract key info.
|
||||
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data);
|
||||
const CdmKeyResponse& key_data,
|
||||
CdmKeySetId& key_set_id);
|
||||
|
||||
// Setup keys for offline usage which were retrived in an earlier key request
|
||||
virtual CdmResponseType RestoreKey(const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id);
|
||||
|
||||
// Cancel session
|
||||
virtual CdmResponseType CancelKeyRequest(const CdmSessionId& session_id);
|
||||
|
||||
@@ -31,28 +31,48 @@ CdmResponseType WvContentDecryptionModule::CloseSession(
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id,
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
CdmKeySystem key_system;
|
||||
return cdm_engine_->GenerateKeyRequest(session_id, false, key_system,
|
||||
init_data, license_type,
|
||||
app_parameters, key_request,
|
||||
server_url);
|
||||
CdmResponseType sts;
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
sts = cdm_engine_->OpenKeySetSession(key_set_id);
|
||||
if (sts != NO_ERROR)
|
||||
return sts;
|
||||
}
|
||||
sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id,
|
||||
init_data, license_type,
|
||||
app_parameters, key_request,
|
||||
server_url);
|
||||
|
||||
if ((license_type == kLicenseTypeRelease) && (sts != KEY_MESSAGE)) {
|
||||
cdm_engine_->CloseKeySetSession(key_set_id);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::AddKey(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data) {
|
||||
return cdm_engine_->AddKey(session_id, key_data);
|
||||
const CdmKeyResponse& key_data,
|
||||
CdmKeySetId& key_set_id) {
|
||||
CdmResponseType sts = cdm_engine_->AddKey(session_id, key_data, key_set_id);
|
||||
if ((sts == KEY_ADDED) && session_id.empty()) // license type release
|
||||
cdm_engine_->CloseKeySetSession(key_set_id);
|
||||
return sts;
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::RestoreKey(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id) {
|
||||
return cdm_engine_->RestoreKey(session_id, key_set_id);
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::CancelKeyRequest(
|
||||
const CdmSessionId& session_id) {
|
||||
CdmKeySystem key_system;
|
||||
return cdm_engine_->CancelKeyRequest(session_id, false, key_system);
|
||||
return cdm_engine_->CancelKeyRequest(session_id);
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::QueryStatus(
|
||||
|
||||
@@ -36,6 +36,16 @@ const std::string kProductionProvisioningServerUrl =
|
||||
"https://www.googleapis.com/"
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
|
||||
// TODO(edwinwong, rfrias): refactor to set these parameters though config
|
||||
std::string kServerSdkClientAuth = "";
|
||||
wvcdm::KeyId kServerSdkKeyId = wvcdm::a2bs_hex(
|
||||
"000000347073736800000000"
|
||||
"edef8ba979d64acea3c827dcd51d21ed00000014"
|
||||
"0801121030313233343536373839414243444546");
|
||||
std::string kServerSdkLicenseServer = "http://kir03fcpg174.widevine.net/"
|
||||
"widevine/cgi-bin/drm.cgi";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -57,34 +67,45 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
|
||||
protected:
|
||||
void GenerateKeyRequest(const std::string& key_system,
|
||||
const std::string& init_data) {
|
||||
const std::string& init_data,
|
||||
CdmLicenseType license_type) {
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
std::string server_url;
|
||||
EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_,
|
||||
init_data,
|
||||
kLicenseTypeStreaming,
|
||||
app_parameters,
|
||||
&key_msg_,
|
||||
&server_url), wvcdm::KEY_MESSAGE);
|
||||
std::string key_set_id;
|
||||
EXPECT_EQ(wvcdm::KEY_MESSAGE,
|
||||
decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data,
|
||||
license_type, app_parameters,
|
||||
&key_msg_, &server_url));
|
||||
EXPECT_EQ(0, static_cast<int>(server_url.size()));
|
||||
}
|
||||
|
||||
void GenerateRenewalRequest(const std::string& key_system,
|
||||
const std::string& not_used) {
|
||||
CdmLicenseType license_type) {
|
||||
// TODO application makes a license request, CDM will renew the license
|
||||
// when appropriate.
|
||||
std::string init_data;
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
std::string server_url;
|
||||
EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_,
|
||||
init_data,
|
||||
kLicenseTypeStreaming,
|
||||
app_parameters,
|
||||
&key_msg_,
|
||||
&server_url), wvcdm::KEY_MESSAGE);
|
||||
std::string key_set_id;
|
||||
EXPECT_EQ(wvcdm::KEY_MESSAGE,
|
||||
decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data,
|
||||
license_type, app_parameters,
|
||||
&key_msg_, &server_url));
|
||||
EXPECT_NE(0, static_cast<int>(server_url.size()));
|
||||
}
|
||||
|
||||
void GenerateKeyRelease(CdmKeySetId key_set_id) {
|
||||
CdmSessionId session_id;
|
||||
CdmInitData init_data;
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
std::string server_url;
|
||||
EXPECT_EQ(wvcdm::KEY_MESSAGE,
|
||||
decryptor_.GenerateKeyRequest(session_id, key_set_id, init_data,
|
||||
kLicenseTypeRelease, app_parameters,
|
||||
&key_msg_, &server_url));
|
||||
EXPECT_EQ(0, static_cast<int>(server_url.size()));
|
||||
}
|
||||
|
||||
void DumpResponse(const std::string& description,
|
||||
const std::string& response) {
|
||||
if (description.empty())
|
||||
@@ -176,7 +197,13 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
return "";
|
||||
}
|
||||
|
||||
url_request.PostRequest(key_msg_);
|
||||
// TODO(edwinwong, rfrias): need a cleaner solution to handle
|
||||
// HTTP servers that use chunking vs those that do not
|
||||
if (server_url.compare(kServerSdkLicenseServer) == 0)
|
||||
url_request.PostRequest(key_msg_);
|
||||
else
|
||||
url_request.PostRequestChunk(key_msg_);
|
||||
|
||||
std::string http_response;
|
||||
std::string message_body;
|
||||
int resp_bytes = url_request.GetResponse(http_response);
|
||||
@@ -248,20 +275,22 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
std::string resp = GetKeyRequestResponse(server_url,
|
||||
client_auth,
|
||||
200);
|
||||
|
||||
if (is_renewal) {
|
||||
// TODO application makes a license request, CDM will renew the license
|
||||
// when appropriate
|
||||
EXPECT_EQ(decryptor_.AddKey(session_id_, resp), wvcdm::KEY_ADDED);
|
||||
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, key_set_id_),
|
||||
wvcdm::KEY_ADDED);
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(decryptor_.AddKey(session_id_, resp), wvcdm::KEY_ADDED);
|
||||
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, key_set_id_),
|
||||
wvcdm::KEY_ADDED);
|
||||
}
|
||||
}
|
||||
|
||||
wvcdm::WvContentDecryptionModule decryptor_;
|
||||
std::string key_msg_;
|
||||
std::string session_id_;
|
||||
CdmKeyMessage key_msg_;
|
||||
CdmSessionId session_id_;
|
||||
CdmKeySetId key_set_id_;
|
||||
};
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
||||
@@ -304,7 +333,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
GetKeyRequestResponse(g_license_server, g_client_auth, 200);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
@@ -313,31 +342,76 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
|
||||
std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id);
|
||||
GenerateKeyRequest(g_key_system, wrong_message);
|
||||
GenerateKeyRequest(g_key_system, wrong_message, kLicenseTypeStreaming);
|
||||
GetKeyRequestResponse(g_license_server, g_client_auth, 500);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, AddKeyTest) {
|
||||
TEST_F(WvCdmRequestLicenseTest, AddSteamingKeyTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
|
||||
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
|
||||
kServerSdkKeyId, false);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
|
||||
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
|
||||
kServerSdkKeyId, false);
|
||||
CdmKeySetId key_set_id = key_set_id_;
|
||||
EXPECT_FALSE(key_set_id_.empty());
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
session_id_.clear();
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, DISABLED_ReleaseOfflineKeyTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline);
|
||||
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
|
||||
kServerSdkKeyId, false);
|
||||
CdmKeySetId key_set_id = key_set_id_;
|
||||
EXPECT_FALSE(key_set_id_.empty());
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
session_id_.clear();
|
||||
key_set_id_.clear();
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
session_id_.clear();
|
||||
key_set_id_.clear();
|
||||
GenerateKeyRelease(key_set_id);
|
||||
VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth,
|
||||
kServerSdkKeyId, false);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, LicenseRenewal) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
GenerateRenewalRequest(g_key_system, g_key_id);
|
||||
GenerateRenewalRequest(g_key_system, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, true);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
CdmQueryMap query_info;
|
||||
@@ -405,7 +479,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
CdmQueryMap query_info;
|
||||
@@ -425,7 +499,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, ClearDecryptionTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
// key 1, clear, 256b
|
||||
@@ -525,7 +599,7 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionNoKeyTest) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, DecryptionTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
// key 1, encrypted, 256b
|
||||
@@ -576,7 +650,7 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionTest) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
uint8_t data_blocks = 2;
|
||||
@@ -657,7 +731,7 @@ TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, PartialBlockDecryptionTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
// key 3, encrypted, 125b, offset 0
|
||||
@@ -700,7 +774,7 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockDecryptionTest) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
// key 3, encrypted, 123b, offset 5
|
||||
@@ -746,7 +820,7 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) {
|
||||
/*
|
||||
TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) {
|
||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
|
||||
DecryptionData data;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -47,7 +47,7 @@ class WVCryptoPluginTest : public Test {
|
||||
};
|
||||
|
||||
TEST_F(WVCryptoPluginTest, CorrectlyReportsSecureBuffers) {
|
||||
MockCDM cdm;
|
||||
StrictMock<MockCDM> cdm;
|
||||
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
|
||||
|
||||
CdmQueryMap l1Map;
|
||||
@@ -71,7 +71,7 @@ TEST_F(WVCryptoPluginTest, CorrectlyReportsSecureBuffers) {
|
||||
}
|
||||
|
||||
TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) {
|
||||
MockCDM cdm;
|
||||
StrictMock<MockCDM> cdm;
|
||||
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
|
||||
|
||||
uint8_t keyId[KEY_ID_SIZE];
|
||||
@@ -182,7 +182,7 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) {
|
||||
}
|
||||
|
||||
TEST_F(WVCryptoPluginTest, CommunicatesSecureBufferRequest) {
|
||||
MockCDM cdm;
|
||||
StrictMock<MockCDM> cdm;
|
||||
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
|
||||
|
||||
uint8_t keyId[KEY_ID_SIZE];
|
||||
|
||||
@@ -50,7 +50,7 @@ class WVDrmPlugin : public android::DrmPlugin,
|
||||
virtual status_t closeSession(const Vector<uint8_t>& sessionId);
|
||||
|
||||
virtual status_t getKeyRequest(
|
||||
const Vector<uint8_t>& sessionId,
|
||||
const Vector<uint8_t>& scope,
|
||||
const Vector<uint8_t>& initData,
|
||||
const String8& mimeType,
|
||||
KeyType keyType,
|
||||
@@ -58,11 +58,11 @@ class WVDrmPlugin : public android::DrmPlugin,
|
||||
Vector<uint8_t>& request,
|
||||
String8& defaultUrl);
|
||||
|
||||
virtual status_t provideKeyResponse(const Vector<uint8_t>& sessionId,
|
||||
virtual status_t provideKeyResponse(const Vector<uint8_t>& scope,
|
||||
const Vector<uint8_t>& response,
|
||||
Vector<uint8_t>& keySetId);
|
||||
|
||||
virtual status_t removeKeys(const Vector<uint8_t>& keySetId);
|
||||
virtual status_t removeKeys(const Vector<uint8_t>& sessionId);
|
||||
|
||||
virtual status_t restoreKeys(const Vector<uint8_t>& sessionId,
|
||||
const Vector<uint8_t>& keySetId);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "WVDrmPlugin.h"
|
||||
|
||||
#include <endian.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -115,7 +116,7 @@ status_t WVDrmPlugin::closeSession(const Vector<uint8_t>& sessionId) {
|
||||
}
|
||||
|
||||
status_t WVDrmPlugin::getKeyRequest(
|
||||
const Vector<uint8_t>& sessionId,
|
||||
const Vector<uint8_t>& scope,
|
||||
const Vector<uint8_t>& initData,
|
||||
const String8& mimeType,
|
||||
KeyType keyType,
|
||||
@@ -123,14 +124,20 @@ status_t WVDrmPlugin::getKeyRequest(
|
||||
Vector<uint8_t>& request,
|
||||
String8& defaultUrl) {
|
||||
CdmLicenseType cdmLicenseType;
|
||||
CdmSessionId cdmSessionId;
|
||||
CdmKeySetId cdmKeySetId;
|
||||
if (keyType == kKeyType_Offline) {
|
||||
cdmLicenseType = kLicenseTypeOffline;
|
||||
cdmSessionId.assign(scope.begin(), scope.end());
|
||||
} else if (keyType == kKeyType_Streaming) {
|
||||
cdmLicenseType = kLicenseTypeStreaming;
|
||||
cdmSessionId.assign(scope.begin(), scope.end());
|
||||
} else if (keyType == kKeyType_Release) {
|
||||
cdmLicenseType = kLicenseTypeRelease;
|
||||
cdmKeySetId.assign(scope.begin(), scope.end());
|
||||
} else {
|
||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||
}
|
||||
CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end());
|
||||
|
||||
// Build PSSH box for PSSH data in initData.
|
||||
static const char psshPrefix[] = {
|
||||
@@ -163,8 +170,8 @@ status_t WVDrmPlugin::getKeyRequest(
|
||||
CdmKeyMessage keyRequest;
|
||||
string cdmDefaultUrl;
|
||||
|
||||
CdmResponseType res = mCDM->GenerateKeyRequest(cdmSessionId, psshBox,
|
||||
cdmLicenseType,
|
||||
CdmResponseType res = mCDM->GenerateKeyRequest(cdmSessionId, cdmKeySetId,
|
||||
psshBox, cdmLicenseType,
|
||||
cdmParameters, &keyRequest,
|
||||
&cdmDefaultUrl);
|
||||
|
||||
@@ -177,31 +184,69 @@ status_t WVDrmPlugin::getKeyRequest(
|
||||
keyRequest.size());
|
||||
}
|
||||
|
||||
return mapAndNotifyOfCdmResponseType(sessionId, res);
|
||||
if (keyType == kKeyType_Release) {
|
||||
// When releasing keys, we do not have a session ID.
|
||||
return mapCdmResponseType(res);
|
||||
} else {
|
||||
// For all other requests, we have a session ID.
|
||||
return mapAndNotifyOfCdmResponseType(scope, res);
|
||||
}
|
||||
}
|
||||
|
||||
status_t WVDrmPlugin::provideKeyResponse(
|
||||
const Vector<uint8_t>& sessionId,
|
||||
const Vector<uint8_t>& scope,
|
||||
const Vector<uint8_t>& response,
|
||||
Vector<uint8_t>& keySetId) {
|
||||
// TODO: return keySetId for persisted offline content
|
||||
CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end());
|
||||
CdmSessionId cdmSessionId;
|
||||
CdmKeyResponse cdmResponse(response.begin(), response.end());
|
||||
CdmKeySetId cdmKeySetId;
|
||||
|
||||
CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse);
|
||||
bool isRequest = (memcmp(scope.array(), SESSION_ID_PREFIX.data(),
|
||||
SESSION_ID_PREFIX.size()) == 0);
|
||||
bool isRelease = (memcmp(scope.array(), KEY_SET_ID_PREFIX.data(),
|
||||
KEY_SET_ID_PREFIX.size()) == 0);
|
||||
|
||||
return mapAndNotifyOfCdmResponseType(sessionId, res);
|
||||
if (isRequest) {
|
||||
cdmSessionId.assign(scope.begin(), scope.end());
|
||||
} else if (isRelease) {
|
||||
cdmKeySetId.assign(scope.begin(), scope.end());
|
||||
} else {
|
||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||
}
|
||||
|
||||
CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, cdmKeySetId);
|
||||
|
||||
if (isRequest && isCdmResponseTypeSuccess(res)) {
|
||||
keySetId.clear();
|
||||
keySetId.appendArray(reinterpret_cast<const uint8_t*>(cdmKeySetId.data()),
|
||||
cdmKeySetId.size());
|
||||
}
|
||||
|
||||
if (isRelease) {
|
||||
// When releasing keys, we do not have a session ID.
|
||||
return mapCdmResponseType(res);
|
||||
} else {
|
||||
// For all other requests, we have a session ID.
|
||||
return mapAndNotifyOfCdmResponseType(scope, res);
|
||||
}
|
||||
}
|
||||
|
||||
status_t WVDrmPlugin::removeKeys(const Vector<uint8_t>& keySetId) {
|
||||
// TODO: remove persisted offline keys associated with keySetId
|
||||
return android::ERROR_UNSUPPORTED;
|
||||
status_t WVDrmPlugin::removeKeys(const Vector<uint8_t>& sessionId) {
|
||||
CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end());
|
||||
|
||||
CdmResponseType res = mCDM->CancelKeyRequest(cdmSessionId);
|
||||
|
||||
return mapAndNotifyOfCdmResponseType(sessionId, res);
|
||||
}
|
||||
|
||||
status_t WVDrmPlugin::restoreKeys(const Vector<uint8_t>& sessionId,
|
||||
const Vector<uint8_t>& keySetId) {
|
||||
// TODO: restore persisted offline keys associated with keySetId
|
||||
return android::ERROR_UNSUPPORTED;
|
||||
CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end());
|
||||
CdmKeySetId cdmKeySetId(keySetId.begin(), keySetId.end());
|
||||
|
||||
CdmResponseType res = mCDM->RestoreKey(cdmSessionId, cdmKeySetId);
|
||||
|
||||
return mapAndNotifyOfCdmResponseType(sessionId, res);
|
||||
}
|
||||
|
||||
status_t WVDrmPlugin::queryKeyStatus(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
@@ -27,17 +28,22 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
|
||||
MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&));
|
||||
|
||||
MOCK_METHOD6(GenerateKeyRequest, CdmResponseType(const CdmSessionId&,
|
||||
MOCK_METHOD7(GenerateKeyRequest, CdmResponseType(const CdmSessionId&,
|
||||
const CdmKeySetId&,
|
||||
const CdmInitData&,
|
||||
const CdmLicenseType,
|
||||
CdmAppParameterMap&,
|
||||
CdmKeyMessage*, string*));
|
||||
|
||||
MOCK_METHOD2(AddKey, CdmResponseType(const CdmSessionId&,
|
||||
const CdmKeyResponse&));
|
||||
MOCK_METHOD3(AddKey, CdmResponseType(const CdmSessionId&,
|
||||
const CdmKeyResponse&,
|
||||
CdmKeySetId&));
|
||||
|
||||
MOCK_METHOD1(CancelKeyRequest, CdmResponseType(const CdmSessionId&));
|
||||
|
||||
MOCK_METHOD2(RestoreKey, CdmResponseType(const CdmSessionId&,
|
||||
const CdmKeySetId&));
|
||||
|
||||
MOCK_METHOD1(QueryStatus, CdmResponseType(CdmQueryMap*));
|
||||
|
||||
MOCK_METHOD2(QueryKeyStatus, CdmResponseType(const CdmSessionId&,
|
||||
@@ -111,6 +117,7 @@ class WVDrmPluginTest : public Test {
|
||||
fread(sessionIdRaw, sizeof(uint8_t), kSessionIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
memcpy(sessionIdRaw, SESSION_ID_PREFIX.data(), SESSION_ID_PREFIX.size());
|
||||
sessionId.appendArray(sessionIdRaw, kSessionIdSize);
|
||||
cdmSessionId.assign(sessionId.begin(), sessionId.end());
|
||||
|
||||
@@ -122,8 +129,8 @@ class WVDrmPluginTest : public Test {
|
||||
};
|
||||
|
||||
TEST_F(WVDrmPluginTest, OpensSessions) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
||||
@@ -149,8 +156,8 @@ TEST_F(WVDrmPluginTest, OpensSessions) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, ClosesSessions) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
EXPECT_CALL(cdm, CloseSession(cdmSessionId))
|
||||
@@ -162,19 +169,27 @@ TEST_F(WVDrmPluginTest, ClosesSessions) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const size_t kInitDataSize = 128;
|
||||
uint8_t initDataRaw[kInitDataSize];
|
||||
static const size_t kRequestSize = 256;
|
||||
uint8_t requestRaw[kRequestSize];
|
||||
static const uint32_t kKeySetIdSize = 32;
|
||||
uint8_t keySetIdRaw[kKeySetIdSize];
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
fread(initDataRaw, sizeof(uint8_t), kInitDataSize, fp);
|
||||
fread(requestRaw, sizeof(uint8_t), kRequestSize, fp);
|
||||
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX.data(), KEY_SET_ID_PREFIX.size());
|
||||
CdmKeySetId cdmKeySetId(reinterpret_cast<char *>(keySetIdRaw), kKeySetIdSize);
|
||||
Vector<uint8_t> keySetId;
|
||||
keySetId.appendArray(keySetIdRaw, kKeySetIdSize);
|
||||
|
||||
Vector<uint8_t> initData;
|
||||
initData.appendArray(initDataRaw, kInitDataSize);
|
||||
|
||||
@@ -209,20 +224,28 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
||||
{
|
||||
InSequence calls;
|
||||
|
||||
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId,
|
||||
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "",
|
||||
ElementsAreArray(psshBox, kPsshBoxSize),
|
||||
kLicenseTypeOffline, cdmParameters, _,
|
||||
_))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmRequest),
|
||||
SetArgPointee<5>(kDefaultUrl),
|
||||
.WillOnce(DoAll(SetArgPointee<5>(cdmRequest),
|
||||
SetArgPointee<6>(kDefaultUrl),
|
||||
Return(wvcdm::KEY_MESSAGE)));
|
||||
|
||||
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId,
|
||||
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "",
|
||||
ElementsAreArray(psshBox, kPsshBoxSize),
|
||||
kLicenseTypeStreaming, cdmParameters, _,
|
||||
_))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmRequest),
|
||||
SetArgPointee<5>(kDefaultUrl),
|
||||
.WillOnce(DoAll(SetArgPointee<5>(cdmRequest),
|
||||
SetArgPointee<6>(kDefaultUrl),
|
||||
Return(wvcdm::KEY_MESSAGE)));
|
||||
|
||||
EXPECT_CALL(cdm, GenerateKeyRequest("", cdmKeySetId,
|
||||
ElementsAreArray(psshBox, kPsshBoxSize),
|
||||
kLicenseTypeRelease, cdmParameters, _,
|
||||
_))
|
||||
.WillOnce(DoAll(SetArgPointee<5>(cdmRequest),
|
||||
SetArgPointee<6>(kDefaultUrl),
|
||||
Return(wvcdm::KEY_MESSAGE)));
|
||||
}
|
||||
|
||||
@@ -233,7 +256,6 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
||||
String8("video/h264"),
|
||||
DrmPlugin::kKeyType_Offline,
|
||||
parameters, request, defaultUrl);
|
||||
|
||||
ASSERT_EQ(OK, res);
|
||||
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
||||
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
||||
@@ -241,44 +263,96 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
||||
res = plugin.getKeyRequest(sessionId, initData, String8("video/h264"),
|
||||
DrmPlugin::kKeyType_Streaming, parameters,
|
||||
request, defaultUrl);
|
||||
ASSERT_EQ(OK, res);
|
||||
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
||||
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
||||
|
||||
res = plugin.getKeyRequest(keySetId, initData, String8("video/h264"),
|
||||
DrmPlugin::kKeyType_Release, parameters,
|
||||
request, defaultUrl);
|
||||
ASSERT_EQ(OK, res);
|
||||
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
||||
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, AddsKeys) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const uint32_t kResponseSize = 256;
|
||||
uint8_t responseRaw[kResponseSize];
|
||||
static const uint32_t kKeySetIdSize = 32;
|
||||
uint8_t keySetIdRaw[kKeySetIdSize];
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
fread(responseRaw, sizeof(uint8_t), kResponseSize, fp);
|
||||
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
Vector<uint8_t> response;
|
||||
response.appendArray(responseRaw, kResponseSize);
|
||||
|
||||
// TODO: Do something with the key set ID.
|
||||
Vector<uint8_t> ignoredKeySetId;
|
||||
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX.data(), KEY_SET_ID_PREFIX.size());
|
||||
CdmKeySetId cdmKeySetId(reinterpret_cast<char *>(keySetIdRaw), kKeySetIdSize);
|
||||
Vector<uint8_t> keySetId;
|
||||
|
||||
EXPECT_CALL(cdm, AddKey(cdmSessionId, ElementsAreArray(responseRaw,
|
||||
kResponseSize)))
|
||||
.WillOnce(Return(wvcdm::KEY_ADDED));
|
||||
Vector<uint8_t> emptyKeySetId;
|
||||
|
||||
status_t res = plugin.provideKeyResponse(sessionId, response,
|
||||
ignoredKeySetId);
|
||||
EXPECT_CALL(cdm, AddKey(cdmSessionId,
|
||||
ElementsAreArray(responseRaw, kResponseSize), _))
|
||||
.WillOnce(DoAll(SetArgReferee<2>(cdmKeySetId),
|
||||
Return(wvcdm::KEY_ADDED)));
|
||||
|
||||
EXPECT_CALL(cdm, AddKey("", ElementsAreArray(responseRaw, kResponseSize),
|
||||
cdmKeySetId))
|
||||
.Times(1);
|
||||
|
||||
status_t res = plugin.provideKeyResponse(sessionId, response, keySetId);
|
||||
ASSERT_EQ(OK, res);
|
||||
ASSERT_THAT(keySetId, ElementsAreArray(keySetIdRaw, kKeySetIdSize));
|
||||
|
||||
res = plugin.provideKeyResponse(keySetId, response, emptyKeySetId);
|
||||
ASSERT_EQ(OK, res);
|
||||
EXPECT_EQ(0u, emptyKeySetId.size());
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, CancelsKeyRequests) {
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
EXPECT_CALL(cdm, CancelKeyRequest(cdmSessionId))
|
||||
.Times(1);
|
||||
|
||||
status_t res = plugin.removeKeys(sessionId);
|
||||
ASSERT_EQ(OK, res);
|
||||
}
|
||||
|
||||
// TODO: Reinstate removeKeys() test once its behavior is finalized.
|
||||
TEST_F(WVDrmPluginTest, RestoresKeys) {
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const size_t kKeySetIdSize = 32;
|
||||
uint8_t keySetIdRaw[kKeySetIdSize];
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
Vector<uint8_t> keySetId;
|
||||
keySetId.appendArray(keySetIdRaw, kKeySetIdSize);
|
||||
|
||||
EXPECT_CALL(cdm, RestoreKey(cdmSessionId,
|
||||
ElementsAreArray(keySetIdRaw, kKeySetIdSize)))
|
||||
.Times(1);
|
||||
|
||||
status_t res = plugin.restoreKeys(sessionId, keySetId);
|
||||
ASSERT_EQ(OK, res);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, QueriesKeyStatus) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
KeyedVector<String8, String8> expectedLicenseStatus;
|
||||
@@ -293,7 +367,7 @@ TEST_F(WVDrmPluginTest, QueriesKeyStatus) {
|
||||
|
||||
EXPECT_CALL(cdm, QueryKeyStatus(cdmSessionId, _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(cdmLicenseStatus),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
|
||||
KeyedVector<String8, String8> licenseStatus;
|
||||
|
||||
@@ -310,8 +384,8 @@ TEST_F(WVDrmPluginTest, QueriesKeyStatus) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, GetsProvisioningRequests) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const uint32_t kRequestSize = 256;
|
||||
@@ -340,8 +414,8 @@ TEST_F(WVDrmPluginTest, GetsProvisioningRequests) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const uint32_t kResponseSize = 512;
|
||||
@@ -363,8 +437,8 @@ TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, GetsSecureStops) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const uint32_t kStopSize = 53;
|
||||
@@ -405,8 +479,8 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, ReleasesSecureStops) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const uint32_t kMessageSize = 128;
|
||||
@@ -428,8 +502,8 @@ TEST_F(WVDrmPluginTest, ReleasesSecureStops) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
CdmQueryMap l1Map;
|
||||
@@ -493,8 +567,8 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
String8 stringResult;
|
||||
@@ -512,8 +586,8 @@ TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, DoesNotSetProperties) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const uint32_t kValueSize = 32;
|
||||
@@ -534,8 +608,8 @@ TEST_F(WVDrmPluginTest, DoesNotSetProperties) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
Vector<uint8_t> keyId;
|
||||
@@ -548,7 +622,7 @@ TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) {
|
||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||
.Times(AtLeast(1))
|
||||
@@ -591,8 +665,8 @@ MATCHER_P(IsIV, iv, "") {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, CallsGenericEncrypt) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const size_t kDataSize = 256;
|
||||
@@ -631,7 +705,7 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) {
|
||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||
.Times(AtLeast(1))
|
||||
@@ -655,8 +729,8 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, CallsGenericDecrypt) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const size_t kDataSize = 256;
|
||||
@@ -695,7 +769,7 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) {
|
||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||
.Times(AtLeast(1))
|
||||
@@ -719,8 +793,8 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, CallsGenericSign) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const size_t kDataSize = 256;
|
||||
@@ -761,7 +835,7 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) {
|
||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||
.Times(AtLeast(1))
|
||||
@@ -785,8 +859,8 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, CallsGenericVerify) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
static const size_t kDataSize = 256;
|
||||
@@ -837,7 +911,7 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) {
|
||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||
.Times(AtLeast(1))
|
||||
@@ -866,8 +940,8 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, RegistersForEvents) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
EXPECT_CALL(cdm, AttachEventListener(cdmSessionId, &plugin))
|
||||
@@ -877,7 +951,7 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) {
|
||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||
.Times(AtLeast(1))
|
||||
@@ -892,8 +966,8 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
|
||||
{
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
@@ -939,8 +1013,8 @@ TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) {
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, MarshalsEvents) {
|
||||
MockCDM cdm;
|
||||
MockCrypto crypto;
|
||||
StrictMock<MockCDM> cdm;
|
||||
StrictMock<MockCrypto> crypto;
|
||||
WVDrmPlugin plugin(&cdm, &crypto);
|
||||
|
||||
sp<MockDrmPluginListener> listener = new MockDrmPluginListener();
|
||||
@@ -965,7 +1039,7 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) {
|
||||
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
||||
.Times(AtLeast(1))
|
||||
|
||||
@@ -323,8 +323,8 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message,
|
||||
}
|
||||
|
||||
bool SessionContext::ParseKeyControl(
|
||||
const std::vector<uint8_t>& key_control_string,
|
||||
KeyControlBlock& key_control_block) {
|
||||
const std::vector<uint8_t>& key_control_string,
|
||||
KeyControlBlock& key_control_block) {
|
||||
|
||||
key_control_block.Invalidate();
|
||||
|
||||
@@ -337,15 +337,6 @@ bool SessionContext::ParseKeyControl(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!key_control_block.Validate()) {
|
||||
LOGE("KCB: BAD Signature");
|
||||
return false;
|
||||
}
|
||||
if (!CheckNonce(key_control_block.nonce())) {
|
||||
LOGE("KCB: BAD Nonce");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("KCB:");
|
||||
LOGD(" valid: %d", key_control_block.valid());
|
||||
LOGD(" duration: %d", key_control_block.duration());
|
||||
@@ -375,6 +366,16 @@ bool SessionContext::ParseKeyControl(
|
||||
const char* cgms_values[4] = {"free", "BAD", "once", "never"};
|
||||
LOGD(" CGMS = %s", cgms_values[cgms_bits]);
|
||||
|
||||
if (!key_control_block.Validate()) {
|
||||
LOGE("KCB: BAD Signature");
|
||||
return false;
|
||||
}
|
||||
if ((key_control_block.control_bits() & kControlNonceEnabled)
|
||||
&& (!CheckNonce(key_control_block.nonce()))) {
|
||||
LOGE("KCB: BAD Nonce");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
//
|
||||
// OEMCrypto unit tests
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_keybox.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
static wvoec_mock::WidevineKeybox kValidKeybox02 = {
|
||||
// Sample keybox used for test vectors
|
||||
{
|
||||
// deviceID
|
||||
0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey02
|
||||
0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
|
||||
}, {
|
||||
// key
|
||||
0x76, 0x5d, 0xce, 0x01, 0x04, 0x89, 0xb3, 0xd0,
|
||||
0xdf, 0xce, 0x54, 0x8a, 0x49, 0xda, 0xdc, 0xb6,
|
||||
}, {
|
||||
// data
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19,
|
||||
0x92, 0x27, 0x0b, 0x1f, 0x1a, 0xd5, 0xc6, 0x93,
|
||||
0x19, 0x3f, 0xaa, 0x74, 0x1f, 0xdd, 0x5f, 0xb4,
|
||||
0xe9, 0x40, 0x2f, 0x34, 0xa4, 0x92, 0xf4, 0xae,
|
||||
0x9a, 0x52, 0x39, 0xbc, 0xb7, 0x24, 0x38, 0x13,
|
||||
0xab, 0xf4, 0x92, 0x96, 0xc4, 0x81, 0x60, 0x33,
|
||||
0xd8, 0xb8, 0x09, 0xc7, 0x55, 0x0e, 0x12, 0xfa,
|
||||
0xa8, 0x98, 0x62, 0x8a, 0xec, 0xea, 0x74, 0x8a,
|
||||
0x4b, 0xfa, 0x5a, 0x9e, 0xb6, 0x49, 0x0d, 0x80,
|
||||
}, {
|
||||
// magic
|
||||
0x6b, 0x62, 0x6f, 0x78,
|
||||
}, {
|
||||
// Crc
|
||||
0x2a, 0x3b, 0x3e, 0xe4,
|
||||
}
|
||||
};
|
||||
|
||||
static wvoec_mock::WidevineKeybox kValidKeybox03 = {
|
||||
// Sample keybox used for test vectors
|
||||
{
|
||||
// deviceID
|
||||
0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey03
|
||||
0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
|
||||
}, {
|
||||
// key
|
||||
0x25, 0xe5, 0x2a, 0x02, 0x29, 0x68, 0x04, 0xa2,
|
||||
0x92, 0xfd, 0x7c, 0x67, 0x0b, 0x67, 0x1f, 0x31,
|
||||
}, {
|
||||
// data
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19,
|
||||
0xf4, 0x0a, 0x0e, 0xa2, 0x0a, 0x71, 0xd5, 0x92,
|
||||
0xfa, 0xa3, 0x25, 0xc6, 0x4b, 0x76, 0xf1, 0x64,
|
||||
0xf4, 0x60, 0xa0, 0x30, 0x72, 0x23, 0xbe, 0x03,
|
||||
0xcd, 0xde, 0x7a, 0x06, 0xd4, 0x01, 0xeb, 0xdc,
|
||||
0xe0, 0x50, 0xc0, 0x53, 0x0a, 0x50, 0xb0, 0x37,
|
||||
0xe5, 0x05, 0x25, 0x0e, 0xa4, 0xc8, 0x5a, 0xff,
|
||||
0x46, 0x6e, 0xa5, 0x31, 0xf3, 0xdd, 0x94, 0xb7,
|
||||
0xe0, 0xd3, 0xf9, 0x04, 0xb2, 0x54, 0xb1, 0x64,
|
||||
}, {
|
||||
// magic
|
||||
0x6b, 0x62, 0x6f, 0x78,
|
||||
}, {
|
||||
// Crc
|
||||
0xa1, 0x99, 0x5f, 0x46,
|
||||
}
|
||||
};
|
||||
|
||||
// Define CAN_INSTALL_KEYBOX if you are compiling with the reference
|
||||
// implementation of OEMCrypto, or if your version of OEMCrypto supports
|
||||
// OEMCrypto_InstallKeybox and OEMCrypto_WrapKeybox.
|
||||
#if defined(CAN_INSTALL_KEYBOX)
|
||||
// The Below tests are based on a specific keybox which is installed for testing.
|
||||
|
||||
class OEMCryptoKeyboxTest : public ::testing::Test {
|
||||
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
}
|
||||
|
||||
void install_keybox(wvoec_mock::WidevineKeybox& keybox, bool good) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize())
|
||||
<< "OEMCrypto_Initialize failed.";
|
||||
OEMCryptoResult sts;
|
||||
uint8_t wrapped[sizeof(wvoec_mock::WidevineKeybox)];
|
||||
size_t length = sizeof(wvoec_mock::WidevineKeybox);
|
||||
sts = OEMCrypto_WrapKeybox(reinterpret_cast<uint8_t*>(&keybox),
|
||||
sizeof(keybox),
|
||||
wrapped,
|
||||
&length,
|
||||
NULL, 0);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox));
|
||||
if( good ) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
} else {
|
||||
// Can return error now, or return error on IsKeyboxValid.
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
OEMCrypto_Terminate();
|
||||
}
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest, DefaultKeybox) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize())
|
||||
<< "OEMCrypto_Initialize failed.";
|
||||
OEMCryptoResult sts;
|
||||
sts = OEMCrypto_IsKeyboxValid();
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest, GoodKeybox) {
|
||||
wvoec_mock::WidevineKeybox keybox = kValidKeybox02;
|
||||
OEMCryptoResult sts;
|
||||
install_keybox(keybox, true);
|
||||
sts = OEMCrypto_IsKeyboxValid();
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
|
||||
keybox = kValidKeybox03;
|
||||
install_keybox(keybox, true);
|
||||
sts = OEMCrypto_IsKeyboxValid();
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest, BadCRCKeybox) {
|
||||
wvoec_mock::WidevineKeybox keybox = kValidKeybox02;
|
||||
keybox.crc_[1] = 42;
|
||||
OEMCryptoResult sts;
|
||||
install_keybox(keybox, false);
|
||||
sts = OEMCrypto_IsKeyboxValid();
|
||||
ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest, BadMagicKeybox) {
|
||||
wvoec_mock::WidevineKeybox keybox = kValidKeybox02;
|
||||
keybox.magic_[1] = 42;
|
||||
OEMCryptoResult sts;
|
||||
install_keybox(keybox, false);
|
||||
sts = OEMCrypto_IsKeyboxValid();
|
||||
ASSERT_EQ(OEMCrypto_ERROR_BAD_MAGIC, sts);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(OEMCryptoKeyboxTest, BadDataKeybox) {
|
||||
wvoec_mock::WidevineKeybox keybox = kValidKeybox02;
|
||||
keybox.data_[1] = 42;
|
||||
OEMCryptoResult sts;
|
||||
install_keybox(keybox, false);
|
||||
sts = OEMCrypto_IsKeyboxValid();
|
||||
ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts);
|
||||
}
|
||||
|
||||
#endif // CAN_INSTALL_KEYBOX
|
||||
|
||||
} // namespace wvoec
|
||||
@@ -934,9 +934,9 @@ class Session {
|
||||
enc_key_ = wvcdm::a2b_hex("D0BFC35DA9E33436E81C4229E78CB9F4");
|
||||
}
|
||||
|
||||
void LoadTestKeys(uint32_t duration, uint32_t control) {
|
||||
void LoadTestKeys(uint32_t duration, uint32_t control, uint32_t nonce) {
|
||||
MessageData data;
|
||||
FillSimpleMessage(&data, duration, control);
|
||||
FillSimpleMessage(&data, duration, control, nonce);
|
||||
MessageData encrypted;
|
||||
EncryptMessage(data, &encrypted);
|
||||
std::vector<uint8_t> signature;
|
||||
@@ -1059,7 +1059,8 @@ class Session {
|
||||
}
|
||||
}
|
||||
|
||||
void FillSimpleMessage(MessageData* data, uint32_t duration, uint32_t control) {
|
||||
void FillSimpleMessage(MessageData* data, uint32_t duration, uint32_t control,
|
||||
uint32_t nonce) {
|
||||
OEMCrypto_GetRandom(data->mac_key_iv, sizeof(data->mac_key_iv));
|
||||
OEMCrypto_GetRandom(data->mac_keys, sizeof(data->mac_keys));
|
||||
for (unsigned int i = 0; i < kNumKeys; i++) {
|
||||
@@ -1072,7 +1073,7 @@ class Session {
|
||||
sizeof(data->keys[i].control_iv));
|
||||
memcpy(data->keys[i].control.verification, "kctl", 4);
|
||||
data->keys[i].control.duration = htonl(duration);
|
||||
data->keys[i].control.nonce = htonl(nonce_);
|
||||
data->keys[i].control.nonce = htonl(nonce);
|
||||
data->keys[i].control.control_bits = htonl(control);
|
||||
}
|
||||
// For the canned decryption content, The first key is:
|
||||
@@ -1918,7 +1919,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyNoNonce) {
|
||||
Session& s = createSession("ONE");
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, 0);
|
||||
s.LoadTestKeys(kDuration, 0, 42);
|
||||
s.close();
|
||||
testTearDown();
|
||||
}
|
||||
@@ -1930,7 +1931,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithNonce) {
|
||||
s.open();
|
||||
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(0, wvoec_mock::kControlNonceEnabled);
|
||||
s.LoadTestKeys(0, wvoec_mock::kControlNonceEnabled, s.get_nonce());
|
||||
s.close();
|
||||
testTearDown();
|
||||
}
|
||||
@@ -1946,7 +1947,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange1) {
|
||||
|
||||
s.GenerateDerivedKeys();
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
@@ -1978,7 +1979,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange2) {
|
||||
|
||||
s.GenerateDerivedKeys();
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
@@ -2011,7 +2012,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange3) {
|
||||
s.GenerateDerivedKeys();
|
||||
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
@@ -2045,7 +2046,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange4) {
|
||||
s.GenerateDerivedKeys();
|
||||
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
@@ -2079,7 +2080,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange5) {
|
||||
s.GenerateDerivedKeys();
|
||||
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
@@ -2113,7 +2114,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange6) {
|
||||
s.GenerateDerivedKeys();
|
||||
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
@@ -2147,7 +2148,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange7) {
|
||||
s.GenerateDerivedKeys();
|
||||
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
@@ -2181,12 +2182,8 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadNonce) {
|
||||
s.GenerateDerivedKeys();
|
||||
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
data.keys[0].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled);
|
||||
data.keys[1].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled);
|
||||
data.keys[2].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled);
|
||||
data.keys[1].control.nonce = 42; // This one is bad.
|
||||
|
||||
s.FillSimpleMessage(&data, 0, wvoec_mock::kControlNonceEnabled,
|
||||
42); // bad nonce.
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
std::vector<uint8_t> signature;
|
||||
@@ -2218,7 +2215,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadVerification) {
|
||||
s.GenerateDerivedKeys();
|
||||
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
data.keys[1].control.verification[2] = 'Z';
|
||||
|
||||
MessageData encrypted;
|
||||
@@ -2252,7 +2249,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeysBadSignature) {
|
||||
s.GenerateDerivedKeys();
|
||||
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
@@ -2287,7 +2284,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeysWithNoDerivedKeys) {
|
||||
// s.GenerateDerivedKeys();
|
||||
|
||||
MessageData data;
|
||||
s.FillSimpleMessage(&data, 0, 0);
|
||||
s.FillSimpleMessage(&data, 0, 0, 0);
|
||||
|
||||
MessageData encrypted;
|
||||
s.EncryptMessage(data, &encrypted);
|
||||
@@ -2321,11 +2318,10 @@ class DISABLED_RefreshKeyTest : public DISABLED_TestKeybox {
|
||||
Session& s = createSession("ONE");
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled);
|
||||
s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce());
|
||||
uint32_t nonce;
|
||||
s.GenerateNonce(&nonce);
|
||||
s.RefreshTestKeys(key_count, wvoec_mock::kControlNonceEnabled, nonce,
|
||||
true);
|
||||
s.RefreshTestKeys(key_count, wvoec_mock::kControlNonceEnabled, nonce, true);
|
||||
s.close();
|
||||
}
|
||||
|
||||
@@ -2333,7 +2329,7 @@ class DISABLED_RefreshKeyTest : public DISABLED_TestKeybox {
|
||||
Session& s = createSession("ONE");
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, 0);
|
||||
s.LoadTestKeys(kDuration, 0, 0);
|
||||
uint32_t nonce;
|
||||
s.GenerateNonce(&nonce);
|
||||
s.RefreshTestKeys(key_count,0, 0, true);
|
||||
@@ -2344,7 +2340,7 @@ class DISABLED_RefreshKeyTest : public DISABLED_TestKeybox {
|
||||
Session& s = createSession("ONE");
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled);
|
||||
s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce());
|
||||
uint32_t nonce = s.get_nonce();
|
||||
s.RefreshTestKeys(key_count, wvoec_mock::kControlNonceEnabled, nonce,
|
||||
false);
|
||||
@@ -2354,7 +2350,7 @@ class DISABLED_RefreshKeyTest : public DISABLED_TestKeybox {
|
||||
Session& s = createSession("ONE");
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled);
|
||||
s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce());
|
||||
uint32_t nonce;
|
||||
s.GenerateNonce(&nonce);
|
||||
nonce = 42;
|
||||
@@ -2395,7 +2391,7 @@ TEST_F(DISABLED_TestKeybox, Decrypt) {
|
||||
s.open();
|
||||
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, 0);
|
||||
s.LoadTestKeys(kDuration, 0, 0);
|
||||
|
||||
// Select the key (from FillSimpleMessage)
|
||||
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
|
||||
@@ -2452,7 +2448,7 @@ TEST_F(DISABLED_TestKeybox, DecryptZeroDuration) {
|
||||
s.open();
|
||||
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(0, 0);
|
||||
s.LoadTestKeys(0, 0, 0);
|
||||
|
||||
// Select the key (from FillSimpleMessage)
|
||||
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
|
||||
@@ -2509,7 +2505,7 @@ TEST_F(DISABLED_TestKeybox, DecryptWithOffset) {
|
||||
s.open();
|
||||
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, 0);
|
||||
s.LoadTestKeys(kDuration, 0, 0);
|
||||
|
||||
// Select the key (from FillSimpleMessage)
|
||||
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
|
||||
@@ -2568,7 +2564,7 @@ TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) {
|
||||
s.open();
|
||||
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, 0);
|
||||
s.LoadTestKeys(kDuration, 0, 0);
|
||||
|
||||
// Select the key (from FillSimpleMessage)
|
||||
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
|
||||
@@ -2658,7 +2654,7 @@ TEST_F(DISABLED_TestKeybox, DecryptSecureToClear) {
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, wvoec_mock::kControlObserveDataPath
|
||||
| wvoec_mock::kControlDataPathSecure);
|
||||
| wvoec_mock::kControlDataPathSecure, 0);
|
||||
|
||||
// Select the key (from FillSimpleMessage)
|
||||
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
|
||||
@@ -2714,7 +2710,7 @@ TEST_F(DISABLED_TestKeybox, KeyDuration) {
|
||||
Session& s = createSession("ONE");
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled);
|
||||
s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce());
|
||||
|
||||
// Select the key (from FillSimpleMessage)
|
||||
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
|
||||
@@ -3168,7 +3164,7 @@ TEST_F(DISABLED_TestKeybox, CertificateDecrypt) {
|
||||
s.open();
|
||||
|
||||
s.InstallRSASessionTestKey(wrapped_rsa_key);
|
||||
s.LoadTestKeys(kDuration, 0);
|
||||
s.LoadTestKeys(kDuration, 0, 0);
|
||||
|
||||
// Select the key (from FillSimpleMessage)
|
||||
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
|
||||
@@ -3228,7 +3224,7 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox {
|
||||
|
||||
|
||||
void MakeFourKeys(Session* s) {
|
||||
s->FillSimpleMessage(&message_data_, kDuration, 0);
|
||||
s->FillSimpleMessage(&message_data_, kDuration, 0, 0);
|
||||
message_data_.keys[0].control.control_bits = htonl(wvoec_mock::kControlAllowEncrypt);
|
||||
message_data_.keys[1].control.control_bits = htonl(wvoec_mock::kControlAllowDecrypt);
|
||||
message_data_.keys[2].control.control_bits = htonl(wvoec_mock::kControlAllowSign);
|
||||
|
||||
Reference in New Issue
Block a user