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