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

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