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 "in_process_tpm.h"
17 
18 #include <stddef.h>
19 
20 #include <tss2/tss2_esys.h>
21 #include <tss2/tss2_rc.h>
22 
23 #include <android-base/endian.h>
24 
25 #include "host/commands/secure_env/tpm_commands.h"
26 
27 extern "C" {
28 #ifndef _WIN32
29 typedef int SOCKET;
30 #endif
31 #include "TpmBuildSwitches.h"
32 #include "TpmTcpProtocol.h"
33 #include "Simulator_fp.h"
34 #include "Manufacture_fp.h"
35 #define delete delete_
36 #include "Platform_fp.h"
37 #undef delete
38 #undef DEBUG
39 }
40 
41 #include <android-base/logging.h>
42 
43 #include <mutex>
44 
45 namespace cuttlefish {
46 
47 struct __attribute__((__packed__)) tpm_message_header {
48   uint16_t tag;
49   uint32_t length;
50   uint32_t ordinal;
51 };
52 
53 class InProcessTpm::Impl {
54  public:
FromContext(TSS2_TCTI_CONTEXT * context)55   static Impl* FromContext(TSS2_TCTI_CONTEXT* context) {
56     auto offset = offsetof(Impl, tcti_context_);
57     char* context_char = reinterpret_cast<char*>(context);
58     return reinterpret_cast<Impl*>(context_char - offset);
59   }
60 
Transmit(TSS2_TCTI_CONTEXT * context,size_t size,uint8_t const * command)61   static TSS2_RC Transmit(
62       TSS2_TCTI_CONTEXT *context, size_t size, uint8_t const *command) {
63     auto impl = FromContext(context);
64     std::lock_guard lock(impl->queue_mutex_);
65     impl->command_queue_.emplace_back(command, command + size);
66     return TSS2_RC_SUCCESS;
67   }
68 
Receive(TSS2_TCTI_CONTEXT * context,size_t * size,uint8_t * response,int32_t)69   static TSS2_RC Receive(
70       TSS2_TCTI_CONTEXT *context,
71       size_t* size,
72       uint8_t* response,
73       int32_t /* timeout */) {
74     auto impl = FromContext(context);
75     // TODO(schuffelen): Use the timeout argument
76     std::vector<uint8_t> request;
77     {
78       std::lock_guard lock(impl->queue_mutex_);
79       if (impl->command_queue_.empty()) {
80         return TSS2_TCTI_RC_GENERAL_FAILURE;
81       }
82       request = std::move(impl->command_queue_.front());
83       impl->command_queue_.pop_front();
84     }
85     auto header = reinterpret_cast<tpm_message_header*>(request.data());
86     LOG(VERBOSE) << "Sending TPM command "
87                 << TpmCommandName(be32toh(header->ordinal));
88     _IN_BUFFER input = {
89         .BufferSize = static_cast<unsigned long>(request.size()),
90         .Buffer = request.data(),
91     };
92     _OUT_BUFFER output = {
93         .BufferSize = (uint32_t) *size,
94         .Buffer = response,
95     };
96     _rpc__Send_Command(3, input, &output);
97     *size = output.BufferSize;
98     header = reinterpret_cast<tpm_message_header*>(response);
99     auto rc = be32toh(header->ordinal);
100     LOG(VERBOSE) << "Received TPM response " << Tss2_RC_Decode(rc)
101                 << " (" << rc << ")";
102     return TSS2_RC_SUCCESS;
103   }
104 
Impl()105   Impl() {
106     {
107       std::lock_guard<std::mutex> lock(global_mutex);
108       // This is a limitation of ms-tpm-20-ref
109       CHECK(!global_instance) << "InProcessTpm internally uses global data, so "
110                               << "only one can exist.";
111       global_instance = this;
112     }
113 
114     tcti_context_.v1.magic = 0xFAD;
115     tcti_context_.v1.version = 1;
116     tcti_context_.v1.transmit = Impl::Transmit;
117     tcti_context_.v1.receive = Impl::Receive;
118     _plat__NVEnable(NULL);
119     if (_plat__NVNeedsManufacture()) {
120       // Can't use android logging here due to a macro conflict with TPM
121       // internals
122       LOG(DEBUG) << "Manufacturing TPM state";
123       if (TPM_Manufacture(1)) {
124         LOG(FATAL) << "Failed to manufacture TPM state";
125       }
126     }
127     _rpc__Signal_PowerOn(false);
128     _rpc__Signal_NvOn();
129 
130     ESYS_CONTEXT* esys = nullptr;
131     auto rc = Esys_Initialize(&esys, TctiContext(), nullptr);
132     if (rc != TPM2_RC_SUCCESS) {
133       LOG(FATAL) << "Could not initialize esys: " << Tss2_RC_Decode(rc) << " ("
134                  << rc << ")";
135     }
136 
137     rc = Esys_Startup(esys, TPM2_SU_CLEAR);
138     if (rc != TPM2_RC_SUCCESS) {
139       LOG(FATAL) << "TPM2_Startup failed: " << Tss2_RC_Decode(rc) << " (" << rc
140                  << ")";
141     }
142 
143     TPM2B_AUTH auth = {};
144     Esys_TR_SetAuth(esys, ESYS_TR_RH_LOCKOUT, &auth);
145 
146     rc = Esys_DictionaryAttackLockReset(
147         /* esysContext */ esys,
148         /* lockHandle */ ESYS_TR_RH_LOCKOUT,
149         /* shandle1 */ ESYS_TR_PASSWORD,
150         /* shandle2 */ ESYS_TR_NONE,
151         /* shandle3 */ ESYS_TR_NONE);
152 
153     if (rc != TPM2_RC_SUCCESS) {
154       LOG(FATAL) << "Could not reset TPM lockout: " << Tss2_RC_Decode(rc)
155                  << " (" << rc << ")";
156     }
157 
158     Esys_Finalize(&esys);
159   }
160 
~Impl()161   ~Impl() {
162     _rpc__Signal_NvOff();
163     _rpc__Signal_PowerOff();
164     std::lock_guard<std::mutex> lock(global_mutex);
165     global_instance = nullptr;
166   }
167 
TctiContext()168   TSS2_TCTI_CONTEXT* TctiContext() {
169     return reinterpret_cast<TSS2_TCTI_CONTEXT*>(&tcti_context_);
170   }
171 
172  private:
173   static std::mutex global_mutex;
174   static Impl* global_instance;
175   TSS2_TCTI_CONTEXT_COMMON_CURRENT tcti_context_;
176   std::list<std::vector<uint8_t>> command_queue_;
177   std::mutex queue_mutex_;
178 };
179 
180 std::mutex InProcessTpm::Impl::global_mutex;
181 InProcessTpm::Impl* InProcessTpm::Impl::global_instance;
182 
InProcessTpm()183 InProcessTpm::InProcessTpm() : impl_(new Impl()) {}
184 
185 InProcessTpm::~InProcessTpm() = default;
186 
TctiContext()187 TSS2_TCTI_CONTEXT* InProcessTpm::TctiContext() { return impl_->TctiContext(); }
188 
189 }  // namespace cuttlefish
190