Source release v3.0.0-0-g8d3792b-ce + third_party

Change-Id: I399e71ddfffcd436171d1c60283c63ab4658e0b1
This commit is contained in:
Joey Parrish
2015-06-19 15:13:34 -07:00
parent 58aba6b2ec
commit 0546ee6732
965 changed files with 426663 additions and 12897 deletions

889
cdm/src/cdm.cpp Normal file
View File

@@ -0,0 +1,889 @@
// Copyright 2015 Google Inc. All Rights Reserved.
#include "cdm.h"
#include <assert.h>
#include <string.h> // memcpy
#include <vector>
// core:
#include "cdm_client_property_set.h"
#include "cdm_engine.h"
#include "clock.h"
#include "crypto_session.h"
#include "file_store.h"
#include "license.h"
#include "log.h"
#include "properties.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
// CE:
#include "cdm_version.h"
#include "override.h"
#include "properties_ce.h"
namespace widevine {
using namespace wvcdm;
namespace {
const int64_t kPolicyTimerDurationMilliseconds = 5000;
void* const kPolicyTimerContext = NULL;
struct HostType {
Cdm::IStorage* storage;
Cdm::IClock* clock;
Cdm::ITimer* timer;
CdmEngine* provisioning_engine;
bool initialized;
HostType()
: storage(NULL),
clock(NULL),
timer(NULL),
provisioning_engine(NULL),
initialized(false) {}
} host;
class PropertySet : public CdmClientPropertySet {
public:
PropertySet()
: use_privacy_mode_(false) {}
virtual ~PropertySet() {}
virtual const std::string& security_level() const OVERRIDE {
// Unused on CE platforms. Used by Android to switch to L3.
return empty_string_;
}
void set_use_privacy_mode(bool use) {
use_privacy_mode_ = use;
}
virtual bool use_privacy_mode() const OVERRIDE {
return use_privacy_mode_;
}
virtual const std::string& service_certificate() const OVERRIDE {
return service_certificate_;
}
virtual void set_service_certificate(const std::string& cert) OVERRIDE {
service_certificate_ = cert;
}
virtual bool is_session_sharing_enabled() const OVERRIDE {
// Unused on CE platforms.
return true;
}
virtual uint32_t session_sharing_id() const OVERRIDE {
// Unused on CE platforms.
return 1;
}
virtual void set_session_sharing_id(uint32_t id) OVERRIDE {
// Unused on CE platforms.
return;
}
virtual const std::string& app_id() const OVERRIDE {
// Unused on CE platforms.
return empty_string_;
}
private:
bool use_privacy_mode_;
std::string service_certificate_;
// This is empty, but g++ 4.8 will not allow app_id() to return a string
// literal as a const reference to std::string.
const std::string empty_string_;
};
class CdmImpl : public Cdm,
public Cdm::ITimer::IClient,
public WvCdmEventListener {
public:
CdmImpl(IEventListener* listener,
bool privacy_mode);
virtual ~CdmImpl();
// Cdm:
virtual Status setServerCertificate(const std::string& certificate) OVERRIDE;
virtual Status createSession(SessionType session_type,
std::string* session_id) OVERRIDE;
virtual Status generateRequest(const std::string& session_id,
InitDataType init_data_type,
const std::string& init_data) OVERRIDE;
virtual Status load(const std::string& session_id) OVERRIDE;
virtual Status update(const std::string& session_id,
const std::string& response) OVERRIDE;
virtual Status getExpiration(const std::string& session_id,
int64_t* expiration) OVERRIDE;
virtual Status getKeyStatuses(const std::string& session_id,
KeyStatusMap* key_statuses) OVERRIDE;
virtual Status close(const std::string& session_id) OVERRIDE;
virtual Status remove(const std::string& session_id) OVERRIDE;
virtual Status decrypt(const InputBuffer& input,
const OutputBuffer& output) OVERRIDE;
// ITimer::IClient:
virtual void onTimerExpired(void* context) OVERRIDE;
// WvCdmEventListener:
virtual void OnSessionRenewalNeeded(const CdmSessionId& session_id) OVERRIDE;
virtual void OnSessionKeysChange(const CdmSessionId& session_id,
const CdmKeyStatusMap& keys_status,
bool has_new_usable_key) OVERRIDE;
virtual void OnExpirationUpdate(const CdmSessionId& session_id,
int64_t new_expiry_time_seconds) OVERRIDE;
private:
IEventListener* listener_;
bool policy_timer_enabled_;
CdmEngine cdm_engine_;
PropertySet property_set_;
std::map<std::string, SessionType> new_session_types_;
std::map<std::string, int64_t> session_expirations_;
std::map<std::string, KeyStatusMap> session_key_statuses_;
};
CdmImpl::CdmImpl(IEventListener* listener,
bool privacy_mode)
: listener_(listener),
policy_timer_enabled_(false) {
property_set_.set_use_privacy_mode(privacy_mode);
}
CdmImpl::~CdmImpl() {}
Cdm::Status CdmImpl::setServerCertificate(const std::string& certificate) {
if (!property_set_.use_privacy_mode()) {
LOGE("Cannot set server certificate if privacy mode is disabled.");
return kNotSupported;
}
if (certificate.empty()) {
LOGE("An empty server certificate is invalid.");
return kInvalidAccess;
}
if (CdmLicense::VerifySignedServiceCertificate(certificate) != NO_ERROR) {
LOGE("Invalid server certificate!");
return kInvalidAccess;
}
property_set_.set_service_certificate(certificate);
return kSuccess;
}
Cdm::Status CdmImpl::createSession(SessionType session_type,
std::string* session_id) {
if (!session_id) {
LOGE("Missing session ID pointer.");
return kInvalidAccess;
}
// Important! The caller may pass a pre-filled string, which must be cleared
// before being given to CdmEngine.
session_id->clear();
switch (session_type) {
case kTemporary:
case kPersistent:
break;
default:
LOGE("Unsupported session type: %d", session_type);
return kNotSupported;
}
std::string empty_origin;
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, empty_origin, this,
NULL, session_id);
switch (result) {
case NO_ERROR:
new_session_types_[*session_id] = session_type;
return kSuccess;
case NEED_PROVISIONING:
LOGE("A device certificate is needed.");
return kNeedsDeviceCertificate;
default:
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
}
Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
InitDataType init_data_type,
const std::string& init_data) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
std::map<std::string, SessionType>::iterator it =
new_session_types_.find(session_id);
if (it == new_session_types_.end()) {
LOGE("Request already generated: %s", session_id.c_str());
return kInvalidState;
}
SessionType session_type = it->second;
CdmLicenseType license_type;
switch (session_type) {
case kTemporary:
license_type = kLicenseTypeStreaming;
break;
case kPersistent:
license_type = kLicenseTypeOffline;
break;
default:
LOGE("Unexpected session type: %d", session_type);
return kUnexpectedError;
}
std::string init_data_type_name;
switch (init_data_type) {
case kCenc:
init_data_type_name = CENC_INIT_DATA_FORMAT;
break;
case kKeyIds:
LOGE("Key IDs init data type is not supported.");
return kNotSupported;
case kWebM:
init_data_type_name = WEBM_INIT_DATA_FORMAT;
break;
default:
LOGE("Invalid init data type: %d", init_data_type);
return kInvalidAccess;
}
InitializationData init_data_obj(init_data_type_name, init_data);
if (init_data_obj.IsEmpty()) {
LOGE("Failed to parse init data.");
return kInvalidAccess;
}
CdmAppParameterMap empty_app_parameters;
std::string key_request;
CdmKeyRequestType key_request_type;
std::string ignored_server_url;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, init_data_obj,
license_type, empty_app_parameters, &key_request, &key_request_type,
&ignored_server_url, NULL);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
new_session_types_.erase(it);
assert(key_request_type == kKeyRequestTypeInitial);
MessageType message_type = kLicenseRequest;
if (property_set_.use_privacy_mode() &&
property_set_.service_certificate().empty()) {
// We can deduce that this is a server cert request, even though CdmEgine
// cannot currently inform us of this.
message_type = kIndividualizationRequest;
LOGI("A server certificate request has been generated.");
} else {
LOGI("A license request has been generated.");
}
listener_->onMessage(session_id, message_type, key_request);
return kSuccess;
}
Cdm::Status CdmImpl::load(const std::string& session_id) {
if (session_id.empty()) {
LOGE("Empty session ID.");
return kInvalidAccess;
}
if (cdm_engine_.IsOpenSession(session_id)) {
LOGE("Session ID already loaded.");
return kQuotaExceeded;
}
std::string empty_origin;
std::string ignored_session_id_output;
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, empty_origin, this,
&session_id, &ignored_session_id_output);
switch (result) {
case NO_ERROR:
break;
case NEED_PROVISIONING:
LOGE("A device certificate is needed.");
return kNeedsDeviceCertificate;
default:
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
result = cdm_engine_.RestoreKey(session_id, session_id);
if (result == GET_LICENSE_ERROR) {
LOGE("Unable to load license: %s", session_id.c_str());
return kSessionNotFound;
} else if (result == GET_RELEASED_LICENSE_ERROR) {
// This was partially removed already.
// The EME spec states that we should send a release message right away.
InitializationData empty_initialization_data;
CdmAppParameterMap empty_app_parameters;
CdmKeyMessage key_request;
std::string ignored_server_url;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, empty_initialization_data,
kLicenseTypeRelease, empty_app_parameters, &key_request, NULL,
&ignored_server_url, NULL);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
cdm_engine_.CloseSession(session_id);
return kUnexpectedError;
}
LOGI("A license release has been generated.");
MessageType message_type = kLicenseRelease;
listener_->onMessage(session_id, message_type, key_request);
} else if (result != KEY_ADDED) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
}
Cdm::Status CdmImpl::update(const std::string& session_id,
const std::string& response) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (new_session_types_.find(session_id) != new_session_types_.end()) {
LOGE("Request not yet generated: %s", session_id.c_str());
return kInvalidState;
}
if (response.empty()) {
LOGE("Empty response.");
return kInvalidAccess;
}
bool predicted_to_be_server_cert_response =
property_set_.use_privacy_mode() &&
property_set_.service_certificate().empty();
// NOTE: If the CdmSession object recognizes that this is not the first
// AddKey(), it will internally delegate to RenewKey().
CdmKeySetId key_set_id = session_id;
CdmResponseType result =
cdm_engine_.AddKey(session_id, response, &key_set_id);
if (result == NEED_KEY) {
// We just provisioned a server certificate.
assert(predicted_to_be_server_cert_response);
// The cert is now available to all sessions in this CDM instance.
// This is consistent with the behavior of the Chrome CDM.
assert(!property_set_.service_certificate().empty());
// The underlying session in CdmEngine has stored a copy of the original
// init data, so we can use an empty one this time.
InitializationData empty_init_data;
CdmAppParameterMap empty_app_parameters;
std::string key_request;
CdmKeyRequestType key_request_type;
std::string ignored_server_url;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, empty_init_data, kLicenseTypeDeferred,
empty_app_parameters, &key_request, &key_request_type,
&ignored_server_url, NULL);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
LOGI("A deferred license request has been generated.");
assert(key_request_type == kKeyRequestTypeInitial);
MessageType message_type = kLicenseRequest;
listener_->onMessage(session_id, message_type, key_request);
return kSuccess;
}
assert(!predicted_to_be_server_cert_response);
if (result != KEY_ADDED) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
if (!policy_timer_enabled_) {
policy_timer_enabled_ = true;
host.timer->setTimeout(kPolicyTimerDurationMilliseconds,
this,
kPolicyTimerContext);
}
if (cdm_engine_.IsReleaseSession(session_id)) {
cdm_engine_.CloseSession(session_id);
listener_->onRemoveComplete(session_id);
}
return kSuccess;
}
Cdm::Status CdmImpl::getExpiration(const std::string& session_id,
int64_t* expiration) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
*expiration = session_expirations_[session_id];
return kSuccess;
}
Cdm::Status CdmImpl::getKeyStatuses(const std::string& session_id,
KeyStatusMap* key_statuses) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
*key_statuses = session_key_statuses_[session_id];
return kSuccess;
}
Cdm::Status CdmImpl::close(const std::string& session_id) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
CdmResponseType result = cdm_engine_.CloseSession(session_id);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
}
Cdm::Status CdmImpl::remove(const std::string& session_id) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (new_session_types_.find(session_id) != new_session_types_.end()) {
LOGE("Request not yet generated: %s", session_id.c_str());
return kInvalidState;
}
if (!cdm_engine_.IsOfflineSession(session_id)) {
LOGE("Not a persistent session: %s", session_id.c_str());
return kInvalidAccess;
}
InitializationData empty_initialization_data;
CdmAppParameterMap empty_app_parameters;
CdmKeyMessage key_request;
std::string ignored_server_url;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, empty_initialization_data,
kLicenseTypeRelease, empty_app_parameters, &key_request, NULL,
&ignored_server_url, NULL);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
cdm_engine_.CloseSession(session_id);
return kUnexpectedError;
}
LOGI("A license release has been generated.");
MessageType message_type = kLicenseRelease;
listener_->onMessage(session_id, message_type, key_request);
return kSuccess;
}
Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
const OutputBuffer& output) {
if (input.is_encrypted && input.iv_length != 16) {
LOGE("The IV must be 16 bytes long.");
return kInvalidAccess;
}
if (PropertiesCE::GetSecureOutputType() == kNoSecureOutput &&
output.is_secure) {
LOGE("The CDM is configured without secure output support.");
return kNotSupported;
}
std::string key_id(reinterpret_cast<const char *>(input.key_id),
input.key_id_length);
std::vector<uint8_t> iv(input.iv, input.iv + input.iv_length);
CdmDecryptionParameters parameters;
parameters.is_encrypted = input.is_encrypted;
parameters.is_secure = output.is_secure;
parameters.key_id = &key_id;
parameters.encrypt_buffer = input.data;
parameters.encrypt_length = input.data_length;
parameters.iv = &iv;
parameters.block_offset = input.block_offset;
parameters.decrypt_buffer = output.data;
parameters.decrypt_buffer_length = output.data_length;
parameters.decrypt_buffer_offset = output.data_offset;
parameters.subsample_flags =
(input.first_subsample ? OEMCrypto_FirstSubsample : 0) |
(input.last_subsample ? OEMCrypto_LastSubsample : 0);
parameters.is_video = input.is_video;
CdmSessionId empty_session_id;
CdmResponseType result = cdm_engine_.Decrypt(empty_session_id, parameters);
if (result == NEED_KEY || result == SESSION_NOT_FOUND_FOR_DECRYPT) {
LOGE("Key not available.");
return kNoKey;
}
if (result == NO_ERROR) {
return kSuccess;
}
LOGE("Decrypt error: %d", result);
return kDecryptError;
}
void CdmImpl::onTimerExpired(void* context) {
if (context == kPolicyTimerContext) {
if (policy_timer_enabled_) {
cdm_engine_.OnTimerEvent();
host.timer->setTimeout(kPolicyTimerDurationMilliseconds,
this,
kPolicyTimerContext);
}
}
}
void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
CdmKeyMessage message;
std::string server_url;
CdmResponseType result =
cdm_engine_.GenerateRenewalRequest(session_id, &message, &server_url);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
return;
}
LOGI("A license renewal has been generated.");
MessageType message_type = kLicenseRenewal;
listener_->onMessage(session_id, message_type, message);
}
void CdmImpl::OnSessionKeysChange(const CdmSessionId& session_id,
const CdmKeyStatusMap& keys_status,
bool has_new_usable_key) {
KeyStatusMap& map = session_key_statuses_[session_id];
CdmKeyStatusMap::const_iterator it;
for (it = keys_status.begin(); it != keys_status.end(); ++it) {
KeyStatus status;
switch (it->second) {
case kKeyStatusUsable:
map[it->first] = kUsable;
break;
case kKeyStatusExpired:
map[it->first] = kExpired;
break;
case kKeyStatusOutputNotAllowed:
map[it->first] = kOutputNotAllowed;
break;
case kKeyStatusPending:
map[it->first] = kStatusPending;
break;
case kKeyStatusInternalError:
map[it->first] = kInternalError;
break;
default:
LOGE("Unrecognized key status: %d", it->second);
map[it->first] = kInternalError;
break;
}
}
listener_->onKeyStatusesChange(session_id);
}
void CdmImpl::OnExpirationUpdate(const CdmSessionId& session_id,
int64_t new_expiry_time_seconds) {
session_expirations_[session_id] = new_expiry_time_seconds * 1000;
}
bool VerifyL1() {
CryptoSession cs;
return cs.GetSecurityLevel() == kSecurityLevelL1;
}
} // namespace
// static
Cdm::Status Cdm::initialize(
SecureOutputType secure_output_type,
const ClientInfo& client_info,
IStorage* storage,
IClock* clock,
ITimer* timer,
DeviceCertificateRequest* device_certificate_request,
LogLevel verbosity) {
// If you want to direct-render on L3, CryptoSession will pass that request
// along to OEMCrypto. But if you want to use an opaque handle on L3,
// CryptoSession will silently ignore you and tell OEMCrypto to treat the
// address as a clear buffer. :-(
//
// So this logic mirrors that in CryptoSession. Effectively, we are
// detecting at init time the conditions that would prevent CryptoSession (in
// its current form) from passing the desired buffer type constant to
// OEMCrypto.
// TODO: Discuss changes to CryptoSession.
switch (secure_output_type) {
case kOpaqueHandle:
// This output type requires an OEMCrypto that reports L1.
// This requirement comes from CryptoSession::SetDestinationBufferType().
if (!VerifyL1()) {
LOGE("Not an L1 implementation, kOpaqueHandle cannot be used!");
return kNotSupported;
}
break;
case kDirectRender:
case kNoSecureOutput:
break;
default:
LOGE("Invalid output type!");
return kInvalidAccess;
}
if (client_info.product_name.empty() ||
client_info.company_name.empty() ||
client_info.model_name.empty()) {
LOGE("Client info requires product_name, company_name, model_name!");
return kInvalidAccess;
}
if (!storage || !clock || !timer) {
LOGE("All interfaces are required!");
return kInvalidAccess;
}
if (!device_certificate_request) {
LOGE("Device certificate request pointer is required!");
return kInvalidAccess;
}
// Our enum values match those in core/include/log.h
g_cutoff = static_cast<LogPriority>(verbosity);
PropertiesCE::SetSecureOutputType(secure_output_type);
PropertiesCE::SetClientInfo(client_info);
Properties::Init();
host.storage = storage;
host.clock = clock;
host.timer = timer;
device_certificate_request->needed = false;
if (!host.provisioning_engine) {
host.provisioning_engine = new CdmEngine();
}
bool has_cert = host.provisioning_engine->IsProvisioned(
kSecurityLevelL1, "" /* origin */);
if (!has_cert) {
device_certificate_request->needed = true;
std::string empty_authority;
std::string empty_origin;
std::string base_url;
std::string signed_request;
CdmResponseType result = host.provisioning_engine->GetProvisioningRequest(
kCertificateWidevine, empty_authority, empty_origin,
&signed_request, &base_url);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
device_certificate_request->url = base_url;
device_certificate_request->url.append("&signedRequest=");
device_certificate_request->url.append(signed_request);
}
host.initialized = true;
return kSuccess;
}
// static
const char* Cdm::version() {
return CDM_VERSION;
}
// static
Cdm* Cdm::create(IEventListener* listener,
bool privacy_mode) {
if (!host.initialized) {
LOGE("Not initialized!");
return NULL;
}
if (!listener) {
LOGE("No listener!");
return NULL;
}
return new CdmImpl(listener, privacy_mode);
}
Cdm::Status Cdm::DeviceCertificateRequest::acceptReply(
const std::string& reply) {
if (!host.provisioning_engine) {
LOGE("Provisioning reply received while not in a provisioning state!");
return kInvalidAccess;
}
std::string empty_origin;
std::string ignored_cert;
std::string ignored_wrapped_key;
CdmResponseType result =
host.provisioning_engine->HandleProvisioningResponse(
empty_origin, reply, &ignored_cert, &ignored_wrapped_key);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
}
} // namespace widevine
// Missing symbols from core:
namespace wvcdm {
using namespace widevine;
int64_t Clock::GetCurrentTime() {
return host.clock->now() / 1000;
}
struct File::Impl {
std::string name;
bool read_only;
bool truncate;
};
File::File() : impl_(NULL) {}
File::~File() {
Close();
}
bool File::Open(const std::string& file_path, int flags) {
if (!(flags & kCreate) && !host.storage->exists(file_path)) {
return false;
}
impl_ = new Impl;
impl_->name = file_path;
impl_->read_only = (flags & kReadOnly);
impl_->truncate = (flags & kTruncate);
return true;
}
ssize_t File::Read(char* buffer, size_t bytes) {
if (!impl_) {
return -1;
}
std::string data;
if (!host.storage->read(impl_->name, &data)) {
return -1;
}
size_t to_copy = std::min(bytes, data.size());
memcpy(buffer, data.data(), to_copy);
return to_copy;
}
ssize_t File::Write(const char* buffer, size_t bytes) {
if (!impl_) {
return -1;
}
if (!impl_->truncate) {
LOGE("Internal error: files cannot be appended to.");
return -1;
}
std::string data(buffer, bytes);
if (!host.storage->write(impl_->name, data)) {
return -1;
}
return bytes;
}
void File::Close() {
if (impl_) {
delete impl_;
}
impl_ = NULL;
}
bool File::Exists(const std::string& file_path) {
return host.storage->exists(file_path);
}
bool File::Remove(const std::string& file_path) {
return host.storage->remove(file_path);
}
bool File::Copy(const std::string& old_path, const std::string& new_path) {
std::string data;
bool read_ok = host.storage->read(old_path, &data);
if (!read_ok) return false;
return host.storage->write(new_path, data);
}
bool File::List(const std::string& path, std::vector<std::string>* files) {
return false;
}
bool File::CreateDirectory(const std::string dir_path) {
return true;
}
bool File::IsDirectory(const std::string& dir_path) {
return false;
}
bool File::IsRegularFile(const std::string& file_path) {
return host.storage->exists(file_path);
}
ssize_t File::FileSize(const std::string& file_path) {
return host.storage->size(file_path);
}
} // namespace wvcdm

