blob: 91819e74c3d3476ac94bffa11e915e7c762a098e [file] [log] [blame]
Andrew Top6f0aefd2018-08-15 16:02:40 -07001// Copyright 2018 The Cobalt Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "starboard/shared/widevine/widevine_storage.h"
16
Chad Duffinac9ac062019-07-23 10:06:45 -070017#include "starboard/common/log.h"
Andrew Top6f0aefd2018-08-15 16:02:40 -070018#include "starboard/file.h"
Andrew Top63c7ad42019-11-25 16:10:13 -080019#include "starboard/shared/widevine/widevine_keybox_hash.h"
Andrew Top6f0aefd2018-08-15 16:02:40 -070020#include "starboard/types.h"
21
22namespace starboard {
23namespace shared {
24namespace widevine {
25
Andrew Top63c7ad42019-11-25 16:10:13 -080026// Reserved key name for referring to the Widevine Keybox checksum value.
27const char WidevineStorage::kCobaltWidevineKeyboxChecksumKey[] =
28 "cobalt_widevine_keybox_checksum";
29
Andrew Top6f0aefd2018-08-15 16:02:40 -070030namespace {
31
32void ReadFile(const std::string& path_name, std::vector<uint8_t>* content) {
33 SB_DCHECK(content);
34
35 ScopedFile file(path_name.c_str(), kSbFileOpenOnly | kSbFileRead);
36
37 if (!file.IsValid()) {
38 content->clear();
39 SB_LOG(INFO) << "Failed to open " << path_name
40 << ", returning empty content.";
41 return;
42 }
43
44 auto size = file.GetSize();
45 if (size < 0) {
46 content->clear();
47 SB_LOG(INFO) << "Failed to get size of " << path_name
48 << ", returning empty content.";
49 return;
50 }
51
52 content->resize(size);
53 if (file.ReadAll(reinterpret_cast<char*>(content->data()), size) != size) {
54 content->clear();
55 SB_LOG(INFO) << "Failed to read content of " << path_name
56 << ", returning empty content.";
57 return;
58 }
59}
60
61bool WriteFile(const std::string& path_name,
62 const std::vector<uint8_t>& content) {
63 ScopedFile file(path_name.c_str(), kSbFileCreateAlways | kSbFileWrite);
64
65 if (!file.IsValid()) {
66 SB_LOG(INFO) << "Failed to create " << path_name << " for writing.";
67 return false;
68 }
69
70 if (file.WriteAll(reinterpret_cast<const char*>(content.data()),
71 static_cast<int>(content.size())) != content.size()) {
72 SB_LOG(INFO) << "Failed to write content to " << path_name << '.';
73 return false;
74 }
75
76 file.Flush();
77
78 return true;
79}
80
81bool ReadString(const std::vector<uint8_t>& data,
82 size_t* offset,
83 std::string* str) {
84 if (*offset + sizeof(int) > data.size()) {
85 SB_LOG(ERROR) << "Failed to read the size of string from |data|.";
86 return false;
87 }
88 int size = *reinterpret_cast<const int*>(data.data() + *offset);
89 *offset += sizeof(int);
90 if (*offset + size > data.size()) {
91 SB_LOG(ERROR) << "Failed to read " << size << " bytes from |data|.";
92 return false;
93 }
94 str->assign(reinterpret_cast<const char*>(data.data() + *offset),
95 reinterpret_cast<const char*>(data.data() + *offset + size));
96 *offset += size;
97 return true;
98}
99
100void WriteString(const std::string& str, std::vector<uint8_t>* content) {
101 content->reserve(content->size() + sizeof(int) + str.size());
102 content->resize(content->size() + sizeof(int));
103 *reinterpret_cast<int*>(content->data() + content->size() - sizeof(int)) =
104 static_cast<int>(str.size());
105 content->insert(content->end(), reinterpret_cast<const uint8_t*>(str.c_str()),
106 reinterpret_cast<const uint8_t*>(str.c_str()) + str.size());
107}
108
109} // namespace
110
111WidevineStorage::WidevineStorage(const std::string& path_name)
112 : path_name_(path_name) {
113 std::vector<uint8_t> content;
114 ReadFile(path_name_, &content);
115 size_t offset = 0;
116
117 while (offset != content.size()) {
118 std::string name, value;
119 if (!ReadString(content, &offset, &name) ||
120 !ReadString(content, &offset, &value)) {
121 cache_.clear();
122 SB_LOG(WARNING) << path_name_ << " is corrupt, returns empty content.";
123 return;
124 }
125 cache_[name] = value;
126 }
127
128 SB_LOG(INFO) << "Loaded " << cache_.size() << " records from " << path_name_;
Andrew Top63c7ad42019-11-25 16:10:13 -0800129
130 // Not a cryptographic hash but is sufficient for this problem space.
131 std::string keybox_checksum = GetWidevineKeyboxHash();
132 if (existsInternal(kCobaltWidevineKeyboxChecksumKey)) {
133 std::string cached_checksum;
134 readInternal(kCobaltWidevineKeyboxChecksumKey, &cached_checksum);
135 if (keybox_checksum == cached_checksum) {
136 return;
137 }
138 SB_LOG(INFO) << "Cobalt widevine keybox checksums do not match. Clearing "
139 "to force re-provisioning.";
140 cache_.clear();
141 // Save the new keybox checksum to be used after provisioning completes.
142 writeInternal(kCobaltWidevineKeyboxChecksumKey, keybox_checksum);
143 return;
144 }
145 SB_LOG(INFO) << "Widevine checksum is not stored on disk. Writing the "
146 "computed checksum to disk.";
147 writeInternal(kCobaltWidevineKeyboxChecksumKey, keybox_checksum);
Andrew Top6f0aefd2018-08-15 16:02:40 -0700148}
149
150bool WidevineStorage::read(const std::string& name, std::string* data) {
Andrew Top63c7ad42019-11-25 16:10:13 -0800151 SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
152 return readInternal(name, data);
Andrew Top6f0aefd2018-08-15 16:02:40 -0700153}
154
155bool WidevineStorage::write(const std::string& name, const std::string& data) {
Andrew Top63c7ad42019-11-25 16:10:13 -0800156 SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
157 return writeInternal(name, data);
Andrew Top6f0aefd2018-08-15 16:02:40 -0700158}
159
160bool WidevineStorage::exists(const std::string& name) {
Andrew Top63c7ad42019-11-25 16:10:13 -0800161 SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
162 return existsInternal(name);
Andrew Top6f0aefd2018-08-15 16:02:40 -0700163}
164
165bool WidevineStorage::remove(const std::string& name) {
Andrew Top63c7ad42019-11-25 16:10:13 -0800166 SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
167 return removeInternal(name);
Andrew Top6f0aefd2018-08-15 16:02:40 -0700168}
169
170int32_t WidevineStorage::size(const std::string& name) {
Andrew Top63c7ad42019-11-25 16:10:13 -0800171 SB_DCHECK(name != kCobaltWidevineKeyboxChecksumKey);
Andrew Top6f0aefd2018-08-15 16:02:40 -0700172 ScopedLock scoped_lock(lock_);
173 auto iter = cache_.find(name);
174 return iter == cache_.end() ? -1 : static_cast<int32_t>(iter->second.size());
175}
176
177bool WidevineStorage::list(std::vector<std::string>* records) {
178 SB_DCHECK(records);
179 ScopedLock scoped_lock(lock_);
180 records->clear();
181 for (auto item : cache_) {
Andrew Top63c7ad42019-11-25 16:10:13 -0800182 if (item.first == kCobaltWidevineKeyboxChecksumKey) {
183 continue;
184 }
Andrew Top6f0aefd2018-08-15 16:02:40 -0700185 records->push_back(item.first);
186 }
187 return !records->empty();
188}
189
Andrew Top63c7ad42019-11-25 16:10:13 -0800190bool WidevineStorage::readInternal(const std::string& name,
191 std::string* data) const {
192 SB_DCHECK(data);
193 ScopedLock scoped_lock(lock_);
194 auto iter = cache_.find(name);
195 if (iter == cache_.end()) {
196 return false;
197 }
198 *data = iter->second;
199 return true;
200}
201
202bool WidevineStorage::writeInternal(const std::string& name,
203 const std::string& data) {
204 ScopedLock scoped_lock(lock_);
205 cache_[name] = data;
206
207 std::vector<uint8_t> content;
208 for (auto iter : cache_) {
209 WriteString(iter.first, &content);
210 WriteString(iter.second, &content);
211 }
212 return WriteFile(path_name_, content);
213}
214
215bool WidevineStorage::existsInternal(const std::string& name) const {
216 ScopedLock scoped_lock(lock_);
217 return cache_.find(name) != cache_.end();
218}
219
220bool WidevineStorage::removeInternal(const std::string& name) {
221 ScopedLock scoped_lock(lock_);
222 auto iter = cache_.find(name);
223 if (iter == cache_.end()) {
224 return false;
225 }
226 cache_.erase(iter);
227 return true;
228}
229
Andrew Top6f0aefd2018-08-15 16:02:40 -0700230} // namespace widevine
231} // namespace shared
232} // namespace starboard