blob: 91819e74c3d3476ac94bffa11e915e7c762a098e [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/widevine/widevine_storage.h"
#include "starboard/common/log.h"
#include "starboard/file.h"
#include "starboard/shared/widevine/widevine_keybox_hash.h"
#include "starboard/types.h"
namespace starboard {
namespace shared {
namespace widevine {
// Reserved key name for referring to the Widevine Keybox checksum value.
const char WidevineStorage::kCobaltWidevineKeyboxChecksumKey[] =
"cobalt_widevine_keybox_checksum";
namespace {
void ReadFile(const std::string& path_name, std::vector<uint8_t>* content) {
SB_DCHECK(content);
ScopedFile file(path_name.c_str(), kSbFileOpenOnly | kSbFileRead);
if (!file.IsValid()) {
content->clear();
SB_LOG(INFO) << "Failed to open " << path_name
<< ", returning empty content.";
return;
}
auto size = file.GetSize();
if (size < 0) {
content->clear();
SB_LOG(INFO) << "Failed to get size of " << path_name
<< ", returning empty content.";
return;
}
content->resize(size);
if (file.ReadAll(reinterpret_cast<char*>(content->data()), size) != size) {
content->clear();
SB_LOG(INFO) << "Failed to read content of " << path_name
<< ", returning empty content.";
return;
}
}
bool WriteFile(const std::string& path_name,
const std::vector<uint8_t>& content) {
ScopedFile file(path_name.c_str(), kSbFileCreateAlways | kSbFileWrite);
if (!file.IsValid()) {
SB_LOG(INFO) << "Failed to create " << path_name << " for writing.";
return false;
}
if (file.WriteAll(reinterpret_cast<const char*>(content.data()),
static_cast<int>(content.size())) != content.size()) {
SB_LOG(INFO) << "Failed to write content to " << path_name << '.';
return false;
}
file.Flush();
return true;
}
bool ReadString(const std::vector<uint8_t>& data,
size_t* offset,
std::string* str) {
if (*offset + sizeof(int) > data.size()) {
SB_LOG(ERROR) << "Failed to read the size of string from |data|.";
return false;
}
int size = *reinterpret_cast<const int*>(data.data() + *offset);
*offset += sizeof(int);
if (*offset + size > data.size()) {
SB_LOG(ERROR) << "Failed to read " << size << " bytes from |data|.";
return false;
}
str->assign(reinterpret_cast<const char*>(data.data() + *offset),
reinterpret_cast<const char*>(data.data() + *offset + size));
*offset += size;
return true;
}
void WriteString(const std::string& str, std::vector<uint8_t>* content) {
content->reserve(content->size() + sizeof(int) + str.size());
content->resize(content->size() + sizeof(int));
*reinterpret_cast<int*>(content->data() + content->size() - sizeof(int)) =
static_cast<int>(str.size());
content->insert(content->end(), reinterpret_cast<const uint8_t*>(str.c_str()),
reinterpret_cast<const uint8_t*>(str.c_str()) + str.size());
}
} // namespace
WidevineStorage::WidevineStorage(const std::string& path_name)
: path_name_(path_name) {
std::vector<uint8_t> content;
ReadFile(path_name_, &content);
size_t offset = 0;
while (offset != content.size()) {
std::string name, value;
if (!ReadString(content, &offset, &name) ||
!ReadString(content, &offset, &value)) {
cache_.clear();
SB_LOG(WARNING) << path_name_ << " is corrupt, returns empty content.";
return;
}
cache_[name] = value;
}
SB_LOG(INFO) << "Loaded " << cache_.size() << " records from " << path_name_;
// Not a cryptographic hash but is sufficient for this problem space.
std::string keybox_checksum = GetWidevineKeyboxHash();
if (existsInternal(kCobaltWidevineKeyboxChecksumKey)) {
std::string cached_checksum;
readInternal(kCobaltWidevineKeyboxChecksumKey, &cached_checksum);
if (keybox_checksum == cached_checksum) {
return;
}
SB_LOG(INFO) << "Cobalt widevine keybox checksums do not match. Clearing "
"to force re-provisioning.";
cache_.clear();
// Save the new keybox checksum to be used after provisioning completes.
writeInternal(kCobaltWidevineKeyboxChecksumKey, keybox_checksum);
return;
}
SB_LOG(INFO) << "Widevine checksum is not stored on disk. Writing the "
"computed checksum to disk.";
writeInternal(kCobaltWidevineKeyboxChecksumKey, keybox_checksum);
}
bool WidevineStorage::read(const std::string& name, std::string* data) {
SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
return readInternal(name, data);
}
bool WidevineStorage::write(const std::string& name, const std::string& data) {
SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
return writeInternal(name, data);
}
bool WidevineStorage::exists(const std::string& name) {
SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
return existsInternal(name);
}
bool WidevineStorage::remove(const std::string& name) {
SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
return removeInternal(name);
}
int32_t WidevineStorage::size(const std::string& name) {
SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
ScopedLock scoped_lock(lock_);
auto iter = cache_.find(name);
return iter == cache_.end() ? -1 : static_cast<int32_t>(iter->second.size());
}
bool WidevineStorage::list(std::vector<std::string>* records) {
SB_DCHECK(records);
ScopedLock scoped_lock(lock_);
records->clear();
for (auto item : cache_) {
if (item.first == kCobaltWidevineKeyboxChecksumKey) {
continue;
}
records->push_back(item.first);
}
return !records->empty();
}
bool WidevineStorage::readInternal(const std::string& name,
std::string* data) const {
SB_DCHECK(data);
ScopedLock scoped_lock(lock_);
auto iter = cache_.find(name);
if (iter == cache_.end()) {
return false;
}
*data = iter->second;
return true;
}
bool WidevineStorage::writeInternal(const std::string& name,
const std::string& data) {
ScopedLock scoped_lock(lock_);
cache_[name] = data;
std::vector<uint8_t> content;
for (auto iter : cache_) {
WriteString(iter.first, &content);
WriteString(iter.second, &content);
}
return WriteFile(path_name_, content);
}
bool WidevineStorage::existsInternal(const std::string& name) const {
ScopedLock scoped_lock(lock_);
return cache_.find(name) != cache_.end();
}
bool WidevineStorage::removeInternal(const std::string& name) {
ScopedLock scoped_lock(lock_);
auto iter = cache_.find(name);
if (iter == cache_.end()) {
return false;
}
cache_.erase(iter);
return true;
}
} // namespace widevine
} // namespace shared
} // namespace starboard