blob: 46db3800f843651623a90fdd33060015082fedbf [file] [log] [blame]
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +02001// Copyright 2017 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Louis Collard3925fc92019-02-26 18:43:22 +08005#include <array>
6
Qijiang Fan713061e2021-03-08 15:45:12 +09007#include <base/check.h>
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +02008#include <base/logging.h>
9#include <base/strings/string_number_conversions.h>
10#include <base/strings/string_util.h>
11#include <base/sys_byteorder.h>
12
13#include "u2fd/tpm_vendor_cmd.h"
14
15namespace {
16
17// All TPM extension commands use this struct for input and output. Any other
18// data follows immediately after. All values are big-endian over the wire.
19struct TpmCmdHeader {
20 uint16_t tag; // TPM_ST_NO_SESSIONS
21 uint32_t size; // including this header
22 uint32_t code; // Command out, Response back.
23 uint16_t subcommand_code; // Additional command/response codes
24} __attribute__((packed));
25
26// TPMv2 Spec mandates that vendor-specific command codes have bit 29 set,
27// while bits 15-0 indicate the command. All other bits should be zero. We
28// define one of those 16-bit command values for Cr50 purposes, and use the
29// subcommand_code in struct TpmCmdHeader to further distinguish the desired
30// operation.
31const uint32_t kTpmCcVendorBit = 0x20000000;
32
33// Vendor-specific command codes
34const uint32_t kTpmCcVendorCr50 = 0x0000;
35
36// Cr50 vendor-specific subcommand codes. 16 bits available.
Louis Collard3925fc92019-02-26 18:43:22 +080037// TODO(louiscollard): Replace this with constants from cr50 header.
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +020038const uint16_t kVendorCcU2fApdu = 27;
Louis Collard3925fc92019-02-26 18:43:22 +080039const uint16_t kVendorCcU2fGenerate = 44;
40const uint16_t kVendorCcU2fSign = 45;
41const uint16_t kVendorCcU2fAttest = 46;
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +020042
43} // namespace
44
45namespace u2f {
46
Louis Collardadf2abd2020-06-18 17:33:23 +080047TpmVendorCommandProxy::TpmVendorCommandProxy() {}
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +020048TpmVendorCommandProxy::~TpmVendorCommandProxy() {}
49
Yicheng Lic03ac7e2019-12-09 16:13:26 -080050base::Lock& TpmVendorCommandProxy::GetLock() {
51 return lock_;
52}
53
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +020054uint32_t TpmVendorCommandProxy::VendorCommand(uint16_t cc,
55 const std::string& input,
56 std::string* output) {
57 // Pack up the header and the input
58 TpmCmdHeader header;
59 header.tag = base::HostToNet16(trunks::TPM_ST_NO_SESSIONS);
60 header.size = base::HostToNet32(sizeof(header) + input.size());
61 header.code = base::HostToNet32(kTpmCcVendorBit | kTpmCcVendorCr50);
62 header.subcommand_code = base::HostToNet16(cc);
63
64 std::string command(reinterpret_cast<char*>(&header), sizeof(header));
65 command += input;
66
67 // Send the command, get the response
68 VLOG(2) << "Out(" << command.size()
69 << "): " << base::HexEncode(command.data(), command.size());
70 std::string response = SendCommandAndWait(command);
71 VLOG(2) << "In(" << response.size()
72 << "): " << base::HexEncode(response.data(), response.size());
73
74 if (response.size() < sizeof(header)) {
75 LOG(ERROR) << "TPM response was too short!";
76 return -1;
77 }
78
79 // Unpack the response header and any output
80 memcpy(&header, response.data(), sizeof(header));
81 header.size = base::NetToHost32(header.size);
82 header.code = base::NetToHost32(header.code);
83
84 // Error of some sort?
85 if (header.code) {
86 if ((header.code & kVendorRcErr) == kVendorRcErr) {
87 LOG(WARNING) << "TPM error code 0x" << std::hex << header.code;
88 }
89 }
90
91 // Pass back any reply beyond the header
92 *output = response.substr(sizeof(header));
93
94 return header.code;
95}
96
Louis Collard3925fc92019-02-26 18:43:22 +080097namespace {
98
99template <typename Request>
100std::string RequestToString(const Request& req) {
101 return std::string(reinterpret_cast<const char*>(&req), sizeof(req));
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +0200102}
103
Louis Collard3925fc92019-02-26 18:43:22 +0800104template <>
Louis Collardbe159282020-02-12 15:29:56 +0800105std::string RequestToString(const struct u2f_attest_req& req) {
Andrey Proninb7084642019-12-30 17:32:38 -0800106 return std::string(reinterpret_cast<const char*>(&req),
Louis Collardbe159282020-02-12 15:29:56 +0800107 offsetof(struct u2f_attest_req, data) + req.dataLen);
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +0200108}
109
Louis Collard3925fc92019-02-26 18:43:22 +0800110} // namespace
Vincent Palatin7b899562017-09-27 11:07:14 +0200111
Louis Collard3925fc92019-02-26 18:43:22 +0800112template <typename Request, typename Response>
113uint32_t TpmVendorCommandProxy::VendorCommandStruct(uint16_t cc,
114 const Request& input,
115 Response* output) {
116 std::string output_str;
117 uint32_t resp_code = VendorCommand(cc, RequestToString(input), &output_str);
Vincent Palatin7b899562017-09-27 11:07:14 +0200118
Louis Collard3925fc92019-02-26 18:43:22 +0800119 if (resp_code == 0) {
120 if (output_str.size() == sizeof(*output)) {
121 memcpy(output, output_str.data(), sizeof(*output));
122 } else {
123 LOG(ERROR) << "Invalid response size for successful vendor command, "
124 << "expected: " << sizeof(*output)
125 << ", actual: " << output_str.size();
126 return kVendorRcInvalidResponse;
127 }
Vincent Palatin7b899562017-09-27 11:07:14 +0200128 }
129
Louis Collard3925fc92019-02-26 18:43:22 +0800130 return resp_code;
Vincent Palatin7b899562017-09-27 11:07:14 +0200131}
132
Louis Collard3925fc92019-02-26 18:43:22 +0800133uint32_t TpmVendorCommandProxy::SendU2fApdu(const std::string& req,
134 std::string* resp_out) {
135 return VendorCommand(kVendorCcU2fApdu, req, resp_out);
136}
137
Louis Collardbe159282020-02-12 15:29:56 +0800138uint32_t TpmVendorCommandProxy::SendU2fGenerate(
Yicheng Li519a56d2020-09-17 19:16:31 +0000139 const struct u2f_generate_req& req, struct u2f_generate_resp* resp_out) {
Yicheng Li5ca11f52020-05-14 16:49:48 -0700140 if ((req.flags & U2F_UV_ENABLED_KH) != 0) {
141 LOG(ERROR) << "Invalid flags in u2f_generate request.";
142 return -1;
143 }
144
Louis Collard3925fc92019-02-26 18:43:22 +0800145 return VendorCommandStruct(kVendorCcU2fGenerate, req, resp_out);
146}
147
Yicheng Li5ca11f52020-05-14 16:49:48 -0700148uint32_t TpmVendorCommandProxy::SendU2fGenerate(
149 const struct u2f_generate_req& req,
150 struct u2f_generate_versioned_resp* resp_out) {
151 if ((req.flags & U2F_UV_ENABLED_KH) == 0) {
152 LOG(ERROR) << "Invalid flags in u2f_generate request.";
153 return -1;
154 }
155
156 return VendorCommandStruct(kVendorCcU2fGenerate, req, resp_out);
157}
158
159template <typename Request>
160uint32_t TpmVendorCommandProxy::SendU2fSignGeneric(
161 const Request& req, struct u2f_sign_resp* resp_out) {
Louis Collard69429352019-03-07 10:57:09 +0800162 std::string output_str;
163 uint32_t resp_code =
164 VendorCommand(kVendorCcU2fSign, RequestToString(req), &output_str);
165
166 if (resp_code == 0) {
167 // A success response may or may not have a body, depending on whether the
168 // request was a full sign request, or simply a 'check only' request, to
169 // test ownership of the specified key handle.
Yicheng Li5ca11f52020-05-14 16:49:48 -0700170 if (((req.flags & U2F_AUTH_CHECK_ONLY) == U2F_AUTH_CHECK_ONLY) &&
171 output_str.size() == 0) {
Louis Collard69429352019-03-07 10:57:09 +0800172 // We asked to test ownership of a key handle; success response code
173 // indicates it is owned. No response body expected.
174 return resp_code;
Louis Collardbe159282020-02-12 15:29:56 +0800175 } else if (output_str.size() == sizeof(struct u2f_sign_resp)) {
Louis Collard69429352019-03-07 10:57:09 +0800176 DCHECK(resp_out); // It is a programming error for this to fail.
177 memcpy(resp_out, output_str.data(), sizeof(*resp_out));
178 } else {
179 LOG(ERROR) << "Invalid response size for successful vendor command, "
Louis Collardbe159282020-02-12 15:29:56 +0800180 << "expected: "
181 << (resp_out ? sizeof(struct u2f_sign_resp) : 0)
Louis Collard69429352019-03-07 10:57:09 +0800182 << ", actual: " << output_str.size();
183 return kVendorRcInvalidResponse;
184 }
185 }
186
187 return resp_code;
Louis Collard3925fc92019-02-26 18:43:22 +0800188}
189
Yicheng Li5ca11f52020-05-14 16:49:48 -0700190uint32_t TpmVendorCommandProxy::SendU2fSign(const struct u2f_sign_req& req,
191 u2f_sign_resp* resp) {
192 return SendU2fSignGeneric(req, resp);
193}
194
195uint32_t TpmVendorCommandProxy::SendU2fSign(
196 const struct u2f_sign_versioned_req& req, u2f_sign_resp* resp) {
197 return SendU2fSignGeneric(req, resp);
198}
199
Louis Collardbe159282020-02-12 15:29:56 +0800200uint32_t TpmVendorCommandProxy::SendU2fAttest(
201 const struct u2f_attest_req& req, struct u2f_attest_resp* resp_out) {
Louis Collard3925fc92019-02-26 18:43:22 +0800202 return VendorCommandStruct(kVendorCcU2fAttest, req, resp_out);
203}
204
205uint32_t TpmVendorCommandProxy::GetG2fCertificate(std::string* cert_out) {
206 constexpr std::array<uint8_t, 0x23> kCertRequest{
207 0x80, 0x02, // TPM_ST_SESSIONS
208 0x00, 0x00, 0x00, 0x23, // size
209 0x00, 0x00, 0x01, 0x4e, // TPM_CC_NV_READ
210 0x01, 0x3f, 0xff, 0x02, // authHandle : TPMI_RH_NV_AUTH
211 0x01, 0x3f, 0xff, 0x02, // nvIndex : TPMI_RH_NV_INDEX
212 0x00, 0x00, 0x00, 0x09, // authorizationSize : UINT32
213 0x40, 0x00, 0x00, 0x09, // sessionHandle : empty password
214 0x00, 0x00, 0x00, 0x00, 0x00, // nonce, sessionAttributes, hmac
215 0x01, 0x3b, // nvSize : UINT16
216 0x00, 0x00 // nvOffset : UINT16
217 };
218
219 constexpr std::array<uint8_t, 16> kExpectedCertResponseHeader{
220 0x80, 0x02, // TPM_ST_SESSIONS
221 0x00, 0x00, 0x01, 0x50, // responseSize
222 0x00, 0x00, 0x00, 0x00, // responseCode : TPM_RC_SUCCESS
223 0x00, 0x00, 0x01, 0x3d, // parameterSize
224 0x01, 0x3b, // TPM2B_MAX_NV_BUFFER : size
225 };
226
227 constexpr int kCertSize = 0x013b;
228 constexpr int kTpmResponseHeaderSize = 10;
229 constexpr int kExpectedCertResponseSize = 0x0150;
230
231 std::string req(kCertRequest.begin(), kCertRequest.end());
232
233 VLOG(2) << "Out(" << req.size()
234 << "): " << base::HexEncode(req.data(), req.size());
235
236 std::string resp = SendCommandAndWait(req);
237
238 VLOG(2) << "In(" << resp.size()
239 << "): " << base::HexEncode(resp.data(), resp.size());
240
241 if (resp.size() < kTpmResponseHeaderSize) {
242 return kVendorRcInvalidResponse;
243 }
244
245 // TODO(louiscollard): This, in a less horrible way.
246 if (resp.size() != kExpectedCertResponseSize ||
247 resp.compare(0, 16,
248 std::string(kExpectedCertResponseHeader.begin(),
249 kExpectedCertResponseHeader.end())) != 0) {
250 return base::NetToHost32(
251 *reinterpret_cast<const uint32_t*>(&resp.data()[6]));
252 }
253
254 *cert_out = resp.substr(kExpectedCertResponseHeader.size(), kCertSize);
255
256 return 0;
257}
258
259void TpmVendorCommandProxy::LogIndividualCertificate() {
260 std::string cert;
261
262 uint32_t cert_status = GetG2fCertificate(&cert);
263
264 if (cert_status) {
265 VLOG(1) << "Failed to retrieve G2F certificate: " << std::hex
266 << cert_status;
267 } else {
268 VLOG(1) << "Certificate: " << base::HexEncode(cert.data(), cert.size());
269 }
270}
271
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +0200272} // namespace u2f