View File

@@ -1,48 +0,0 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include <assert.h>
#include "wv_content_decryption_module_1.h"
#include "wv_content_decryption_module_4.h"
#include "wv_cdm_version.h"
void InitializeCdmModule() {}
void DeinitializeCdmModule() {}
void* CreateCdmInstance(int cdm_interface_version, const char* key_system,
uint32_t key_system_size,
GetCdmHostFunc get_cdm_host_func, void* user_data) {
void *host = NULL;
switch (cdm_interface_version) {
case cdm::ContentDecryptionModule_1::kVersion:
host = get_cdm_host_func(cdm::Host_1::kVersion, user_data);
break;
case cdm::ContentDecryptionModule_4::kVersion:
host = get_cdm_host_func(cdm::Host_4::kVersion, user_data);
break;
}
if (!host)
return NULL;
switch (cdm_interface_version) {
case cdm::ContentDecryptionModule_1::kVersion:
return new wvcdm::WvContentDecryptionModule_1(
static_cast<cdm::Host_1*>(host));
case cdm::ContentDecryptionModule_4::kVersion:
return new wvcdm::WvContentDecryptionModule_4(
static_cast<cdm::Host_4*>(host));
}
assert(false); // NOT REACHED
return NULL;
}
const char* GetCdmVersion() {
return WV_CDM_VERSION;
}

