Home | History | Annotate | Download | only in tpm_lite
      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. */
     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))
     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. */
     44 int TlclPacketSize(const uint8_t* packet) {
     45   return TpmCommandSize(packet);
     46 }
     47 
     48 /* Gets the code field of a TPM command. */
     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. */
     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  */
     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. */
    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. */
    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 
    147 uint32_t TlclLibInit(void) {
    148   return VbExTpmInit();
    149 }
    150 
    151 uint32_t TlclLibClose(void) {
    152   return VbExTpmClose();
    153 }
    154 
    155 uint32_t TlclStartup(void) {
    156   VBDEBUG(("TPM: Startup\n"));
    157   return Send(tpm_startup_cmd.buffer);
    158 }
    159 
    160 uint32_t TlclSaveState(void) {
    161   VBDEBUG(("TPM: SaveState\n"));
    162   return Send(tpm_savestate_cmd.buffer);
    163 }
    164 
    165 uint32_t TlclResume(void) {
    166   VBDEBUG(("TPM: Resume\n"));
    167   return Send(tpm_resume_cmd.buffer);
    168 }
    169 
    170 uint32_t TlclSelfTestFull(void) {
    171   VBDEBUG(("TPM: Self test full\n"));
    172   return Send(tpm_selftestfull_cmd.buffer);
    173 }
    174 
    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 
    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 
    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 
    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 
    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 
    254 uint32_t TlclWriteLock(uint32_t index) {
    255   VBDEBUG(("TPM: Write lock 0x%x\n", index));
    256   return TlclWrite(index, NULL, 0);
    257 }
    258 
    259 uint32_t TlclReadLock(uint32_t index) {
    260   VBDEBUG(("TPM: Read lock 0x%x\n", index));
    261   return TlclRead(index, NULL, 0);
    262 }
    263 
    264 uint32_t TlclAssertPhysicalPresence(void) {
    265   VBDEBUG(("TPM: Asserting physical presence\n"));
    266   return Send(tpm_ppassert_cmd.buffer);
    267 }
    268 
    269 uint32_t TlclPhysicalPresenceCMDEnable(void) {
    270   VBDEBUG(("TPM: Enable the physical presence command\n"));
    271   return Send(tpm_ppenable_cmd.buffer);
    272 }
    273 
    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 
    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 
    284 uint32_t TlclLockPhysicalPresence(void) {
    285   VBDEBUG(("TPM: Lock physical presence\n"));
    286   return Send(tpm_pplock_cmd.buffer);
    287 }
    288 
    289 uint32_t TlclSetNvLocked(void) {
    290   VBDEBUG(("TPM: Set NV locked\n"));
    291   return TlclDefineSpace(TPM_NV_INDEX_LOCK, 0, 0);
    292 }
    293 
    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 
    302 uint32_t TlclForceClear(void) {
    303   VBDEBUG(("TPM: Force clear\n"));
    304   return Send(tpm_forceclear_cmd.buffer);
    305 }
    306 
    307 uint32_t TlclSetEnable(void) {
    308   VBDEBUG(("TPM: Enabling TPM\n"));
    309   return Send(tpm_physicalenable_cmd.buffer);
    310 }
    311 
    312 uint32_t TlclClearEnable(void) {
    313   VBDEBUG(("TPM: Disabling TPM\n"));
    314   return Send(tpm_physicaldisable_cmd.buffer);
    315 }
    316 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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