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:
Jeff Tinker
2013-05-15 19:23:36 -07:00
parent 898d870126
commit 1b295f4c81
30 changed files with 1647 additions and 471 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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, &current_time, sizeof(int64_t));
SHA256_Update(&sha256, &random, sizeof(random));
SHA256_Final(&hash[0], &sha256);
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
hash[i%(SHA256_DIGEST_LENGTH/4)] ^= hash[i];
}
hash.resize(SHA256_DIGEST_LENGTH/4);
key_set_id = KEY_SET_ID_PREFIX + b2a_hex(hash);
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);
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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