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 struct __attribute__((__packed__)) tpm_message_header {
41 uint16_t tag;
42 uint32_t length;
43 uint32_t ordinal;
44 };
45
46 struct InProcessTpm::Impl {
FromContextInProcessTpm::Impl47 static Impl* FromContext(TSS2_TCTI_CONTEXT* context) {
48 auto offset = offsetof(Impl, tcti_context_);
49 char* context_char = reinterpret_cast<char*>(context);
50 return reinterpret_cast<Impl*>(context_char - offset);
51 }
52
TransmitInProcessTpm::Impl53 static TSS2_RC Transmit(
54 TSS2_TCTI_CONTEXT *context, size_t size, uint8_t const *command) {
55 auto impl = FromContext(context);
56 std::lock_guard lock(impl->queue_mutex_);
57 impl->command_queue_.emplace_back(command, command + size);
58 return TSS2_RC_SUCCESS;
59 }
60
ReceiveInProcessTpm::Impl61 static TSS2_RC Receive(
62 TSS2_TCTI_CONTEXT *context,
63 size_t* size,
64 uint8_t* response,
65 int32_t /* timeout */) {
66 auto impl = FromContext(context);
67 // TODO(schuffelen): Use the timeout argument
68 std::vector<uint8_t> request;
69 {
70 std::lock_guard lock(impl->queue_mutex_);
71 if (impl->command_queue_.empty()) {
72 return TSS2_TCTI_RC_GENERAL_FAILURE;
73 }
74 request = std::move(impl->command_queue_.front());
75 impl->command_queue_.pop_front();
76 }
77 auto header = reinterpret_cast<tpm_message_header*>(request.data());
78 LOG(VERBOSE) << "Sending TPM command "
79 << TpmCommandName(be32toh(header->ordinal));
80 _IN_BUFFER input = {
81 .BufferSize = request.size(),
82 .Buffer = request.data(),
83 };
84 _OUT_BUFFER output = {
85 .BufferSize = (uint32_t) *size,
86 .Buffer = response,
87 };
88 _rpc__Send_Command(3, input, &output);
89 *size = output.BufferSize;
90 header = reinterpret_cast<tpm_message_header*>(response);
91 auto rc = be32toh(header->ordinal);
92 LOG(VERBOSE) << "Received TPM response " << Tss2_RC_Decode(rc)
93 << " (" << rc << ")";
94 return TSS2_RC_SUCCESS;
95 }
96
97 TSS2_TCTI_CONTEXT_COMMON_CURRENT tcti_context_;
98 std::list<std::vector<uint8_t>> command_queue_;
99 std::mutex queue_mutex_;
100 };
101
InProcessTpm()102 InProcessTpm::InProcessTpm() : impl_(new Impl()) {
103 impl_->tcti_context_.v1.magic = 0xFAD;
104 impl_->tcti_context_.v1.version = 1;
105 impl_->tcti_context_.v1.transmit = Impl::Transmit;
106 impl_->tcti_context_.v1.receive = Impl::Receive;
107 _plat__NVEnable(NULL);
108 if (_plat__NVNeedsManufacture()) {
109 // Can't use android logging here due to a macro conflict with TPM internals
110 LOG(DEBUG) << "Manufacturing TPM state";
111 if (TPM_Manufacture(1)) {
112 LOG(FATAL) << "Failed to manufacture TPM state";
113 }
114 }
115 _rpc__Signal_PowerOn(false);
116 _rpc__Signal_NvOn();
117
118 ESYS_CONTEXT* esys = nullptr;
119 auto rc = Esys_Initialize(&esys, TctiContext(), nullptr);
120 if (rc != TPM2_RC_SUCCESS) {
121 LOG(FATAL) << "Could not initialize esys: " << Tss2_RC_Decode(rc)
122 << " (" << rc << ")";
123 }
124
125 rc = Esys_Startup(esys, TPM2_SU_CLEAR);
126 if (rc != TPM2_RC_SUCCESS) {
127 LOG(FATAL) << "TPM2_Startup failed: " << Tss2_RC_Decode(rc)
128 << " (" << rc << ")";
129 }
130
131 TPM2B_AUTH auth = {};
132 Esys_TR_SetAuth(esys, ESYS_TR_RH_LOCKOUT, &auth);
133
134 rc = Esys_DictionaryAttackLockReset(
135 /* esysContext */ esys,
136 /* lockHandle */ ESYS_TR_RH_LOCKOUT,
137 /* shandle1 */ ESYS_TR_PASSWORD,
138 /* shandle2 */ ESYS_TR_NONE,
139 /* shandle3 */ ESYS_TR_NONE);
140
141 if (rc != TPM2_RC_SUCCESS) {
142 LOG(FATAL) << "Could not reset TPM lockout: " << Tss2_RC_Decode(rc)
143 << " (" << rc << ")";
144 }
145
146 Esys_Finalize(&esys);
147 }
148
~InProcessTpm()149 InProcessTpm::~InProcessTpm() {
150 _rpc__Signal_NvOff();
151 _rpc__Signal_PowerOff();
152 }
153
TctiContext()154 TSS2_TCTI_CONTEXT* InProcessTpm::TctiContext() {
155 return reinterpret_cast<TSS2_TCTI_CONTEXT*>(&impl_->tcti_context_);
156 }
157