Source release v2.2.0-0-903 + third_party libs
Change-Id: I03f670eaeb052bc741abb347be06f8ddc58418e7
This commit is contained in:
48
cdm/src/cdm_library_init.cpp
Normal file
48
cdm/src/cdm_library_init.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
// 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,73 +1,168 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#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 is all we're
|
||||
// setting up for now.
|
||||
// The file handler for cert.bin, aka DeviceCertificate, and usage.bin,
|
||||
// aka UsageTable, are all we're setting up for now.
|
||||
|
||||
bool File::Impl::Exists(const std::string& name) {
|
||||
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_->GetPlatformString("DeviceCertificate", &value);
|
||||
return !value.empty();
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool File::Impl::Open(const std::string& name) {
|
||||
if (name == "cert.bin") {
|
||||
fname_ = name;
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool File::Impl::Close() {
|
||||
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;
|
||||
}
|
||||
|
||||
bool File::Impl::Remove(const std::string& name) {
|
||||
if (name == "cert.bin") {
|
||||
host_->SetPlatformString("DeviceCertificate", "");
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (fname_ == "cert.bin") {
|
||||
std::string value;
|
||||
host_->GetPlatformString("DeviceCertificate", &value);
|
||||
memcpy(buffer, value.data(), std::min(bytes, value.size()));
|
||||
return value.size() ? value.size() : -1;
|
||||
}
|
||||
return -1;
|
||||
return file_api_->Read(buffer, bytes);
|
||||
}
|
||||
|
||||
ssize_t File::Impl::Write(const char* buffer, size_t bytes) {
|
||||
if (fname_ == "cert.bin") {
|
||||
std::string value(buffer, bytes);
|
||||
host_->SetPlatformString("DeviceCertificate", value);
|
||||
return bytes;
|
||||
}
|
||||
return -1;
|
||||
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) {
|
||||
if (name == "cert.bin") {
|
||||
std::string value;
|
||||
host_->GetPlatformString("DeviceCertificate", &value);
|
||||
return value.size() ? value.size() : -1;
|
||||
}
|
||||
return -1;
|
||||
return file_api_->FileSize(name);
|
||||
}
|
||||
|
||||
File::File() : impl_(File::Impl::factory_->NewFileImpl()) {}
|
||||
@@ -81,9 +176,7 @@ bool File::Open(const std::string& name, int flags) {
|
||||
return impl_->Open(name);
|
||||
}
|
||||
|
||||
void File::Close() {
|
||||
impl_->Close();
|
||||
}
|
||||
void File::Close() { impl_->Close(); }
|
||||
|
||||
ssize_t File::Read(char* buffer, size_t bytes) {
|
||||
return impl_->Read(buffer, bytes);
|
||||
@@ -93,13 +186,9 @@ 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::Exists(const std::string& path) { return impl_->Exists(path); }
|
||||
|
||||
bool File::Remove(const std::string& path) {
|
||||
return impl_->Remove(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
|
||||
|
||||
66
cdm/src/host_4_file_io_client.cpp
Normal file
66
cdm/src/host_4_file_io_client.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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,20 +1,19 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "wv_content_decryption_module.h"
|
||||
#include "wv_content_decryption_module_1.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "initialization_data.h"
|
||||
#include "log.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "properties.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
#include "wv_cdm_version.h"
|
||||
|
||||
namespace {
|
||||
|
||||
enum {
|
||||
// individual error codes
|
||||
kAttachEventListenerError = 0x0001,
|
||||
@@ -24,31 +23,6 @@ enum {
|
||||
kGenerateKeyRequestErrorBase = 0x0200,
|
||||
kAddKeyErrorBase = 0x0300,
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void INITIALIZE_CDM_MODULE() {}
|
||||
|
||||
void DeinitializeCdmModule() {}
|
||||
|
||||
void* CreateCdmInstance(int cdm_interface_version, const char* key_system,
|
||||
int key_system_size, GetCdmHostFunc get_cdm_host_func,
|
||||
void* user_data) {
|
||||
if (cdm_interface_version != cdm::kCdmInterfaceVersion) return NULL;
|
||||
|
||||
cdm::Host* host = static_cast<cdm::Host*>(
|
||||
get_cdm_host_func(cdm::kHostInterfaceVersion, user_data));
|
||||
|
||||
if (!host) return NULL;
|
||||
|
||||
return static_cast<cdm::ContentDecryptionModule*>(
|
||||
new wvcdm::WvContentDecryptionModule(host));
|
||||
}
|
||||
|
||||
int GetCdmVersion() { return cdm::kCdmInterfaceVersion; }
|
||||
|
||||
namespace {
|
||||
|
||||
static const std::string kWvCdmVersionString(WV_CDM_VERSION);
|
||||
|
||||
const int kCdmPolicyTimerDurationSeconds = 5;
|
||||
|
||||
@@ -58,10 +32,8 @@ 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;
|
||||
if (NULL == counter) return false;
|
||||
if (0 == block_count) return true;
|
||||
uint8_t carry = 0;
|
||||
uint8_t n = kIvSize - 1;
|
||||
while (n >= kCencIvSize) {
|
||||
@@ -79,35 +51,33 @@ bool Ctr128Add(size_t block_count, uint8_t* counter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// An empty iv string signals that the frame is unencrypted.
|
||||
bool IsBufferEncrypted(const cdm::InputBuffer& input_buffer) {
|
||||
return input_buffer.iv_size != 0;
|
||||
}
|
||||
// cdm::ContentDecryptionModule_1 implementation.
|
||||
|
||||
// cdm::ContentDecryptionModule implementation.
|
||||
|
||||
WvContentDecryptionModule::WvContentDecryptionModule(cdm::Host* host)
|
||||
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::~WvContentDecryptionModule() {
|
||||
WvContentDecryptionModule_1::~WvContentDecryptionModule_1() {
|
||||
DisablePolicyTimer();
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule::GenerateKeyRequest(
|
||||
cdm::Status WvContentDecryptionModule_1::GenerateKeyRequest(
|
||||
const char* type, int type_size, const uint8_t* init_data,
|
||||
int init_data_size) {
|
||||
LOGI("WvContentDecryptionModule::GenerateKeyRequest()");
|
||||
LOGI("WvContentDecryptionModule_1::GenerateKeyRequest()");
|
||||
CdmInitData init_data_internal(reinterpret_cast<const char*>(init_data),
|
||||
init_data_size);
|
||||
InitializationData initialization_data(ISO_BMFF_VIDEO_MIME_TYPE,
|
||||
std::string type_string(type, type_size);
|
||||
InitializationData initialization_data(type_string,
|
||||
init_data_internal);
|
||||
CdmKeyMessage key_request;
|
||||
CdmSessionId session_id;
|
||||
@@ -124,8 +94,8 @@ cdm::Status WvContentDecryptionModule::GenerateKeyRequest(
|
||||
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);
|
||||
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");
|
||||
@@ -150,7 +120,7 @@ cdm::Status WvContentDecryptionModule::GenerateKeyRequest(
|
||||
|
||||
result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, key_set_id, initialization_data, kLicenseTypeStreaming,
|
||||
app_parameters, &key_request, &server_url);
|
||||
app_parameters, &key_request, &server_url, NULL);
|
||||
if (KEY_MESSAGE != result) {
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
host_->SendKeyError("", 0, cdm::kClientError,
|
||||
@@ -164,18 +134,16 @@ cdm::Status WvContentDecryptionModule::GenerateKeyRequest(
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule::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::AddKey()");
|
||||
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);
|
||||
CdmResponseType response =
|
||||
cdm_engine_.AddKey(session_id_internal, key_data, &key_set_id);
|
||||
|
||||
if (response == KEY_ADDED) {
|
||||
EnablePolicyTimer();
|
||||
@@ -187,66 +155,58 @@ cdm::Status WvContentDecryptionModule::AddKey(const char* session_id,
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
bool WvContentDecryptionModule::IsKeyValid(const uint8_t* key_id,
|
||||
int key_id_size) {
|
||||
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::CloseSession(const char* session_id,
|
||||
int session_id_size) {
|
||||
LOGI("WvContentDecryptionModule::CloseSession()");
|
||||
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::TimerExpired(void* context) {
|
||||
LOGI("WvContentDecryptionModule::TimerExpired()");
|
||||
void WvContentDecryptionModule_1::TimerExpired(void* context) {
|
||||
LOGI("WvContentDecryptionModule_1::TimerExpired()");
|
||||
if (timer_enabled_) {
|
||||
cdm_engine_.OnTimerEvent();
|
||||
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule::Decrypt(
|
||||
cdm::Status WvContentDecryptionModule_1::Decrypt(
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
LOGI("WvContentDecryptionModule::Decrypt()");
|
||||
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);
|
||||
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);
|
||||
|
||||
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id?
|
||||
|
||||
if (!encrypted_buffer.subsamples ||
|
||||
encrypted_buffer.num_subsamples <= 0)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
CdmDecryptionParameters parameters(&key_id,
|
||||
encrypted_buffer.data, 0, &iv, 0,
|
||||
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
|
||||
NULL);
|
||||
parameters.is_secure = Properties::oem_crypto_use_secure_buffers();
|
||||
|
||||
return DoSubsampleDecrypt(parameters,
|
||||
iv,
|
||||
encrypted_buffer,
|
||||
decrypted_block);
|
||||
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::DecryptDecodeAndRenderFrame(
|
||||
cdm::Status WvContentDecryptionModule_1::DecryptDecodeAndRenderFrame(
|
||||
const cdm::InputBuffer& encrypted_buffer) {
|
||||
LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderFrame()");
|
||||
LOGI("WvContentDecryptionModule_1::DecryptDecodeAndRenderFrame()");
|
||||
|
||||
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
|
||||
return cdm::kDecryptError;
|
||||
@@ -255,24 +215,23 @@ cdm::Status WvContentDecryptionModule::DecryptDecodeAndRenderFrame(
|
||||
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);
|
||||
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id.
|
||||
|
||||
if (!encrypted_buffer.subsamples ||
|
||||
encrypted_buffer.num_subsamples <= 0)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
|
||||
NULL);
|
||||
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, 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::DecryptDecodeAndRenderSamples(
|
||||
cdm::Status WvContentDecryptionModule_1::DecryptDecodeAndRenderSamples(
|
||||
const cdm::InputBuffer& encrypted_buffer) {
|
||||
LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderSamples()");
|
||||
LOGI("WvContentDecryptionModule_1::DecryptDecodeAndRenderSamples()");
|
||||
|
||||
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
|
||||
return cdm::kDecryptError;
|
||||
@@ -281,92 +240,78 @@ cdm::Status WvContentDecryptionModule::DecryptDecodeAndRenderSamples(
|
||||
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);
|
||||
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id.
|
||||
|
||||
if (!encrypted_buffer.subsamples ||
|
||||
encrypted_buffer.num_subsamples <= 0)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
|
||||
NULL);
|
||||
parameters.is_video = false; // override the default true value for audio.
|
||||
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
|
||||
|
||||
if (encrypted_buffer.num_subsamples)
|
||||
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
|
||||
else
|
||||
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule::Destroy() { delete this; }
|
||||
void WvContentDecryptionModule_1::Destroy() { delete this; }
|
||||
|
||||
// Provisioning related methods
|
||||
cdm::Status WvContentDecryptionModule::GetProvisioningRequest(
|
||||
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) {
|
||||
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::HandleProvisioningResponse(
|
||||
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) {
|
||||
static_cast<CdmProvisioningRequest&>(response), &cert,
|
||||
&wrapped_key) == NO_ERROR) {
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule::EnablePolicyTimer() {
|
||||
LOGI("WvContentDecryptionModule::EnablePolicyTimer()");
|
||||
void WvContentDecryptionModule_1::EnablePolicyTimer() {
|
||||
LOGI("WvContentDecryptionModule_1::EnablePolicyTimer()");
|
||||
if (!timer_enabled_) {
|
||||
timer_enabled_ = true;
|
||||
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule::DisablePolicyTimer() {
|
||||
LOGI("WvContentDecryptionModule::DisablePolicyTimer()");
|
||||
void WvContentDecryptionModule_1::DisablePolicyTimer() {
|
||||
LOGI("WvContentDecryptionModule_1::DisablePolicyTimer()");
|
||||
timer_enabled_ = false;
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule::DoSubsampleDecrypt(
|
||||
CdmDecryptionParameters& parameters,
|
||||
std::vector < uint8_t >& iv,
|
||||
cdm::Status WvContentDecryptionModule_1::DoSubsampleDecrypt(
|
||||
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
|
||||
/* This routine assumes session_id and iv have already
|
||||
been initialized by the caller and encrypted_buffer contains subsample
|
||||
information. Also, parameters is expected to be pre-initialized with any
|
||||
needed parameters not related to subsample parsing.
|
||||
decrypted_block may be NULL. */
|
||||
|
||||
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id.
|
||||
CdmResponseType status = NO_ERROR;
|
||||
if (!encrypted_buffer.subsamples)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
size_t output_size = 0;
|
||||
for (int i = 0; i < encrypted_buffer.num_subsamples; ++i) {
|
||||
const cdm::SubsampleEntry& subsample = encrypted_buffer.subsamples[i];
|
||||
output_size += subsample.cipher_bytes + subsample.clear_bytes;
|
||||
}
|
||||
parameters.decrypt_buffer_length = output_size;
|
||||
assert(output_size ==
|
||||
encrypted_buffer.data_size - encrypted_buffer.data_offset);
|
||||
|
||||
if (decrypted_block) {
|
||||
cdm::Buffer* buffer = host_->Allocate(output_size);
|
||||
decrypted_block->SetDecryptedBuffer(buffer);
|
||||
parameters.decrypt_buffer = buffer->Data();
|
||||
} else {
|
||||
parameters.decrypt_buffer = NULL;
|
||||
}
|
||||
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;
|
||||
bool first = true;
|
||||
const cdm::SubsampleEntry* subsamples = encrypted_buffer.subsamples;
|
||||
|
||||
for (int i = 0; i < encrypted_buffer.num_subsamples; ++i) {
|
||||
const cdm::SubsampleEntry& subsample = subsamples[i];
|
||||
@@ -374,53 +319,103 @@ cdm::Status WvContentDecryptionModule::DoSubsampleDecrypt(
|
||||
for (int is_encrypted = 0; is_encrypted < 2; ++is_encrypted) {
|
||||
size_t bytes =
|
||||
is_encrypted ? subsample.cipher_bytes : subsample.clear_bytes;
|
||||
if (0 == bytes)
|
||||
continue;
|
||||
if (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 (is_encrypted)
|
||||
encrypted_offset += bytes;
|
||||
if (bytes == 0) continue;
|
||||
|
||||
parameters.is_encrypted = is_encrypted;
|
||||
parameters.subsample_flags =
|
||||
first ? OEMCrypto_FirstSubsample : 0;
|
||||
parameters.subsample_flags |= (
|
||||
offset == static_cast<size_t>(encrypted_buffer.data_size)
|
||||
? OEMCrypto_LastSubsample : 0);
|
||||
((offset == 0) ? OEMCrypto_FirstSubsample : 0) |
|
||||
((offset + bytes == output_size) ? OEMCrypto_LastSubsample : 0);
|
||||
|
||||
first = false;
|
||||
|
||||
status = cdm_engine_.Decrypt(session_id, parameters);
|
||||
|
||||
switch (status) {
|
||||
case wvcdm::NEED_KEY:
|
||||
return cdm::kNoKey;
|
||||
case wvcdm::NO_ERROR:
|
||||
break;
|
||||
default:
|
||||
return cdm::kDecryptError;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
int64_t WvContentDecryptionModule::GetCurrentTimeInSeconds() {
|
||||
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::NewFileImpl() {
|
||||
File::Impl* WvContentDecryptionModule_1::NewFileImpl() {
|
||||
return new File::Impl(host_);
|
||||
}
|
||||
|
||||
597
cdm/src/wv_content_decryption_module_4.cpp
Normal file
597
cdm/src/wv_content_decryption_module_4.cpp
Normal file
@@ -0,0 +1,597 @@
|
||||
// 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 (int 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 (int 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