diff options
-rw-r--r-- | firmware/include/tpm2_tss_constants.h | 50 | ||||
-rw-r--r-- | firmware/lib/tpm2_lite/marshaling.c | 151 | ||||
-rw-r--r-- | firmware/lib/tpm2_lite/tlcl.c | 22 |
3 files changed, 209 insertions, 14 deletions
diff --git a/firmware/include/tpm2_tss_constants.h b/firmware/include/tpm2_tss_constants.h index 7b1ab7b6..f24c7694 100644 --- a/firmware/include/tpm2_tss_constants.h +++ b/firmware/include/tpm2_tss_constants.h @@ -19,6 +19,7 @@ /* TPM2 command codes. */ #define TPM2_Hierarchy_Control ((TPM_CC)0x00000121) #define TPM2_Clear ((TPM_CC)0x00000126) +#define TPM2_NV_DefineSpace ((TPM_CC)0x0000012A) #define TPM2_NV_Write ((TPM_CC)0x00000137) #define TPM2_NV_WriteLock ((TPM_CC)0x00000138) #define TPM2_SelfTest ((TPM_CC)0x00000143) @@ -40,6 +41,7 @@ #define HR_SHIFT 24 #define TPM_HT_NV_INDEX 0x01 #define HR_NV_INDEX (TPM_HT_NV_INDEX << HR_SHIFT) +#define TPM_RH_OWNER 0x40000001 #define TPM_RH_PLATFORM 0x4000000C #define TPM_RS_PW 0x40000009 @@ -59,6 +61,36 @@ #define TPM_SU_CLEAR ((TPM_SU)0x0000) #define TPM_SU_STATE ((TPM_SU)0x0001) +/* TPM algorithm IDs. */ +#define TPM_ALG_SHA1 ((TPM_ALG_ID)0x0004) +#define TPM_ALG_SHA256 ((TPM_ALG_ID)0x000B) +#define TPM_ALG_NULL ((TPM_ALG_ID)0x0010) + +/* NV index attributes. */ +#define TPMA_NV_PPWRITE ((TPMA_NV)(1UL << 0)) +#define TPMA_NV_OWNERWRITE ((TPMA_NV)(1UL << 1)) +#define TPMA_NV_AUTHWRITE ((TPMA_NV)(1UL << 2)) +#define TPMA_NV_POLICYWRITE ((TPMA_NV)(1UL << 3)) +#define TPMA_NV_MASK_WRITE (TPMA_NV_PPWRITE | TPMA_NV_OWNERWRITE |\ + TPMA_NV_AUTHWRITE | TPMA_NV_POLICYWRITE) +#define TPMA_NV_PPREAD ((TPMA_NV)(1UL << 16)) +#define TPMA_NV_OWNERREAD ((TPMA_NV)(1UL << 17)) +#define TPMA_NV_AUTHREAD ((TPMA_NV)(1UL << 18)) +#define TPMA_NV_POLICYREAD ((TPMA_NV)(1UL << 19)) +#define TPMA_NV_MASK_READ (TPMA_NV_PPREAD | TPMA_NV_OWNERREAD |\ + TPMA_NV_AUTHREAD | TPMA_NV_POLICYREAD) +#define TPMA_NV_PLATFORMCREATE ((TPMA_NV)(1UL << 30)) + +/* Starting indexes of NV index ranges, as defined in "Registry of reserved + * TPM 2.0 handles and localities". + */ +#define TPMI_RH_NV_INDEX_TPM_START ((TPMI_RH_NV_INDEX)0x01000000) +#define TPMI_RH_NV_INDEX_PLATFORM_START ((TPMI_RH_NV_INDEX)0x01400000) +#define TPMI_RH_NV_INDEX_OWNER_START ((TPMI_RH_NV_INDEX)0x01800000) +#define TPMI_RH_NV_INDEX_TCG_OEM_START ((TPMI_RH_NV_INDEX)0x01C00000) +#define TPMI_RH_NV_INDEX_TCG_WG_START ((TPMI_RH_NV_INDEX)0x01C40000) +#define TPMI_RH_NV_INDEX_RESERVED_START ((TPMI_RH_NV_INDEX)0x01C90000) + typedef uint8_t TPMI_YES_NO; typedef uint32_t TPM_CC; typedef uint32_t TPM_HANDLE; @@ -67,11 +99,14 @@ typedef TPM_HANDLE TPMI_RH_ENABLES; typedef uint32_t TPM_CAP; typedef uint32_t TPM_PT; typedef uint16_t TPM_SU; +typedef uint16_t TPM_ALG_ID; +typedef TPM_ALG_ID TPMI_ALG_HASH; +typedef uint32_t TPMA_NV; typedef struct { uint16_t size; uint8_t *buffer; -} TPM2B; +} TPM2B, TPM2B_DIGEST, TPM2B_AUTH; typedef union { struct { @@ -100,6 +135,19 @@ typedef struct { TPMU_CAPABILITIES data; } TPMS_CAPABILITY_DATA; +typedef struct { + TPMI_RH_NV_INDEX nvIndex; + TPMI_ALG_HASH nameAlg; + TPMA_NV attributes; + TPM2B authPolicy; + uint16_t dataSize; +} TPMS_NV_PUBLIC; + +struct tpm2_nv_define_space_cmd { + TPM2B auth; + TPMS_NV_PUBLIC publicInfo; +}; + struct tpm2_nv_read_cmd { TPMI_RH_NV_INDEX nvIndex; uint16_t size; diff --git a/firmware/lib/tpm2_lite/marshaling.c b/firmware/lib/tpm2_lite/marshaling.c index a51c1f66..57a05530 100644 --- a/firmware/lib/tpm2_lite/marshaling.c +++ b/firmware/lib/tpm2_lite/marshaling.c @@ -266,19 +266,67 @@ static void marshal_u32(void **buffer, uint32_t value, int *buffer_space) #define unmarshal_TPM_CC(a, b) unmarshal_u32(a, b) #define marshal_TPM_HANDLE(a, b, c) marshal_u32(a, b, c) #define marshal_TPM_SU(a, b, c) marshal_u16(a, b, c) +#define marshal_ALG_ID(a, b, c) marshal_u16(a, b, c) + +/* + * For TPM2B* structures the size field (16 or 32 bits) goes before the data. + * When marshaling, we first reserve the space for the size field, then + * marshal the data, then fill the reserved size field with the actual size + * of the marshaled data. + */ +typedef struct { + int size; + void *location; +} tpm2_marshal_size_field; + +static void marshal_reserve_size_field(void **buffer, + tpm2_marshal_size_field *field, + int field_size, + int *buffer_space) +{ + if (field_size != sizeof(uint32_t) && field_size != sizeof(uint16_t)) { + VBDEBUG(("%s:%d:Unsupported size field size: %d\n", + __FILE__, __LINE__, field_size)); + *buffer_space = -1; + return; + } + if (*buffer_space < field_size) { + *buffer_space = -1; + return; + } + field->size = field_size; + field->location = *buffer; + *buffer_space -= field_size; + *buffer = (void *)(((uintptr_t) *buffer) + field_size); +} + +static void marshal_fill_size_field(void **buffer, + tpm2_marshal_size_field *field, + int include_size_field, + int *buffer_space) +{ + uintptr_t size = (uintptr_t) *buffer - (uintptr_t) field->location; + + if (*buffer_space < 0) + return; /* The structure did not fit. */ + + if (!include_size_field) + size -= field->size; + if (field->size == sizeof(uint32_t)) + marshal_u32(&field->location, size, &field->size); + else /* if (field->size == sizeof(uint16_t)) */ + marshal_u16(&field->location, size, &field->size); +} static void marshal_session_header(void **buffer, struct tpm2_session_header *session_header, int *buffer_space) { - int base_size; - void *size_location = *buffer; + tpm2_marshal_size_field size_field; /* Skip room for the session header size. */ - *buffer_space -= sizeof(uint32_t); - *buffer = (void *)(((uintptr_t) *buffer) + sizeof(uint32_t)); - - base_size = *buffer_space; + marshal_reserve_size_field(buffer, &size_field, + sizeof(uint32_t), buffer_space); marshal_u32(buffer, session_header->session_handle, buffer_space); marshal_u16(buffer, session_header->nonce_size, buffer_space); @@ -289,11 +337,8 @@ static void marshal_session_header(void **buffer, marshal_blob(buffer, session_header->auth, session_header->auth_size, buffer_space); - if (*buffer_space < 0) - return; /* The structure did not fit. */ - /* Paste in the session size. */ - marshal_u32(&size_location, base_size - *buffer_space, &base_size); + marshal_fill_size_field(buffer, &size_field, 0, buffer_space); } static void marshal_TPM2B(void **buffer, @@ -312,13 +357,88 @@ static void marshal_TPM2B(void **buffer, *buffer_space -= data->size; } +static void marshal_TPMS_NV_PUBLIC(void **buffer, + TPMS_NV_PUBLIC *data, + int *buffer_space) +{ + tpm2_marshal_size_field size_field; + + /* Skip room for the size. */ + marshal_reserve_size_field(buffer, &size_field, + sizeof(uint16_t), buffer_space); + + marshal_TPM_HANDLE(buffer, data->nvIndex, buffer_space); + marshal_ALG_ID(buffer, data->nameAlg, buffer_space); + marshal_u32(buffer, data->attributes, buffer_space); + marshal_TPM2B(buffer, &data->authPolicy, buffer_space); + marshal_u16(buffer, data->dataSize, buffer_space); + + /* Paste in the structure size. */ + marshal_fill_size_field(buffer, &size_field, 0, buffer_space); +} + +static void marshal_nv_define_space(void **buffer, + struct tpm2_nv_define_space_cmd + *command_body, + int *buffer_space) +{ + struct tpm2_session_header session_header; + + /* Use platform authorization if PLATFORMCREATE is set, and owner + * authorization otherwise (per TPM2 Spec. Part 2. Section 31.3.1). + * Owner authorization with empty password will work only until + * ownership is taken. Platform authorization will work only until + * platform hierarchy is disabled (i.e. in firmware or in recovery + * mode). + */ + if (command_body->publicInfo.attributes & TPMA_NV_PLATFORMCREATE) + marshal_TPM_HANDLE(buffer, TPM_RH_PLATFORM, buffer_space); + else + marshal_TPM_HANDLE(buffer, TPM_RH_OWNER, buffer_space); + + memset(&session_header, 0, sizeof(session_header)); + session_header.session_handle = TPM_RS_PW; + marshal_session_header(buffer, &session_header, buffer_space); + tpm_tag = TPM_ST_SESSIONS; + + marshal_TPM2B(buffer, &command_body->auth, buffer_space); + marshal_TPMS_NV_PUBLIC(buffer, &command_body->publicInfo, buffer_space); +} + +/* Determine which authorization should be used when writing or write-locking + * an NV index. + * + * Use a simplified approach: + * 1) Use platform auth for indexes defined by TPM and Platform, as + * specified in "Registry of reserved TPM 2.0 handles and localities". + * That will only work for indexes with PPWRITE, and until the platform + * hierarchy is disabled. + * 2) Use empty password auth for other indexes. + * That will only work for indexes with AUTHWRITE and empty auth value. + * + * A more honest approach would require the caller to specify the + * authorization, or would check the NV index attributes. + * But that's not needed now, as all indexes defined by firmware are + * in the TPM range and have PPWRITE. The indexes defined by the + * OS are in the Owner range and have either OWNERWRITE or AUTHWRITE, + * but we don't ever use Tlcl to write to OWNERWRITE indexes. + */ +static TPM_HANDLE get_nv_index_write_auth(TPMI_RH_NV_INDEX nvIndex) +{ + return nvIndex >= TPMI_RH_NV_INDEX_OWNER_START ? + nvIndex : + TPM_RH_PLATFORM; +} + static void marshal_nv_write(void **buffer, struct tpm2_nv_write_cmd *command_body, int *buffer_space) { struct tpm2_session_header session_header; - marshal_TPM_HANDLE(buffer, TPM_RH_PLATFORM, buffer_space); + marshal_TPM_HANDLE(buffer, + get_nv_index_write_auth(command_body->nvIndex), + buffer_space); marshal_TPM_HANDLE(buffer, command_body->nvIndex, buffer_space); memset(&session_header, 0, sizeof(session_header)); session_header.session_handle = TPM_RS_PW; @@ -370,7 +490,9 @@ static void marshal_nv_write_lock(void **buffer, struct tpm2_session_header session_header; tpm_tag = TPM_ST_SESSIONS; - marshal_TPM_HANDLE(buffer, TPM_RH_PLATFORM, buffer_space); + marshal_TPM_HANDLE(buffer, + get_nv_index_write_auth(command_body->nvIndex), + buffer_space); marshal_TPM_HANDLE(buffer, command_body->nvIndex, buffer_space); memset(&session_header, 0, sizeof(session_header)); session_header.session_handle = TPM_RS_PW; @@ -458,6 +580,10 @@ int tpm_marshal_command(TPM_CC command, void *tpm_command_body, switch (command) { + case TPM2_NV_DefineSpace: + marshal_nv_define_space(&cmd_body, tpm_command_body, &body_size); + break; + case TPM2_NV_Read: marshal_nv_read(&cmd_body, tpm_command_body, &body_size); break; @@ -558,6 +684,7 @@ struct tpm2_response *tpm_unmarshal_response(TPM_CC command, case TPM2_SelfTest: case TPM2_Startup: case TPM2_Shutdown: + case TPM2_NV_DefineSpace: /* Session data included in response can be safely ignored. */ cr_size = 0; break; diff --git a/firmware/lib/tpm2_lite/tlcl.c b/firmware/lib/tpm2_lite/tlcl.c index 96900b07..3b4ba480 100644 --- a/firmware/lib/tpm2_lite/tlcl.c +++ b/firmware/lib/tpm2_lite/tlcl.c @@ -168,7 +168,27 @@ uint32_t TlclContinueSelfTest(void) uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { - VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__)); + struct tpm2_response *response; + struct tpm2_nv_define_space_cmd define_space; + + /* For backwards-compatibility, if no READ or WRITE permissions are set, + * assume readable/writeable with empty auth value. + */ + if (!(perm & TPMA_NV_MASK_WRITE)) + perm |= TPMA_NV_AUTHWRITE; + if (!(perm & TPMA_NV_MASK_READ)) + perm |= TPMA_NV_AUTHREAD; + + memset(&define_space, 0, sizeof(define_space)); + define_space.publicInfo.nvIndex = HR_NV_INDEX + index; + define_space.publicInfo.dataSize = size; + define_space.publicInfo.attributes = perm; + define_space.publicInfo.nameAlg = TPM_ALG_SHA256; + + response = tpm_process_command(TPM2_NV_DefineSpace, &define_space); + if (!response || response->hdr.tpm_code) + return TPM_E_IOERROR; + return TPM_SUCCESS; } |