View File

@@ -1,24 +0,0 @@
#include "clock.h"
#include <cstddef>
#include "cdm_host_clock.h"
namespace wvcdm {
IClock* HostClock::impl_ = NULL;
IClock::~IClock() {
HostClock::impl_ = NULL;
}
int64_t Clock::GetCurrentTime() {
return HostClock::impl_ ?
HostClock::impl_->GetCurrentTimeInSeconds() : -1;
}
void HostClock::SetClockInterface(IClock* iclock) {
HostClock::impl_ = iclock;
}
} // namespace wvcdm

View File

@@ -1,222 +0,0 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "cdm_host_file.h"
#include <string.h>
#include <algorithm>
#include <string>
namespace wvcdm {
IFileFactory* File::Impl::factory_ = NULL;
// File::Impl() Section
// The file handler for cert.bin, aka DeviceCertificate, and usage.bin,
// aka UsageTable, are all we're setting up for now.
namespace {
const char* const kDeviceCertificateKey = "DeviceCertificate";
const char* const kUsageInfoKey = "UsageInfo";
const char* const kUsageTableKey = "MockOemCryptoUsageTable";
const char* const kGenerationNumberKey = "MockOemCryptoGenerationNumber";
const char* GetKeyForFileName(const std::string& name) {
if (name == "cert.bin") {
return kDeviceCertificateKey;
} else if (name == "usage.bin") {
return kUsageInfoKey;
} else if (name == "UsageTable.dat") {
return kUsageTableKey;
} else if (name == "GenerationNumber.dat") {
return kGenerationNumberKey;
} else {
return "";
}
}
} // namespace
// File1 implmentation.
bool File1Impl::Open(const std::string& name) {
if (name.empty())
return false;
fname_ = name;
return true;
}
ssize_t File1Impl::Read(char* buffer, size_t bytes) {
std::string key(GetKeyForFileName(fname_));
if (key.length() > 0) {
std::string value;
host_1_->GetPlatformString(key, &value);
size_t bytes_to_copy = std::min(bytes, value.size());
memcpy(buffer, value.data(), bytes_to_copy);
return value.size() ? bytes_to_copy : -1;
} else {
return -1;
}
}
bool File1Impl::Remove(const std::string& name) {
std::string key(GetKeyForFileName(name));
if (key.length() > 0) {
host_1_->SetPlatformString(key, "");
return true;
} else {
return false;
}
}
ssize_t File1Impl::FileSize(const std::string& name) {
std::string key(GetKeyForFileName(name));
if (key.length() > 0) {
std::string value;
host_1_->GetPlatformString(key, &value);
return value.empty() ? -1 : value.size();
} else {
return -1;
}
}
ssize_t File1Impl::Write(const char* buffer, size_t bytes) {
std::string key(GetKeyForFileName(fname_));
if (key.length() > 0) {
std::string value(buffer, bytes);
host_1_->SetPlatformString(key, value);
return bytes;
} else {
return -1;
}
}
bool File1Impl::Close() {
fname_.clear();
return true;
}
// File 4 Implementation.
bool File4Impl::Open(const std::string& name) {
return host_4_file_io_client_.Open(name);
}
ssize_t File4Impl::Read(char* buffer, size_t bytes) {
if (host_4_file_io_client_.Read(buffer, bytes) &&
host_4_file_io_client_.data_size() > 0) {
return std::min<size_t>(host_4_file_io_client_.data_size(), bytes);
} else {
return -1;
}
}
ssize_t File4Impl::Write(const char* buffer, size_t bytes) {
if (host_4_file_io_client_.Write(buffer, bytes)) {
return bytes;
} else {
return -1;
}
}
bool File4Impl::Close() {
return host_4_file_io_client_.Close();
}
bool File4Impl::Remove(const std::string& name) {
Host4FileIOClient file_io_client(host_4_);
return file_io_client.Open(name) && file_io_client.Write("", 0);
}
ssize_t File4Impl::FileSize(const std::string& name) {
Host4FileIOClient file_io_client(host_4_);
if (file_io_client.Open(name) && file_io_client.ReadFileSize() &&
file_io_client.data_size() > 0) {
return file_io_client.data_size();
} else {
return -1;
}
}
// Common file implementation.
bool File::Impl::Open(const std::string& name) {
return file_api_->Open(name);
}
ssize_t File::Impl::Read(char* buffer, size_t bytes) {
return file_api_->Read(buffer, bytes);
}
ssize_t File::Impl::Write(const char* buffer, size_t bytes) {
return file_api_->Write(buffer, bytes);
}
bool File::Impl::Close() {
return file_api_->Close();
}
bool File::Impl::Exists(const std::string& name) {
return FileSize(name) > 0;
}
bool File::Impl::Remove(const std::string& name) {
return file_api_->Remove(name);
}
ssize_t File::Impl::FileSize(const std::string& name) {
return file_api_->FileSize(name);
}
File::File() : impl_(File::Impl::factory_->NewFileImpl()) {}
File::~File() {
Close();
delete impl_;
}
bool File::Open(const std::string& name, int flags) {
return impl_->Open(name);
}
void File::Close() { impl_->Close(); }
ssize_t File::Read(char* buffer, size_t bytes) {
return impl_->Read(buffer, bytes);
}
ssize_t File::Write(const char* buffer, size_t bytes) {
return impl_->Write(buffer, bytes);
}
bool File::Exists(const std::string& path) { return impl_->Exists(path); }
bool File::Remove(const std::string& path) { return impl_->Remove(path); }
bool File::Copy(const std::string& from, const std::string& to) {
// Required for linkage only - no API implementation is needed by the CDM
return false;
}
bool File::List(const std::string& path, std::vector<std::string>* files) {
// Required for linkage only - no API implementation is needed by the CDM
return false;
}
bool File::CreateDirectory(std::string path) {
// Required for linkage only - no API implementation is needed by the CDM
return true;
}
bool File::IsDirectory(const std::string& path) {
// Required for linkage only - no API implementation is needed by the CDM
return false;
}
bool File::IsRegularFile(const std::string& path) {
// Required for linkage only - no API implementation is needed by the CDM
return false;
}
ssize_t File::FileSize(const std::string& path) {
return impl_->FileSize(path);
}
} // namespace wvcdm

