diff options
-rw-r--r-- | firmware/include/tlcl.h | 23 | ||||
-rw-r--r-- | firmware/include/tpm1_tss_constants.h | 20 | ||||
-rw-r--r-- | firmware/lib/tpm2_lite/tlcl.c | 25 | ||||
-rw-r--r-- | firmware/lib/tpm_lite/include/tlcl_structures.h | 12 | ||||
-rw-r--r-- | firmware/lib/tpm_lite/tlcl.c | 222 | ||||
-rw-r--r-- | tests/tlcl_tests.c | 180 | ||||
-rw-r--r-- | utility/include/tpmextras.h | 3 | ||||
-rw-r--r-- | utility/tlcl_generator.c | 43 |
8 files changed, 494 insertions, 34 deletions
diff --git a/firmware/include/tlcl.h b/firmware/include/tlcl.h index acb65309..a2a6308e 100644 --- a/firmware/include/tlcl.h +++ b/firmware/include/tlcl.h @@ -85,6 +85,29 @@ uint32_t TlclContinueSelfTest(void); uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size); /** + * Define a space using owner authorization secret [owner_auth]. The space is + * set up to have permission [perm]. [index] is the index for the space, [size] + * the usable data size. Optional auth policy (such as PCR selections) can be + * passed via [auth_policy]. The TPM error code is returned. + */ +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); + +/** + * Initializes [auth_policy] to require PCR binding of the given + * [pcr_selection_bitmap]. The PCR values are passed in the [pcr_values] + * parameter with each entry corresponding to the sequence of indexes that + * corresponds to the bits that are set in [pcr_selection_bitmap]. Returns + * TPM_SUCCESS if successful, TPM_E_BUFFER_SIZE if the provided buffer is too + * short. The actual size of the policy will be set in [auth_policy_size] upon + * return, also for the case of insufficient buffer size. + */ +uint32_t TlclInitNvAuthPolicy(uint32_t pcr_selection_bitmap, + const uint8_t pcr_values[][TPM_PCR_DIGEST], + void* auth_policy, uint32_t* auth_policy_size); + +/** * Write [length] bytes of [data] to space at [index]. The TPM error code is * returned. */ diff --git a/firmware/include/tpm1_tss_constants.h b/firmware/include/tpm1_tss_constants.h index 625b9aea..572d5c61 100644 --- a/firmware/include/tpm1_tss_constants.h +++ b/firmware/include/tpm1_tss_constants.h @@ -68,6 +68,8 @@ typedef uint32_t TPM_CAPABILITY_AREA; #define TPM_PID_OWNER ((uint16_t) 0x0005) +#define TPM_ET_OWNER ((uint32_t) 0x02) + #define TPM_ST_CLEAR ((uint16_t) 0x0001) #define TPM_ST_STATE ((uint16_t) 0x0002) #define TPM_ST_DEACTIVATED ((uint16_t) 0x0003) @@ -78,6 +80,9 @@ typedef uint32_t TPM_CAPABILITY_AREA; #define TPM_LOC_ONE (((uint32_t)1)<<1) #define TPM_LOC_ZERO (((uint32_t)1)<<0) +#define TPM_ALL_LOCALITIES (TPM_LOC_ZERO | TPM_LOC_ONE | TPM_LOC_TWO \ + | TPM_LOC_THREE | TPM_LOC_FOUR) /* 0x1f */ + #define TPM_PHYSICAL_PRESENCE_LOCK ((uint16_t) 0x0004) #define TPM_PHYSICAL_PRESENCE_PRESENT ((uint16_t) 0x0008) #define TPM_PHYSICAL_PRESENCE_NOTPRESENT ((uint16_t) 0x0010) @@ -104,21 +109,21 @@ typedef TPM_DIGEST TPM_COMPOSITE_HASH; typedef struct tdTPM_PCR_SELECTION { uint16_t sizeOfSelect; - uint8_t *pcrSelect; -} TPM_PCR_SELECTION; + uint8_t pcrSelect[3]; +} __attribute__((packed)) TPM_PCR_SELECTION; typedef struct tdTPM_NV_ATTRIBUTES { TPM_STRUCTURE_TAG tag; TPM_NV_PER_ATTRIBUTES attributes; -} TPM_NV_ATTRIBUTES; +} __attribute__((packed)) TPM_NV_ATTRIBUTES; typedef struct tdTPM_PCR_INFO_SHORT { TPM_PCR_SELECTION pcrSelection; TPM_LOCALITY_SELECTION localityAtRelease; TPM_COMPOSITE_HASH digestAtRelease; -} TPM_PCR_INFO_SHORT; +} __attribute__((packed)) TPM_PCR_INFO_SHORT; typedef struct tdTPM_PERMANENT_FLAGS { @@ -188,6 +193,12 @@ typedef struct tdTPM_IFX_FIELDUPGRADEINFO uint16_t wFieldUpgradeCounter; } TPM_IFX_FIELDUPGRADEINFO; +typedef struct tdTPM_NV_AUTH_POLICY +{ + TPM_PCR_INFO_SHORT pcr_info_read; + TPM_PCR_INFO_SHORT pcr_info_write; +} TPM_NV_AUTH_POLICY; + #define TPM_IFX_FieldUpgradeInfoRequest2 ((uint8_t) 0x11) /* Ordinals */ @@ -202,6 +213,7 @@ typedef struct tdTPM_IFX_FIELDUPGRADEINFO #define TPM_ORD_NV_ReadValue ((uint32_t) 0x000000CF) #define TPM_ORD_NV_WriteValue ((uint32_t) 0x000000CD) #define TPM_ORD_OIAP ((uint32_t) 0x0000000A) +#define TPM_ORD_OSAP ((uint32_t) 0x0000000B) #define TPM_ORD_PcrRead ((uint32_t) 0x00000015) #define TPM_ORD_PhysicalEnable ((uint32_t) 0x0000006F) #define TPM_ORD_PhysicalDisable ((uint32_t) 0x00000070) diff --git a/firmware/lib/tpm2_lite/tlcl.c b/firmware/lib/tpm2_lite/tlcl.c index aec3e2b3..70d584f0 100644 --- a/firmware/lib/tpm2_lite/tlcl.c +++ b/firmware/lib/tpm2_lite/tlcl.c @@ -190,8 +190,18 @@ uint32_t TlclContinueSelfTest(void) uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { + 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) +{ struct tpm2_nv_define_space_cmd define_space; + /* Authentication support is not implemented. */ + VbAssert(owner_auth == NULL && owner_auth_size == 0); + /* For backwards-compatibility, if no READ or WRITE permissions are set, * assume readable/writeable with empty auth value. */ @@ -205,10 +215,25 @@ uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) define_space.publicInfo.dataSize = size; define_space.publicInfo.attributes = perm; define_space.publicInfo.nameAlg = TPM_ALG_SHA256; + if (auth_policy && auth_policy_size > 0) { + define_space.publicInfo.authPolicy.size = auth_policy_size; + define_space.publicInfo.authPolicy.buffer = + (uint8_t*) auth_policy; + } return tpm_get_response_code(TPM2_NV_DefineSpace, &define_space); } +uint32_t TlclInitNvAuthPolicy(uint32_t pcr_selection_bitmap, + const uint8_t pcr_values[][TPM_PCR_DIGEST], + void* auth_policy, uint32_t* auth_policy_size) +{ + /* Actual PCR selection isn't implemented. */ + VbAssert(pcr_selection_bitmap == 0); + *auth_policy_size = 0; + return TPM_SUCCESS; +} + /** * Issue a ForceClear. The TPM error code is returned. */ diff --git a/firmware/lib/tpm_lite/include/tlcl_structures.h b/firmware/lib/tpm_lite/include/tlcl_structures.h index 192fe355..73ed889e 100644 --- a/firmware/lib/tpm_lite/include/tlcl_structures.h +++ b/firmware/lib/tpm_lite/include/tlcl_structures.h @@ -7,6 +7,14 @@ const struct s_tpm_takeownership_cmd{ } tpm_takeownership_cmd = {{0x0, 0xc2, 0x0, 0x0, 0x2, 0x70, 0x0, 0x0, 0x0, 0xd, 0x0, 0x5, 0x0, 0x0, 0x1, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x0, 0x1, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, }, 16, 276, }; +const struct s_tpm_osap_cmd{ + uint8_t buffer[36]; + uint16_t entityType; + uint16_t entityValue; + uint16_t nonceOddOSAP; +} tpm_osap_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0xb, }, +10, 12, 16, }; + const struct s_tpm_oiap_cmd{ uint8_t buffer[10]; } tpm_oiap_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, }, @@ -152,10 +160,12 @@ const struct s_tpm_nv_write_cmd{ const struct s_tpm_nv_definespace_cmd{ uint8_t buffer[101]; uint16_t index; + uint16_t pcr_info_read; + uint16_t pcr_info_write; uint16_t perm; uint16_t size; } tpm_nv_definespace_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x18, 0, 0, 0, 0, 0x0, 0x3, 0, 0, 0, 0x1f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x3, 0, 0, 0, 0x1f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x17, }, -12, 70, 77, }; +12, 16, 42, 70, 77, }; const int kWriteInfoLength = 12; const int kNvDataPublicPermissionsOffset = 60; 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) diff --git a/tests/tlcl_tests.c b/tests/tlcl_tests.c index 31df17a9..65c48f50 100644 --- a/tests/tlcl_tests.c +++ b/tests/tlcl_tests.c @@ -266,6 +266,184 @@ static void ReadWriteTest(void) } /** + * Test DefineSpaceEx + */ +void DefineSpaceExTest(void) { + uint8_t osap_response[] = { + 0x00, 0xc4, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x41, 0x3d, 0xce, 0x20, 0xa2, + 0x5a, 0xa5, 0x95, 0xbe, 0x26, 0xe8, 0x76, 0x74, + 0x6c, 0x61, 0xf7, 0xa7, 0x24, 0x17, 0xa1, 0x06, + 0xcf, 0x53, 0x6d, 0xd4, 0x26, 0x98, 0x68, 0x86, + 0xe6, 0xf6, 0x62, 0x58, 0xdb, 0xa2, 0x9f, 0x5b, + 0x18, 0xa6, 0xae, 0x36, 0x32, 0x5d, + }; + uint8_t define_space_response[] = { + 0x00, 0xc5, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, + 0x00, 0x00, 0x42, 0xe6, 0x38, 0xc6, 0x37, 0x2a, + 0xf2, 0xfe, 0xb4, 0x01, 0x4b, 0x29, 0x63, 0x30, + 0x4e, 0x2f, 0x2e, 0x74, 0x58, 0xcd, 0x00, 0x40, + 0x42, 0x10, 0x40, 0xac, 0x93, 0x0c, 0xff, 0x8a, + 0xc4, 0x98, 0x78, 0xe3, 0xfe, 0x48, 0x5b, 0xb7, + 0xc8, 0x8d, 0xf4, + }; + uint8_t owner_secret[TPM_AUTH_DATA_LEN] = { 0 }; + TPM_NV_AUTH_POLICY policy; + + ResetMocks(); + calls[0].rsp = osap_response; + calls[0].rsp_size = sizeof(osap_response); + calls[1].rsp = define_space_response; + calls[1].rsp_size = sizeof(define_space_response); + TEST_EQ(TlclDefineSpaceEx(owner_secret, sizeof(owner_secret), + 0x20000005, 0x2000, 0x10, NULL, 0), + TPM_SUCCESS, "DefineSpace"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_OSAP, " osap cmd"); + TEST_EQ(calls[1].req_cmd, TPM_ORD_NV_DefineSpace, " definespace cmd"); + + /* Pass an auth policy. */ + ResetMocks(); + calls[0].rsp = osap_response; + calls[0].rsp_size = sizeof(osap_response); + calls[1].rsp = define_space_response; + calls[1].rsp_size = sizeof(define_space_response); + TEST_EQ(TlclDefineSpaceEx(owner_secret, sizeof(owner_secret), + 0x20000005, 0x2000, 0x10, &policy, + sizeof(policy)), + TPM_SUCCESS, "DefineSpace"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_OSAP, " osap cmd"); + TEST_EQ(calls[1].req_cmd, TPM_ORD_NV_DefineSpace, " definespace cmd"); + + /* Verify that the response gets authenticated. */ + ResetMocks(); + calls[0].rsp = osap_response; + calls[0].rsp_size = sizeof(osap_response); + calls[1].rsp = define_space_response; + calls[1].rsp_size = sizeof(define_space_response); + define_space_response[31] = 0; + TEST_EQ(TlclDefineSpaceEx(owner_secret, sizeof(owner_secret), + 0x20000005, 0x2000, 0x10, NULL, 0), + TPM_E_AUTHFAIL, "DefineSpace - response auth"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_OSAP, " osap cmd"); + TEST_EQ(calls[1].req_cmd, TPM_ORD_NV_DefineSpace, " definespace cmd"); + define_space_response[31] = 0x40; + + /* Verify that a short OSAP response gets caught. */ + ResetMocks(); + calls[0].rsp = osap_response; + calls[0].rsp_size = sizeof(osap_response); + ToTpmUint32(osap_response + sizeof(uint16_t), + kTpmRequestHeaderLength + sizeof(uint32_t) + + 2 * sizeof(TPM_NONCE) - 1); + TEST_EQ(TlclDefineSpaceEx(owner_secret, sizeof(owner_secret), + 0x20000005, 0x2000, 0x10, NULL, 0), + TPM_E_INVALID_RESPONSE, "DefineSpace - short OSAP response"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_OSAP, " osap cmd"); + ToTpmUint32(osap_response + sizeof(uint16_t), sizeof(osap_response)); + + /* Verify that a short DefineSpace response gets caught. */ + ResetMocks(); + calls[0].rsp = osap_response; + calls[0].rsp_size = sizeof(osap_response); + calls[1].rsp = define_space_response; + calls[1].rsp_size = sizeof(define_space_response); + ToTpmUint32(define_space_response + sizeof(uint16_t), + kTpmResponseHeaderLength + kTpmResponseAuthBlockLength - 1); + TEST_EQ(TlclDefineSpaceEx(owner_secret, sizeof(owner_secret), + 0x20000005, 0x2000, 0x10, NULL, 0), + TPM_E_INVALID_RESPONSE, + "DefineSpace - short DefineSpace response"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_OSAP, " osap cmd"); + TEST_EQ(calls[1].req_cmd, TPM_ORD_NV_DefineSpace, " definespace cmd"); + ToTpmUint32(define_space_response + sizeof(uint16_t), + sizeof(define_space_response)); +} + +/** + * Test TlclInitNvAuthPolicy. + */ +void InitNvAuthPolicyTest(void) { + const uint8_t empty_selection_digest[] = { + 0x79, 0xdd, 0xda, 0xfd, 0xc1, 0x97, 0xdc, 0xcc, + 0xe9, 0x98, 0x9a, 0xee, 0xf5, 0x52, 0x89, 0xee, + 0x24, 0x96, 0x4c, 0xac, + }; + const uint8_t pcr0_selection_digest[] = { + 0xb3, 0x2b, 0x96, 0x30, 0xd3, 0x21, 0x1e, 0x99, + 0x78, 0x9e, 0xd3, 0x1f, 0x11, 0x8e, 0x96, 0xbc, + 0xf7, 0x7e, 0x7b, 0x06, + }; + const uint8_t empty_selection_encoding[] = { 0x0, 0x0, 0x0 }; + const uint8_t pcr0_selection_encoding[] = { 0x1, 0x0, 0x0 }; + const uint8_t pcr_values[][TPM_PCR_DIGEST] = { + { 0x06, 0x4a, 0xec, 0x9b, 0xbd, 0x94, 0xde, 0xa1, + 0x23, 0x1a, 0xe7, 0x57, 0x67, 0x64, 0x7f, 0x09, + 0x8c, 0x39, 0x8e, 0x79, }, + }; + TPM_NV_AUTH_POLICY policy; + + /* Test empty selection. */ + uint32_t policy_size = sizeof(policy); + TlclInitNvAuthPolicy(0x0, NULL, &policy, &policy_size); + TEST_EQ(policy_size, sizeof(policy), "policy size"); + + uint16_t size_of_select; + FromTpmUint16( + (uint8_t*)&policy.pcr_info_read.pcrSelection.sizeOfSelect, + &size_of_select); + TEST_EQ(size_of_select, 3, "empty PCR selection read size of select"); + TEST_EQ(memcmp(policy.pcr_info_read.pcrSelection.pcrSelect, + empty_selection_encoding, + sizeof(empty_selection_encoding)), 0, + "empty PCR selection read selection encoding"); + TEST_EQ(policy.pcr_info_read.localityAtRelease, + TPM_ALL_LOCALITIES & ~TPM_LOC_THREE, + "empty PCR selection read locality"); + TEST_EQ(memcmp(empty_selection_digest, + policy.pcr_info_read.digestAtRelease.digest, + TPM_PCR_DIGEST), + 0, "empty PCR selection read digest"); + + FromTpmUint16( + (uint8_t*)&policy.pcr_info_write.pcrSelection.sizeOfSelect, + &size_of_select); + TEST_EQ(size_of_select, 3, "empty PCR selection write size of select"); + TEST_EQ(memcmp(policy.pcr_info_write.pcrSelection.pcrSelect, + empty_selection_encoding, + sizeof(empty_selection_encoding)), 0, + "empty PCR selection write selection encoding"); + TEST_EQ(policy.pcr_info_write.localityAtRelease, + TPM_ALL_LOCALITIES & ~TPM_LOC_THREE, + "empty PCR selection write locality"); + TEST_EQ(memcmp(empty_selection_digest, + policy.pcr_info_write.digestAtRelease.digest, + TPM_PCR_DIGEST), + 0, "empty PCR selection write digest"); + + /* Test PCR0 selection. */ + TlclInitNvAuthPolicy(0x1, pcr_values, &policy, &policy_size); + TEST_EQ(policy_size, sizeof(policy), "policy size"); + + TEST_EQ(memcmp(policy.pcr_info_read.pcrSelection.pcrSelect, + pcr0_selection_encoding, + sizeof(pcr0_selection_encoding)), 0, + "PCR0 selection read selection encoding"); + TEST_EQ(memcmp(pcr0_selection_digest, + policy.pcr_info_read.digestAtRelease.digest, + TPM_PCR_DIGEST), + 0, "PCR0 selection read digest"); + + TEST_EQ(memcmp(policy.pcr_info_write.pcrSelection.pcrSelect, + pcr0_selection_encoding, + sizeof(pcr0_selection_encoding)), 0, + "PCR0 selection write selection encoding"); + TEST_EQ(memcmp(pcr0_selection_digest, + policy.pcr_info_write.digestAtRelease.digest, + TPM_PCR_DIGEST), + 0, "PCR0 selection write digest"); +} + +/** * Test PCR funcs * * TODO: check params/data read/written. @@ -818,6 +996,8 @@ int main(void) TlclTest(); SendCommandTest(); ReadWriteTest(); + DefineSpaceExTest(); + InitNvAuthPolicyTest(); PcrTest(); FlagsTest(); RandomTest(); diff --git a/utility/include/tpmextras.h b/utility/include/tpmextras.h index 0fe0982a..23813e82 100644 --- a/utility/include/tpmextras.h +++ b/utility/include/tpmextras.h @@ -15,9 +15,6 @@ #define TPM_ENCAUTH_SIZE 20 #define TPM_PUBEK_SIZE 256 -#define TPM_ALL_LOCALITIES (TPM_LOC_ZERO | TPM_LOC_ONE | TPM_LOC_TWO \ - | TPM_LOC_THREE | TPM_LOC_FOUR) /* 0x1f */ - typedef struct tdTPM_WRITE_INFO { uint32_t nvIndex; uint32_t offset; diff --git a/utility/tlcl_generator.c b/utility/tlcl_generator.c index 972892f8..bb3301f0 100644 --- a/utility/tlcl_generator.c +++ b/utility/tlcl_generator.c @@ -96,12 +96,6 @@ Command* newCommand(TPM_COMMAND_CODE code, int size) { return newCommandWithTag(code, size, TPM_TAG_RQU_COMMAND); } -/* The TPM_PCR_SELECTION structure in /usr/include/tss/tpm.h contains a pointer - * instead of an array[3] of bytes, so we need to adjust sizes and offsets - * accordingly. - */ -#define PCR_SELECTION_FIX (3 - sizeof(char *)) - /* BuildXXX builds TPM command XXX. */ Command* BuildDefineSpaceCommand(void) { @@ -109,34 +103,29 @@ Command* BuildDefineSpaceCommand(void) { int nv_index = nv_data_public + offsetof(TPM_NV_DATA_PUBLIC, nvIndex); int nv_pcr_info_read = nv_data_public + offsetof(TPM_NV_DATA_PUBLIC, pcrInfoRead); - /* - * Here we need to carefully add PCR_SELECTION_FIX (or twice that much) in - * all the places where the offset calculation would be wrong without it. - * The mismatch occurs in the TPM_PCR_SELECTION structure, and it must be - * accounted for in all the structures that include it, directly or - * indirectly. - */ int read_locality = nv_pcr_info_read + - offsetof(TPM_PCR_INFO_SHORT, localityAtRelease) + PCR_SELECTION_FIX; + offsetof(TPM_PCR_INFO_SHORT, localityAtRelease); int nv_pcr_info_write = nv_data_public + - offsetof(TPM_NV_DATA_PUBLIC, pcrInfoWrite) + PCR_SELECTION_FIX; + offsetof(TPM_NV_DATA_PUBLIC, pcrInfoWrite); int write_locality = nv_pcr_info_write + - offsetof(TPM_PCR_INFO_SHORT, localityAtRelease) + PCR_SELECTION_FIX; + offsetof(TPM_PCR_INFO_SHORT, localityAtRelease); int nv_permission = nv_data_public + - offsetof(TPM_NV_DATA_PUBLIC, permission) + 2 * PCR_SELECTION_FIX; + offsetof(TPM_NV_DATA_PUBLIC, permission); int nv_permission_tag = nv_permission + offsetof(TPM_NV_ATTRIBUTES, tag); int nv_permission_attributes = nv_permission + offsetof(TPM_NV_ATTRIBUTES, attributes); int nv_datasize = nv_data_public + - offsetof(TPM_NV_DATA_PUBLIC, dataSize) + 2 * PCR_SELECTION_FIX; + offsetof(TPM_NV_DATA_PUBLIC, dataSize); int size = kTpmRequestHeaderLength + sizeof(TPM_NV_DATA_PUBLIC) + - 2 * PCR_SELECTION_FIX + kEncAuthLength; + kEncAuthLength; Command* cmd = newCommand(TPM_ORD_NV_DefineSpace, size); cmd->name = "tpm_nv_definespace_cmd"; AddVisibleField(cmd, "index", nv_index); + AddVisibleField(cmd, "pcr_info_read", nv_pcr_info_read); + AddVisibleField(cmd, "pcr_info_write", nv_pcr_info_write); AddVisibleField(cmd, "perm", nv_permission_attributes); AddVisibleField(cmd, "size", nv_datasize); @@ -431,6 +420,20 @@ Command* BuildOIAPCommand(void) { return cmd; } +Command* BuildOSAPCommand(void) { + int size = kTpmRequestHeaderLength + sizeof(uint16_t) + sizeof(uint32_t) + + sizeof(TPM_NONCE); + Command* cmd = newCommand(TPM_ORD_OSAP, size); + cmd->name = "tpm_osap_cmd"; + AddVisibleField(cmd, "entityType", kTpmRequestHeaderLength); + AddVisibleField(cmd, "entityValue", + kTpmRequestHeaderLength + sizeof(uint16_t)); + AddVisibleField( + cmd, "nonceOddOSAP", + kTpmRequestHeaderLength + sizeof(uint16_t) + sizeof(uint32_t)); + return cmd; +} + Command* BuildTakeOwnershipCommand(void) { Command* cmd = newCommandWithTag(TPM_ORD_TakeOwnership, 624, TPM_TAG_RQU_AUTH1_COMMAND); @@ -616,6 +619,7 @@ Command* (*builders[])(void) = { BuildGetVersionValCommand, BuildIFXFieldUpgradeInfoRequest2Command, BuildOIAPCommand, + BuildOSAPCommand, BuildTakeOwnershipCommand, }; @@ -650,7 +654,6 @@ int main(void) { printf("const int kWriteInfoLength = %d;\n", (int) sizeof(TPM_WRITE_INFO)); printf("const int kNvDataPublicPermissionsOffset = %d;\n", (int) (offsetof(TPM_NV_DATA_PUBLIC, permission) + - 2 * PCR_SELECTION_FIX + offsetof(TPM_NV_ATTRIBUTES, attributes))); FreeCommands(commands); |