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