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:
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "buffer_reader.h"
|
||||
#include "cdm_session.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_engine.h"
|
||||
#include "device_files.h"
|
||||
#include "license_protocol.pb.h"
|
||||
@@ -35,10 +36,13 @@ using video_widevine_server::sdk::ProvisioningRequest;
|
||||
using video_widevine_server::sdk::ProvisioningResponse;
|
||||
using video_widevine_server::sdk::SignedProvisioningMessage;
|
||||
|
||||
typedef std::map<CdmSessionId,CdmSession*>::const_iterator CdmSessionIter;
|
||||
typedef std::map<CdmSessionId, CdmSession*>::const_iterator CdmSessionIter;
|
||||
typedef std::map<CdmKeySetId, CdmSessionId>::iterator CdmReleaseKeySetIter;
|
||||
|
||||
CdmEngine::CdmEngine() : provisioning_session_(NULL) {
|
||||
Properties::Init();
|
||||
Clock clock;
|
||||
srand(static_cast<int>(clock.GetCurrentTime() & 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
CdmEngine::~CdmEngine() {
|
||||
@@ -65,23 +69,22 @@ CdmResponseType CdmEngine::OpenSession(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): Save key_system in session for validation checks
|
||||
CdmSession* new_session = new CdmSession();
|
||||
if (!new_session) {
|
||||
LOGE("CdmEngine::OpenSession: session creation failed");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (new_session->session_id().empty()) {
|
||||
CdmSessionId new_session_id = new_session->session_id();
|
||||
|
||||
if (new_session_id.empty()) {
|
||||
LOGE("CdmEngine::OpenSession: failure to generate session ID");
|
||||
delete(new_session);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionId new_session_id = new_session->session_id();
|
||||
|
||||
CdmResponseType sts = new_session->Init();
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("CdmEngine::OpenSession: bad session init");
|
||||
delete(new_session);
|
||||
return sts;
|
||||
}
|
||||
@@ -91,6 +94,24 @@ CdmResponseType CdmEngine::OpenSession(
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::OpenKeySetSession");
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::OpenKeySetSession: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionId session_id;
|
||||
CdmResponseType sts = OpenSession(KEY_SYSTEM, &session_id);
|
||||
|
||||
if (sts != NO_ERROR)
|
||||
return sts;
|
||||
|
||||
release_key_sets_[key_set_id] = session_id;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::CloseSession");
|
||||
|
||||
@@ -108,10 +129,24 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::CloseKeySetSession");
|
||||
|
||||
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
|
||||
key_set_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType sts = CloseSession(iter->second);
|
||||
release_key_sets_.erase(iter);
|
||||
return sts;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
const CdmSessionId& session_id,
|
||||
bool is_key_system_present,
|
||||
const CdmKeySystem& key_system,
|
||||
const CdmKeySetId& key_set_id,
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
@@ -119,14 +154,36 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
std::string* server_url) {
|
||||
LOGI("CdmEngine::GenerateKeyRequest");
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
CdmSessionId id = session_id;
|
||||
CdmResponseType sts;
|
||||
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
if (key_set_id.empty()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: invalid key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!session_id.empty()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: invalid session ID = %s",
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
}
|
||||
|
||||
if (is_key_system_present) {
|
||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
||||
CdmSessionIter iter = sessions_.find(id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
|
||||
id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
@@ -136,33 +193,60 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
|
||||
key_request->clear();
|
||||
|
||||
// TODO(edwinwong, rfrias): need to pass in license type and app parameters
|
||||
CdmResponseType sts = iter->second->GenerateKeyRequest(init_data,
|
||||
license_type,
|
||||
app_parameters,
|
||||
key_request,
|
||||
server_url);
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
|
||||
if (sts != KEY_ADDED) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
|
||||
"sts = %d", (int)sts);
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
|
||||
sts = iter->second->GenerateKeyRequest(init_data, license_type,
|
||||
app_parameters, key_request,
|
||||
server_url);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d",
|
||||
(int)sts);
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||
"sts = %d", (int)sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): persist init_data, license_type, app_parameters
|
||||
// in session
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
OnKeyReleaseEvent(key_set_id);
|
||||
}
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::AddKey(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data) {
|
||||
const CdmKeyResponse& key_data,
|
||||
CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::AddKey");
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionId id = session_id;
|
||||
bool license_type_release = session_id.empty();
|
||||
|
||||
if (license_type_release) {
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::AddKey: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
}
|
||||
|
||||
CdmSessionIter iter = sessions_.find(id);
|
||||
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::AddKey: session_id not found = %s", session_id.c_str());
|
||||
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -171,18 +255,41 @@ CdmResponseType CdmEngine::AddKey(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->AddKey(key_data);
|
||||
CdmResponseType sts = iter->second->AddKey(key_data, &key_set_id);
|
||||
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts);
|
||||
return sts;
|
||||
}
|
||||
EnablePolicyTimer();
|
||||
return sts;
|
||||
|
||||
if (!license_type_release) {
|
||||
EnablePolicyTimer();
|
||||
}
|
||||
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::CancelKeyRequest(
|
||||
CdmResponseType CdmEngine::RestoreKey(
|
||||
const CdmSessionId& session_id,
|
||||
bool is_key_system_present,
|
||||
const CdmKeySystem& key_system) {
|
||||
const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::RestoreKey");
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::RestoreKey: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
return iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::CancelKeyRequest");
|
||||
|
||||
//TODO(gmorgan): Issue: what is semantics of canceling a key request. Should
|
||||
@@ -197,10 +304,6 @@ CdmResponseType CdmEngine::CancelKeyRequest(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (is_key_system_present) {
|
||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): unload keys here
|
||||
DisablePolicyTimer();
|
||||
return NO_ERROR;
|
||||
@@ -747,37 +850,64 @@ bool CdmEngine::ExtractWidevinePssh(
|
||||
while (1) {
|
||||
// size of PSSH atom, used for skipping
|
||||
uint32_t size;
|
||||
if (!reader.Read4(&size)) return false;
|
||||
if (!reader.Read4(&size)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
|
||||
return false;
|
||||
}
|
||||
|
||||
// "pssh"
|
||||
std::vector<uint8_t> pssh;
|
||||
if (!reader.ReadVec(&pssh, 4)) return false;
|
||||
if (memcmp(&pssh[0], "pssh", 4)) return false;
|
||||
if (!reader.ReadVec(&pssh, 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH literal");
|
||||
return false;
|
||||
}
|
||||
if (memcmp(&pssh[0], "pssh", 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
// flags
|
||||
uint32_t flags;
|
||||
if (!reader.Read4(&flags)) return false;
|
||||
if (flags != 0) return false;
|
||||
if (!reader.Read4(&flags)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH flags");
|
||||
return false;
|
||||
}
|
||||
if (flags != 0) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH flags not zero");
|
||||
return false;
|
||||
}
|
||||
|
||||
// system id
|
||||
std::vector<uint8_t> system_id;
|
||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) return false;
|
||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(&system_id[0], kWidevineSystemId,
|
||||
sizeof(kWidevineSystemId))) {
|
||||
// skip the remaining contents of the atom,
|
||||
// after size field, atom name, flags and system id
|
||||
if (!reader.SkipBytes(
|
||||
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) return false;
|
||||
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// size of PSSH box
|
||||
uint32_t pssh_length;
|
||||
if (!reader.Read4(&pssh_length)) return false;
|
||||
if (!reader.Read4(&pssh_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
|
||||
return false;
|
||||
}
|
||||
|
||||
output->clear();
|
||||
if (!reader.ReadString(output, pssh_length)) return false;
|
||||
if (!reader.ReadString(output, pssh_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -806,4 +936,12 @@ void CdmEngine::OnTimerEvent() {
|
||||
}
|
||||
}
|
||||
|
||||
void CdmEngine::OnKeyReleaseEvent(CdmKeySetId key_set_id) {
|
||||
|
||||
for (CdmSessionIter iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnKeyReleaseEvent(key_set_id);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "clock.h"
|
||||
#include "cdm_engine.h"
|
||||
#include "crypto_engine.h"
|
||||
#include "device_files.h"
|
||||
#include "log.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
@@ -28,17 +30,22 @@ CdmResponseType CdmSession::Init() {
|
||||
|
||||
crypto_session_ = crypto_engine->CreateSession(session_id_);
|
||||
if (!crypto_session_) {
|
||||
LOGE("CdmSession::Init crypto session creation failure");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
std::string token;
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (!LoadDeviceCertificate(&token, &wrapped_key_))
|
||||
if (!LoadDeviceCertificate(&token, &wrapped_key_)) {
|
||||
LOGE("CdmSession::Init provisioning needed");
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!crypto_engine->GetToken(&token))
|
||||
if (!crypto_engine->GetToken(&token)) {
|
||||
LOGE("CdmSession::Init token retrieval failure");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (license_parser_.Init(token, crypto_session_, &policy_engine_))
|
||||
@@ -60,6 +67,49 @@ bool CdmSession::DestroySession() {
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
const CdmKeySetId& key_set_id,
|
||||
const CdmLicenseType license_type) {
|
||||
key_set_id_ = key_set_id;
|
||||
|
||||
// Retrieve license information from persistent store
|
||||
DeviceFiles::LicenseState license_state;
|
||||
|
||||
if (!DeviceFiles::RetrieveLicense(key_set_id, &license_state,
|
||||
&offline_pssh_data_,
|
||||
&offline_key_request_,
|
||||
&offline_key_response_,
|
||||
&offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_,
|
||||
&offline_release_server_url_)) {
|
||||
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (license_state != DeviceFiles::kLicenseStateActive) {
|
||||
LOGE("CdmSession::Init invalid offline license state = %s", license_state);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (!crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
}
|
||||
|
||||
if (license_type == kLicenseTypeOffline) {
|
||||
if (!license_parser_.RestoreOfflineLicense(offline_key_request_,
|
||||
offline_key_response_,
|
||||
offline_key_renewal_response_))
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
license_received_ = true;
|
||||
license_type_ = license_type;
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
bool CdmSession::VerifySession(const CdmKeySystem& key_system,
|
||||
const CdmInitData& init_data) {
|
||||
// TODO(gmorgan): Compare key_system and init_data with value received
|
||||
@@ -70,7 +120,7 @@ bool CdmSession::VerifySession(const CdmKeySystem& key_system,
|
||||
CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
|
||||
@@ -82,7 +132,6 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
reinitialize_session_ = false;
|
||||
}
|
||||
|
||||
|
||||
if (!crypto_session_) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -93,10 +142,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (license_received_) {
|
||||
license_type_ = license_type;
|
||||
|
||||
if (license_type_ == kLicenseTypeRelease) {
|
||||
return GenerateReleaseRequest(key_request, server_url);
|
||||
}
|
||||
else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request() ?
|
||||
UNKNOWN_ERROR : GenerateRenewalRequest(key_request,
|
||||
server_url);
|
||||
UNKNOWN_ERROR : GenerateRenewalRequest(key_request, server_url);
|
||||
}
|
||||
else {
|
||||
CdmInitData pssh_data;
|
||||
@@ -117,14 +170,22 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
key_request,
|
||||
server_url)) {
|
||||
return KEY_ERROR;
|
||||
} else {
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_pssh_data_ = pssh_data;
|
||||
offline_key_request_ = *key_request;
|
||||
offline_release_server_url_ = *server_url;
|
||||
}
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType CdmSession::AddKey(
|
||||
const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id) {
|
||||
if (!crypto_session_) {
|
||||
LOGW("CdmSession::AddKey: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -135,17 +196,34 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (license_received_) {
|
||||
if (license_type_ == kLicenseTypeRelease) {
|
||||
return ReleaseKey(key_response);
|
||||
}
|
||||
else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request() ?
|
||||
UNKNOWN_ERROR : RenewKey(key_response);
|
||||
}
|
||||
else {
|
||||
CdmResponseType sts = license_parser_.HandleKeyResponse(key_response);
|
||||
|
||||
if (sts == KEY_ADDED)
|
||||
license_received_ = true;
|
||||
if (sts != KEY_ADDED)
|
||||
return sts;
|
||||
|
||||
return sts;
|
||||
license_received_ = true;
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_key_response_ = key_response;
|
||||
key_set_id_ = GenerateKeySetId(offline_pssh_data_);
|
||||
if (!StoreLicense(true)) {
|
||||
LOGE("CdmSession::AddKey: Unable to store license");
|
||||
ReInit();
|
||||
key_set_id_.clear();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
*key_set_id = key_set_id_;
|
||||
return KEY_ADDED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,17 +285,49 @@ CdmResponseType CdmSession::Decrypt(bool is_encrypted,
|
||||
// session keys.
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
if (!license_parser_.PrepareKeyRenewalRequest(key_request,
|
||||
server_url)) {
|
||||
if (!license_parser_.PrepareKeyUpdateRequest(true, key_request, server_url))
|
||||
return KEY_ERROR;
|
||||
} else {
|
||||
return KEY_MESSAGE;
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_key_renewal_request_ = *key_request;
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
// RenewKey() - Accept renewal response and update key info.
|
||||
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
return license_parser_.HandleKeyRenewalResponse(key_response);
|
||||
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(true,
|
||||
key_response);
|
||||
if (sts != KEY_ADDED)
|
||||
return sts;
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_key_renewal_response_ = key_response;
|
||||
if (!StoreLicense(true))
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
if (license_parser_.PrepareKeyUpdateRequest(false, key_request,
|
||||
server_url)) {
|
||||
// Mark license as being released
|
||||
if (!StoreLicense(false))
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// ReleaseKey() - Accept release response and release license.
|
||||
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false,
|
||||
key_response);
|
||||
DeviceFiles::DeleteLicense(key_set_id_);
|
||||
return sts;
|
||||
}
|
||||
|
||||
bool CdmSession::IsKeyValid(const KeyId& key_id) {
|
||||
@@ -227,11 +337,39 @@ bool CdmSession::IsKeyValid(const KeyId& key_id) {
|
||||
}
|
||||
|
||||
CdmSessionId CdmSession::GenerateSessionId() {
|
||||
static const std::string kSessionPrefix("Session");
|
||||
static int session_num = 1;
|
||||
// TODO(rkuroiwa): Want this to be unique. Probably doing Hash(time+init_data)
|
||||
// to get something that is reasonably unique.
|
||||
return kSessionPrefix + IntToString(++session_num);
|
||||
return SESSION_ID_PREFIX + IntToString(++session_num);
|
||||
}
|
||||
|
||||
|
||||
CdmSessionId CdmSession::GenerateKeySetId(CdmInitData& pssh_data) {
|
||||
Clock clock;
|
||||
int64_t current_time = clock.GetCurrentTime();
|
||||
std::string key_set_id;
|
||||
|
||||
while (key_set_id.empty()) {
|
||||
int random = rand();
|
||||
|
||||
std::vector<uint8_t> hash(SHA256_DIGEST_LENGTH, 0);
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
SHA256_Update(&sha256, pssh_data.data(), pssh_data.size());
|
||||
SHA256_Update(&sha256, ¤t_time, sizeof(int64_t));
|
||||
SHA256_Update(&sha256, &random, sizeof(random));
|
||||
SHA256_Final(&hash[0], &sha256);
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
|
||||
hash[i%(SHA256_DIGEST_LENGTH/4)] ^= hash[i];
|
||||
}
|
||||
hash.resize(SHA256_DIGEST_LENGTH/4);
|
||||
key_set_id = KEY_SET_ID_PREFIX + b2a_hex(hash);
|
||||
|
||||
if (DeviceFiles::LicenseExists(key_set_id)) { // key set collision
|
||||
key_set_id.clear();
|
||||
}
|
||||
}
|
||||
return key_set_id;
|
||||
}
|
||||
|
||||
bool CdmSession::LoadDeviceCertificate(std::string* certificate,
|
||||
@@ -240,6 +378,18 @@ bool CdmSession::LoadDeviceCertificate(std::string* certificate,
|
||||
wrapped_key);
|
||||
}
|
||||
|
||||
bool CdmSession::StoreLicense(bool active) {
|
||||
DeviceFiles::LicenseState state = DeviceFiles::kLicenseStateReleasing;
|
||||
if (active)
|
||||
state = DeviceFiles::kLicenseStateActive;
|
||||
|
||||
return DeviceFiles::StoreLicense(key_set_id_, state, offline_pssh_data_,
|
||||
offline_key_request_, offline_key_response_,
|
||||
offline_key_renewal_request_,
|
||||
offline_key_renewal_response_,
|
||||
offline_release_server_url_);
|
||||
}
|
||||
|
||||
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
||||
std::pair<CdmEventListenerIter, bool> result = listeners_.insert(listener);
|
||||
return result.second;
|
||||
@@ -258,7 +408,22 @@ void CdmSession::OnTimerEvent() {
|
||||
if (event_occurred) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
(*iter)->onEvent(session_id(), event);
|
||||
CdmSessionId id = (*iter)->session_id();
|
||||
if (id.empty() || (id.compare(session_id_) == 0)) {
|
||||
(*iter)->onEvent(session_id_, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CdmSession::OnKeyReleaseEvent(CdmKeySetId key_set_id) {
|
||||
if (key_set_id_.compare(key_set_id) == 0) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
CdmSessionId id = (*iter)->session_id();
|
||||
if (id.empty() || (id.compare(session_id_) == 0)) {
|
||||
(*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,14 @@ namespace wvcdm {
|
||||
const char* DeviceFiles::kBasePath = "/data/mediadrm/IDM";
|
||||
const char* DeviceFiles::kPathDelimiter = "/";
|
||||
const char* DeviceFiles::kDeviceCertificateFileName = "cert.bin";
|
||||
const char* DeviceFiles::kLicenseFileNameExt = ".lic";
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_client::sdk::DeviceCertificate;
|
||||
using video_widevine_client::sdk::HashedFile;
|
||||
using video_widevine_client::sdk::License;
|
||||
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
|
||||
using video_widevine_client::sdk::License_LicenseState_RELEASING;
|
||||
|
||||
bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
const std::string& wrapped_private_key) {
|
||||
@@ -107,6 +111,148 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreLicense(
|
||||
const std::string& key_set_id,
|
||||
const LicenseState state,
|
||||
const CdmInitData& pssh_data,
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_message,
|
||||
const CdmKeyMessage& license_renewal_request,
|
||||
const CdmKeyResponse& license_renewal,
|
||||
const std::string& release_server_url) {
|
||||
// Fill in file information
|
||||
video_widevine_client::sdk::File file;
|
||||
|
||||
file.set_type(video_widevine_client::sdk::File::LICENSE);
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
|
||||
License* license = file.mutable_license();
|
||||
switch(state) {
|
||||
case kLicenseStateActive:
|
||||
license->set_state(License_LicenseState_ACTIVE);
|
||||
break;
|
||||
case kLicenseStateReleasing:
|
||||
license->set_state(License_LicenseState_RELEASING);
|
||||
break;
|
||||
default:
|
||||
LOGW("DeviceFiles::StoreLicense: Unknown license state: %u", state);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
license->set_pssh_data(pssh_data);
|
||||
license->set_license_request(license_request);
|
||||
license->set_license(license_message);
|
||||
license->set_renewal_request(license_renewal_request);
|
||||
license->set_renewal(license_renewal);
|
||||
license->set_release_server_url(release_server_url);
|
||||
|
||||
std::string serialized_string;
|
||||
file.SerializeToString(&serialized_string);
|
||||
|
||||
// calculate SHA hash
|
||||
std::string hash;
|
||||
if (!Hash(serialized_string, &hash)) {
|
||||
LOGW("DeviceFiles::StoreLicense: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// File in hashed file data
|
||||
HashedFile hashed_file;
|
||||
hashed_file.set_file(serialized_string);
|
||||
hashed_file.set_hash(hash);
|
||||
|
||||
hashed_file.SerializeToString(&serialized_string);
|
||||
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
return StoreFile(file_name.c_str(), serialized_string);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveLicense(
|
||||
const std::string& key_set_id,
|
||||
LicenseState* state,
|
||||
CdmInitData* pssh_data,
|
||||
CdmKeyMessage* license_request,
|
||||
CdmKeyResponse* license_message,
|
||||
CdmKeyMessage* license_renewal_request,
|
||||
CdmKeyResponse* license_renewal,
|
||||
std::string* release_server_url) {
|
||||
std::string serialized_hashed_file;
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
if (!RetrieveFile(file_name.c_str(), &serialized_hashed_file))
|
||||
return false;
|
||||
|
||||
HashedFile hashed_file;
|
||||
if (!hashed_file.ParseFromString(serialized_hashed_file)) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Unable to parse hash file");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash;
|
||||
if (!Hash(hashed_file.file(), &hash)) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash.compare(hashed_file.hash())) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Hash mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(hashed_file.file())) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.type() != video_widevine_client::sdk::File::LICENSE) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Incorrect file type");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Incorrect file version");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file.has_license()) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: License not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
License license = file.license();
|
||||
|
||||
switch(license.state()) {
|
||||
case License_LicenseState_ACTIVE:
|
||||
*state = kLicenseStateActive;
|
||||
break;
|
||||
case License_LicenseState_RELEASING:
|
||||
*state = kLicenseStateReleasing;
|
||||
break;
|
||||
default:
|
||||
LOGW("DeviceFiles::RetrieveLicense: Unrecognized license state: %u",
|
||||
kLicenseStateUnknown);
|
||||
*state = kLicenseStateUnknown;
|
||||
break;
|
||||
}
|
||||
*pssh_data = license.pssh_data();
|
||||
*license_request = license.license_request();
|
||||
*license_message = license.license();
|
||||
*license_renewal_request = license.renewal_request();
|
||||
*license_renewal = license.renewal();
|
||||
*release_server_url = license.release_server_url();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
||||
std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt;
|
||||
return File::Remove(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt;
|
||||
return File::Exists(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash)
|
||||
return false;
|
||||
@@ -147,6 +293,7 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("DeviceFiles::StoreFile: success: %s (%db)", path.c_str(), data.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -184,6 +331,8 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("DeviceFiles::RetrieveFile: success: %s (%db)", path.c_str(),
|
||||
data->size());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,18 @@ message DeviceCertificate {
|
||||
}
|
||||
|
||||
message License {
|
||||
optional bytes key_set_id = 1;
|
||||
enum LicenseState {
|
||||
ACTIVE = 1;
|
||||
RELEASING = 2;
|
||||
}
|
||||
|
||||
optional LicenseState state = 1;
|
||||
optional bytes pssh_data = 2;
|
||||
optional bytes license_request = 3;
|
||||
optional bytes license = 4;
|
||||
optional bytes renewal_request = 5;
|
||||
optional bytes renewal = 6;
|
||||
optional bytes release_server_url = 7;
|
||||
}
|
||||
|
||||
message File {
|
||||
@@ -38,7 +46,7 @@ message File {
|
||||
optional FileType type = 1;
|
||||
optional FileVersion version = 2 [default = VERSION_1];
|
||||
optional DeviceCertificate device_certificate = 3;
|
||||
repeated License licenses = 4;
|
||||
optional License license = 4;
|
||||
}
|
||||
|
||||
message HashedFile {
|
||||
|
||||
@@ -36,8 +36,6 @@ using video_widevine_server::sdk::License;
|
||||
using video_widevine_server::sdk::License_KeyContainer;
|
||||
using video_widevine_server::sdk::LicenseError;
|
||||
using video_widevine_server::sdk::SignedMessage;
|
||||
using video_widevine_server::sdk::STREAMING;
|
||||
using video_widevine_server::sdk::VERSION_2_1;
|
||||
|
||||
|
||||
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
@@ -97,7 +95,7 @@ bool CdmLicense::Init(const std::string& token,
|
||||
|
||||
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
if (!session_ ||
|
||||
@@ -180,7 +178,20 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
|
||||
content_id->mutable_cenc_id();
|
||||
cenc_content_id->add_pssh(init_data);
|
||||
cenc_content_id->set_license_type(STREAMING);
|
||||
|
||||
switch (license_type) {
|
||||
case kLicenseTypeOffline:
|
||||
cenc_content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
|
||||
break;
|
||||
case kLicenseTypeStreaming:
|
||||
cenc_content_id->set_license_type(video_widevine_server::sdk::STREAMING);
|
||||
break;
|
||||
default:
|
||||
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %u",
|
||||
(int)license_type);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
cenc_content_id->set_request_id(request_id);
|
||||
|
||||
// TODO(jfore): The time field will be updated once the cdm wrapper
|
||||
@@ -197,7 +208,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
}
|
||||
license_request.set_key_control_nonce(UintToString(nonce));
|
||||
LOGD("PrepareKeyRequest: nonce=%u", nonce);
|
||||
license_request.set_protocol_version(VERSION_2_1);
|
||||
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
|
||||
|
||||
// License request is complete. Serialize it.
|
||||
std::string serialized_license_req;
|
||||
@@ -215,6 +226,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
}
|
||||
|
||||
if (license_request_signature.empty()) {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: License request signature empty");
|
||||
signed_request->clear();
|
||||
return false;
|
||||
}
|
||||
@@ -231,22 +243,27 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
if (!session_) {
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: Invalid crypto session");
|
||||
return false;
|
||||
}
|
||||
if (!signed_request) {
|
||||
LOGE("CdmLicense::PrepareKeyRenewalRequest : No signed request provided.");
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: No signed request provided");
|
||||
return false;
|
||||
}
|
||||
if (!server_url) {
|
||||
LOGE("CdmLicense::PrepareKeyRenewalRequest : No server url provided.");
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: No server url provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
LicenseRequest license_request;
|
||||
license_request.set_type(LicenseRequest::RENEWAL);
|
||||
if (is_renewal)
|
||||
license_request.set_type(LicenseRequest::RENEWAL);
|
||||
else
|
||||
license_request.set_type(LicenseRequest::RELEASE);
|
||||
|
||||
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
|
||||
license_request.mutable_content_id()->mutable_license();
|
||||
@@ -259,8 +276,8 @@ bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request,
|
||||
return false;
|
||||
}
|
||||
license_request.set_key_control_nonce(UintToString(nonce));
|
||||
LOGD("PrepareKeyRenewalRequest: nonce=%u", nonce);
|
||||
license_request.set_protocol_version(VERSION_2_1);
|
||||
LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce);
|
||||
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
|
||||
|
||||
// License request is complete. Serialize it.
|
||||
std::string serialized_license_req;
|
||||
@@ -272,7 +289,11 @@ bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request,
|
||||
&license_request_signature))
|
||||
return false;
|
||||
|
||||
if (license_request_signature.empty()) return false;
|
||||
if (license_request_signature.empty()) {
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: empty license request"
|
||||
" signature");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Put serialize license request and signature together
|
||||
SignedMessage signed_message;
|
||||
@@ -369,44 +390,60 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyRenewalResponse(
|
||||
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
bool is_renewal,
|
||||
const CdmKeyResponse& license_response) {
|
||||
if (!session_) {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
if (license_response.empty()) {
|
||||
LOGE("CdmLicense::HandleKeyRenewalResponse : Empty license response.");
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse : Empty license response.");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
SignedMessage signed_response;
|
||||
if (!signed_response.ParseFromString(license_response))
|
||||
if (!signed_response.ParseFromString(license_response)) {
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse signed message");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (signed_response.type() == SignedMessage::ERROR) {
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
}
|
||||
|
||||
if (!signed_response.has_signature())
|
||||
if (!signed_response.has_signature()) {
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: signature missing");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
License license;
|
||||
if (!license.ParseFromString(signed_response.msg()))
|
||||
if (!license.ParseFromString(signed_response.msg())) {
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse license"
|
||||
" from signed message");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (!license.has_id()) return KEY_ERROR;
|
||||
if (!license.has_id()) {
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: license id not present");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (license.id().version() > license_id_.version()) {
|
||||
// This is the normal case.
|
||||
license_id_.CopyFrom(license.id());
|
||||
|
||||
if (license.policy().has_renewal_server_url() &&
|
||||
license.policy().renewal_server_url().size() > 0) {
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
if (is_renewal) {
|
||||
if (license.policy().has_renewal_server_url() &&
|
||||
license.policy().renewal_server_url().size() > 0) {
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
}
|
||||
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
if (!is_renewal)
|
||||
return KEY_ADDED;
|
||||
|
||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||
|
||||
if (session_->RefreshKeys(signed_response.msg(),
|
||||
@@ -423,15 +460,67 @@ CdmResponseType CdmLicense::HandleKeyRenewalResponse(
|
||||
// This isn't supposed to happen.
|
||||
// TODO(jfore): Handle wrap? We can miss responses and that should be
|
||||
// considered normal until retries are exhausted.
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: license version: expected > %u,"
|
||||
" actual = %u", license_id_.version(), license.id().version());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
bool CdmLicense::RestoreOfflineLicense(
|
||||
CdmKeyMessage& license_request,
|
||||
CdmKeyResponse& license_response,
|
||||
CdmKeyResponse& license_renewal_response) {
|
||||
|
||||
if (license_request.empty() || license_response.empty()) {
|
||||
LOGE("CdmLicense::RestoreOfflineLicense: key_request or response empty: "
|
||||
"%u %u", license_request.size(), license_response.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
SignedMessage signed_request;
|
||||
if (!signed_request.ParseFromString(license_request)) {
|
||||
LOGE("CdmLicense::RestoreOfflineLicense: license_request parse failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
|
||||
LOGE("CdmLicense::RestoreOfflineLicense: license request type: expected = "
|
||||
"%d, actual = %d",
|
||||
SignedMessage::LICENSE_REQUEST,
|
||||
signed_request.type());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
key_request_ = signed_request.msg();
|
||||
}
|
||||
else {
|
||||
if (!session_->GenerateDerivedKeys(signed_request.msg()))
|
||||
return false;
|
||||
}
|
||||
|
||||
CdmResponseType sts = HandleKeyResponse(license_response);
|
||||
|
||||
if (sts != KEY_ADDED)
|
||||
return false;
|
||||
|
||||
if (!license_renewal_response.empty()) {
|
||||
sts = HandleKeyUpdateResponse(true, license_renewal_response);
|
||||
|
||||
if (sts != KEY_ADDED)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
const SignedMessage& signed_message) {
|
||||
|
||||
LicenseError license_error;
|
||||
if (!license_error.ParseFromString(signed_message.msg()))
|
||||
if (!license_error.ParseFromString(signed_message.msg())) {
|
||||
LOGE("CdmLicense::HandleKeyErrorResponse: Unable to parse license error");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
switch (license_error.error_code()) {
|
||||
case LicenseError::INVALID_CREDENTIALS:
|
||||
@@ -440,6 +529,8 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
return DEVICE_REVOKED;
|
||||
case LicenseError::SERVICE_UNAVAILABLE:
|
||||
default:
|
||||
LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d",
|
||||
license_error.error_code());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user