This reverts commit f6f5099604.
Reason for revert: Feature missed deadline
Bug: 135283522
Change-Id: Ic86930ee3444c5a6aa1d78ae3a12a9030c29ef92
278 lines
9.2 KiB
C++
278 lines
9.2 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine
|
|
// License Agreement.
|
|
//
|
|
// Reference implementation of OEMCrypto APIs
|
|
//
|
|
#include "oemcrypto_engine_ref.h"
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <openssl/aes.h>
|
|
#include <openssl/err.h>
|
|
|
|
#include "clock.h"
|
|
#include "keys.h"
|
|
#include "log.h"
|
|
#include "oemcrypto_key_ref.h"
|
|
#include "oemcrypto_rsa_key_shared.h"
|
|
#include "string_conversions.h"
|
|
|
|
namespace {
|
|
|
|
// Lower bits in SessionId are actual session id. The rest higher bits are
|
|
// session type.
|
|
const uint32_t kSessionIdTypeShift = 28;
|
|
const uint32_t kSessionIdMask = (1u << kSessionIdTypeShift) - 1u;
|
|
} // namespace
|
|
|
|
namespace wvoec_ref {
|
|
|
|
// Note: The class CryptoEngine is configured at compile time by compiling in
|
|
// different device property files. The methods in this file are generic to
|
|
// all configurations. See the files oemcrypto_engine_device_properties*.cpp
|
|
// for methods that are configured for specific configurations.
|
|
|
|
CryptoEngine::CryptoEngine(std::unique_ptr<wvcdm::FileSystem>&& file_system)
|
|
: file_system_(std::move(file_system)), usage_table_() {
|
|
ERR_load_crypto_strings();
|
|
}
|
|
|
|
CryptoEngine::~CryptoEngine() {
|
|
ERR_free_strings();
|
|
}
|
|
|
|
bool CryptoEngine::Initialize() {
|
|
std::string file_path = GetUsageTimeFileFullPath();
|
|
LoadOfflineTimeInfo(file_path);
|
|
usage_table_.reset(MakeUsageTable());
|
|
return root_of_trust_.Initialize(config_provisioning_method());
|
|
}
|
|
|
|
void CryptoEngine::Terminate() {
|
|
std::string file_path = GetUsageTimeFileFullPath();
|
|
SaveOfflineTimeInfo(file_path);
|
|
std::unique_lock<std::mutex> lock(session_table_lock_);
|
|
ActiveSessions::iterator it;
|
|
for (it = sessions_.begin(); it != sessions_.end(); ++it) {
|
|
delete it->second;
|
|
}
|
|
sessions_.clear();
|
|
root_of_trust_.Clear();
|
|
}
|
|
|
|
SessionId CryptoEngine::OpenSession() {
|
|
std::unique_lock<std::mutex> lock(session_table_lock_);
|
|
static OEMCrypto_SESSION unique_id = 1;
|
|
SessionId id = ++unique_id;
|
|
// Check if too many sessions have been opened.
|
|
if (SessionTypeBits(id) != 0) {
|
|
return 0;
|
|
}
|
|
// Apply session type to higher bits.
|
|
id = (kSessionTypeOEMCrypto << kSessionIdTypeShift) | (id & kSessionIdMask);
|
|
sessions_[id] = MakeSession(id);
|
|
return id;
|
|
}
|
|
|
|
SessionContext* CryptoEngine::MakeSession(SessionId sid) {
|
|
return new SessionContext(this, sid, root_of_trust_.SharedRsaKey());
|
|
}
|
|
|
|
UsageTable* CryptoEngine::MakeUsageTable() { return new UsageTable(this); }
|
|
|
|
bool CryptoEngine::DestroySession(SessionId sid) {
|
|
SessionContext* sctx = FindSession(sid);
|
|
std::unique_lock<std::mutex> lock(session_table_lock_);
|
|
if (sctx) {
|
|
sessions_.erase(sid);
|
|
delete sctx;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
SessionContext* CryptoEngine::FindSession(SessionId sid) {
|
|
std::unique_lock<std::mutex> lock(session_table_lock_);
|
|
ActiveSessions::iterator it = sessions_.find(sid);
|
|
if (it != sessions_.end()) {
|
|
return it->second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int64_t CryptoEngine::MonotonicTime() {
|
|
// Use the monotonic clock for times that don't have to be stable across
|
|
// device boots.
|
|
int64_t now =
|
|
wvcdm::Clock().GetCurrentTime() + offline_time_info_.rollback_offset;
|
|
static int64_t then = now;
|
|
if (now < then) {
|
|
LOGW("Clock rollback detected: %ld seconds", then - now);
|
|
offline_time_info_.rollback_offset += then - now;
|
|
now = then;
|
|
}
|
|
then = now;
|
|
return now;
|
|
}
|
|
|
|
int64_t CryptoEngine::SystemTime() {
|
|
const int64_t current_time = MonotonicTime();
|
|
// Write time info to disk if kTimeInfoUpdateWindowInSeconds has elapsed since
|
|
// last write.
|
|
if (current_time - offline_time_info_.previous_time >
|
|
kTimeInfoUpdateWindowInSeconds) {
|
|
std::string file_path = GetUsageTimeFileFullPath();
|
|
SaveOfflineTimeInfo(file_path);
|
|
}
|
|
return current_time;
|
|
}
|
|
|
|
std::string CryptoEngine::GetUsageTimeFileFullPath() const {
|
|
std::string file_path;
|
|
// Note: file path is OK for a real implementation, but using security
|
|
// level 1 would be better.
|
|
// TODO(fredgc, jfore): Address how this property is presented to the ref.
|
|
// For now, the file path is empty.
|
|
/*if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
|
|
&file_path)) {
|
|
LOGE("Unable to get base path");
|
|
}*/
|
|
return file_path + kStoredUsageTimeFileName;
|
|
}
|
|
|
|
bool CryptoEngine::LoadOfflineTimeInfo(const std::string& file_path) {
|
|
memset(&offline_time_info_, 0, sizeof(TimeInfo));
|
|
wvcdm::FileSystem* file_system = file_system_.get();
|
|
if (file_system->Exists(file_path)) {
|
|
std::unique_ptr<wvcdm::File> file =
|
|
file_system->Open(file_path, wvcdm::FileSystem::kReadOnly);
|
|
if (!file) {
|
|
// This error is expected at first initialization.
|
|
LOGE("File open failed (this is expected on first initialization): %s",
|
|
file_path.c_str());
|
|
return false;
|
|
}
|
|
// Load time info from previous call.
|
|
file->Read(reinterpret_cast<char*>(&offline_time_info_), sizeof(TimeInfo));
|
|
|
|
// Detect offline time rollback after loading from disk.
|
|
// Add any time offsets in the past to the current time.
|
|
int64_t current_time = MonotonicTime();
|
|
if (offline_time_info_.previous_time > current_time) {
|
|
// Current time is earlier than the previously saved time. Time has been
|
|
// rolled back. Update the rollback offset.
|
|
offline_time_info_.rollback_offset +=
|
|
offline_time_info_.previous_time - current_time;
|
|
// Keep current time at previous recorded time.
|
|
current_time = offline_time_info_.previous_time;
|
|
}
|
|
// The new previous_time will either stay the same or move forward.
|
|
offline_time_info_.previous_time = current_time;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CryptoEngine::SaveOfflineTimeInfo(const std::string& file_path) {
|
|
// Add any time offsets in the past to the current time. If there was an
|
|
// earlier offline rollback, the rollback offset will be updated in
|
|
// LoadOfflineTimeInfo(). It guarantees that the current time to be saved
|
|
// will never go back.
|
|
const int64_t current_time = MonotonicTime();
|
|
// The new previous_time will either stay the same or move forward.
|
|
if (current_time > offline_time_info_.previous_time)
|
|
offline_time_info_.previous_time = current_time;
|
|
|
|
std::unique_ptr<wvcdm::File> file;
|
|
wvcdm::FileSystem* file_system = file_system_.get();
|
|
// Write the current time and offset to disk.
|
|
file = file_system->Open(
|
|
file_path, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate);
|
|
if (!file) {
|
|
LOGE("File open failed: %s", file_path.c_str());
|
|
return false;
|
|
}
|
|
file->Write(reinterpret_cast<char*>(&offline_time_info_), sizeof(TimeInfo));
|
|
return true;
|
|
}
|
|
|
|
bool CryptoEngine::NonceCollision(uint32_t nonce) {
|
|
for (const auto& session_pair : sessions_) {
|
|
const SessionContext* session = session_pair.second;
|
|
if (nonce == session->nonce()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
OEMCrypto_HDCP_Capability CryptoEngine::config_current_hdcp_capability() {
|
|
return config_local_display_only() ? HDCP_NO_DIGITAL_OUTPUT : HDCP_V1;
|
|
}
|
|
|
|
OEMCrypto_HDCP_Capability CryptoEngine::config_maximum_hdcp_capability() {
|
|
return HDCP_NO_DIGITAL_OUTPUT;
|
|
}
|
|
|
|
OEMCryptoResult CryptoEngine::SetDestination(
|
|
const OEMCrypto_DestBufferDesc& out_description, size_t data_length,
|
|
uint8_t subsample_flags) {
|
|
size_t max_length = 0;
|
|
switch (out_description.type) {
|
|
case OEMCrypto_BufferType_Clear:
|
|
destination_ = out_description.buffer.clear.address;
|
|
max_length = out_description.buffer.clear.address_length;
|
|
break;
|
|
case OEMCrypto_BufferType_Secure:
|
|
if (out_description.buffer.secure.handle_length <
|
|
out_description.buffer.secure.offset) {
|
|
LOGE("Secure buffer offset too large: %zu < %zu",
|
|
out_description.buffer.secure.handle_length,
|
|
out_description.buffer.secure.offset);
|
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
|
}
|
|
destination_ =
|
|
reinterpret_cast<uint8_t*>(out_description.buffer.secure.handle) +
|
|
out_description.buffer.secure.offset;
|
|
max_length = out_description.buffer.secure.handle_length -
|
|
out_description.buffer.secure.offset;
|
|
break;
|
|
case OEMCrypto_BufferType_Direct:
|
|
// Direct buffer type is only used on some specialized devices where
|
|
// oemcrypto has a direct connection to the screen buffer. It is not,
|
|
// for example, supported on Android.
|
|
destination_ = nullptr;
|
|
break;
|
|
default:
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
const size_t max_allowed = max_sample_size();
|
|
if (max_allowed > 0 &&
|
|
(max_allowed < max_length || max_allowed < data_length)) {
|
|
LOGE("Output too large (or buffer too small).");
|
|
return OEMCrypto_ERROR_OUTPUT_TOO_LARGE;
|
|
}
|
|
|
|
if (out_description.type != OEMCrypto_BufferType_Direct &&
|
|
max_length < data_length) {
|
|
LOGE("[SetDestination(): OEMCrypto_ERROR_SHORT_BUFFER]");
|
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
|
}
|
|
adjust_destination(out_description, data_length, subsample_flags);
|
|
if ((out_description.type != OEMCrypto_BufferType_Direct) &&
|
|
(destination_ == nullptr)) {
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
uint32_t CryptoEngine::SessionTypeBits(SessionId sid) {
|
|
return sid >> kSessionIdTypeShift;
|
|
}
|
|
|
|
} // namespace wvoec_ref
|