aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2016-12-29 11:14:03 -0800
committerchrome-bot <chrome-bot@chromium.org>2017-01-04 19:42:20 -0800
commit21756127fdebc0b0825aba875c7d0cc75057d3d8 (patch)
treee8c14f3634be389b148314425c3ea23000ddfcab
parent569c3c58dc69d5c8628f3c329937c136be38df3f (diff)
downloadtpm2-21756127fdebc0b0825aba875c7d0cc75057d3d8.tar.gz
serialize objects in NVMEM
Reference implementation stores OBJECT structures in NVRAM unmarshaled, even though this structure layout is such that most of its 1540 bytes remain unused by the object stored in the structure. Marshaling the structure before storing it in NVMEM allows to save a lot of room there. To make sure that marshaling is not processing junk data, clear the entire structure before allocating a new OBJECT. This change is meant to be backwards compatible. When data is read from NVMEM, in case its size is equal the size of OBJECT structure, data is considered stored unmarshaled and is copied to the output directly. If the stored size is smaller - unmarshaling function is invoked. BUG=chrome-os-partner:60502 TEST=tcg test suite passes (not that it exercises this a lot, just five instances of storing/retrieving objects for the entire suite). Will test on real tpm to verify NVMEM storage format backwards compatibility. Also tried taking a chrome os device through enterprise enrollment. With the old code after enrollment there is room for just two eviction objects left: # command to retrieve number of objects in nvmem(is in the last # byte of the response) localhost ~ # trunks_send --raw 80 01 00 00 00 16 00 00 01 7a 00\ 00 00 06 00 00 02 08 00 00 00 01 80010000001B000000000100000006000000010000020800000003 # command to retrieve how many objects the tpm estimates it is # still possible to store in nvram (is in the last byte of the # response) localhost ~ # trunks_send --raw 80 01 00 00 00 16 00 00 01 7a \ 00 00 00 06 00 00 02 09 00 00 00 01 80010000001B000000000100000006000000010000020900000002 with the new code after enrollment the responses the above commands are: 80010000001B000000000100000006000000010000020800000003 80010000001B000000000100000006000000010000020900000004 That is with three objects stored there is room for 4 more objects. Also verified that the device enrolled with the old version of the cr50 firmware remains enrolled after firmware update, which demonstrates backward compatibility. Change-Id: Ic2d5f902220b451523b740b57edb7867441d1faa Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/424171 Reviewed-by: Andrey Pronin <apronin@chromium.org>
-rw-r--r--Implementation.h8
-rw-r--r--NV.c162
-rw-r--r--Object.c4
3 files changed, 145 insertions, 29 deletions
diff --git a/Implementation.h b/Implementation.h
index a503d2a..3a9fcc5 100644
--- a/Implementation.h
+++ b/Implementation.h
@@ -262,8 +262,12 @@
#define NV_MEMORY_SIZE 16076
// Versioning NV storage format will allow to smoothly migrate NVRAM contents.
// Versions:
-// 1 - full non-serialized objects in NVRAM, max SHA digest is SHA-256
-// 2 - full non-serialized objects in NVRAM, max SHA digest is SHA-512
+// 1 - full non-serialized objects in NVMEM, max SHA digest is SHA-256
+// 2 - a mix of serialized and non-serialized objects in NVMEM, max SHA digest
+// is SHA-512. Eviction objects can be stored either serialized or
+// non-serialized. The size of the stored entity smaller than
+// sizeof(OBJECT) is considered an indication of the serialized form.
+
#define NV_FORMAT_VERSION 2
#else
#define NV_MEMORY_SIZE 16384
diff --git a/NV.c b/NV.c
index f714c91..0099c19 100644
--- a/NV.c
+++ b/NV.c
@@ -1101,6 +1101,53 @@ NvIsUndefinedEvictHandle(
else
return FALSE;
}
+
+//
+//
+// NvUnmarshalObject()
+//
+// This function accepts a buffer containing a marshaled OBJECT
+// structure, a pointer to the area where the input data should be
+// unmarshaled, and a pointer to the size of the output area.
+//
+// No error checking is performed, unmarshaled data is guaranteed not to
+// spill over the allocated space.
+//
+static TPM_RC NvUnmarshalObject(OBJECT *o, BYTE **buf, INT32 *size)
+{
+ TPM_RC result;
+
+ // There is no generated function to unmarshal the attributes field, do it
+ // by hand.
+ MemoryCopy(&o->attributes, *buf, sizeof(o->attributes), *size);
+ *buf += sizeof(o->attributes);
+ *size -= sizeof(o->attributes);
+
+ result = TPMT_PUBLIC_Unmarshal(&o->publicArea, buf, size);
+ if (result != TPM_RC_SUCCESS)
+ return result;
+
+ result = TPMT_SENSITIVE_Unmarshal(&o->sensitive, buf, size);
+ if (result != TPM_RC_SUCCESS)
+ return result;
+
+#ifdef TPM_ALG_RSA
+ result = TPM2B_PUBLIC_KEY_RSA_Unmarshal(&o->privateExponent, buf, size);
+ if (result != TPM_RC_SUCCESS)
+ return result;
+#endif
+
+ result = TPM2B_NAME_Unmarshal(&o->qualifiedName, buf, size);
+ if (result != TPM_RC_SUCCESS)
+ return result;
+
+ result = TPMI_DH_OBJECT_Unmarshal(&o->evictHandle, buf, size, TRUE);
+ if (result != TPM_RC_SUCCESS)
+ return result;
+
+ return TPM2B_NAME_Unmarshal(&o->name, buf, size);
+}
+
//
//
// NvGetEvictObject()
@@ -1123,13 +1170,34 @@ NvGetEvictObject(
// Find the address of evict object
entityAddr = NvFindHandle(handle);
// If handle is not found, return an error
- if(entityAddr == 0)
+ if(entityAddr == 0) {
result = TPM_RC_HANDLE;
- else
- // Read evict object
- _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE),
- sizeof(OBJECT),
- object);
+ } else {
+ UINT32 storedSize;
+ UINT32 nextEntryAddr;
+
+ // Let's calculate the size of object as stored in NVMEM.
+ _plat__NvMemoryRead(entityAddr - sizeof(UINT32),
+ sizeof(UINT32), &nextEntryAddr);
+
+ storedSize = nextEntryAddr - entityAddr;
+
+ if (storedSize == (sizeof(TPM_HANDLE) + sizeof(OBJECT))) {
+ // Read evict object stored unmarshaled.
+ _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE),
+ sizeof(OBJECT),
+ object);
+ } else {
+ // Must be stored marshaled, let's unmarshal it.
+ BYTE marshaled[sizeof(OBJECT)];
+ INT32 max_size = sizeof(marshaled);
+ BYTE *marshaledPtr = marshaled;
+
+ _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE),
+ storedSize, marshaled);
+ result = NvUnmarshalObject(object, &marshaledPtr, &max_size);
+ }
+ }
// whether there is an error or not, make sure that the evict
// status of the object is set so that the slot will get freed on exit
object->attributes.evict = SET;
@@ -1494,6 +1562,51 @@ NvDefineIndex(
NvAddRAM(publicArea->nvIndex, publicArea->dataSize);
return TPM_RC_SUCCESS;
}
+
+//
+//
+// NvMarshalObject()
+//
+// This function marshals the passed in OBJECT structure into a buffer. A
+// pointer to pointer to the buffer and a pointer to the size of the
+// buffer are passed in for this function to update as appropriate.
+//
+// On top of marshaling the object, this function also modifies one of
+// the object's properties and sets the evictHandle field of the
+// marshaled object to the requested value.
+//
+// Returns
+//
+// Marshaled size of the object.
+//
+static UINT16 NvMarshalObject(OBJECT *o, TPMI_DH_OBJECT evictHandle,
+ BYTE **buf, INT32 *size)
+{
+ UINT16 marshaledSize;
+ OBJECT_ATTRIBUTES stored_attributes;
+
+ stored_attributes = o->attributes;
+ stored_attributes.evict = SET;
+ marshaledSize = sizeof(stored_attributes);
+ MemoryCopy(*buf, &stored_attributes, marshaledSize, *size);
+ *buf += marshaledSize;
+ *size -= marshaledSize;
+
+ marshaledSize += TPMT_PUBLIC_Marshal(&o->publicArea, buf, size);
+ marshaledSize += TPMT_SENSITIVE_Marshal(&o->sensitive, buf, size);
+#ifdef TPM_ALG_RSA
+ marshaledSize += TPM2B_PUBLIC_KEY_RSA_Marshal(&o->privateExponent,
+ buf, size);
+#endif
+ marshaledSize += TPM2B_NAME_Marshal(&o->qualifiedName, buf, size);
+
+ // Use the supplied handle instead of the object contents.
+ marshaledSize += TPMI_DH_OBJECT_Marshal(&evictHandle, buf, size);
+ marshaledSize += TPM2B_NAME_Marshal(&o->name, buf, size);
+
+ return marshaledSize;
+}
+
//
//
// NvAddEvictObject()
@@ -1514,35 +1627,34 @@ NvAddEvictObject(
{
// The buffer to be written to NV memory
BYTE nvBuffer[sizeof(TPM_HANDLE) + sizeof(OBJECT)];
- OBJECT *nvObject; // a pointer to the OBJECT data in
- // nvBuffer
UINT16 entrySize; // size of entry
+ BYTE *marshalSpace;
+ INT32 marshalRoom;
+
// evict handle type should match the object hierarchy
pAssert( ( NvIsPlatformPersistentHandle(evictHandle)
&& object->attributes.ppsHierarchy == SET)
|| ( NvIsOwnerPersistentHandle(evictHandle)
&& ( object->attributes.spsHierarchy == SET
|| object->attributes.epsHierarchy == SET)));
- // An evict needs 4 bytes of handle + sizeof OBJECT
- entrySize = sizeof(TPM_HANDLE) + sizeof(OBJECT);
- // Check if we have enough space to add the evict object
- // An evict object needs 8 bytes in index table + sizeof OBJECT
- // In this implementation, the only resource limitation is the available NV
- // space. Other implementation may have other limitation on evict object
- // handle space
- if(!NvTestSpace(entrySize, FALSE)) return TPM_RC_NV_SPACE;
- // Allocate a new evict handle
+
+ // Do not attemp storing a duplicate handle.
if(!NvIsUndefinedEvictHandle(evictHandle))
return TPM_RC_NV_DEFINED;
- // Copy evict object to nvBuffer
+
// Copy handle
- memcpy(nvBuffer, &evictHandle, sizeof(TPM_HANDLE));
- // Copy OBJECT
- nvObject = (OBJECT *) (nvBuffer + sizeof(TPM_HANDLE));
- *nvObject = *object;
- // Set evict attribute and handle
- nvObject->attributes.evict = SET;
- nvObject->evictHandle = evictHandle;
+ entrySize = sizeof(TPM_HANDLE);
+ memcpy(nvBuffer, &evictHandle, entrySize);
+
+ // Let's serialize the object before storing it in NVMEM
+ marshalSpace = nvBuffer + entrySize;
+ marshalRoom = sizeof(nvBuffer) - entrySize;
+ entrySize += NvMarshalObject(object, evictHandle,
+ &marshalSpace, &marshalRoom);
+
+ // Check if we have enough space to add this evict object
+ if(!NvTestSpace(entrySize, FALSE)) return TPM_RC_NV_SPACE;
+
// Add evict to NV memory
NvAdd(entrySize, entrySize, nvBuffer);
return TPM_RC_SUCCESS;
diff --git a/Object.c b/Object.c
index 06d717f..14ec018 100644
--- a/Object.c
+++ b/Object.c
@@ -268,8 +268,8 @@ ObjectAllocateSlot(
if(i == MAX_LOADED_OBJECTS) return FALSE;
*handle = i + TRANSIENT_FIRST;
*object = &s_objects[i].object.entity;
- // Initialize the object attributes
- MemorySet(&((*object)->attributes), 0, sizeof(OBJECT_ATTRIBUTES));
+ // Initialize the container.
+ MemorySet(*object, 0, sizeof(**object));
return TRUE;
}
//