View File

@@ -1,66 +0,0 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "host_4_file_io_client.h"
#include <string.h>
#include <algorithm>
namespace wvcdm {
Host4FileIOClient::~Host4FileIOClient() {
if (file_io_ != NULL) Close();
}
bool Host4FileIOClient::Open(const std::string& name) {
if (host_ == NULL) return false;
if (file_io_ != NULL) return false;
file_io_ = host_->CreateFileIO(this);
file_io_->Open(name.data(), name.length());
return status_ == kSuccess;
}
bool Host4FileIOClient::Read(char* buffer, size_t buffer_size) {
if (file_io_ == NULL) return false;
buffer_ = buffer;
buffer_size_ = buffer_size;
file_io_->Read();
return status_ == kSuccess;
}
bool Host4FileIOClient::Write(const char* data, size_t data_size) {
if (file_io_ == NULL) return false;
file_io_->Write(reinterpret_cast<const uint8_t*>(data), data_size);
return status_ == kSuccess;
}
bool Host4FileIOClient::Close() {
if (file_io_ == NULL) return false;
file_io_->Close();
file_io_ = NULL;
status_ = kSuccess;
data_size_ = 0;
return true;
}
void Host4FileIOClient::OnOpenComplete(Status status) {
status_ = status;
data_size_ = 0;
}
void Host4FileIOClient::OnReadComplete(Status status, const uint8_t* data,
uint32_t data_size) {
status_ = status;
data_size_ = data_size;
if (buffer_ != NULL) {
memcpy(buffer_, data, std::min<size_t>(data_size, buffer_size_));
buffer_ = NULL;
buffer_size_ = 0;
}
}
void Host4FileIOClient::OnWriteComplete(Status status) {
status_ = status;
data_size_ = 0;
}
} // namespace wvcdm

View File

@@ -1,38 +0,0 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "host_event_listener.h"
#include "log.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
namespace wvcdm {
void HostEventListener::OnEvent(const CdmSessionId& session_id,
CdmEventType cdm_event) {
switch (cdm_event) {
case LICENSE_RENEWAL_NEEDED_EVENT: {
wvcdm::CdmKeyMessage cdm_message;
std::string server_url;
CdmResponseType result = cdm_engine_->GenerateRenewalRequest(
session_id, &cdm_message, &server_url);
if (result == wvcdm::KEY_MESSAGE) {
host_->SendKeyMessage(session_id.data(), session_id.length(),
cdm_message.data(), cdm_message.length(),
server_url.data(), server_url.length());
} else {
LOGD("Error on Generating a Renewal Request!");
host_->SendKeyError(session_id.data(), session_id.size(),
cdm::kUnknownError, 0);
}
break;
}
case LICENSE_EXPIRED_EVENT: {
host_->SendKeyError(session_id.data(), session_id.size(),
cdm::kUnknownError, 0);
break;
}
}
}
} // namespace wvcdm

32
cdm/src/lock.cpp Normal file
View File

@@ -0,0 +1,32 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Lock class - provides a simple mutex implementation.
#include "lock.h"
#include <pthread.h>
namespace wvcdm {
struct Lock::Impl {
pthread_mutex_t mutex;
};
Lock::Lock() : impl_(new Lock::Impl()) {
pthread_mutex_init(&impl_->mutex, NULL);
}
Lock::~Lock() {
pthread_mutex_destroy(&impl_->mutex);
delete impl_;
}
void Lock::Acquire() {
pthread_mutex_lock(&impl_->mutex);
}
void Lock::Release() {
pthread_mutex_unlock(&impl_->mutex);
}
} // namespace wvcdm

38
cdm/src/log.cpp Normal file
View File

@@ -0,0 +1,38 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Log - implemented using stderr.
#include "log.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
namespace wvcdm {
LogPriority g_cutoff = LOG_WARN;
void InitLogging() {}
void Log(const char* file, int line, LogPriority level, const char* fmt, ...) {
const char* severities[] = { "ERROR", "WARN", "INFO", "DEBUG", "VERBOSE" };
if (level >= sizeof(severities) / sizeof(*severities)) {
fprintf(stderr, "[FATAL:%s(%d)] Invalid log priority level: %d\n",
file, line, level);
return;
}
if (level > g_cutoff) return;
fprintf(stderr, "[%s:%s(%d)] ", severities[level], file, line);
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
fflush(stderr);
}
} // namespace wvcdm

146
cdm/src/properties_ce.cpp Normal file
View File

