diff options
Diffstat (limited to 'firmware/lib/tpm_lite/tlcl.c')
-rw-r--r-- | firmware/lib/tpm_lite/tlcl.c | 222 |
1 files changed, 216 insertions, 6 deletions
diff --git a/firmware/lib/tpm_lite/tlcl.c b/firmware/lib/tpm_lite/tlcl.c index 8f17380f..fe82bd71 100644 --- a/firmware/lib/tpm_lite/tlcl.c +++ b/firmware/lib/tpm_lite/tlcl.c @@ -192,6 +192,63 @@ static uint32_t StartOIAPSession(struct auth_session* session, return result; } +static uint32_t StartOSAPSession( + struct auth_session* session, + uint16_t entity_type, + uint32_t entity_value, + const uint8_t entity_usage_auth[TPM_AUTH_DATA_LEN]) +{ + session->valid = 0; + + /* Build OSAP command. */ + struct s_tpm_osap_cmd cmd; + memcpy(&cmd, &tpm_osap_cmd, sizeof(cmd)); + ToTpmUint16(cmd.buffer + cmd.entityType, entity_type); + ToTpmUint32(cmd.buffer + cmd.entityValue, entity_value); + if (VbExTpmGetRandom(cmd.buffer + cmd.nonceOddOSAP, + sizeof(TPM_NONCE)) != VB2_SUCCESS) { + return TPM_E_INTERNAL_ERROR; + } + + /* Send OSAP command. */ + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint32_t result = TlclSendReceive(cmd.buffer, response, + sizeof(response)); + if (result != TPM_SUCCESS) { + return result; + } + + /* Parse response. */ + uint32_t size; + FromTpmUint32(response + sizeof(uint16_t), &size); + if (size < kTpmResponseHeaderLength + sizeof(uint32_t) + + 2 * sizeof(TPM_NONCE)) { + return TPM_E_INVALID_RESPONSE; + } + + const uint8_t* cursor = response + kTpmResponseHeaderLength; + session->handle = ReadTpmUint32(&cursor); + memcpy(session->nonce_even.nonce, cursor, sizeof(TPM_NONCE)); + cursor += sizeof(TPM_NONCE); + const uint8_t* nonce_even_osap = cursor; + cursor += sizeof(TPM_NONCE); + VbAssert(cursor - response <= TPM_LARGE_ENOUGH_COMMAND_SIZE); + + /* Compute shared secret */ + uint8_t hmac_input[2 * sizeof(TPM_NONCE)]; + memcpy(hmac_input, nonce_even_osap, sizeof(TPM_NONCE)); + memcpy(hmac_input + sizeof(TPM_NONCE), cmd.buffer + cmd.nonceOddOSAP, + sizeof(TPM_NONCE)); + if (hmac(VB2_HASH_SHA1, entity_usage_auth, TPM_AUTH_DATA_LEN, + hmac_input, sizeof(hmac_input), session->shared_secret, + sizeof(session->shared_secret))) { + return TPM_E_INTERNAL_ERROR; + } + session->valid = 1; + + return result; +} + /* Fills in the authentication block at the end of the command. The command body * should already be initialized in |command_buffer|, and the included command * size should account for the auth block that gets filled in. */ @@ -378,13 +435,166 @@ uint32_t TlclContinueSelfTest(void) uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { - struct s_tpm_nv_definespace_cmd cmd; VB2_DEBUG("TPM: TlclDefineSpace(0x%x, 0x%x, %d)\n", index, perm, size); - memcpy(&cmd, &tpm_nv_definespace_cmd, sizeof(cmd)); - ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.index, index); - ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.perm, perm); - ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.size, size); - return Send(cmd.buffer); + return TlclDefineSpaceEx(NULL, 0, index, perm, size, NULL, 0); +} + +uint32_t TlclDefineSpaceEx(const uint8_t* owner_auth, uint32_t owner_auth_size, + uint32_t index, uint32_t perm, uint32_t size, + const void* auth_policy, uint32_t auth_policy_size) +{ + uint32_t result; + + /* Build the request data. */ + uint8_t cmd[sizeof(tpm_nv_definespace_cmd.buffer) + + kTpmRequestAuthBlockLength]; + memcpy(cmd, &tpm_nv_definespace_cmd, sizeof(tpm_nv_definespace_cmd)); + ToTpmUint32(cmd + tpm_nv_definespace_cmd.index, index); + ToTpmUint32(cmd + tpm_nv_definespace_cmd.perm, perm); + ToTpmUint32(cmd + tpm_nv_definespace_cmd.size, size); + if (auth_policy != NULL) { + if (auth_policy_size != sizeof(TPM_NV_AUTH_POLICY)) { + return TPM_E_BUFFER_SIZE; + } + + const TPM_NV_AUTH_POLICY* policy = auth_policy; + memcpy(cmd + tpm_nv_definespace_cmd.pcr_info_read, + &policy->pcr_info_read, sizeof(policy->pcr_info_read)); + memcpy(cmd + tpm_nv_definespace_cmd.pcr_info_write, + &policy->pcr_info_write, sizeof(policy->pcr_info_write)); + } + +#ifdef CHROMEOS_ENVIRONMENT + struct auth_session auth_session; + if (owner_auth) { + if (owner_auth_size != TPM_AUTH_DATA_LEN) { + return TPM_E_AUTHFAIL; + } + + result = StartOSAPSession(&auth_session, TPM_ET_OWNER, 0, + owner_auth); + if (result != TPM_SUCCESS) { + return result; + } + + ToTpmUint32(cmd + sizeof(uint16_t), sizeof(cmd)); + ToTpmUint16(cmd, TPM_TAG_RQU_AUTH1_COMMAND); + result = AddRequestAuthBlock(&auth_session, cmd, sizeof(cmd), + 0); + if (result != TPM_SUCCESS) { + return result; + } + } +#endif + + /* Send the command. */ + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + result = TlclSendReceive(cmd, response, sizeof(response)); + if (result != TPM_SUCCESS) { + return result; + } + +#ifdef CHROMEOS_ENVIRONMENT + if (owner_auth) { + result = CheckResponseAuthBlock(&auth_session, + TPM_ORD_NV_DefineSpace, + response, sizeof(response)); + } +#endif + + return result; +} + +uint32_t TlclInitNvAuthPolicy(uint32_t pcr_selection_bitmap, + const uint8_t pcr_values[][TPM_PCR_DIGEST], + void* auth_policy, uint32_t* auth_policy_size) +{ + uint32_t buffer_size = *auth_policy_size; + *auth_policy_size = sizeof(TPM_NV_AUTH_POLICY); + if (!auth_policy || buffer_size < sizeof(TPM_NV_AUTH_POLICY)) { + return TPM_E_BUFFER_SIZE; + } + TPM_NV_AUTH_POLICY* policy = auth_policy; + + /* Note that although the struct definition allocates space for 3 bytes + * worth of PCR selection, it is technically a variably-sized field in + * the TPM structure definition. Since this is part of the policy + * digest, we need to carefully match our selection sizes. For now, we + * use 3 bytes, which aligns with TrouSerS behavior. */ + TPM_PCR_SELECTION* select = &policy->pcr_info_read.pcrSelection; + ToTpmUint16((uint8_t*)&select->sizeOfSelect, sizeof(select->pcrSelect)); + select->pcrSelect[0] = (pcr_selection_bitmap >> 0) & 0xff; + select->pcrSelect[1] = (pcr_selection_bitmap >> 8) & 0xff; + select->pcrSelect[2] = (pcr_selection_bitmap >> 16) & 0xff; + VbAssert((pcr_selection_bitmap & 0xff000000) == 0); + + /* Allow all localities except locality 3. Rationale: + * + * We don't actually care about restricting NVRAM access to specific + * localities. In fact localities aren't used in Chrome OS and locality + * 0 is used for everything. + * + * However, the TPM specification makes an effort to not allow NVRAM + * spaces that do not have some write access control configured: When + * defining a space, either at least one of OWNERWRITE, AUTHWRITE, + * WRITEDEFINE, PPWRITE or a locality restriction must be specified (See + * TPM_NV_DefineSpace command description in the spec). + * + * This complicates matters when defining an NVRAM space that should be + * writable and lockable (for the remainder of the boot cycle) by the OS + * even when the TPM is not owned: + * * OWNERWRITE doesn't work because there might be no owner. + * * PPWRITE restricts writing to firmware only. + * * Use of WRITEDEFINE prevents use of WRITE_STCLEAR (i.e. the space + * can either not be locked, or it would remain locked until next TPM + * clear). + * * AUTHWRITE looks workable at first sight (by setting a well-known + * auth secret). However writes must use TPM_NV_WriteValueAuth, which + * only works when the TPM is owned. Interestingly, the spec admits + * to that being a mistake in the TPM_NV_WriteValueAuth comment but + * asserts that the behavior is normative. + * + * Having ruled out all attributes, we're left with locality restriction + * as the only option to pass the access control requirement check in + * TPM_NV_DefineSpace. We choose to disallow locality 3, since that is + * the most unlikely one to be used in practice, i.e. the spec says + * locality 3 is for "Auxiliary components. Use of this is optional and, + * if used, it is implementation dependent." + */ + policy->pcr_info_read.localityAtRelease = + TPM_ALL_LOCALITIES & ~TPM_LOC_THREE; + + struct vb2_sha1_context sha1_ctx; + vb2_sha1_init(&sha1_ctx); + + vb2_sha1_update(&sha1_ctx, + (const uint8_t*)&policy->pcr_info_read.pcrSelection, + sizeof(policy->pcr_info_read.pcrSelection)); + + uint32_t num_pcrs = 0; + int i; + for (i = 0; i < sizeof(pcr_selection_bitmap) * 8; ++i) { + if ((1 << i) & pcr_selection_bitmap) { + num_pcrs++; + } + } + + uint8_t pcrs_size[sizeof(uint32_t)]; + ToTpmUint32(pcrs_size, num_pcrs * TPM_PCR_DIGEST); + vb2_sha1_update(&sha1_ctx, pcrs_size, sizeof(pcrs_size)); + + for (i = 0; i < num_pcrs; ++i) { + vb2_sha1_update(&sha1_ctx, pcr_values[i], TPM_PCR_DIGEST); + } + + vb2_sha1_finalize(&sha1_ctx, + policy->pcr_info_read.digestAtRelease.digest); + + /* Make the write policy an identical copy of the read auth policy. */ + memcpy(&policy->pcr_info_write, &policy->pcr_info_read, + sizeof(policy->pcr_info_read)); + + return TPM_SUCCESS; } uint32_t TlclWrite(uint32_t index, const void* data, uint32_t length) |