summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/include/tpm2_tss_constants.h50
-rw-r--r--firmware/lib/tpm2_lite/marshaling.c151
-rw-r--r--firmware/lib/tpm2_lite/tlcl.c22
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;
}