@@ -0,0 +1,146 @@
// Copyright 2015 Google Inc. All Rights Reserved.
#include "properties.h"
#include "properties_ce.h"
#include "cdm_version.h"
#include "log.h"
// This anonymous namespace is shared between both the widevine namespace and
// wvcdm namespace objects below.
namespace {
bool use_secure_buffers_ = false;
bool use_fifo_ = false;
bool use_userspace_buffers_ = true;
widevine::Cdm::SecureOutputType secure_output_type_ =
widevine::Cdm::kNoSecureOutput;
widevine::Cdm::ClientInfo client_info_;
bool GetValue(const std::string& source, std::string* output) {
if (!output) {
return false;
}
*output = source;
return source.size() != 0;
}
} // namespace
namespace widevine {
// static
void PropertiesCE::SetSecureOutputType(
Cdm::SecureOutputType secure_output_type) {
secure_output_type_ = secure_output_type;
switch (secure_output_type) {
case Cdm::kOpaqueHandle:
use_secure_buffers_ = true;
use_fifo_ = false;
use_userspace_buffers_ = false;
break;
case Cdm::kDirectRender:
use_secure_buffers_ = false;
use_fifo_ = true;
use_userspace_buffers_ = false;
break;
case Cdm::kNoSecureOutput:
default:
use_secure_buffers_ = false;
use_fifo_ = false;
use_userspace_buffers_ = true;
break;
}
}
// static
Cdm::SecureOutputType PropertiesCE::GetSecureOutputType() {
return secure_output_type_;
}
// static
void PropertiesCE::SetClientInfo(const Cdm::ClientInfo& client_info) {
client_info_ = client_info;
}
// static
Cdm::ClientInfo PropertiesCE::GetClientInfo() {
return client_info_;
}
} // namespace widevine
namespace wvcdm {
// static
void Properties::Init() {
oem_crypto_use_secure_buffers_ = use_secure_buffers_;
oem_crypto_use_fifo_ = use_fifo_;
oem_crypto_use_userspace_buffers_ = use_userspace_buffers_;
use_certificates_as_identification_ = true;
security_level_path_backward_compatibility_support_ = false;
session_property_set_.reset(new CdmClientPropertySetMap());
}
// static
bool Properties::GetCompanyName(std::string* company_name) {
return GetValue(client_info_.company_name, company_name);
}
// static
bool Properties::GetModelName(std::string* model_name) {
return GetValue(client_info_.model_name, model_name);
}
// static
bool Properties::GetArchitectureName(std::string* arch_name) {
return GetValue(client_info_.arch_name, arch_name);
}
// static
bool Properties::GetDeviceName(std::string* device_name) {
return GetValue(client_info_.device_name, device_name);
}
// static
bool Properties::GetProductName(std::string* product_name) {
return GetValue(client_info_.product_name, product_name);
}
// static
bool Properties::GetBuildInfo(std::string* build_info) {
return GetValue(client_info_.build_info, build_info);
}
// static
bool Properties::GetWVCdmVersion(std::string* version) {
return GetValue(CDM_VERSION, version);
}
// static
bool Properties::GetDeviceFilesBasePath(CdmSecurityLevel security_level,
std::string* base_path) {
// A no-op, but successful.
base_path->clear();
return true;
}
// static
bool Properties::GetFactoryKeyboxPath(std::string* keybox) {
// Unused on CE devices.
return false;
}
// static
bool Properties::GetOEMCryptoPath(std::string* library_name) {
// Unused on CE devices.
return false;
}
// static
bool Properties::AlwaysUseKeySetIds() {
return true;
}
} // namespace wvcdm

View File

@@ -1,90 +0,0 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "properties.h"
#include <string>
#include "log.h"
namespace wvcdm {
bool Properties::GetCompanyName(std::string* company_name) {
if (!company_name) {
LOGW("Properties::GetCompanyName: Invalid parameter");
return false;
}
*company_name = PLATFORM_COMPANY_NAME_WV;
return true;
}
bool Properties::GetModelName(std::string* model_name) {
if (!model_name) {
LOGW("Properties::GetModelName: Invalid parameter");
return false;
}
*model_name = PLATFORM_MODEL_NAME_WV;
return true;
}
bool Properties::GetArchitectureName(std::string* arch_name) {
if (!arch_name) {
LOGW("Properties::GetArchitectureName: Invalid parameter");
return false;
}
*arch_name = PLATFORM_ARCHITECTURE_NAME_WV;
return true;
}
bool Properties::GetDeviceName(std::string* device_name) {
if (!device_name) {
LOGW("Properties::GetDeviceName: Invalid parameter");
return false;
}
*device_name = PLATFORM_DEVICE_NAME_WV;
return true;
}
bool Properties::GetProductName(std::string* product_name) {
if (!product_name) {
LOGW("Properties::GetProductName: Invalid parameter");
return false;
}
*product_name = PLATFORM_PRODUCT_NAME_WV;
return true;
}
bool Properties::GetBuildInfo(std::string* build_info) {
if (!build_info) {
LOGW("Properties::GetBuildInfo: Invalid parameter");
return false;
}
*build_info = PLATFORM_BUILDINFO_WV;
return true;
}
bool Properties::GetDeviceFilesBasePath(CdmSecurityLevel security_level,
std::string* base_path) {
// no-op
return true;
}
bool Properties::GetFactoryKeyboxPath(std::string* keybox) {
if (!keybox) {
LOGW("Properties::GetFactoryKeyboxPath: Invalid parameter");
return false;
}
return true;
}
bool Properties::GetOEMCryptoPath(std::string* library_name) {
LOGE("Properties::GetOEMCryptoPath: Unimplemented property");
return false;
}
} // namespace wvcdm

View File

