summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMattias Nissler <mnissler@chromium.org>2017-12-05 16:27:42 +0100
committerMattias Nissler <mnissler@chromium.org>2018-04-13 10:03:32 +0000
commitbc5b2db15b93f37820574b8f14a1b2e165012403 (patch)
tree04579ca3414f4258c7c3070075ce88e56fbeb877
parentac2286e8f8337a6ced00f219ec59aab52a2ac6d7 (diff)
downloadvboot-bc5b2db15b93f37820574b8f14a1b2e165012403.tar.gz
tpm_lite: Add more general DefineSpaceEx function
Add a TlclDefineSpaceEx function that allows to pass additional parameters when creating NVRAM spaces, i.e. owner authorization as well as PCR bindings. BRANCH=None BUG=chromium:788719 TEST=New unit tests. Change-Id: I73404c05528a89604fea3bcb1f00741fb865ba77 Reviewed-on: https://chromium-review.googlesource.com/814114 Reviewed-by: Andrey Pronin <apronin@chromium.org> Trybot-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Trybot-Ready: Mattias Nissler <mnissler@chromium.org> Tested-by: Mattias Nissler <mnissler@chromium.org>
-rw-r--r--firmware/include/tlcl.h23
-rw-r--r--firmware/include/tpm1_tss_constants.h20
-rw-r--r--firmware/lib/tpm2_lite/tlcl.c25
-rw-r--r--firmware/lib/tpm_lite/include/tlcl_structures.h12
-rw-r--r--firmware/lib/tpm_lite/tlcl.c222
-rw-r--r--tests/tlcl_tests.c180
-rw-r--r--utility/include/tpmextras.h3
-rw-r--r--utility/tlcl_generator.c43
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);