Source release v3.0.0-0-g8d3792b-ce + third_party
Change-Id: I399e71ddfffcd436171d1c60283c63ab4658e0b1
This commit is contained in:
889
cdm/src/cdm.cpp
Normal file
889
cdm/src/cdm.cpp
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
32
cdm/src/lock.cpp
Normal 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
38
cdm/src/log.cpp
Normal 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
146
cdm/src/properties_ce.cpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user