Source release 16.2.0
This commit is contained in:
233
cdm/src/cdm.cpp
233
cdm/src/cdm.cpp
@@ -7,6 +7,8 @@
|
||||
#include <limits.h> // LLONG_MAX
|
||||
#include <string.h> // memcpy
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -20,7 +22,7 @@
|
||||
#include "file_store.h"
|
||||
#include "license.h"
|
||||
#include "log.h"
|
||||
#include "metrics_collections.h"
|
||||
#include "metrics.pb.h"
|
||||
#include "properties.h"
|
||||
#include "service_certificate.h"
|
||||
#include "string_conversions.h"
|
||||
@@ -57,7 +59,7 @@ struct HostType {
|
||||
initialized(false) {}
|
||||
} host;
|
||||
|
||||
class PropertySet : public CdmClientPropertySet {
|
||||
class PropertySet final : public CdmClientPropertySet {
|
||||
public:
|
||||
PropertySet() : use_privacy_mode_(false) {}
|
||||
|
||||
@@ -119,7 +121,7 @@ class PropertySet : public CdmClientPropertySet {
|
||||
// bugs are resolved, we can change this wrapper to return errors on write
|
||||
// attempts. The wrapper itself will still be necessary, in order to prevent any
|
||||
// future bugs from corrupting ATSC files.
|
||||
class ReadOnlyStorage : public Cdm::IStorage {
|
||||
class ReadOnlyStorage final : public Cdm::IStorage {
|
||||
public:
|
||||
explicit ReadOnlyStorage(Cdm::IStorage* storage) : storage_(storage) {}
|
||||
~ReadOnlyStorage() override {}
|
||||
@@ -158,7 +160,23 @@ class ReadOnlyStorage : public Cdm::IStorage {
|
||||
Cdm::IStorage* storage_; // Lifetime is managed by the caller
|
||||
};
|
||||
|
||||
class CdmImpl : public Cdm, public WvCdmEventListener {
|
||||
// This class wraps a raw C array passed as a pointer and length in the
|
||||
// appropriate interface to be used with functions that expect a C++11 range.
|
||||
template <typename T>
|
||||
class CArrayRangeView {
|
||||
public:
|
||||
CArrayRangeView(const T* data, size_t length)
|
||||
: data_(data), length_(length) {}
|
||||
|
||||
const T* begin() const { return data_; }
|
||||
const T* end() const { return data_ + length_; }
|
||||
|
||||
private:
|
||||
const T* const data_;
|
||||
const size_t length_;
|
||||
};
|
||||
|
||||
class CdmImpl final : public Cdm, public WvCdmEventListener {
|
||||
public:
|
||||
CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode,
|
||||
const ReadOnlyStorage* owned_storage_object);
|
||||
@@ -189,8 +207,6 @@ class CdmImpl : public Cdm, public WvCdmEventListener {
|
||||
|
||||
Status removeProvisioning() override;
|
||||
|
||||
Status removeUsageTable() override;
|
||||
|
||||
Status listStoredLicenses(std::vector<std::string>* key_set_ids) override;
|
||||
|
||||
Status listUsageRecords(std::vector<std::string>* ksids) override;
|
||||
@@ -246,10 +262,10 @@ class CdmImpl : public Cdm, public WvCdmEventListener {
|
||||
|
||||
Status forceRemove(const std::string& session_id) override;
|
||||
|
||||
Status decrypt(const InputBuffer& input, const OutputBuffer& output) override;
|
||||
Status decrypt(const DecryptionBatch& batch) override;
|
||||
|
||||
Status decrypt(const std::string& session_id, const InputBuffer& input,
|
||||
const OutputBuffer& output) override;
|
||||
Status decrypt(const std::string& session_id,
|
||||
const DecryptionBatch& batch) override;
|
||||
|
||||
Status genericEncrypt(const std::string& session_id,
|
||||
const std::string& in_buffer, const std::string& key_id,
|
||||
@@ -272,6 +288,9 @@ class CdmImpl : public Cdm, public WvCdmEventListener {
|
||||
|
||||
Status setVideoResolution(const std::string& session_id, uint32_t width,
|
||||
uint32_t height) override;
|
||||
|
||||
Status getMetrics(std::string* serialized_metrics) override;
|
||||
|
||||
// ITimerClient:
|
||||
void onTimerExpired(void* context) override;
|
||||
|
||||
@@ -522,18 +541,6 @@ Cdm::Status CdmImpl::removeProvisioning() {
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::removeUsageTable() {
|
||||
CdmResponseType result = cdm_engine_->DeleteUsageTable(kSecurityLevelL1);
|
||||
if (result == SYSTEM_INVALIDATED_ERROR) {
|
||||
LOGE("System invalidated");
|
||||
return kSystemStateLost;
|
||||
} else if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::listStoredLicenses(std::vector<std::string>* key_set_ids) {
|
||||
if (key_set_ids == nullptr) {
|
||||
LOGE("Missing vector parameter to receive key_set_ids.");
|
||||
@@ -881,6 +888,9 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
} else if (result == STORAGE_PROHIBITED) {
|
||||
LOGE("A temporary session cannot be used for a persistent usage records.");
|
||||
return kRangeError;
|
||||
} else if (result == NEED_PROVISIONING) {
|
||||
LOGE("The device needs to reprovision.");
|
||||
return kNeedsDeviceCertificate;
|
||||
} else if (result != KEY_ADDED) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
@@ -1190,54 +1200,90 @@ Cdm::Status CdmImpl::forceRemove(const std::string& session_id) {
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
|
||||
const OutputBuffer& output) {
|
||||
Cdm::Status CdmImpl::decrypt(const DecryptionBatch& batch) {
|
||||
std::string empty_session_id;
|
||||
return decrypt(empty_session_id, input, output);
|
||||
return decrypt(empty_session_id, batch);
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::decrypt(const std::string& session_id,
|
||||
const InputBuffer& input,
|
||||
const OutputBuffer& output) {
|
||||
const bool is_encrypted = (input.encryption_scheme != kClear);
|
||||
if (is_encrypted && input.iv_length != 16) {
|
||||
LOGE("The IV must be 16 bytes long.");
|
||||
return kTypeError;
|
||||
const DecryptionBatch& batch) {
|
||||
if (batch.encryption_scheme == kAesCtr &&
|
||||
(batch.pattern.encrypted_blocks != 0 ||
|
||||
batch.pattern.clear_blocks != 0)) {
|
||||
LOGE("The 'cens' schema is not supported.");
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
if (PropertiesCE::GetSecureOutputType() == kNoSecureOutput &&
|
||||
output.is_secure) {
|
||||
batch.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);
|
||||
const CArrayRangeView<Sample> samples(batch.samples, batch.samples_length);
|
||||
for (const Sample& sample : samples) {
|
||||
if (sample.input.data_length >
|
||||
(sample.output.data_length - sample.output.data_offset)) {
|
||||
LOGE("The output buffer is too small to contain the input buffer.");
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
CdmDecryptionParameters parameters;
|
||||
parameters.is_encrypted = is_encrypted;
|
||||
parameters.is_secure = output.is_secure;
|
||||
if (input.encryption_scheme == kAesCtr) {
|
||||
const CArrayRangeView<Subsample> subsamples(sample.input.subsamples,
|
||||
sample.input.subsamples_length);
|
||||
const bool is_encrypted =
|
||||
(batch.encryption_scheme != kClear) &&
|
||||
std::any_of(std::begin(subsamples), std::end(subsamples),
|
||||
[](const Subsample& subsample) -> bool {
|
||||
return subsample.protected_bytes > 0;
|
||||
});
|
||||
if (is_encrypted && sample.input.iv_length != 16) {
|
||||
LOGE("The IV must be 16 bytes long.");
|
||||
return kTypeError;
|
||||
}
|
||||
}
|
||||
|
||||
// With the input validated, we may copy it into a CdmDecryptionParametersV16
|
||||
CdmDecryptionParametersV16 parameters;
|
||||
parameters.key_id.assign(batch.key_id, batch.key_id + batch.key_id_length);
|
||||
parameters.is_secure = batch.is_secure;
|
||||
if (batch.encryption_scheme == kAesCtr) {
|
||||
parameters.cipher_mode = kCipherModeCtr;
|
||||
} else if (input.encryption_scheme == kAesCbc) {
|
||||
} else if (batch.encryption_scheme == kAesCbc) {
|
||||
parameters.cipher_mode = kCipherModeCbc;
|
||||
}
|
||||
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;
|
||||
parameters.pattern_descriptor.encrypt_blocks = input.pattern.encrypted_blocks;
|
||||
parameters.pattern_descriptor.skip_blocks = input.pattern.clear_blocks;
|
||||
parameters.is_video = batch.is_video;
|
||||
parameters.pattern.encrypt_blocks = batch.pattern.encrypted_blocks;
|
||||
parameters.pattern.skip_blocks = batch.pattern.clear_blocks;
|
||||
|
||||
CdmResponseType result = cdm_engine_->Decrypt(session_id, parameters);
|
||||
parameters.samples.reserve(batch.samples_length);
|
||||
std::transform(
|
||||
std::begin(samples), std::end(samples),
|
||||
std::back_inserter(parameters.samples),
|
||||
[](const Sample& sample) -> CdmDecryptionSample {
|
||||
CdmDecryptionSample cdm_sample;
|
||||
cdm_sample.encrypt_buffer = sample.input.data;
|
||||
cdm_sample.encrypt_buffer_length = sample.input.data_length;
|
||||
cdm_sample.decrypt_buffer = sample.output.data;
|
||||
cdm_sample.decrypt_buffer_size = sample.output.data_length;
|
||||
cdm_sample.decrypt_buffer_offset = sample.output.data_offset;
|
||||
cdm_sample.iv.assign(sample.input.iv,
|
||||
sample.input.iv + sample.input.iv_length);
|
||||
|
||||
const CArrayRangeView<Subsample> subsamples(
|
||||
sample.input.subsamples, sample.input.subsamples_length);
|
||||
cdm_sample.subsamples.reserve(sample.input.subsamples_length);
|
||||
std::transform(
|
||||
std::begin(subsamples), std::end(subsamples),
|
||||
std::back_inserter(cdm_sample.subsamples),
|
||||
[](const Subsample& subsample) -> CdmDecryptionSubsample {
|
||||
return CdmDecryptionSubsample(subsample.clear_bytes,
|
||||
subsample.protected_bytes);
|
||||
});
|
||||
|
||||
return cdm_sample;
|
||||
});
|
||||
|
||||
CdmResponseType result = cdm_engine_->DecryptV16(session_id, parameters);
|
||||
|
||||
if (result == NO_ERROR) {
|
||||
return kSuccess;
|
||||
@@ -1442,6 +1488,20 @@ Cdm::Status CdmImpl::setVideoResolution(const std::string& session_id,
|
||||
}
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::getMetrics(std::string* serialized_metrics) {
|
||||
if (serialized_metrics == nullptr) {
|
||||
LOGE("Missing string parameter to receive serialized metrics.");
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
drm_metrics::WvCdmMetrics metrics;
|
||||
if (!cdm_engine_->GetMetricsSnapshot(&metrics) ||
|
||||
!metrics.SerializeToString(serialized_metrics)) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
void CdmImpl::onTimerExpired(void* context) {
|
||||
if (context == kPolicyTimerContext) {
|
||||
if (policy_timer_enabled_) {
|
||||
@@ -1674,21 +1734,21 @@ using namespace widevine;
|
||||
|
||||
int64_t Clock::GetCurrentTime() { return host.clock->now() / 1000; }
|
||||
|
||||
class FileImpl : public File {
|
||||
class FileImpl final : public File {
|
||||
public:
|
||||
FileImpl() {}
|
||||
|
||||
~FileImpl() override {}
|
||||
FileImpl(Cdm::IStorage* storage, const std::string& path, int flags)
|
||||
: storage_(storage),
|
||||
name_(path),
|
||||
read_only_(flags & FileSystem::kReadOnly),
|
||||
truncate_(flags & FileSystem::kTruncate) {
|
||||
assert(storage_);
|
||||
}
|
||||
|
||||
ssize_t Read(char* buffer, size_t bytes) override {
|
||||
if (!buffer) {
|
||||
LOGW("File::Read: buffer is empty");
|
||||
return -1;
|
||||
}
|
||||
if (!storage_) {
|
||||
LOGW("File::Open: buffer is empty");
|
||||
return -1;
|
||||
}
|
||||
std::string data;
|
||||
if (!storage_->read(name_, &data)) {
|
||||
return -1;
|
||||
@@ -1704,8 +1764,8 @@ class FileImpl : public File {
|
||||
LOGW("File::Write: buffer is empty");
|
||||
return -1;
|
||||
}
|
||||
if (!storage_) {
|
||||
LOGW("File::Write: file not open");
|
||||
if (read_only_) {
|
||||
LOGE("File::Write: file is read-only.");
|
||||
return -1;
|
||||
}
|
||||
if (!truncate_) {
|
||||
@@ -1719,67 +1779,54 @@ class FileImpl : public File {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
Cdm::IStorage* storage_;
|
||||
std::string name_;
|
||||
bool read_only_;
|
||||
bool truncate_;
|
||||
private:
|
||||
Cdm::IStorage* const storage_;
|
||||
const std::string name_;
|
||||
const bool read_only_;
|
||||
const bool truncate_;
|
||||
};
|
||||
|
||||
class FileSystem::Impl {
|
||||
public:
|
||||
widevine::Cdm::IStorage* storage_;
|
||||
Impl(widevine::Cdm::IStorage* storage) : storage_(storage) {
|
||||
assert(storage);
|
||||
}
|
||||
|
||||
widevine::Cdm::IStorage* const storage_;
|
||||
};
|
||||
|
||||
FileSystem::FileSystem() : impl_(new Impl()) {
|
||||
assert(nullptr != host.storage);
|
||||
impl_->storage_ = host.storage;
|
||||
}
|
||||
FileSystem::FileSystem() : impl_(new Impl(host.storage)) {}
|
||||
|
||||
FileSystem::FileSystem(const std::string& origin, void* extra_data)
|
||||
: impl_(new Impl()), origin_(origin) {
|
||||
assert(nullptr != extra_data);
|
||||
impl_->storage_ = (widevine::Cdm::IStorage*)extra_data;
|
||||
}
|
||||
: impl_(new Impl(reinterpret_cast<widevine::Cdm::IStorage*>(extra_data))),
|
||||
origin_(origin) {}
|
||||
|
||||
FileSystem::~FileSystem() {}
|
||||
|
||||
std::unique_ptr<File> FileSystem::Open(const std::string& file_path,
|
||||
int flags) {
|
||||
if (!impl_ || (!(flags & kCreate) && !impl_->storage_->exists(file_path))) {
|
||||
if (!(flags & kCreate) && !impl_->storage_->exists(file_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<FileImpl> file_impl(new FileImpl());
|
||||
file_impl->storage_ = impl_->storage_;
|
||||
file_impl->name_ = file_path;
|
||||
file_impl->read_only_ = (flags & kReadOnly);
|
||||
file_impl->truncate_ = (flags & kTruncate);
|
||||
return std::move(file_impl);
|
||||
return std::unique_ptr<File>(new FileImpl(impl_->storage_, file_path, flags));
|
||||
}
|
||||
|
||||
bool FileSystem::Exists(const std::string& file_path) {
|
||||
if (!impl_) return false;
|
||||
|
||||
return !file_path.empty() && impl_->storage_->exists(file_path);
|
||||
}
|
||||
|
||||
bool FileSystem::Remove(const std::string& file_path) {
|
||||
if (!impl_) return false;
|
||||
|
||||
return impl_->storage_->remove(file_path);
|
||||
}
|
||||
|
||||
ssize_t FileSystem::FileSize(const std::string& file_path) {
|
||||
if (!impl_) return -1;
|
||||
|
||||
return impl_->storage_->size(file_path);
|
||||
}
|
||||
|
||||
bool FileSystem::List(const std::string&,
|
||||
std::vector<std::string>* file_names) {
|
||||
if (!impl_ || !file_names) return false;
|
||||
|
||||
return impl_->storage_->list(file_names);
|
||||
return file_names && impl_->storage_->list(file_names);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -20,7 +20,8 @@ void InitLogging() {}
|
||||
void Log(const char* file, const char* function, int line, LogPriority level,
|
||||
const char* fmt, ...) {
|
||||
const char* severities[] = {"ERROR", "WARN", "INFO", "DEBUG", "VERBOSE"};
|
||||
if (level >= sizeof(severities) / sizeof(*severities)) {
|
||||
if (level >=
|
||||
static_cast<LogPriority>(sizeof(severities) / sizeof(*severities))) {
|
||||
fprintf(stderr, "[FATAL:%s(%d)] Invalid log priority level: %d\n", file,
|
||||
line, level);
|
||||
return;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
#include "properties.h"
|
||||
#include "properties_ce.h"
|
||||
|
||||
#include "cdm_version.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
|
||||
// This anonymous namespace is shared between both the widevine namespace and
|
||||
// wvcdm namespace objects below.
|
||||
@@ -93,6 +93,8 @@ void Properties::InitOnce() {
|
||||
provisioning_messages_are_binary_ = set_provisioning_messages_to_binary_;
|
||||
allow_service_certificate_requests_ = false;
|
||||
device_files_is_a_real_filesystem_ = false;
|
||||
allow_restore_of_offline_licenses_with_release_ = true;
|
||||
delay_oem_crypto_termination_ = false;
|
||||
session_property_set_.reset(new CdmClientPropertySetMap());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user