Files
ce_cdm/cdm/src/wv_content_decryption_module.cpp
Joey Parrish 66794025d4 Initial source release: v2.0.8-0-679
Change-Id: Idf6316a8faf4b4fdc54265aad12084e5aa60707a
2014-05-20 11:08:09 -07:00

411 lines
14 KiB
C++

// Copyright 2013 Google Inc. All Rights Reserved.
#include "wv_content_decryption_module.h"
#include <iostream>
#include <string.h>
#include "cdm_client_property_set.h"
#include "content_decryption_module.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"
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 = 1;
const int kCdmPolicyTimerCancel = 0;
// 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 {
// 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 implementation.
WvContentDecryptionModule::~WvContentDecryptionModule() {
DisablePolicyTimer();
}
cdm::Status WvContentDecryptionModule::GenerateKeyRequest(
const char* type, int type_size, const uint8_t* init_data,
int init_data_size) {
LOGI("Enter WvContentDecryptionModule::GenerateKeyRequest()");
CdmInitData init_data_internal(reinterpret_cast<const char*>(init_data),
init_data_size);
CdmKeyMessage key_request;
CdmSessionId session_id;
std::string security_level;
std::string privacy_mode;
kVectorBytes service_certificate;
host_->GetPlatformString("SecurityLevel", &security_level);
host_->GetPlatformString("PrivacyOn", &privacy_mode);
host_->GetPlatformByteArray("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 (NEED_PROVISIONING == result) {
LOGI("Need to aquire a Device Certificate from the Provisioning Server");
return cdm::kNeedsDeviceCertificate;
}
if (NO_ERROR != result) return cdm::kSessionError;
if (!cdm_engine_.AttachEventListener(session_id, &host_event_listener_)) {
cdm_engine_.CloseSession(session_id);
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, init_data_internal, kLicenseTypeStreaming,
app_parameters, &key_request, &server_url);
if (KEY_MESSAGE != result) {
cdm_engine_.CloseSession(session_id);
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::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("Enter WvContentDecryptionModule::AddKey()\n");
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;
} else {
return cdm::kSessionError;
}
}
cdm::Status WvContentDecryptionModule::CancelKeyRequest(const char* session_id,
int session_id_size) {
LOGI("Enter WvContentDecryptionModule::CancelKeyRequest()\n");
CdmSessionId session_id_internal(session_id, session_id_size);
return cdm_engine_.CancelKeyRequest(session_id_internal) == NO_ERROR
? cdm::kSuccess
: cdm::kSessionError;
}
void WvContentDecryptionModule::TimerExpired(void* context) {
LOGI("Timer expired, send cdm_engine OnTimerEvent");
if (this != context) {
LOGD("Context should have been set, Timer Expired Error\n");
return;
}
OnTimerEvent();
}
cdm::Status WvContentDecryptionModule::Decrypt(
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
LOGI("=>Enter WvContentDecryptionModule::Decrypt()\n");
if (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);
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id?
if (NULL == encrypted_buffer.subsamples
|| encrypted_buffer.num_subsamples <= 0)
return cdm::kDecryptError;
CdmDecryptionParameters parameters(&key_id,
encrypted_buffer.data, 0, &iv, 0,
NULL);
parameters.is_secure = false;
return DoSubsampleDecrypt(session_id,
parameters,
iv,
encrypted_buffer,
decrypted_block);
}
cdm::Status WvContentDecryptionModule::InitializeAudioDecoder(
const cdm::AudioDecoderConfig& audio_decoder_config) {
LOGI("WvContentDecryptionModule::InitializeAudioDecoder() Not implemented\n");
return cdm::kDecodeError;
}
cdm::Status WvContentDecryptionModule::InitializeVideoDecoder(
const cdm::VideoDecoderConfig& video_decoder_config) {
LOGI("WvContentDecryptionModule::InitializeVideoDecoder() Not implemented\n");
return cdm::kDecodeError;
}
void WvContentDecryptionModule::DeinitializeDecoder(
cdm::StreamType decoder_type) {
LOGI("WvContentDecryptionModule::DeInitializeDecoder() Not implemented\n");
}
void WvContentDecryptionModule::ResetDecoder(cdm::StreamType decoder_type) {
LOGI("WvContentDecryptionModule::ResetDecoder() Not implemented\n");
}
cdm::Status WvContentDecryptionModule::DecryptAndDecodeFrame(
const cdm::InputBuffer& encrypted_buffer, cdm::VideoFrame* video_frame) {
LOGI("WvContentDecryptionModule::DecryptAndDecodeFrame() Not implemented\n");
return cdm::kDecodeError;
}
cdm::Status WvContentDecryptionModule::DecryptAndDecodeSamples(
const cdm::InputBuffer& encrypted_buffer, cdm::AudioFrames* audio_frames) {
LOGI(
"WvContentDecryptionModule::DecryptAndDecodeSamples() Not implemented\n");
return cdm::kDecodeError;
}
// 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(
const cdm::InputBuffer& encrypted_buffer) {
LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderFrame()\n");
if (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);
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id.
if (NULL == encrypted_buffer.subsamples
|| encrypted_buffer.num_subsamples <= 0)
return cdm::kDecryptError;
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL);
return DoSubsampleDecrypt(session_id, 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(
const cdm::InputBuffer& encrypted_buffer) {
LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderSamples()\n");
if (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);
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id.
if (NULL == 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(session_id, parameters, iv, encrypted_buffer,
NULL);
}
void WvContentDecryptionModule::Destroy() { delete this; }
// Provisioning related methods
cdm::Status WvContentDecryptionModule::GetProvisioningRequest(
std::string* request, std::string* provisioning_server_url) {
if (cdm_engine_.GetProvisioningRequest(
static_cast<CdmProvisioningRequest*>(request),
provisioning_server_url) == NO_ERROR) {
return cdm::kSuccess;
}
return cdm::kSessionError;
}
cdm::Status WvContentDecryptionModule::HandleProvisioningResponse(
std::string& response) {
if (cdm_engine_.HandleProvisioningResponse(
static_cast<CdmProvisioningRequest&>(response)) == NO_ERROR) {
return cdm::kSuccess;
}
return cdm::kSessionError;
}
void WvContentDecryptionModule::EnablePolicyTimer() {
LOGI("WvContentDecryptionModule::EnablePolicyTimer()\n");
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, this);
}
void WvContentDecryptionModule::DisablePolicyTimer() {
LOGI("WvContentDecryptionModule::DisablePolicyTimer()\n");
host_->SetTimer(kCdmPolicyTimerCancel, NULL);
}
void WvContentDecryptionModule::OnTimerEvent() {
LOGI("WvContentDecryptionModule::OnTimerEvent()\n");
cdm_engine_.OnTimerEvent();
}
cdm::Status WvContentDecryptionModule::DoSubsampleDecrypt(
CdmSessionId& session_id,
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. */
CdmResponseType status = NO_ERROR;
uint8_t* output_buffer = decrypted_block
? reinterpret_cast<uint8_t*>(
decrypted_block->DecryptedBuffer()->Data())
: NULL;
size_t offset = 0;
size_t encrypted_offset = 0;
uint32_t block_ctr = 0;
const cdm::SubsampleEntry *subsamples = encrypted_buffer.subsamples;
bool first = true;
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 (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];
if (output_buffer)
parameters.decrypt_buffer = &output_buffer[offset];
parameters.encrypt_length = bytes;
parameters.decrypt_buffer_length = encrypted_buffer.data_size - offset;
parameters.block_offset = encrypted_offset % kIvSize;
offset += bytes;
if (is_encrypted)
encrypted_offset += bytes;
parameters.is_encrypted = is_encrypted;
parameters.subsample_flags =
(true == first) ? OEMCrypto_FirstSubsample : 0;
parameters.subsample_flags |= (
offset == encrypted_buffer.data_size ? OEMCrypto_LastSubsample : 0);
first = false;
status = cdm_engine_.Decrypt(session_id, parameters);
switch (status) {
case wvcdm::NEED_KEY:
return cdm::kNoKey;
break;
case wvcdm::NO_ERROR:
break;
default:
return cdm::kDecryptError;
break;
}
}
}
return cdm::kSuccess;
}
} // namespace wvcdm