1 /* Copyright (c) 2012 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 */
5
6 /* A lightweight TPM command library.
7 *
8 * The general idea is that TPM commands are array of bytes whose
9 * fields are mostly compile-time constant. The goal is to build much
10 * of the commands at compile time (or build time) and change some of
11 * the fields at run time as needed. The code in
12 * utility/tlcl_generator.c builds structures containing the commands,
13 * as well as the offsets of the fields that need to be set at run
14 * time.
15 */
16
17 #include "sysincludes.h"
18
19 #include "tlcl.h"
20 #include "tlcl_internal.h"
21 #include "tlcl_structures.h"
22 #include "utility.h"
23 #include "vboot_api.h"
24
25 #ifdef FOR_TEST
26 /* Allow unit testing implementation of TlclSendReceive() */
27 #undef CHROMEOS_ENVIRONMENT
28 #endif
29
30 /* Sets the size field of a TPM command. */
SetTpmCommandSize(uint8_t * buffer,uint32_t size)31 static inline void SetTpmCommandSize(uint8_t* buffer, uint32_t size) {
32 ToTpmUint32(buffer + sizeof(uint16_t), size);
33 }
34
35 /* Gets the size field of a TPM command. */
36 __attribute__((unused))
TpmCommandSize(const uint8_t * buffer)37 static inline int TpmCommandSize(const uint8_t* buffer) {
38 uint32_t size;
39 FromTpmUint32(buffer + sizeof(uint16_t), &size);
40 return (int) size;
41 }
42
43 /* Gets the size field of a TPM request or response. */
TlclPacketSize(const uint8_t * packet)44 int TlclPacketSize(const uint8_t* packet) {
45 return TpmCommandSize(packet);
46 }
47
48 /* Gets the code field of a TPM command. */
TpmCommandCode(const uint8_t * buffer)49 static inline int TpmCommandCode(const uint8_t* buffer) {
50 uint32_t code;
51 FromTpmUint32(buffer + sizeof(uint16_t) + sizeof(uint32_t), &code);
52 return code;
53 }
54
55 /* Gets the return code field of a TPM result. */
TpmReturnCode(const uint8_t * buffer)56 static inline int TpmReturnCode(const uint8_t* buffer) {
57 return TpmCommandCode(buffer);
58 }
59
60 /* Like TlclSendReceive below, but do not retry if NEEDS_SELFTEST or
61 * DOING_SELFTEST errors are returned.
62 */
TlclSendReceiveNoRetry(const uint8_t * request,uint8_t * response,int max_length)63 static uint32_t TlclSendReceiveNoRetry(const uint8_t* request,
64 uint8_t* response, int max_length) {
65
66 uint32_t response_length = max_length;
67 uint32_t result;
68
69 #ifdef EXTRA_LOGGING
70 VBDEBUG(("TPM: command: %x%x %x%x%x%x %x%x%x%x\n",
71 request[0], request[1],
72 request[2], request[3], request[4], request[5],
73 request[6], request[7], request[8], request[9]));
74 #endif
75
76 result = VbExTpmSendReceive(request, TpmCommandSize(request),
77 response, &response_length);
78 if (0 != result) {
79 /* Communication with TPM failed, so response is garbage */
80 VBDEBUG(("TPM: command 0x%x send/receive failed: 0x%x\n",
81 TpmCommandCode(request), result));
82 return result;
83 }
84 /* Otherwise, use the result code from the response */
85 result = TpmReturnCode(response);
86
87 /* TODO: add paranoia about returned response_length vs. max_length
88 * (and possibly expected length from the response header). See
89 * crosbug.com/17017 */
90
91 #ifdef EXTRA_LOGGING
92 VBDEBUG(("TPM: response: %x%x %x%x%x%x %x%x%x%x\n",
93 response[0], response[1],
94 response[2], response[3], response[4], response[5],
95 response[6], response[7], response[8], response[9]));
96 #endif
97
98 VBDEBUG(("TPM: command 0x%x returned 0x%x\n",
99 TpmCommandCode(request), result));
100
101 return result;
102 }
103
104
105 /* Sends a TPM command and gets a response. Returns 0 if success or the TPM
106 * error code if error. In the firmware, waits for the self test to complete
107 * if needed. In the host, reports the first error without retries. */
TlclSendReceive(const uint8_t * request,uint8_t * response,int max_length)108 uint32_t TlclSendReceive(const uint8_t* request, uint8_t* response,
109 int max_length) {
110 uint32_t result = TlclSendReceiveNoRetry(request, response, max_length);
111 /* When compiling for the firmware, hide command failures due to the self
112 * test not having run or completed. */
113 #ifndef CHROMEOS_ENVIRONMENT
114 /* If the command fails because the self test has not completed, try it
115 * again after attempting to ensure that the self test has completed. */
116 if (result == TPM_E_NEEDS_SELFTEST || result == TPM_E_DOING_SELFTEST) {
117 result = TlclContinueSelfTest();
118 if (result != TPM_SUCCESS) {
119 return result;
120 }
121 #if defined(TPM_BLOCKING_CONTINUESELFTEST) || defined(VB_RECOVERY_MODE)
122 /* Retry only once */
123 result = TlclSendReceiveNoRetry(request, response, max_length);
124 #else
125 /* This needs serious testing. The TPM specification says: "iii. The
126 * caller MUST wait for the actions of TPM_ContinueSelfTest to complete
127 * before reissuing the command C1." But, if ContinueSelfTest is
128 * non-blocking, how do we know that the actions have completed other than
129 * trying again? */
130 do {
131 result = TlclSendReceiveNoRetry(request, response, max_length);
132 } while (result == TPM_E_DOING_SELFTEST);
133 #endif
134 }
135 #endif /* ! defined(CHROMEOS_ENVIRONMENT) */
136 return result;
137 }
138
139 /* Sends a command and returns the error code. */
Send(const uint8_t * command)140 static uint32_t Send(const uint8_t* command) {
141 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
142 return TlclSendReceive(command, response, sizeof(response));
143 }
144
145 /* Exported functions. */
146
TlclLibInit(void)147 uint32_t TlclLibInit(void) {
148 return VbExTpmInit();
149 }
150
TlclLibClose(void)151 uint32_t TlclLibClose(void) {
152 return VbExTpmClose();
153 }
154
TlclStartup(void)155 uint32_t TlclStartup(void) {
156 VBDEBUG(("TPM: Startup\n"));
157 return Send(tpm_startup_cmd.buffer);
158 }
159
TlclSaveState(void)160 uint32_t TlclSaveState(void) {
161 VBDEBUG(("TPM: SaveState\n"));
162 return Send(tpm_savestate_cmd.buffer);
163 }
164
TlclResume(void)165 uint32_t TlclResume(void) {
166 VBDEBUG(("TPM: Resume\n"));
167 return Send(tpm_resume_cmd.buffer);
168 }
169
TlclSelfTestFull(void)170 uint32_t TlclSelfTestFull(void) {
171 VBDEBUG(("TPM: Self test full\n"));
172 return Send(tpm_selftestfull_cmd.buffer);
173 }
174
TlclContinueSelfTest(void)175 uint32_t TlclContinueSelfTest(void) {
176 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
177 VBDEBUG(("TPM: Continue self test\n"));
178 /* Call the No Retry version of SendReceive to avoid recursion. */
179 return TlclSendReceiveNoRetry(tpm_continueselftest_cmd.buffer,
180 response, sizeof(response));
181 }
182
TlclDefineSpace(uint32_t index,uint32_t perm,uint32_t size)183 uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) {
184 struct s_tpm_nv_definespace_cmd cmd;
185 VBDEBUG(("TPM: TlclDefineSpace(0x%x, 0x%x, %d)\n", index, perm, size));
186 Memcpy(&cmd, &tpm_nv_definespace_cmd, sizeof(cmd));
187 ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.index, index);
188 ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.perm, perm);
189 ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.size, size);
190 return Send(cmd.buffer);
191 }
192
TlclWrite(uint32_t index,const void * data,uint32_t length)193 uint32_t TlclWrite(uint32_t index, const void* data, uint32_t length) {
194 struct s_tpm_nv_write_cmd cmd;
195 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
196 const int total_length =
197 kTpmRequestHeaderLength + kWriteInfoLength + length;
198
199 VBDEBUG(("TPM: TlclWrite(0x%x, %d)\n", index, length));
200 Memcpy(&cmd, &tpm_nv_write_cmd, sizeof(cmd));
201 VbAssert(total_length <= TPM_LARGE_ENOUGH_COMMAND_SIZE);
202 SetTpmCommandSize(cmd.buffer, total_length);
203
204 ToTpmUint32(cmd.buffer + tpm_nv_write_cmd.index, index);
205 ToTpmUint32(cmd.buffer + tpm_nv_write_cmd.length, length);
206 Memcpy(cmd.buffer + tpm_nv_write_cmd.data, data, length);
207
208 return TlclSendReceive(cmd.buffer, response, sizeof(response));
209 }
210
TlclRead(uint32_t index,void * data,uint32_t length)211 uint32_t TlclRead(uint32_t index, void* data, uint32_t length) {
212 struct s_tpm_nv_read_cmd cmd;
213 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
214 uint32_t result_length;
215 uint32_t result;
216
217 VBDEBUG(("TPM: TlclRead(0x%x, %d)\n", index, length));
218 Memcpy(&cmd, &tpm_nv_read_cmd, sizeof(cmd));
219 ToTpmUint32(cmd.buffer + tpm_nv_read_cmd.index, index);
220 ToTpmUint32(cmd.buffer + tpm_nv_read_cmd.length, length);
221
222 result = TlclSendReceive(cmd.buffer, response, sizeof(response));
223 if (result == TPM_SUCCESS && length > 0) {
224 uint8_t* nv_read_cursor = response + kTpmResponseHeaderLength;
225 FromTpmUint32(nv_read_cursor, &result_length);
226 nv_read_cursor += sizeof(uint32_t);
227 Memcpy(data, nv_read_cursor, result_length);
228 }
229
230 return result;
231 }
232
TlclPCRRead(uint32_t index,void * data,uint32_t length)233 uint32_t TlclPCRRead(uint32_t index, void* data, uint32_t length) {
234 struct s_tpm_pcr_read_cmd cmd;
235 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
236 uint32_t result;
237
238 VBDEBUG(("TPM: TlclPCRRead(0x%x, %d)\n", index, length));
239 if (length < kPcrDigestLength) {
240 return TPM_E_IOERROR;
241 }
242 Memcpy(&cmd, &tpm_pcr_read_cmd, sizeof(cmd));
243 ToTpmUint32(cmd.buffer + tpm_pcr_read_cmd.pcrNum, index);
244
245 result = TlclSendReceive(cmd.buffer, response, sizeof(response));
246 if (result == TPM_SUCCESS) {
247 uint8_t* pcr_read_cursor = response + kTpmResponseHeaderLength;
248 Memcpy(data, pcr_read_cursor, kPcrDigestLength);
249 }
250
251 return result;
252 }
253
TlclWriteLock(uint32_t index)254 uint32_t TlclWriteLock(uint32_t index) {
255 VBDEBUG(("TPM: Write lock 0x%x\n", index));
256 return TlclWrite(index, NULL, 0);
257 }
258
TlclReadLock(uint32_t index)259 uint32_t TlclReadLock(uint32_t index) {
260 VBDEBUG(("TPM: Read lock 0x%x\n", index));
261 return TlclRead(index, NULL, 0);
262 }
263
TlclAssertPhysicalPresence(void)264 uint32_t TlclAssertPhysicalPresence(void) {
265 VBDEBUG(("TPM: Asserting physical presence\n"));
266 return Send(tpm_ppassert_cmd.buffer);
267 }
268
TlclPhysicalPresenceCMDEnable(void)269 uint32_t TlclPhysicalPresenceCMDEnable(void) {
270 VBDEBUG(("TPM: Enable the physical presence command\n"));
271 return Send(tpm_ppenable_cmd.buffer);
272 }
273
TlclFinalizePhysicalPresence(void)274 uint32_t TlclFinalizePhysicalPresence(void) {
275 VBDEBUG(("TPM: Enable PP cmd, disable HW pp, and set lifetime lock\n"));
276 return Send(tpm_finalizepp_cmd.buffer);
277 }
278
TlclAssertPhysicalPresenceResult(void)279 uint32_t TlclAssertPhysicalPresenceResult(void) {
280 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
281 return TlclSendReceive(tpm_ppassert_cmd.buffer, response, sizeof(response));
282 }
283
TlclLockPhysicalPresence(void)284 uint32_t TlclLockPhysicalPresence(void) {
285 VBDEBUG(("TPM: Lock physical presence\n"));
286 return Send(tpm_pplock_cmd.buffer);
287 }
288
TlclSetNvLocked(void)289 uint32_t TlclSetNvLocked(void) {
290 VBDEBUG(("TPM: Set NV locked\n"));
291 return TlclDefineSpace(TPM_NV_INDEX_LOCK, 0, 0);
292 }
293
TlclIsOwned(void)294 int TlclIsOwned(void) {
295 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE + TPM_PUBEK_SIZE];
296 uint32_t result;
297 result = TlclSendReceive(tpm_readpubek_cmd.buffer,
298 response, sizeof(response));
299 return (result != TPM_SUCCESS);
300 }
301
TlclForceClear(void)302 uint32_t TlclForceClear(void) {
303 VBDEBUG(("TPM: Force clear\n"));
304 return Send(tpm_forceclear_cmd.buffer);
305 }
306
TlclSetEnable(void)307 uint32_t TlclSetEnable(void) {
308 VBDEBUG(("TPM: Enabling TPM\n"));
309 return Send(tpm_physicalenable_cmd.buffer);
310 }
311
TlclClearEnable(void)312 uint32_t TlclClearEnable(void) {
313 VBDEBUG(("TPM: Disabling TPM\n"));
314 return Send(tpm_physicaldisable_cmd.buffer);
315 }
316
TlclSetDeactivated(uint8_t flag)317 uint32_t TlclSetDeactivated(uint8_t flag) {
318 struct s_tpm_physicalsetdeactivated_cmd cmd;
319 VBDEBUG(("TPM: SetDeactivated(%d)\n", flag));
320 Memcpy(&cmd, &tpm_physicalsetdeactivated_cmd, sizeof(cmd));
321 *(cmd.buffer + cmd.deactivated) = flag;
322 return Send(cmd.buffer);
323 }
324
TlclGetPermanentFlags(TPM_PERMANENT_FLAGS * pflags)325 uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS* pflags) {
326 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
327 uint32_t size;
328 uint32_t result =
329 TlclSendReceive(tpm_getflags_cmd.buffer, response, sizeof(response));
330 if (result != TPM_SUCCESS)
331 return result;
332 FromTpmUint32(response + kTpmResponseHeaderLength, &size);
333 /* TODO(crbug.com/379255): This fails. Find out why.
334 * VbAssert(size == sizeof(TPM_PERMANENT_FLAGS));
335 */
336 Memcpy(pflags,
337 response + kTpmResponseHeaderLength + sizeof(size),
338 sizeof(TPM_PERMANENT_FLAGS));
339 return result;
340 }
341
TlclGetSTClearFlags(TPM_STCLEAR_FLAGS * vflags)342 uint32_t TlclGetSTClearFlags(TPM_STCLEAR_FLAGS* vflags) {
343 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
344 uint32_t size;
345 uint32_t result =
346 TlclSendReceive(tpm_getstclearflags_cmd.buffer, response, sizeof(response));
347 if (result != TPM_SUCCESS)
348 return result;
349 FromTpmUint32(response + kTpmResponseHeaderLength, &size);
350 /* Ugly assertion, but the struct is padded up by one byte. */
351 /* TODO(crbug.com/379255): This fails. Find out why.
352 * VbAssert(size == 7 && sizeof(TPM_STCLEAR_FLAGS) - 1 == 7);
353 */
354 Memcpy(vflags,
355 response + kTpmResponseHeaderLength + sizeof(size),
356 sizeof(TPM_STCLEAR_FLAGS));
357 return result;
358 }
359
TlclGetFlags(uint8_t * disable,uint8_t * deactivated,uint8_t * nvlocked)360 uint32_t TlclGetFlags(uint8_t* disable,
361 uint8_t* deactivated,
362 uint8_t *nvlocked) {
363 TPM_PERMANENT_FLAGS pflags;
364 uint32_t result = TlclGetPermanentFlags(&pflags);
365 if (result == TPM_SUCCESS) {
366 if (disable)
367 *disable = pflags.disable;
368 if (deactivated)
369 *deactivated = pflags.deactivated;
370 if (nvlocked)
371 *nvlocked = pflags.nvLocked;
372 VBDEBUG(("TPM: Got flags disable=%d, deactivated=%d, nvlocked=%d\n",
373 pflags.disable, pflags.deactivated, pflags.nvLocked));
374 }
375 return result;
376 }
377
TlclSetGlobalLock(void)378 uint32_t TlclSetGlobalLock(void) {
379 uint32_t x;
380 VBDEBUG(("TPM: Set global lock\n"));
381 return TlclWrite(TPM_NV_INDEX0, (uint8_t*) &x, 0);
382 }
383
TlclExtend(int pcr_num,const uint8_t * in_digest,uint8_t * out_digest)384 uint32_t TlclExtend(int pcr_num, const uint8_t* in_digest,
385 uint8_t* out_digest) {
386 struct s_tpm_extend_cmd cmd;
387 uint8_t response[kTpmResponseHeaderLength + kPcrDigestLength];
388 uint32_t result;
389
390 Memcpy(&cmd, &tpm_extend_cmd, sizeof(cmd));
391 ToTpmUint32(cmd.buffer + tpm_extend_cmd.pcrNum, pcr_num);
392 Memcpy(cmd.buffer + cmd.inDigest, in_digest, kPcrDigestLength);
393
394 result = TlclSendReceive(cmd.buffer, response, sizeof(response));
395 if (result != TPM_SUCCESS)
396 return result;
397
398 Memcpy(out_digest, response + kTpmResponseHeaderLength, kPcrDigestLength);
399 return result;
400 }
401
TlclGetPermissions(uint32_t index,uint32_t * permissions)402 uint32_t TlclGetPermissions(uint32_t index, uint32_t* permissions) {
403 struct s_tpm_getpermissions_cmd cmd;
404 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
405 uint8_t* nvdata;
406 uint32_t result;
407 uint32_t size;
408
409 Memcpy(&cmd, &tpm_getpermissions_cmd, sizeof(cmd));
410 ToTpmUint32(cmd.buffer + tpm_getpermissions_cmd.index, index);
411 result = TlclSendReceive(cmd.buffer, response, sizeof(response));
412 if (result != TPM_SUCCESS)
413 return result;
414
415 nvdata = response + kTpmResponseHeaderLength + sizeof(size);
416 FromTpmUint32(nvdata + kNvDataPublicPermissionsOffset, permissions);
417 return result;
418 }
419
TlclGetOwnership(uint8_t * owned)420 uint32_t TlclGetOwnership(uint8_t* owned) {
421 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
422 uint32_t size;
423 uint32_t result =
424 TlclSendReceive(tpm_getownership_cmd.buffer, response, sizeof(response));
425 if (result != TPM_SUCCESS)
426 return result;
427 FromTpmUint32(response + kTpmResponseHeaderLength, &size);
428 /* TODO(crbug.com/379255): This fails. Find out why.
429 * VbAssert(size == sizeof(*owned));
430 */
431 Memcpy(owned,
432 response + kTpmResponseHeaderLength + sizeof(size),
433 sizeof(*owned));
434 return result;
435 }
436
TlclGetRandom(uint8_t * data,uint32_t length,uint32_t * size)437 uint32_t TlclGetRandom(uint8_t* data, uint32_t length, uint32_t *size) {
438 struct s_tpm_get_random_cmd cmd;
439 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
440 uint32_t result;
441
442 VBDEBUG(("TPM: TlclGetRandom(%d)\n", length));
443 Memcpy(&cmd, &tpm_get_random_cmd, sizeof(cmd));
444 ToTpmUint32(cmd.buffer + tpm_get_random_cmd.bytesRequested, length);
445 /* There must be room in the response buffer for the bytes. */
446 if (length > TPM_LARGE_ENOUGH_COMMAND_SIZE - kTpmResponseHeaderLength
447 - sizeof(uint32_t)) {
448 return TPM_E_IOERROR;
449 }
450
451 result = TlclSendReceive(cmd.buffer, response, sizeof(response));
452 if (result == TPM_SUCCESS) {
453 uint8_t* get_random_cursor;
454 FromTpmUint32(response + kTpmResponseHeaderLength, size);
455
456 /* There must be room in the target buffer for the bytes. */
457 if (*size > length) {
458 return TPM_E_RESPONSE_TOO_LARGE;
459 }
460 get_random_cursor = response + kTpmResponseHeaderLength
461 + sizeof(uint32_t);
462 Memcpy(data, get_random_cursor, *size);
463 }
464
465 return result;
466 }
467