@@ -1,422 +0,0 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "wv_content_decryption_module_1.h"
#include <assert.h>
#include <string.h>
#include "initialization_data.h"
#include "log.h"
#include "OEMCryptoCENC.h"
#include "properties.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
namespace {
enum {
// individual error codes
kAttachEventListenerError = 0x0001,
// error classes to be OR'd with cdm engine result values
kOpenSessionErrorBase = 0x0100,
kGenerateKeyRequestErrorBase = 0x0200,
kAddKeyErrorBase = 0x0300,
};
const int kCdmPolicyTimerDurationSeconds = 5;
// The iso spec only uses the lower 8 bytes of the iv as
// the counter.
const uint32_t kCencIvSize = 8;
const uint32_t kIvSize = 16;
bool Ctr128Add(size_t block_count, uint8_t* counter) {
if (NULL == counter) return false;
if (0 == block_count) return true;
uint8_t carry = 0;
uint8_t n = kIvSize - 1;
while (n >= kCencIvSize) {
uint32_t temp = block_count & 0xff;
temp += counter[n];
temp += carry;
counter[n] = temp & 0xff;
carry = (temp & 0x100) ? 1 : 0;
block_count = block_count >> 8;
n--;
if (!block_count && !carry) {
break;
}
}
return true;
}
} // namespace
namespace wvcdm {
// cdm::ContentDecryptionModule_1 implementation.
WvContentDecryptionModule_1::WvContentDecryptionModule_1(cdm::Host_1* host)
: host_(host),
host_event_listener_(host, &cdm_engine_),
cdm_engine_(),
property_set_(),
timer_enabled_(false) {
HostClock::SetClockInterface(this);
}
WvContentDecryptionModule_1::~WvContentDecryptionModule_1() {
DisablePolicyTimer();
}
cdm::Status WvContentDecryptionModule_1::GenerateKeyRequest(
const char* type, int type_size, const uint8_t* init_data,
int init_data_size) {
LOGI("WvContentDecryptionModule_1::GenerateKeyRequest()");
CdmInitData init_data_internal(reinterpret_cast<const char*>(init_data),
init_data_size);
std::string type_string(type, type_size);
InitializationData initialization_data(type_string,
init_data_internal);
CdmKeyMessage key_request;
CdmSessionId session_id;
std::string security_level;
std::string privacy_mode;
std::string service_certificate;
host_->GetPlatformString("SecurityLevel", &security_level);
host_->GetPlatformString("PrivacyOn", &privacy_mode);
host_->GetPlatformString("ServiceCertificate", &service_certificate);
property_set_.set_security_level(security_level);
property_set_.set_use_privacy_mode(privacy_mode == "True" ? 1 : 0);
property_set_.set_service_certificate(service_certificate);
CdmResponseType result = cdm_engine_.OpenSession("com.widevine.alpha",
&property_set_, &session_id);
if (result == NEED_PROVISIONING) {
LOGI("Need to aquire a Device Certificate from the Provisioning Server");
return cdm::kNeedsDeviceCertificate;
}
if (result != NO_ERROR) {
host_->SendKeyError("", 0, cdm::kClientError,
kOpenSessionErrorBase | result);
return cdm::kSessionError;
}
if (!cdm_engine_.AttachEventListener(session_id, &host_event_listener_)) {
cdm_engine_.CloseSession(session_id);
host_->SendKeyError("", 0, cdm::kClientError, kAttachEventListenerError);
return cdm::kSessionError;
}
CdmAppParameterMap app_parameters; // empty
CdmKeySetId key_set_id; // empty
std::string server_url;
result = cdm_engine_.GenerateKeyRequest(
session_id, key_set_id, initialization_data, kLicenseTypeStreaming,
app_parameters, &key_request, &server_url, NULL);
if (KEY_MESSAGE != result) {
cdm_engine_.CloseSession(session_id);
host_->SendKeyError("", 0, cdm::kClientError,
kGenerateKeyRequestErrorBase | result);
return cdm::kSessionError;
}
host_->SendKeyMessage(session_id.data(), session_id.length(),
key_request.data(), key_request.length(),
server_url.data(), server_url.length());
return cdm::kSuccess;
}
cdm::Status WvContentDecryptionModule_1::AddKey(
const char* session_id, int session_id_size, const uint8_t* key,
int key_size, const uint8_t* key_id, int key_id_size) {
LOGI("WvContentDecryptionModule_1::AddKey()");
CdmSessionId session_id_internal(session_id, session_id_size);
CdmKeyResponse key_data((const char*)key, key_size);
CdmKeySetId key_set_id;
CdmResponseType response =
cdm_engine_.AddKey(session_id_internal, key_data, &key_set_id);
if (response == KEY_ADDED) {
EnablePolicyTimer();
return cdm::kSuccess;
}
host_->SendKeyError(session_id, session_id_size, cdm::kClientError,
kAddKeyErrorBase | response);
return cdm::kSessionError;
}
bool WvContentDecryptionModule_1::IsKeyValid(const uint8_t* key_id,
int key_id_size) {
KeyId key(reinterpret_cast<const char*>(key_id), key_id_size);
return cdm_engine_.IsKeyLoaded(key);
}
cdm::Status WvContentDecryptionModule_1::CloseSession(const char* session_id,
int session_id_size) {
LOGI("WvContentDecryptionModule_1::CloseSession()");
CdmSessionId session_id_internal(session_id, session_id_size);
return cdm_engine_.CloseSession(session_id_internal) == NO_ERROR
? cdm::kSuccess
: cdm::kSessionError;
}
void WvContentDecryptionModule_1::TimerExpired(void* context) {
LOGI("WvContentDecryptionModule_1::TimerExpired()");
if (timer_enabled_) {
cdm_engine_.OnTimerEvent();
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
}
}
cdm::Status WvContentDecryptionModule_1::Decrypt(
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
LOGI("WvContentDecryptionModule_1::Decrypt()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError;
std::vector<uint8_t> iv(KEY_IV_SIZE);
memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size);
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL);
parameters.is_secure = Properties::oem_crypto_use_secure_buffers();
if (encrypted_buffer.num_subsamples)
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, decrypted_block);
else
return DoDecrypt(parameters, iv, encrypted_buffer, decrypted_block);
}
// This is the Level 1 API. When the host application calls the CDM's
// DecryptDecodeAndRenderFrame(), rather than the CDM's Decrypt(),
// OEMCrypto_DecryptCTR() will be told to use direct rendering with no
// cleartext in the return.
cdm::Status WvContentDecryptionModule_1::DecryptDecodeAndRenderFrame(
const cdm::InputBuffer& encrypted_buffer) {
LOGI("WvContentDecryptionModule_1::DecryptDecodeAndRenderFrame()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError;
std::vector<uint8_t> iv(KEY_IV_SIZE);
memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size);
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL);
if (encrypted_buffer.num_subsamples)
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
else
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
}
// This is the Level 1 API. When the host application calls the CDM's
// DecryptDecodeAndRenderSamples(), rather than the CDM's Decrypt(),
// OEMCrypto_DecryptCTR() will be told to use direct rendering with no cleartext
// in the return.
cdm::Status WvContentDecryptionModule_1::DecryptDecodeAndRenderSamples(
const cdm::InputBuffer& encrypted_buffer) {
LOGI("WvContentDecryptionModule_1::DecryptDecodeAndRenderSamples()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError;
std::vector<uint8_t> iv(KEY_IV_SIZE);
memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size);
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL);
parameters.is_video = false; // override the default true value for audio.
if (encrypted_buffer.num_subsamples)
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
else
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
}
void WvContentDecryptionModule_1::Destroy() { delete this; }
// Provisioning related methods
cdm::Status WvContentDecryptionModule_1::GetProvisioningRequest(
std::string* request, std::string* provisioning_server_url) {
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
if (cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority,
static_cast<CdmProvisioningRequest*>(request),
provisioning_server_url) == NO_ERROR) {
return cdm::kSuccess;
}
return cdm::kSessionError;
}
cdm::Status WvContentDecryptionModule_1::HandleProvisioningResponse(
std::string& response) {
std::string cert, wrapped_key;
if (cdm_engine_.HandleProvisioningResponse(
static_cast<CdmProvisioningRequest&>(response), &cert,
&wrapped_key) == NO_ERROR) {
return cdm::kSuccess;
}
return cdm::kSessionError;
}
void WvContentDecryptionModule_1::EnablePolicyTimer() {
LOGI("WvContentDecryptionModule_1::EnablePolicyTimer()");
if (!timer_enabled_) {
timer_enabled_ = true;
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
}
}
void WvContentDecryptionModule_1::DisablePolicyTimer() {
LOGI("WvContentDecryptionModule_1::DisablePolicyTimer()");
timer_enabled_ = false;
}
cdm::Status WvContentDecryptionModule_1::DoSubsampleDecrypt(
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
if (!encrypted_buffer.subsamples)
return cdm::kDecryptError;
size_t output_size = 0;
for (uint32_t i = 0; i < encrypted_buffer.num_subsamples; ++i) {
const cdm::SubsampleEntry& subsample = encrypted_buffer.subsamples[i];
output_size += subsample.cipher_bytes + subsample.clear_bytes;
}
assert(output_size ==
encrypted_buffer.data_size - encrypted_buffer.data_offset);
SetSizesAndAllocate(output_size, parameters, decrypted_block);
size_t offset = 0;
size_t encrypted_offset = 0;
uint32_t block_ctr = 0;
const cdm::SubsampleEntry* subsamples = encrypted_buffer.subsamples;
for (uint32_t i = 0; i < encrypted_buffer.num_subsamples; ++i) {
const cdm::SubsampleEntry& subsample = subsamples[i];
for (int is_encrypted = 0; is_encrypted < 2; ++is_encrypted) {
size_t bytes =
is_encrypted ? subsample.cipher_bytes : subsample.clear_bytes;
if (bytes == 0) continue;
parameters.is_encrypted = is_encrypted;
parameters.subsample_flags =
((offset == 0) ? OEMCrypto_FirstSubsample : 0) |
((offset + bytes == output_size) ? OEMCrypto_LastSubsample : 0);
cdm::Status status =
DecryptAndUpdateCounters(parameters, iv, encrypted_buffer,
bytes, decrypted_block, offset,
encrypted_offset, block_ctr);
if (status != cdm::kSuccess)
return status;
}
}
return cdm::kSuccess;
}
cdm::Status WvContentDecryptionModule_1::DoDecrypt(
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
assert(!encrypted_buffer.subsamples);
size_t output_size =
encrypted_buffer.data_size - encrypted_buffer.data_offset;
SetSizesAndAllocate(output_size, parameters, decrypted_block);
size_t offset = 0;
size_t encrypted_offset = 0;
uint32_t block_ctr = 0;
parameters.is_encrypted = true;
parameters.subsample_flags =
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample;
return DecryptAndUpdateCounters(parameters, iv, encrypted_buffer,
output_size, decrypted_block, offset,
encrypted_offset, block_ctr);
}
void WvContentDecryptionModule_1::SetSizesAndAllocate(
size_t output_size,
CdmDecryptionParameters& parameters,
cdm::DecryptedBlock* decrypted_block) {
parameters.decrypt_buffer_length = output_size;
if (decrypted_block) {
cdm::Buffer* buffer = host_->Allocate(output_size);
buffer->SetSize(output_size);
decrypted_block->SetDecryptedBuffer(buffer);
parameters.decrypt_buffer = buffer->Data();
} else {
parameters.decrypt_buffer = NULL;
}
}
cdm::Status WvContentDecryptionModule_1::DecryptAndUpdateCounters(
CdmDecryptionParameters& parameters,
std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
const size_t bytes,
cdm::DecryptedBlock* decrypted_block,
size_t& offset,
size_t& encrypted_offset,
uint32_t& block_ctr) {
if (parameters.is_encrypted) {
uint32_t counter = encrypted_offset / kIvSize;
Ctr128Add(counter - block_ctr, &iv[0]);
block_ctr = counter;
}
parameters.encrypt_buffer =
&encrypted_buffer.data[encrypted_buffer.data_offset + offset];
parameters.decrypt_buffer_offset = offset;
parameters.encrypt_length = bytes;
parameters.block_offset = encrypted_offset % kIvSize;
offset += bytes;
if (parameters.is_encrypted) encrypted_offset += bytes;
CdmSessionId session_id; // cdm_engine will locate via key_id.
CdmResponseType status = cdm_engine_.Decrypt(session_id, parameters);
switch (status) {
case wvcdm::NEED_KEY:
return cdm::kNoKey;
case wvcdm::NO_ERROR:
return cdm::kSuccess;
default:
return cdm::kDecryptError;
}
}
int64_t WvContentDecryptionModule_1::GetCurrentTimeInSeconds() {
return host_->GetCurrentWallTimeInSeconds();
}
File::Impl* WvContentDecryptionModule_1::NewFileImpl() {
return new File::Impl(host_);
}
} // namespace wvcdm

View File

