summaryrefslogtreecommitdiff
path: root/firmware/lib/tpm_lite/tlcl.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/lib/tpm_lite/tlcl.c')
-rw-r--r--firmware/lib/tpm_lite/tlcl.c222
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)