• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace cuttlefish {
27 
28 static constexpr char kEntries[] = "entries";
29 static constexpr char kKey[] = "key";
30 static constexpr char kHandle[] = "handle";
31 
FragileTpmStorage(TpmResourceManager & resource_manager,const std::string & index_file)32 FragileTpmStorage::FragileTpmStorage(
33     TpmResourceManager& resource_manager, const std::string& index_file)
34     : resource_manager_(resource_manager), index_file_(index_file) {
35   index_ = ReadProtectedJsonFromFile(resource_manager_, index_file);
36   if (!index_.isMember(kEntries)
37       || index_[kEntries].type() != Json::arrayValue) {
38     if (index_.empty()) {
39       LOG(DEBUG) << "Initializing secure index file";
40     } else {
41       LOG(WARNING) << "Index file missing entries, likely corrupted.";
42     }
43     index_[kEntries] = Json::Value(Json::arrayValue);
44   } else {
45     LOG(DEBUG) << "Restoring index from file";
46   }
47 }
48 
GenerateRandomHandle()49 TPM2_HANDLE FragileTpmStorage::GenerateRandomHandle() {
50   TpmRandomSource random_source{resource_manager_.Esys()};
51   TPM2_HANDLE handle = 0;
52   random_source.GenerateRandom(
53       reinterpret_cast<uint8_t*>(&handle), sizeof(handle));
54   if (handle == 0) {
55     LOG(WARNING) << "TPM randomness failed. Falling back to software RNG.";
56     handle = rand();
57   }
58   handle = handle % (TPM2_NV_INDEX_LAST + 1 - TPM2_NV_INDEX_FIRST);
59   handle += TPM2_NV_INDEX_FIRST;
60   return handle;
61 }
62 
63 static constexpr size_t MAX_HANDLE_ATTEMPTS = 1;
64 
Allocate(const Json::Value & key,uint16_t size)65 bool FragileTpmStorage::Allocate(const Json::Value& key, uint16_t size) {
66   if (HasKey(key)) {
67     LOG(WARNING) << "Key " << key << " is already defined.";
68     return false;
69   }
70   TPM2_HANDLE handle;
71   for (int i = 0; i < MAX_HANDLE_ATTEMPTS; i++) {
72     handle = GenerateRandomHandle();
73     TPM2B_NV_PUBLIC public_info = {
74       .size = 0,
75       .nvPublic = {
76         .nvIndex = handle,
77         .nameAlg = TPM2_ALG_SHA1,
78         .attributes = TPMA_NV_AUTHWRITE | TPMA_NV_AUTHREAD,
79         .authPolicy = { .size = 0, .buffer = {} },
80         .dataSize = size,
81       }
82     };
83     TPM2B_AUTH auth = { .size = 0, .buffer = {} };
84     Esys_TR_SetAuth(resource_manager_.Esys(), ESYS_TR_RH_OWNER, &auth);
85     ESYS_TR nv_handle;
86     auto rc = Esys_NV_DefineSpace(
87       /* esysContext */ resource_manager_.Esys(),
88       /* authHandle */ ESYS_TR_RH_OWNER,
89       /* shandle1 */ ESYS_TR_PASSWORD,
90       /* shandle2 */ ESYS_TR_NONE,
91       /* shandle3 */ ESYS_TR_NONE,
92       /* auth */ &auth,
93       /* publicInfo */ &public_info,
94       /* nvHandle */ &nv_handle);
95     if (rc == TPM2_RC_NV_DEFINED) {
96       LOG(VERBOSE) << "Esys_NV_DefineSpace failed with TPM2_RC_NV_DEFINED";
97       continue;
98     } else if (rc == TPM2_RC_SUCCESS) {
99       Esys_TR_Close(resource_manager_.Esys(), &nv_handle);
100       break;
101     } else {
102       LOG(DEBUG) << "Esys_NV_DefineSpace failed with " << rc << ": "
103                  << Tss2_RC_Decode(rc);
104     }
105   }
106   Json::Value entry(Json::objectValue);
107   entry[kKey] = key;
108   entry[kHandle] = handle;
109   index_[kEntries].append(entry);
110 
111   if (!WriteProtectedJsonToFile(resource_manager_, index_file_, index_)) {
112     LOG(ERROR) << "Failed to save changes to " << index_file_;
113     return false;
114   }
115   return true;
116 }
117 
GetHandle(const Json::Value & key) const118 TPM2_HANDLE FragileTpmStorage::GetHandle(const Json::Value& key) const {
119   for (const auto& entry : index_[kEntries]) {
120     if (!entry.isMember(kKey)) {
121       LOG(ERROR) << "Index was corrupted";
122       return 0;
123     }
124     if (entry[kKey] != key) {
125       continue;
126     }
127     if (!entry.isMember(kHandle)) {
128       LOG(ERROR) << "Index was corrupted";
129       return 0;
130     }
131     return entry[kHandle].asUInt();
132   }
133   return 0;
134 }
135 
HasKey(const Json::Value & key) const136 bool FragileTpmStorage::HasKey(const Json::Value& key) const {
137   return GetHandle(key) != 0;
138 }
139 
Read(const Json::Value & key) const140 std::unique_ptr<TPM2B_MAX_NV_BUFFER> FragileTpmStorage::Read(
141     const Json::Value& key) const {
142   auto handle = GetHandle(key);
143   if (handle == 0) {
144     LOG(WARNING) << "Could not read from " << key;
145     return {};
146   }
147   auto close_tr = [this](ESYS_TR* handle) {
148     Esys_TR_Close(resource_manager_.Esys(), handle);
149     delete handle;
150   };
151   std::unique_ptr<ESYS_TR, decltype(close_tr)> nv_handle(new ESYS_TR, close_tr);
152   auto rc = Esys_TR_FromTPMPublic(
153       /* esysContext */ resource_manager_.Esys(),
154       /* tpm_handle */ handle,
155       /* optionalSession1 */ ESYS_TR_NONE,
156       /* optionalSession2 */ ESYS_TR_NONE,
157       /* optionalSession3 */ ESYS_TR_NONE,
158       /* object */ nv_handle.get());
159   if (rc != TPM2_RC_SUCCESS) {
160     LOG(ERROR) << "Esys_TR_FromTPMPublic failed: " << rc << ": "
161                << Tss2_RC_Decode(rc);
162     return {};
163   }
164   TPM2B_AUTH auth = { .size = 0, .buffer = {} };
165   Esys_TR_SetAuth(resource_manager_.Esys(), *nv_handle, &auth);
166 
167   TPM2B_NV_PUBLIC* public_area;
168   rc = Esys_NV_ReadPublic(
169       /* esysContext */ resource_manager_.Esys(),
170       /* nvIndex */ *nv_handle,
171       /* shandle1 */ ESYS_TR_NONE,
172       /* shandle2 */ ESYS_TR_NONE,
173       /* shandle3 */ ESYS_TR_NONE,
174       /* nvPublic */ &public_area,
175       /* nvName */ nullptr);
176   if (rc != TPM2_RC_SUCCESS || public_area == nullptr) {
177     LOG(ERROR) << "Esys_NV_ReadPublic failed: " << rc << ": "
178                << Tss2_RC_Decode(rc);
179     return {};
180   }
181   std::unique_ptr<TPM2B_NV_PUBLIC, decltype(Esys_Free)*>
182       public_deleter(public_area, Esys_Free);
183   TPM2B_MAX_NV_BUFFER* buffer = nullptr;
184   rc = Esys_NV_Read(
185       /* esysContext */ resource_manager_.Esys(),
186       /* authHandle */ *nv_handle,
187       /* nvIndex */ *nv_handle,
188       /* shandle1 */ ESYS_TR_PASSWORD,
189       /* shandle2 */ ESYS_TR_NONE,
190       /* shandle3 */ ESYS_TR_NONE,
191       /* size */ public_area->nvPublic.dataSize,
192       /* offset */ 0,
193       /* data */ &buffer);
194   if (rc != TSS2_RC_SUCCESS || buffer == nullptr) {
195     LOG(ERROR) << "Esys_NV_Read failed with return code " << rc
196                << " (" << Tss2_RC_Decode(rc) << ")";
197     return {};
198   }
199   auto ret = std::make_unique<TPM2B_MAX_NV_BUFFER>(*buffer);
200   return ret;
201 }
202 
Write(const Json::Value & key,const TPM2B_MAX_NV_BUFFER & data)203 bool FragileTpmStorage::Write(
204     const Json::Value& key, const TPM2B_MAX_NV_BUFFER& data) {
205   auto handle = GetHandle(key);
206   if (handle == 0) {
207     LOG(WARNING) << "Could not read from " << key;
208     return false;
209   }
210   ESYS_TR nv_handle;
211   auto rc = Esys_TR_FromTPMPublic(
212       /* esysContext */ resource_manager_.Esys(),
213       /* tpm_handle */ handle,
214       /* optionalSession1 */ ESYS_TR_NONE,
215       /* optionalSession2 */ ESYS_TR_NONE,
216       /* optionalSession3 */ ESYS_TR_NONE,
217       /* object */ &nv_handle);
218   if (rc != TPM2_RC_SUCCESS) {
219     LOG(ERROR) << "Esys_TR_FromTPMPublic failed: " << rc << ": "
220                << Tss2_RC_Decode(rc);
221     return false;
222   }
223   TPM2B_AUTH auth = { .size = 0, .buffer = {} };
224   Esys_TR_SetAuth(resource_manager_.Esys(), nv_handle, &auth);
225 
226   rc = Esys_NV_Write(
227       /* esysContext */ resource_manager_.Esys(),
228       /* authHandle */ nv_handle,
229       /* nvIndex */ nv_handle,
230       /* shandle1 */ ESYS_TR_PASSWORD,
231       /* shandle2 */ ESYS_TR_NONE,
232       /* shandle3 */ ESYS_TR_NONE,
233       /* data */ &data,
234       /* offset */ 0);
235   Esys_TR_Close(resource_manager_.Esys(), &nv_handle);
236   if (rc != TSS2_RC_SUCCESS) {
237     LOG(ERROR) << "Esys_NV_Write failed with return code " << rc
238                << " (" << Tss2_RC_Decode(rc) << ")";
239     return false;
240   }
241   return true;
242 }
243 
244 }  // namespace cuttlefish
245