1 //
2 // Copyright (C) 2020 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 #include "host/commands/secure_env/fragile_tpm_storage.h"
17
18 #include <fstream>
19
20 #include <android-base/logging.h>
21 #include <tss2/tss2_rc.h>
22
23 #include "host/commands/secure_env/json_serializable.h"
24 #include "host/commands/secure_env/tpm_random_source.h"
25
26 static constexpr char kEntries[] = "entries";
27 static constexpr char kKey[] = "key";
28 static constexpr char kHandle[] = "handle";
29
FragileTpmStorage(TpmResourceManager & resource_manager,const std::string & index_file)30 FragileTpmStorage::FragileTpmStorage(
31 TpmResourceManager& resource_manager, const std::string& index_file)
32 : resource_manager_(resource_manager), index_file_(index_file) {
33 index_ = ReadProtectedJsonFromFile(resource_manager_, index_file);
34 if (!index_.isMember(kEntries)
35 || index_[kEntries].type() != Json::arrayValue) {
36 if (index_.empty()) {
37 LOG(DEBUG) << "Initializing secure index file";
38 } else {
39 LOG(WARNING) << "Index file missing entries, likely corrupted.";
40 }
41 index_[kEntries] = Json::Value(Json::arrayValue);
42 } else {
43 LOG(DEBUG) << "Restoring index from file";
44 }
45 }
46
GenerateRandomHandle()47 TPM2_HANDLE FragileTpmStorage::GenerateRandomHandle() {
48 TpmRandomSource random_source{resource_manager_.Esys()};
49 TPM2_HANDLE handle = 0;
50 random_source.GenerateRandom(
51 reinterpret_cast<uint8_t*>(&handle), sizeof(handle));
52 if (handle == 0) {
53 LOG(WARNING) << "TPM randomness failed. Falling back to software RNG.";
54 handle = rand();
55 }
56 handle = handle % (TPM2_NV_INDEX_LAST + 1 - TPM2_NV_INDEX_FIRST);
57 handle += TPM2_NV_INDEX_FIRST;
58 return handle;
59 }
60
61 static constexpr size_t MAX_HANDLE_ATTEMPTS = 1;
62
Allocate(const Json::Value & key,uint16_t size)63 bool FragileTpmStorage::Allocate(const Json::Value& key, uint16_t size) {
64 if (HasKey(key)) {
65 LOG(WARNING) << "Key " << key << " is already defined.";
66 return false;
67 }
68 TPM2_HANDLE handle;
69 for (int i = 0; i < MAX_HANDLE_ATTEMPTS; i++) {
70 handle = GenerateRandomHandle();
71 TPM2B_NV_PUBLIC public_info = {
72 .size = 0,
73 .nvPublic = {
74 .nvIndex = handle,
75 .nameAlg = TPM2_ALG_SHA1,
76 .attributes = TPMA_NV_AUTHWRITE | TPMA_NV_AUTHREAD,
77 .authPolicy = { .size = 0, .buffer = {} },
78 .dataSize = size,
79 }
80 };
81 TPM2B_AUTH auth = { .size = 0, .buffer = {} };
82 Esys_TR_SetAuth(resource_manager_.Esys(), ESYS_TR_RH_OWNER, &auth);
83 ESYS_TR nv_handle;
84 auto rc = Esys_NV_DefineSpace(
85 /* esysContext */ resource_manager_.Esys(),
86 /* authHandle */ ESYS_TR_RH_OWNER,
87 /* shandle1 */ ESYS_TR_PASSWORD,
88 /* shandle2 */ ESYS_TR_NONE,
89 /* shandle3 */ ESYS_TR_NONE,
90 /* auth */ &auth,
91 /* publicInfo */ &public_info,
92 /* nvHandle */ &nv_handle);
93 if (rc == TPM2_RC_NV_DEFINED) {
94 LOG(VERBOSE) << "Esys_NV_DefineSpace failed with TPM2_RC_NV_DEFINED";
95 continue;
96 } else if (rc == TPM2_RC_SUCCESS) {
97 Esys_TR_Close(resource_manager_.Esys(), &nv_handle);
98 break;
99 } else {
100 LOG(DEBUG) << "Esys_NV_DefineSpace failed with " << rc << ": "
101 << Tss2_RC_Decode(rc);
102 }
103 }
104 Json::Value entry(Json::objectValue);
105 entry[kKey] = key;
106 entry[kHandle] = handle;
107 index_[kEntries].append(entry);
108
109 if (!WriteProtectedJsonToFile(resource_manager_, index_file_, index_)) {
110 LOG(ERROR) << "Failed to save changes to " << index_file_;
111 return false;
112 }
113 return true;
114 }
115
GetHandle(const Json::Value & key) const116 TPM2_HANDLE FragileTpmStorage::GetHandle(const Json::Value& key) const {
117 for (const auto& entry : index_[kEntries]) {
118 if (!entry.isMember(kKey)) {
119 LOG(ERROR) << "Index was corrupted";
120 return 0;
121 }
122 if (entry[kKey] != key) {
123 continue;
124 }
125 if (!entry.isMember(kHandle)) {
126 LOG(ERROR) << "Index was corrupted";
127 return 0;
128 }
129 return entry[kHandle].asUInt();
130 }
131 return 0;
132 }
133
HasKey(const Json::Value & key) const134 bool FragileTpmStorage::HasKey(const Json::Value& key) const {
135 return GetHandle(key) != 0;
136 }
137
Read(const Json::Value & key) const138 std::unique_ptr<TPM2B_MAX_NV_BUFFER> FragileTpmStorage::Read(
139 const Json::Value& key) const {
140 auto handle = GetHandle(key);
141 if (handle == 0) {
142 LOG(WARNING) << "Could not read from " << key;
143 return {};
144 }
145 auto close_tr = [this](ESYS_TR* handle) {
146 Esys_TR_Close(resource_manager_.Esys(), handle);
147 delete handle;
148 };
149 std::unique_ptr<ESYS_TR, decltype(close_tr)> nv_handle(new ESYS_TR, close_tr);
150 auto rc = Esys_TR_FromTPMPublic(
151 /* esysContext */ resource_manager_.Esys(),
152 /* tpm_handle */ handle,
153 /* optionalSession1 */ ESYS_TR_NONE,
154 /* optionalSession2 */ ESYS_TR_NONE,
155 /* optionalSession3 */ ESYS_TR_NONE,
156 /* object */ nv_handle.get());
157 if (rc != TPM2_RC_SUCCESS) {
158 LOG(ERROR) << "Esys_TR_FromTPMPublic failed: " << rc << ": "
159 << Tss2_RC_Decode(rc);
160 return {};
161 }
162 TPM2B_AUTH auth = { .size = 0, .buffer = {} };
163 Esys_TR_SetAuth(resource_manager_.Esys(), *nv_handle, &auth);
164
165 TPM2B_NV_PUBLIC* public_area;
166 rc = Esys_NV_ReadPublic(
167 /* esysContext */ resource_manager_.Esys(),
168 /* nvIndex */ *nv_handle,
169 /* shandle1 */ ESYS_TR_NONE,
170 /* shandle2 */ ESYS_TR_NONE,
171 /* shandle3 */ ESYS_TR_NONE,
172 /* nvPublic */ &public_area,
173 /* nvName */ nullptr);
174 if (rc != TPM2_RC_SUCCESS || public_area == nullptr) {
175 LOG(ERROR) << "Esys_NV_ReadPublic failed: " << rc << ": "
176 << Tss2_RC_Decode(rc);
177 return {};
178 }
179 std::unique_ptr<TPM2B_NV_PUBLIC, decltype(Esys_Free)*>
180 public_deleter(public_area, Esys_Free);
181 TPM2B_MAX_NV_BUFFER* buffer = nullptr;
182 rc = Esys_NV_Read(
183 /* esysContext */ resource_manager_.Esys(),
184 /* authHandle */ *nv_handle,
185 /* nvIndex */ *nv_handle,
186 /* shandle1 */ ESYS_TR_PASSWORD,
187 /* shandle2 */ ESYS_TR_NONE,
188 /* shandle3 */ ESYS_TR_NONE,
189 /* size */ public_area->nvPublic.dataSize,
190 /* offset */ 0,
191 /* data */ &buffer);
192 if (rc != TSS2_RC_SUCCESS || buffer == nullptr) {
193 LOG(ERROR) << "Esys_NV_Read failed with return code " << rc
194 << " (" << Tss2_RC_Decode(rc) << ")";
195 return {};
196 }
197 auto ret = std::make_unique<TPM2B_MAX_NV_BUFFER>(*buffer);
198 return ret;
199 }
200
Write(const Json::Value & key,const TPM2B_MAX_NV_BUFFER & data)201 bool FragileTpmStorage::Write(
202 const Json::Value& key, const TPM2B_MAX_NV_BUFFER& data) {
203 auto handle = GetHandle(key);
204 if (handle == 0) {
205 LOG(WARNING) << "Could not read from " << key;
206 return false;
207 }
208 ESYS_TR nv_handle;
209 auto rc = Esys_TR_FromTPMPublic(
210 /* esysContext */ resource_manager_.Esys(),
211 /* tpm_handle */ handle,
212 /* optionalSession1 */ ESYS_TR_NONE,
213 /* optionalSession2 */ ESYS_TR_NONE,
214 /* optionalSession3 */ ESYS_TR_NONE,
215 /* object */ &nv_handle);
216 if (rc != TPM2_RC_SUCCESS) {
217 LOG(ERROR) << "Esys_TR_FromTPMPublic failed: " << rc << ": "
218 << Tss2_RC_Decode(rc);
219 return false;
220 }
221 TPM2B_AUTH auth = { .size = 0, .buffer = {} };
222 Esys_TR_SetAuth(resource_manager_.Esys(), nv_handle, &auth);
223
224 rc = Esys_NV_Write(
225 /* esysContext */ resource_manager_.Esys(),
226 /* authHandle */ nv_handle,
227 /* nvIndex */ nv_handle,
228 /* shandle1 */ ESYS_TR_PASSWORD,
229 /* shandle2 */ ESYS_TR_NONE,
230 /* shandle3 */ ESYS_TR_NONE,
231 /* data */ &data,
232 /* offset */ 0);
233 Esys_TR_Close(resource_manager_.Esys(), &nv_handle);
234 if (rc != TSS2_RC_SUCCESS) {
235 LOG(ERROR) << "Esys_NV_Write failed with return code " << rc
236 << " (" << Tss2_RC_Decode(rc) << ")";
237 return false;
238 }
239 return true;
240 }
241