• 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 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