bug: 8620943 This is a merge of changes made to the Widevine CDM repository during certificate provisioning verification. The following changes are included: Fixes for certificate based licensing https://widevine-internal-review.googlesource.com/#/c/5162/ Base64 encode and decode now handles non-multiple of 24-bits input https://widevine-internal-review.googlesource.com/#/c/4981/ Fixed issues with device provisioning response handling https://widevine-internal-review.googlesource.com/#/c/5153/ Persistent storage to support device certificates https://widevine-internal-review.googlesource.com/#/c/5161/ Enable loading of certificates https://widevine-internal-review.googlesource.com/#/c/5172/ Provide license server url https://widevine-internal-review.googlesource.com/#/c/5173/ Change-Id: I0c032c1ae0055dcc1a7a77ad4b0ea0898030dc7d
244 lines
7.2 KiB
C++
244 lines
7.2 KiB
C++
// Copyright 2012 Google Inc. All Rights Reserved.
|
|
// Author: jfore@google.com (Jeff Fore), rkuroiwa@google.com (Rintaro Kuroiwa)
|
|
|
|
#include "cdm_session.h"
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#include "clock.h"
|
|
#include "crypto_engine.h"
|
|
#include "device_files.h"
|
|
#include "log.h"
|
|
#include "properties.h"
|
|
#include "string_conversions.h"
|
|
#include "wv_cdm_constants.h"
|
|
|
|
namespace wvcdm {
|
|
|
|
typedef std::set<WvCdmEventListener*>::iterator CdmEventListenerIter;
|
|
|
|
CdmResponseType CdmSession::Init() {
|
|
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
|
if (!crypto_engine) {
|
|
LOGE("CdmSession::Init failed to get CryptoEngine instance.");
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
crypto_session_ = crypto_engine->CreateSession(session_id_);
|
|
if (!crypto_session_) {
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
std::string token;
|
|
if (Properties::use_certificates_as_identification()) {
|
|
if (!LoadDeviceCertificate(&token, &wrapped_key_))
|
|
return NEED_PROVISIONING;
|
|
}
|
|
else {
|
|
if (!crypto_engine->GetToken(&token))
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (license_parser_.Init(token, crypto_session_, &policy_engine_))
|
|
return NO_ERROR;
|
|
else
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
bool CdmSession::DestroySession() {
|
|
if (crypto_session_) {
|
|
delete crypto_session_;
|
|
crypto_session_ = NULL;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CdmSession::VerifySession(const CdmKeySystem& key_system,
|
|
const CdmInitData& init_data) {
|
|
// TODO(gmorgan): Compare key_system and init_data with value received
|
|
// during session startup - they should be the same.
|
|
return true;
|
|
}
|
|
|
|
CdmResponseType CdmSession::GenerateKeyRequest(
|
|
const CdmInitData& pssh_data,
|
|
const CdmLicenseType license_type,
|
|
CdmAppParameterMap& app_parameters,
|
|
CdmKeyMessage* key_request,
|
|
std::string* server_url) {
|
|
if (!crypto_session_) {
|
|
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!crypto_session_->IsOpen()) {
|
|
LOGW("CdmSession::GenerateKeyRequest: Crypto session not open");
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (license_received_) {
|
|
return Properties::require_explicit_renew_request() ?
|
|
UNKNOWN_ERROR : GenerateRenewalRequest(key_request,
|
|
server_url);
|
|
}
|
|
else {
|
|
if (Properties::use_certificates_as_identification()) {
|
|
if (!crypto_session_->LoadCertificatePrivateKey(wrapped_key_))
|
|
return NEED_PROVISIONING;
|
|
}
|
|
|
|
if (!license_parser_.PrepareKeyRequest(pssh_data,
|
|
license_type,
|
|
app_parameters,
|
|
key_request,
|
|
server_url)) {
|
|
return KEY_ERROR;
|
|
} else {
|
|
return KEY_MESSAGE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// AddKey() - Accept license response and extract key info.
|
|
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
|
|
if (!crypto_session_) {
|
|
LOGW("CdmSession::AddKey: Invalid crypto session");
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!crypto_session_->IsOpen()) {
|
|
LOGW("CdmSession::AddKey: Crypto session not open");
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (license_received_) {
|
|
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;
|
|
|
|
return sts;
|
|
}
|
|
}
|
|
|
|
CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
|
|
return policy_engine_.Query(key_info);
|
|
}
|
|
|
|
CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
|
|
if ((!crypto_session_) || (!crypto_session_->IsOpen()))
|
|
return UNKNOWN_ERROR;
|
|
|
|
std::stringstream ss;
|
|
ss << crypto_session_->oec_session_id();
|
|
(*key_info)[QUERY_KEY_OEMCRYPTO_SESSION_ID] = ss.str();
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// CancelKeyRequest() - Cancel session.
|
|
CdmResponseType CdmSession::CancelKeyRequest() {
|
|
// TODO(gmorgan): cancel and clean up session
|
|
crypto_session_->Close();
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// Decrypt() - Accept encrypted buffer and return decrypted data.
|
|
CdmResponseType CdmSession::Decrypt(bool is_encrypted,
|
|
bool is_secure,
|
|
const KeyId& key_id,
|
|
const uint8_t* encrypt_buffer,
|
|
size_t encrypt_length,
|
|
const std::vector<uint8_t>& iv,
|
|
size_t block_offset,
|
|
void* decrypt_buffer,
|
|
size_t decrypt_buffer_offset,
|
|
bool is_video) {
|
|
if (!crypto_session_ || !crypto_session_->IsOpen())
|
|
return UNKNOWN_ERROR;
|
|
|
|
// Check if key needs to be selected
|
|
if (is_encrypted) {
|
|
if (key_id_.compare(key_id) != 0) {
|
|
if (crypto_session_->SelectKey(key_id)) {
|
|
key_id_ = key_id;
|
|
}
|
|
else {
|
|
return NEED_KEY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return crypto_session_->Decrypt(is_encrypted, is_secure, encrypt_buffer,
|
|
encrypt_length, iv, block_offset,
|
|
decrypt_buffer, decrypt_buffer_offset,
|
|
is_video);
|
|
}
|
|
|
|
// License renewal
|
|
// GenerateRenewalRequest() - Construct valid renewal request for the current
|
|
// session keys.
|
|
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
|
std::string* server_url) {
|
|
if (!license_parser_.PrepareKeyRenewalRequest(key_request,
|
|
server_url)) {
|
|
return KEY_ERROR;
|
|
} else {
|
|
return KEY_MESSAGE;
|
|
}
|
|
}
|
|
|
|
// RenewKey() - Accept renewal response and update key info.
|
|
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
|
return license_parser_.HandleKeyRenewalResponse(key_response);
|
|
}
|
|
|
|
bool CdmSession::IsKeyValid(const KeyId& key_id) {
|
|
// TODO(gmorgan): lookup key and determine if valid.
|
|
// return (session_keys_.find(key_id) != session_keys_.end());
|
|
return true;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
bool CdmSession::LoadDeviceCertificate(std::string* certificate,
|
|
std::string* wrapped_key) {
|
|
return DeviceFiles::RetrieveCertificate(certificate,
|
|
wrapped_key);
|
|
}
|
|
|
|
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
|
std::pair<CdmEventListenerIter, bool> result = listeners_.insert(listener);
|
|
return result.second;
|
|
}
|
|
|
|
bool CdmSession::DetachEventListener(WvCdmEventListener* listener) {
|
|
return (listeners_.erase(listener) == 1);
|
|
}
|
|
|
|
void CdmSession::OnTimerEvent() {
|
|
bool event_occurred = false;
|
|
CdmEventType event;
|
|
|
|
policy_engine_.OnTimerEvent(event_occurred, event);
|
|
|
|
if (event_occurred) {
|
|
for (CdmEventListenerIter iter = listeners_.begin();
|
|
iter != listeners_.end(); ++iter) {
|
|
(*iter)->onEvent(session_id(), event);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace wvcdm
|