@@ -1,597 +0,0 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include "wv_content_decryption_module_4.h"
#include <assert.h>
#include "log.h"
#include "properties.h"
#include "wv_cdm_constants.h"
namespace {
enum {
// individual error codes
kAttachEventListenerError = 0x0001,
kLicenseExpiredNotification = 0x0002,
kSessionNotFoundError = 0x0003,
kSessionAlreadyExistsError = 0x0004,
kInvalidParameter = 0x0005,
kNotImplemented = 0x0006,
// error classes to be OR'd with cdm engine result values
kOpenSessionErrorBase = 0x0100,
kGenerateKeyRequestErrorBase = 0x0200,
kGenerateRenewalRequestErrorBase = 0x0300,
kUpdateSessionErrorBase = 0x0400,
kRestoreKeyErrorBase = 0x0500,
kOpenKeySetSessionErrorBase = 0x0600,
kGetProvisioningRequestErrorBase = 0x0700,
kHandleProvisioningResponseErrorBase = 0x0800,
};
const int kCdmPolicyTimerDurationSeconds = 5;
// The iso spec only uses the lower 8 bytes of the iv as
// the counter.
const uint32_t kCencIvSize = 8;
const uint32_t kIvSize = 16;
void Ctr128Add(size_t block_count, uint8_t* counter) {
assert(NULL != counter);
if (0 == block_count) return;
uint8_t carry = 0;
uint8_t n = kIvSize - 1;
while (n >= kCencIvSize) {
uint32_t temp = block_count & 0xff;
temp += counter[n];
temp += carry;
counter[n] = temp & 0xff;
carry = (temp & 0x100) ? 1 : 0;
block_count = block_count >> 8;
n--;
if (!block_count && !carry) {
break;
}
}
}
} // namespace
namespace wvcdm {
// cdm::ContentDecryptionModule_4 implementation.
WvContentDecryptionModule_4::WvContentDecryptionModule_4(cdm::Host_4* host)
: host_(host), timer_enabled_(false) {
HostClock::SetClockInterface(this);
}
WvContentDecryptionModule_4::~WvContentDecryptionModule_4() {
DisablePolicyTimer();
}
void WvContentDecryptionModule_4::CreateSession(uint32_t session_id,
const char* mime_type,
uint32_t mime_type_size,
const uint8_t* init_data,
uint32_t init_data_size,
cdm::SessionType session_type) {
if (session_type == cdm::kProvisioning) {
// CreateProvisionSession() dispatches its own errors to the host.
CreateProvisionSession(session_id);
} else {
CdmLicenseType license_type;
switch (session_type) {
case cdm::kTemporary:
license_type = kLicenseTypeStreaming;
break;
case cdm::kPersistent:
license_type = kLicenseTypeOffline;
break;
default:
LOGE(
"WvContentDecryptionModule_4::CreateSession: Unsupported session "
"type ",
session_type);
host_->OnSessionError(session_id, cdm::kSessionError,
kInvalidParameter);
return;
}
if (!CallOpenSession(session_id)) {
// CallOpenSession() dispatches its own errors to the host.
return;
}
CdmSessionId internal_session_id = session_map_[session_id].webid();
CdmKeySetId empty_key_set_id;
std::string mime_type_string(mime_type, mime_type_size);
CdmInitData init_data_internal(reinterpret_cast<const char*>(init_data),
init_data_size);
InitializationData initialization_data(mime_type_string,
init_data_internal);
CdmAppParameterMap empty_app_parameters;
CdmKeyMessage key_request;
std::string server_url;
CdmKeySetId key_set_id;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
internal_session_id, empty_key_set_id, initialization_data,
license_type, empty_app_parameters, &key_request, &server_url,
&key_set_id);
if (KEY_MESSAGE == result) {
if (session_type == cdm::kPersistent) {
host_->OnSessionCreated(session_id, key_set_id.c_str(),
key_set_id.length());
} else {
host_->OnSessionCreated(session_id, internal_session_id.c_str(),
internal_session_id.length());
}
host_->OnSessionMessage(session_id, key_request.c_str(),
key_request.length(), server_url.c_str(),
server_url.length());
} else {
cdm_engine_.CloseSession(internal_session_id);
session_map_.erase(session_id);
host_->OnSessionError(session_id, cdm::kSessionError,
kGenerateKeyRequestErrorBase | result);
}
}
}
void WvContentDecryptionModule_4::LoadSession(uint32_t session_id,
const char* web_session_id,
uint32_t web_session_id_length) {
if (!CallOpenSession(session_id)) {
// CallOpenSession() dispatches its own errors to the host.
return;
}
CdmSessionId internal_session_id = session_map_[session_id].webid();
CdmKeySetId key_set_id(web_session_id, web_session_id_length);
CdmResponseType result =
cdm_engine_.RestoreKey(internal_session_id, key_set_id);
if (result != KEY_ADDED) {
cdm_engine_.CloseSession(internal_session_id);
session_map_.erase(session_id);
host_->OnSessionError(session_id, cdm::kSessionError,
kRestoreKeyErrorBase | result);
return;
}
host_->OnSessionCreated(session_id, web_session_id, web_session_id_length);
}
void WvContentDecryptionModule_4::UpdateSession(uint32_t session_id,
const uint8_t* response,
uint32_t response_size) {
LOGI("WvContentDecryptionModule_4::UpdateSession()");
if (session_map_.count(session_id) == 0) {
host_->OnSessionError(session_id, cdm::kSessionError,
kSessionNotFoundError);
return;
}
const InternalSession& session = session_map_[session_id];
if (session.is_provision()) {
// UpdateProvisionSession() dispatches its own errors to the host.
UpdateProvisionSession(session_id, response, response_size);
} else {
bool is_release = session.is_release();
CdmSessionId session_id_internal;
CdmKeySetId key_set_id;
if (!is_release) {
session_id_internal = session.webid();
} else {
key_set_id = session.webid();
}
CdmKeyResponse key_data((const char*)response, response_size);
CdmResponseType status =
cdm_engine_.AddKey(session_id_internal, key_data, &key_set_id);
if (status != KEY_ADDED) {
host_->OnSessionError(session_id, cdm::kSessionError,
status | kUpdateSessionErrorBase);
return;
}
if (!is_release) EnablePolicyTimer();
host_->OnSessionUpdated(session_id);
}
}
bool WvContentDecryptionModule_4::IsKeyValid(const uint8_t* key_id,
int key_id_size) {
KeyId cdm_key_id(reinterpret_cast<const char*>(key_id), key_id_size);
return cdm_engine_.IsKeyLoaded(cdm_key_id);
}
void WvContentDecryptionModule_4::ReleaseSession(uint32_t session_id) {
if (session_map_.count(session_id) == 0) {
host_->OnSessionError(session_id, cdm::kSessionError,
kSessionNotFoundError);
return;
}
if (session_map_[session_id].is_release()) {
cdm_engine_.CloseKeySetSession(session_map_[session_id].webid());
} else if (session_map_[session_id].is_decrypt()) {
cdm_engine_.CloseSession(session_map_[session_id].webid());
}
host_->OnSessionClosed(session_id);
session_map_.erase(session_id);
}
void WvContentDecryptionModule_4::RemoveSession(
uint32_t session_id, const char* web_session_id,
uint32_t web_session_id_length) {
if (session_map_.count(session_id) != 0) {
host_->OnSessionError(session_id, cdm::kSessionError,
kSessionAlreadyExistsError);
return;
}
CdmKeySetId key_set_id(web_session_id, web_session_id_length);
CdmResponseType result = cdm_engine_.OpenKeySetSession(key_set_id);
if (result != NO_ERROR) {
host_->OnSessionError(session_id, cdm::kSessionError,
kOpenKeySetSessionErrorBase | result);
return;
}
session_map_[session_id] =
InternalSession(key_set_id, InternalSession::kRelease);
CdmSessionId empty_session_id;
InitializationData empty_initialization_data;
CdmAppParameterMap empty_app_parameters;
CdmKeyMessage key_request;
std::string server_url;
result = cdm_engine_.GenerateKeyRequest(
empty_session_id, key_set_id, empty_initialization_data,
kLicenseTypeRelease, empty_app_parameters, &key_request, &server_url,
NULL);
if (KEY_MESSAGE == result) {
host_->OnSessionCreated(session_id, key_set_id.c_str(),
key_set_id.length());
host_->OnSessionMessage(session_id, key_request.c_str(),
key_request.length(), server_url.c_str(),
server_url.length());
} else {
cdm_engine_.CloseKeySetSession(key_set_id);
host_->OnSessionError(session_id, cdm::kSessionError,
kGenerateKeyRequestErrorBase | result);
}
}
cdm::Status WvContentDecryptionModule_4::UsePrivacyMode() {
if (!session_map_.empty()) return cdm::kSessionError;
property_set_.set_use_privacy_mode(true);
return cdm::kSuccess;
}
cdm::Status WvContentDecryptionModule_4::SetServerCertificate(
const uint8_t* server_certificate_data,
uint32_t server_certificate_data_size) {
if (!session_map_.empty()) return cdm::kSessionError;
cdm::Status result = UsePrivacyMode();
if (result != cdm::kSuccess) return result;
std::string server_certificate(
reinterpret_cast<const char*>(server_certificate_data),
server_certificate_data_size);
property_set_.set_service_certificate(server_certificate);
return cdm::kSuccess;
}
void WvContentDecryptionModule_4::TimerExpired(void* context) {
LOGI("WvContentDecryptionModule_4::TimerExpired()");
if (timer_enabled_) {
cdm_engine_.OnTimerEvent();
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
}
}
cdm::Status WvContentDecryptionModule_4::Decrypt(
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
LOGI("WvContentDecryptionModule_4::Decrypt()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError;
std::vector<uint8_t> iv(encrypted_buffer.iv,
encrypted_buffer.iv + encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size);
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL);
parameters.is_secure = Properties::oem_crypto_use_secure_buffers();
if (encrypted_buffer.num_subsamples)
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer,
decrypted_block);
else
return DoDecrypt(parameters, iv, encrypted_buffer, decrypted_block);
}
cdm::Status WvContentDecryptionModule_4::DecryptDecodeAndRender(
const cdm::InputBuffer& encrypted_buffer, cdm::StreamType stream_type) {
LOGI("WvContentDecryptionModule_4::DecryptDecodeAndRender()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError;
std::vector<uint8_t> iv(encrypted_buffer.iv,
encrypted_buffer.iv + encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size);
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL);
parameters.is_video = (stream_type == cdm::kStreamTypeVideo);
if (encrypted_buffer.num_subsamples)
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
else
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
}
void WvContentDecryptionModule_4::Destroy() { delete this; }
File::Impl* WvContentDecryptionModule_4::NewFileImpl() {
return new File::Impl(host_);
}
cdm::Status WvContentDecryptionModule_4::DoSubsampleDecrypt(
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
if (!encrypted_buffer.subsamples) return cdm::kDecryptError;
size_t output_size = 0;
for (uint32_t i = 0; i < encrypted_buffer.num_subsamples; ++i) {
const cdm::SubsampleEntry& subsample = encrypted_buffer.subsamples[i];
output_size += subsample.cipher_bytes + subsample.clear_bytes;
}
assert(output_size ==
encrypted_buffer.data_size - encrypted_buffer.data_offset);
SetSizesAndAllocate(output_size, parameters, decrypted_block);
size_t offset = 0;
size_t encrypted_offset = 0;
uint32_t block_ctr = 0;
const cdm::SubsampleEntry* subsamples = encrypted_buffer.subsamples;
for (uint32_t i = 0; i < encrypted_buffer.num_subsamples; ++i) {
const cdm::SubsampleEntry& subsample = subsamples[i];
for (int is_encrypted = 0; is_encrypted < 2; ++is_encrypted) {
size_t bytes =
is_encrypted ? subsample.cipher_bytes : subsample.clear_bytes;
if (bytes == 0) continue;
parameters.is_encrypted = is_encrypted;
parameters.subsample_flags =
((offset == 0) ? OEMCrypto_FirstSubsample : 0) |
((offset + bytes == output_size) ? OEMCrypto_LastSubsample : 0);
cdm::Status status = DecryptAndUpdateCounters(
parameters, iv, encrypted_buffer, bytes, decrypted_block, offset,
encrypted_offset, block_ctr);
if (status != cdm::kSuccess) return status;
}
}
return cdm::kSuccess;
}
cdm::Status WvContentDecryptionModule_4::DoDecrypt(
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
assert(!encrypted_buffer.subsamples);
size_t output_size =
encrypted_buffer.data_size - encrypted_buffer.data_offset;
SetSizesAndAllocate(output_size, parameters, decrypted_block);
size_t offset = 0;
size_t encrypted_offset = 0;
uint32_t block_ctr = 0;
parameters.is_encrypted = true;
parameters.subsample_flags =
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample;
return DecryptAndUpdateCounters(parameters, iv, encrypted_buffer, output_size,
decrypted_block, offset, encrypted_offset,
block_ctr);
}
void WvContentDecryptionModule_4::SetSizesAndAllocate(
size_t output_size, CdmDecryptionParameters& parameters,
cdm::DecryptedBlock* decrypted_block) {
parameters.decrypt_buffer_length = output_size;
if (decrypted_block) {
cdm::Buffer* buffer = host_->Allocate(output_size);
buffer->SetSize(output_size);
decrypted_block->SetDecryptedBuffer(buffer);
parameters.decrypt_buffer = buffer->Data();
} else {
parameters.decrypt_buffer = NULL;
}
}
cdm::Status WvContentDecryptionModule_4::DecryptAndUpdateCounters(
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer, const size_t bytes,
cdm::DecryptedBlock* decrypted_block, size_t& offset,
size_t& encrypted_offset, uint32_t& block_ctr) {
if (parameters.is_encrypted) {
uint32_t counter = encrypted_offset / kIvSize;
Ctr128Add(counter - block_ctr, &iv[0]);
block_ctr = counter;
}
parameters.encrypt_buffer =
&encrypted_buffer.data[encrypted_buffer.data_offset + offset];
parameters.decrypt_buffer_offset = offset;
parameters.encrypt_length = bytes;
parameters.block_offset = encrypted_offset % kIvSize;
offset += bytes;
if (parameters.is_encrypted) encrypted_offset += bytes;
CdmSessionId session_id; // cdm_engine will locate via key_id.
CdmResponseType status = cdm_engine_.Decrypt(session_id, parameters);
switch (status) {
case wvcdm::NEED_KEY:
return cdm::kNoKey;
case wvcdm::NO_ERROR:
return cdm::kSuccess;
default:
return cdm::kDecryptError;
}
}
void WvContentDecryptionModule_4::EnablePolicyTimer() {
LOGI("WvContentDecryptionModule_4::EnablePolicyTimer()");
if (!timer_enabled_) {
timer_enabled_ = true;
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
}
}
void WvContentDecryptionModule_4::DisablePolicyTimer() {
LOGI("WvContentDecryptionModule_4::DisablePolicyTimer()");
timer_enabled_ = false;
}
void WvContentDecryptionModule_4::CreateProvisionSession(uint32_t session_id) {
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
CdmProvisioningRequest request;
std::string default_url;
CdmResponseType status = cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, &request, &default_url);
if (status != NO_ERROR) {
host_->OnSessionError(session_id, cdm::kSessionError,
kGetProvisioningRequestErrorBase | status);
return;
}
std::string blank_web_id;
session_map_[session_id] =
InternalSession(blank_web_id, InternalSession::kProvision);
host_->OnSessionCreated(session_id, blank_web_id.c_str(),
blank_web_id.length());
host_->OnSessionMessage(session_id, request.c_str(), request.length(),
default_url.c_str(), default_url.length());
}
void WvContentDecryptionModule_4::UpdateProvisionSession(
uint32_t session_id, const uint8_t* response, uint32_t response_size) {
CdmProvisioningResponse cdm_response(reinterpret_cast<const char*>(response),
response_size);
std::string cert;
std::string wrapped_key;
CdmResponseType status =
cdm_engine_.HandleProvisioningResponse(cdm_response, &cert, &wrapped_key);
if (status != NO_ERROR) {
host_->OnSessionError(session_id, cdm::kSessionError,
kHandleProvisioningResponseErrorBase | status);
return;
}
host_->OnSessionUpdated(session_id);
}
bool WvContentDecryptionModule_4::CallOpenSession(uint32_t session_id) {
CdmSessionId internal_session_id;
if (session_map_.count(session_id) != 0) {
host_->OnSessionError(session_id, cdm::kSessionError,
kSessionAlreadyExistsError);
return false;
}
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, &internal_session_id);
if (result != NO_ERROR) {
if (result == NEED_PROVISIONING) {
host_->OnSessionError(session_id, cdm::kNeedsDeviceCertificate,
kOpenSessionErrorBase | result);
} else {
host_->OnSessionError(session_id, cdm::kSessionError,
kOpenSessionErrorBase | result);
}
return false;
}
if (!cdm_engine_.AttachEventListener(internal_session_id, this)) {
cdm_engine_.CloseSession(internal_session_id);
host_->OnSessionError(session_id, cdm::kSessionError,
kAttachEventListenerError);
return false;
}
session_map_[session_id] =
InternalSession(internal_session_id, InternalSession::kDecrypt);
return true;
}
int64_t WvContentDecryptionModule_4::GetCurrentTimeInSeconds() {
return host_->GetCurrentWallTimeInSeconds();
}
void WvContentDecryptionModule_4::OnEvent(const CdmSessionId& session_id,
CdmEventType cdm_event) {
bool found = false;
std::map<uint32_t, InternalSession>::iterator session_pair_iterator;
uint32_t external_session_id = 0;
for (session_pair_iterator = session_map_.begin();
session_pair_iterator != session_map_.end(); ++session_pair_iterator) {
if (session_pair_iterator->second.webid() == session_id) {
found = true;
external_session_id = session_pair_iterator->first;
break;
}
}
if (!found) {
LOGE("Unmapped Session Event on Session %s", session_id.c_str());
host_->OnSessionError(0, cdm::kSessionError, kSessionNotFoundError);
return;
}
switch (cdm_event) {
case LICENSE_RENEWAL_NEEDED_EVENT: {
wvcdm::CdmKeyMessage cdm_message;
std::string server_url;
CdmResponseType result = cdm_engine_.GenerateRenewalRequest(
session_id, &cdm_message, &server_url);
if (result == wvcdm::KEY_MESSAGE) {
LOGI("Renewal created");
host_->OnSessionMessage(external_session_id, cdm_message.c_str(),
cdm_message.length(), server_url.c_str(),
server_url.length());
} else {
LOGD("Error on Generating a Renewal Request!");
host_->OnSessionError(external_session_id, cdm::kSessionError,
kGenerateRenewalRequestErrorBase | result);
}
break;
}
case LICENSE_EXPIRED_EVENT: {
LOGD("License Expired Event");
host_->OnSessionError(external_session_id, cdm::kSessionError,
kLicenseExpiredNotification);
break;
}
}
}
} // namespace wvcdm