diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:41:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:41:43 +0000 |
commit | 92cccad89d1c12b39165d5f0ed7ccd2d44965a1a (patch) | |
tree | f59a2764cd8c50959050a428bd8fc935138df750 /src/tpm12/tpm_nvram.c | |
parent | Initial commit. (diff) | |
download | libtpms-92cccad89d1c12b39165d5f0ed7ccd2d44965a1a.tar.xz libtpms-92cccad89d1c12b39165d5f0ed7ccd2d44965a1a.zip |
Adding upstream version 0.9.2.upstream/0.9.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tpm12/tpm_nvram.c')
-rw-r--r-- | src/tpm12/tpm_nvram.c | 3747 |
1 files changed, 3747 insertions, 0 deletions
diff --git a/src/tpm12/tpm_nvram.c b/src/tpm12/tpm_nvram.c new file mode 100644 index 0000000..1235973 --- /dev/null +++ b/src/tpm12/tpm_nvram.c @@ -0,0 +1,3747 @@ +/********************************************************************************/ +/* */ +/* NVRAM Utilities */ +/* Written by Ken Goldman */ +/* IBM Thomas J. Watson Research Center */ +/* $Id: tpm_nvram.c 4724 2014-08-11 20:33:23Z kgoldman $ */ +/* */ +/* (c) Copyright IBM Corporation 2006, 2010. */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions are */ +/* met: */ +/* */ +/* Redistributions of source code must retain the above copyright notice, */ +/* this list of conditions and the following disclaimer. */ +/* */ +/* Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in the */ +/* documentation and/or other materials provided with the distribution. */ +/* */ +/* Neither the names of the IBM Corporation nor the names of its */ +/* contributors may be used to endorse or promote products derived from */ +/* this software without specific prior written permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ +/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ +/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */ +/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */ +/* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ +/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY */ +/* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ +/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ +/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/********************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include "tpm_auth.h" +#include "tpm_crypto.h" +#include "tpm_cryptoh.h" +#include "tpm_debug.h" +#include "tpm_digest.h" +#include "tpm_error.h" +#include "tpm_io.h" +#include "tpm_memory.h" +#include "tpm_nvfile.h" +#include "tpm_pcr.h" +#include "tpm_permanent.h" +#include "tpm_platform.h" +#include "tpm_process.h" +#include "tpm_secret.h" +#include "tpm_storage.h" +#include "tpm_structures.h" + +#include "tpm_nvram.h" + +/* + NV Defined Space Utilities +*/ + +/* + TPM_NV_ATTRIBUTES +*/ + +/* TPM_NVAttributes_Init() + + sets members to default values + sets all pointers to NULL and sizes to 0 + always succeeds - no return code +*/ + +void TPM_NVAttributes_Init(TPM_NV_ATTRIBUTES *tpm_nv_attributes) +{ + printf(" TPM_NVAttributes_Init:\n"); + tpm_nv_attributes->attributes = 0; + return; +} + +/* TPM_NVAttributes_Load() + + deserialize the structure from a 'stream' + 'stream_size' is checked for sufficient data + returns 0 or error codes + + Before use, call TPM_NVAttributes_Init() + After use, call TPM_NVAttributes_Delete() to free memory +*/ + +TPM_RESULT TPM_NVAttributes_Load(TPM_NV_ATTRIBUTES *tpm_nv_attributes, + unsigned char **stream, + uint32_t *stream_size) +{ + TPM_RESULT rc = 0; + + printf(" TPM_NVAttributes_Load:\n"); + /* check tag */ + if (rc == 0) { + rc = TPM_CheckTag(TPM_TAG_NV_ATTRIBUTES, stream, stream_size); + } + /* load attributes */ + if (rc == 0) { + rc = TPM_Load32(&(tpm_nv_attributes->attributes), stream, stream_size); + } + return rc; +} + +/* TPM_NVAttributes_Store() + + serialize the structure to a stream contained in 'sbuffer' + returns 0 or error codes +*/ + +TPM_RESULT TPM_NVAttributes_Store(TPM_STORE_BUFFER *sbuffer, + const TPM_NV_ATTRIBUTES *tpm_nv_attributes) +{ + TPM_RESULT rc = 0; + + printf(" TPM_NVAttributes_Store:\n"); + /* store tag */ + if (rc == 0) { + rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_NV_ATTRIBUTES); + } + if (rc == 0) { + rc = TPM_Sbuffer_Append32(sbuffer, tpm_nv_attributes->attributes); + } + return rc; +} + +/* TPM_NVAttributes_Delete() + + No-OP if the parameter is NULL, else: + frees memory allocated for the nv_attributes + sets pointers to NULL + calls TPM_NVAttributes_Init to set members back to default values + The object itself is not freed +*/ + +void TPM_NVAttributes_Delete(TPM_NV_ATTRIBUTES *tpm_nv_attributes) +{ + printf(" TPM_NVAttributes_Delete:\n"); + if (tpm_nv_attributes != NULL) { + TPM_NVAttributes_Init(tpm_nv_attributes); + } + return; +} + +void TPM_NVAttributes_Copy(TPM_NV_ATTRIBUTES *tpm_nv_attributes_dest, + TPM_NV_ATTRIBUTES *tpm_nv_attributes_src) +{ + tpm_nv_attributes_dest->attributes = tpm_nv_attributes_src->attributes; + return; +} + +/* + TPM_NV_DATA_PUBLIC +*/ + +/* TPM_NVDataPublic_Init() + + sets members to default values + sets all pointers to NULL and sizes to 0 + always succeeds - no return code +*/ + +void TPM_NVDataPublic_Init(TPM_NV_DATA_PUBLIC *tpm_nv_data_public) +{ + printf(" TPM_NVDataPublic_Init:\n"); + tpm_nv_data_public->nvIndex = TPM_NV_INDEX_LOCK; /* mark unused */ + TPM_PCRInfoShort_Init(&(tpm_nv_data_public->pcrInfoRead)); + TPM_PCRInfoShort_Init(&(tpm_nv_data_public->pcrInfoWrite)); + TPM_NVAttributes_Init(&(tpm_nv_data_public->permission)); + tpm_nv_data_public->bReadSTClear = FALSE; + tpm_nv_data_public->bWriteSTClear = FALSE; + tpm_nv_data_public->bWriteDefine = FALSE; + tpm_nv_data_public->dataSize = 0; + return; +} + +/* TPM_NVDataPublic_Load() + + deserialize the structure from a 'stream' + 'stream_size' is checked for sufficient data + returns 0 or error codes + + Before use, call TPM_NVDataPublic_Init() + After use, call TPM_NVDataPublic_Delete() to free memory +*/ + +TPM_RESULT TPM_NVDataPublic_Load(TPM_NV_DATA_PUBLIC *tpm_nv_data_public, + unsigned char **stream, + uint32_t *stream_size, + TPM_BOOL optimize) +{ + TPM_RESULT rc = 0; + + printf(" TPM_NVDataPublic_Load:\n"); + /* check tag */ + if (rc == 0) { + rc = TPM_CheckTag(TPM_TAG_NV_DATA_PUBLIC, stream, stream_size); + } + /* load nvIndex */ + if (rc == 0) { + rc = TPM_Load32(&(tpm_nv_data_public->nvIndex), stream, stream_size); + } + /* load pcrInfoRead */ + if (rc == 0) { + rc = TPM_PCRInfoShort_Load(&(tpm_nv_data_public->pcrInfoRead), stream, stream_size, optimize); + } + /* load pcrInfoWrite */ + if (rc == 0) { + rc = TPM_PCRInfoShort_Load(&(tpm_nv_data_public->pcrInfoWrite), stream, stream_size, optimize); + } + /* load permission */ + if (rc == 0) { + rc = TPM_NVAttributes_Load(&(tpm_nv_data_public->permission), stream, stream_size); + } + /* load bReadSTClear */ + if (rc == 0) { + rc = TPM_LoadBool(&(tpm_nv_data_public->bReadSTClear), stream, stream_size); + } + /* load bWriteSTClear */ + if (rc == 0) { + rc = TPM_LoadBool(&(tpm_nv_data_public->bWriteSTClear), stream, stream_size); + } + /* load bWriteDefine */ + if (rc == 0) { + rc = TPM_LoadBool(&(tpm_nv_data_public->bWriteDefine), stream, stream_size); + } + /* load dataSize */ + if (rc == 0) { + rc = TPM_Load32(&(tpm_nv_data_public->dataSize), stream, stream_size); + } + return rc; +} + +/* TPM_NVDataPublic_Store() + + serialize the structure to a stream contained in 'sbuffer' + returns 0 or error codes +*/ + +TPM_RESULT TPM_NVDataPublic_Store(TPM_STORE_BUFFER *sbuffer, + const TPM_NV_DATA_PUBLIC *tpm_nv_data_public, + TPM_BOOL optimize) +{ + TPM_RESULT rc = 0; + + printf(" TPM_NVDataPublic_Store:\n"); + /* store tag */ + if (rc == 0) { + rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_NV_DATA_PUBLIC); + } + /* store nvIndex */ + if (rc == 0) { + rc = TPM_Sbuffer_Append32(sbuffer, tpm_nv_data_public->nvIndex); + } + /* store pcrInfoRead */ + if (rc == 0) { + rc = TPM_PCRInfoShort_Store(sbuffer, &(tpm_nv_data_public->pcrInfoRead), optimize); + } + /* store pcrInfoWrite */ + if (rc == 0) { + rc = TPM_PCRInfoShort_Store(sbuffer, &(tpm_nv_data_public->pcrInfoWrite), optimize); + } + /* store permission */ + if (rc == 0) { + rc = TPM_NVAttributes_Store(sbuffer, &(tpm_nv_data_public->permission)); + } + /* store bReadSTClear */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, &(tpm_nv_data_public->bReadSTClear), sizeof(TPM_BOOL)); + } + /* store bWriteSTClear */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, &(tpm_nv_data_public->bWriteSTClear), sizeof(TPM_BOOL)); + } + /* store bWriteDefine */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, &(tpm_nv_data_public->bWriteDefine), sizeof(TPM_BOOL)); + } + /* store dataSize */ + if (rc == 0) { + rc = TPM_Sbuffer_Append32(sbuffer, tpm_nv_data_public->dataSize); + } + return rc; +} + +/* TPM_NVDataPublic_Delete() + + No-OP if the parameter is NULL, else: + frees memory allocated for the object + sets pointers to NULL + calls TPM_NVDataPublic_Init to set members back to default values + The object itself is not freed +*/ + +void TPM_NVDataPublic_Delete(TPM_NV_DATA_PUBLIC *tpm_nv_data_public) +{ + printf(" TPM_NVDataPublic_Delete:\n"); + if (tpm_nv_data_public != NULL) { + TPM_PCRInfoShort_Delete(&(tpm_nv_data_public->pcrInfoRead)); + TPM_PCRInfoShort_Delete(&(tpm_nv_data_public->pcrInfoWrite)); + TPM_NVAttributes_Delete(&(tpm_nv_data_public->permission)); + TPM_NVDataPublic_Init(tpm_nv_data_public); + } + return; +} + +/* + TPM_NV_DATA_SENSITIVE +*/ + +/* TPM_NVDataSensitive_Init() + + sets members to default values + sets all pointers to NULL and sizes to 0 + always succeeds - no return code +*/ + +void TPM_NVDataSensitive_Init(TPM_NV_DATA_SENSITIVE *tpm_nv_data_sensitive) +{ + printf(" TPM_NVDataSensitive_Init:\n"); + TPM_NVDataPublic_Init(&(tpm_nv_data_sensitive->pubInfo)); + TPM_Secret_Init(tpm_nv_data_sensitive->authValue); + tpm_nv_data_sensitive->data = NULL; + TPM_Digest_Init(tpm_nv_data_sensitive->digest); + return; +} + +/* TPM_NVDataSensitive_Load() + + deserialize the structure from a 'stream' + 'stream_size' is checked for sufficient data + returns 0 or error codes + + Before use, call TPM_NVDataSensitive_Init() + After use, call TPM_NVDataSensitive_Delete() to free memory +*/ + +TPM_RESULT TPM_NVDataSensitive_Load(TPM_NV_DATA_SENSITIVE *tpm_nv_data_sensitive, + TPM_TAG nvEntriesVersion, + unsigned char **stream, + uint32_t *stream_size) +{ + TPM_RESULT rc = 0; + TPM_BOOL optimize; + TPM_BOOL isGPIO; + + printf(" TPM_NVDataSensitive_Load: nvEntriesVersion %04hx\n", nvEntriesVersion); + /* check tag */ + if (rc == 0) { + rc = TPM_CheckTag(TPM_TAG_NV_DATA_SENSITIVE, stream, stream_size); + } + /* load pubInfo */ + if (rc == 0) { + /* versions after V1 optimise the serialization */ + optimize = (nvEntriesVersion != TPM_TAG_NVSTATE_NV_V1); + rc = TPM_NVDataPublic_Load(&(tpm_nv_data_sensitive->pubInfo), + stream, stream_size, + optimize); /* optimize digestAtRelease */ + } + /* load authValue */ + if (rc == 0) { + rc = TPM_Secret_Load(tpm_nv_data_sensitive->authValue, stream, stream_size); + } + /* is the nvIndex GPIO space */ + if (rc == 0) { + rc = TPM_NVDataSensitive_IsGPIO(&isGPIO, tpm_nv_data_sensitive->pubInfo.nvIndex); + } + /* allocate memory for data */ + if ((rc == 0) && !isGPIO) { + rc = TPM_Malloc(&(tpm_nv_data_sensitive->data), + tpm_nv_data_sensitive->pubInfo.dataSize); + } + /* load data */ + if ((rc == 0) && !isGPIO) { + rc = TPM_Loadn(tpm_nv_data_sensitive->data, tpm_nv_data_sensitive->pubInfo.dataSize, + stream, stream_size); + } + /* create digest. The digest is not stored to save NVRAM space */ + if (rc == 0) { + rc = TPM_SHA1(tpm_nv_data_sensitive->digest, + sizeof(TPM_NV_INDEX), + (unsigned char *)&tpm_nv_data_sensitive->pubInfo.nvIndex, + TPM_AUTHDATA_SIZE, tpm_nv_data_sensitive->authValue, + 0, NULL); + } + return rc; +} + +/* TPM_NVDataSensitive_Store() + + serialize the structure to a stream contained in 'sbuffer' + returns 0 or error codes + + nvWrite TRUE indicates a write command, not a command to define the space. +*/ + +TPM_RESULT TPM_NVDataSensitive_Store(TPM_STORE_BUFFER *sbuffer, + const TPM_NV_DATA_SENSITIVE *tpm_nv_data_sensitive) +{ + TPM_RESULT rc = 0; + TPM_BOOL isGPIO; + + printf(" TPM_NVDataSensitive_Store:\n"); + /* store tag */ + if (rc == 0) { + rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_NV_DATA_SENSITIVE); + } + /* store pubInfo */ + if (rc == 0) { + rc = TPM_NVDataPublic_Store(sbuffer, &(tpm_nv_data_sensitive->pubInfo), + TRUE); /* optimize digestAtRelease */ + } + /* store authValue */ + if (rc == 0) { + rc = TPM_Secret_Store(sbuffer, tpm_nv_data_sensitive->authValue); + } + /* is the nvIndex GPIO space */ + if (rc == 0) { + rc = TPM_NVDataSensitive_IsGPIO(&isGPIO, tpm_nv_data_sensitive->pubInfo.nvIndex); + } + /* store data */ + if ((rc == 0) && !isGPIO) { + rc = TPM_Sbuffer_Append(sbuffer, tpm_nv_data_sensitive->data, + tpm_nv_data_sensitive->pubInfo.dataSize); + } + return rc; +} + +/* TPM_NVDataSensitive_Delete() + + No-OP if the parameter is NULL, else: + frees memory allocated for the object + sets pointers to NULL + calls TPM_NVDataSensitive_Init to set members back to default values + The object itself is not freed +*/ + +void TPM_NVDataSensitive_Delete(TPM_NV_DATA_SENSITIVE *tpm_nv_data_sensitive) +{ + printf(" TPM_NVDataSensitive_Delete:\n"); + if (tpm_nv_data_sensitive != NULL) { + /* zero any secrets in NV index data */ + if (tpm_nv_data_sensitive->data != NULL) { + memset(tpm_nv_data_sensitive->data, 0xff, tpm_nv_data_sensitive->pubInfo.dataSize); + } + TPM_NVDataPublic_Delete(&(tpm_nv_data_sensitive->pubInfo)); + TPM_Secret_Delete(tpm_nv_data_sensitive->authValue); + free(tpm_nv_data_sensitive->data); + TPM_NVDataSensitive_Init(tpm_nv_data_sensitive); + } + return; +} + +/* TPM_NVDataSensitive_IsValidIndex() determines if 'nvIndex' is permissible for an NV defined space + TPM_NV_DATA_SENSITIVE structure. + + Some values have special meaning, so they are allowed for the TPM_NV_DefineSpace command but will + not actually define a space. +*/ + +TPM_RESULT TPM_NVDataSensitive_IsValidIndex(TPM_NV_INDEX nvIndex) +{ + TPM_RESULT rc = 0; + TPM_BOOL isGPIO; + + printf(" TPM_NVDataSensitive_IsValidIndex: nvIndex %08x\n", nvIndex); + if (rc == 0) { + if ((nvIndex == TPM_NV_INDEX_LOCK) || + (nvIndex == TPM_NV_INDEX0) || + (nvIndex == TPM_NV_INDEX_DIR)) { + printf("TPM_NVDataSensitive_IsValidIndex: Error, illegal special index\n"); + rc = TPM_BADINDEX; + } + } + if (rc == 0) { + if ((nvIndex & TPM_NV_INDEX_RESVD) != 0) { + printf("TPM_NVDataSensitive_IsValidIndex: Error, illegal reserved index\n"); + rc = TPM_BADINDEX; + } + } + if (rc == 0) { + rc = TPM_NVDataSensitive_IsValidPlatformIndex(nvIndex); + } + /* The GPIO range validity is platform dependent */ + if (rc == 0) { + rc = TPM_NVDataSensitive_IsGPIO(&isGPIO, nvIndex); + } + return rc; +} + +/* TPM_NVDataSensitive_IsGPIO() determines if 'nvIndex' is in the GPIO range and is valid. + + Returns: + + TPM_SUCCESS , FALSE if 'nvIndex' is not in the GPIO range + TPM_SUCCESS , TRUE if 'nvIndex' is in the GPIO range and the platform allows GPIO defined space + TPM_BADINDEX, FALSE if 'nvIndex' is in the GPIO range and the platform does not allow GPIO + defined space +*/ + +TPM_RESULT TPM_NVDataSensitive_IsGPIO(TPM_BOOL *isGPIO, TPM_NV_INDEX nvIndex) +{ + TPM_RESULT rc = 0; + + printf(" TPM_NVDataSensitive_IsGPIO: nvIndex %08x\n", nvIndex); + *isGPIO = FALSE; +#if defined TPM_PCCLIENT + if (rc == 0) { + /* GPIO space allowed for PC Client */ + if ((nvIndex >= TPM_NV_INDEX_GPIO_START) && + (nvIndex <= TPM_NV_INDEX_GPIO_END)) { + printf(" TPM_NVDataSensitive_IsGPIO: nvIndex is GPIO space\n"); + *isGPIO = TRUE; + } + } + /* #elif */ +#else + if (rc == 0) { + /* GPIO space cannot be defined in platforms with no GPIO */ + if ((nvIndex >= TPM_NV_INDEX_GPIO_START) && + (nvIndex <= TPM_NV_INDEX_GPIO_END)) { + printf("TPM_NVDataSensitive_IsGPIO: Error, illegal index\n"); + rc = TPM_BADINDEX; + } + } +#endif + return rc; +} + +TPM_RESULT TPM_NVDataSensitive_IsValidPlatformIndex(TPM_NV_INDEX nvIndex) +{ + TPM_RESULT rc = 0; + + printf(" TPM_NVDataSensitive_IsValidPlatformIndex: nvIndex %08x\n", nvIndex); +#ifndef TPM_PCCLIENT + if (rc == 0) { + if (((nvIndex & TPM_NV_INDEX_PURVIEW_MASK) >> TPM_NV_INDEX_PURVIEW_BIT) == TPM_PC) { + printf(" TPM_NVDataSensitive_IsValidPlatformIndex: Error, PC Client index\n"); + rc = TPM_BADINDEX; + } + } +#endif + return rc; +} + +/* + NV Index Entries + + This handles the in-memory copy of NV defined space +*/ + +/* + TPM_NVIndexEntries_Init() initializes the TPM_NV_INDEX_ENTRIES array +*/ + +void TPM_NVIndexEntries_Init(TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + printf(" TPM_NVIndexEntries_Init:\n"); + tpm_nv_index_entries->nvIndexCount = 0; + tpm_nv_index_entries->tpm_nvindex_entry = NULL; + return; +} + +/* + TPM_NVIndexEntries_Delete() iterates through the entire TPM_NV_INDEX_ENTRIES array, deleting any + used entries. + + It then frees and reinitializes the array. +*/ + + +void TPM_NVIndexEntries_Delete(TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + size_t i; + + printf(" TPM_NVIndexEntries_Delete: Deleting from %u slots\n", + tpm_nv_index_entries->nvIndexCount); + /* free the entries */ + for (i = 0 ; i < tpm_nv_index_entries->nvIndexCount ; i++) { + TPM_NVDataSensitive_Delete(&(tpm_nv_index_entries->tpm_nvindex_entry[i])); + } + /* free the array */ + free(tpm_nv_index_entries->tpm_nvindex_entry); + TPM_NVIndexEntries_Init(tpm_nv_index_entries); + return; +} + +/* TPM_NVIndexEntries_Trace() traces the TPM_NV_INDEX_ENTRIES array. + + Edit and call as required for debugging. +*/ + +void TPM_NVIndexEntries_Trace(TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + uint32_t i; + TPM_NV_DATA_SENSITIVE *tpm_nv_data_sensitive; + + printf("\tTPM_NVIndexEntries_Trace: %u slots\n", tpm_nv_index_entries->nvIndexCount); + for (i = 0 ; i < tpm_nv_index_entries->nvIndexCount ; i++) { + tpm_nv_data_sensitive = &(tpm_nv_index_entries->tpm_nvindex_entry[i]); + printf("\tTPM_NVIndexEntries_Trace: TPM_NV_DATA_SENSITIVE.data %p\n", + tpm_nv_data_sensitive->data); + } + return; +} + +/* + TPM_NVIndexEntries_Load() loads the TPM_NV_INDEX_ENTRIES array from a stream. + + The first data in the stream must be a uint32_t count of the number of entries to follow. +*/ + +TPM_RESULT TPM_NVIndexEntries_Load(TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries, + unsigned char **stream, + uint32_t *stream_size) +{ + TPM_RESULT rc = 0; + uint32_t i; + TPM_TAG nvEntriesVersion; + + printf(" TPM_NVIndexEntries_Load:\n"); + /* get the NV entries version number */ + if (rc == 0) { + rc = TPM_Load16(&nvEntriesVersion, stream, stream_size); + } + /* check tag */ + if (rc == 0) { + switch (nvEntriesVersion) { + case TPM_TAG_NVSTATE_NV_V1: + case TPM_TAG_NVSTATE_NV_V2: + break; + default: + printf("TPM_NVIndexEntries_Load: Error (fatal), version %04x unsupported\n", + nvEntriesVersion); + rc = TPM_FAIL; + break; + } + } + /* nvIndexCount */ + if (rc == 0) { + rc = TPM_Load32(&(tpm_nv_index_entries->nvIndexCount), stream, stream_size); + } + /* allocate memory for the array, nvIndexCount TPM_NV_DATA_SENSITIVE structures */ + if ((rc == 0) && (tpm_nv_index_entries->nvIndexCount > 0)) { + printf(" TPM_NVIndexEntries_Load: Loading %u slots\n", tpm_nv_index_entries->nvIndexCount); + rc = TPM_Malloc((unsigned char **)&(tpm_nv_index_entries->tpm_nvindex_entry), + sizeof(TPM_NV_DATA_SENSITIVE) * tpm_nv_index_entries->nvIndexCount); + } + /* immediately after allocating, initialize so that _Delete is safe even on a _Load error */ + for (i = 0 ; (rc == 0) && (i < tpm_nv_index_entries->nvIndexCount) ; i++) { + TPM_NVDataSensitive_Init(&(tpm_nv_index_entries->tpm_nvindex_entry[i])); + } + /* tpm_nvindex_entry array */ + for (i = 0 ; (rc == 0) && (i < tpm_nv_index_entries->nvIndexCount) ; i++) { + printf(" TPM_NVIndexEntries_Load: Loading slot %u\n", i); + if (rc == 0) { + rc = TPM_NVDataSensitive_Load(&(tpm_nv_index_entries->tpm_nvindex_entry[i]), + nvEntriesVersion, stream, stream_size); + } + /* should never load an unused entry */ + if (rc == 0) { + printf(" TPM_NVIndexEntries_Load: Loaded NV index %08x\n", + tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex); + if (tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex == TPM_NV_INDEX_LOCK) { + printf("TPM_NVIndexEntries_Load: Error (fatal) Entry %u bad NV index %08x\n", + i, tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex); + rc = TPM_FAIL; + } + } + } + return rc; +} + +/* + TPM_NVIndexEntries_Store() serializes the TPM_NV_INDEX_ENTRIES array into a stream. Only used + entries are serialized. + + The first data in the stream is the used count, obtained by iterating through the array. +*/ + +TPM_RESULT TPM_NVIndexEntries_Store(TPM_STORE_BUFFER *sbuffer, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + TPM_RESULT rc = 0; + uint32_t count; /* number of used entries to store */ + size_t i; + + printf(" TPM_NVIndexEntries_Store: Storing from %u slots\n", + tpm_nv_index_entries->nvIndexCount); + /* append the NV entries version number to the stream */ + if (rc == 0) { + rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_NVSTATE_NV_V2); + } + /* count the number of used entries */ + if (rc == 0) { + rc = TPM_NVIndexEntries_GetUsedCount(&count, tpm_nv_index_entries); + } + /* store the actual used count, not the number of array entries */ + if (rc == 0) { + rc = TPM_Sbuffer_Append32(sbuffer, count); + } + /* tpm_nvindex_entry array */ + for (i = 0 ; (rc == 0) && (i < tpm_nv_index_entries->nvIndexCount) ; i++) { + /* if the entry is used */ + if (tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex != TPM_NV_INDEX_LOCK) { + printf(" TPM_NVIndexEntries_Store: Storing slot %lu NV index %08x\n", + (unsigned long)i, tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex); + rc = TPM_NVDataSensitive_Store(sbuffer, &(tpm_nv_index_entries->tpm_nvindex_entry[i])); + } + else { + printf(" TPM_NVIndexEntries_Store: Skipping unused slot %lu\n", (unsigned long)i); + } + } + return rc; +} + +/* TPM_NVIndexEntries_StClear() steps through each entry in the NV TPM_NV_INDEX_ENTRIES array, + setting the volatile flags to FALSE. +*/ + +void TPM_NVIndexEntries_StClear(TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + size_t i; + + printf(" TPM_NVIndexEntries_StClear: Clearing %u slots\n", tpm_nv_index_entries->nvIndexCount); + /* bReadSTClear and bWriteSTClear are volatile, in that they are set FALSE at + TPM_Startup(ST_Clear) */ + for (i = 0 ; i < tpm_nv_index_entries->nvIndexCount ; i++) { + tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.bReadSTClear = FALSE; + tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.bWriteSTClear = FALSE; + } + return; +} + +/* TPM_NVIndexEntries_LoadVolatile() deserializes the stream into the volatile members of the + TPM_NV_INDEX_ENTRIES array. +*/ + +TPM_RESULT TPM_NVIndexEntries_LoadVolatile(TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries, + unsigned char **stream, + uint32_t *stream_size) +{ + TPM_RESULT rc = 0; + uint32_t usedCount; + uint32_t entryIndex; + TPM_NV_DATA_PUBLIC *tpm_nv_data_public; + + printf(" TPM_NVIndexEntries_LoadVolatile:\n"); + /* check tag */ + if (rc == 0) { + rc = TPM_CheckTag(TPM_TAG_NV_INDEX_ENTRIES_VOLATILE_V1, stream, stream_size); + } + /* Get the number of used slots. This should be equal to the total number of slots. */ + if (rc == 0) { + rc = TPM_Load32(&usedCount, stream, stream_size); + } + if (rc == 0) { + printf(" TPM_NVIndexEntries_LoadVolatile: usedCount %u\n", usedCount); + if (usedCount != tpm_nv_index_entries->nvIndexCount) { + printf("TPM_NVIndexEntries_LoadVolatile: Error (fatal), " + "usedCount %u does not equal slot count %u\n", + usedCount, tpm_nv_index_entries->nvIndexCount); + rc = TPM_FAIL; + } + } + /* deserialize the stream into the TPM_NV_INDEX_ENTRIES array */ + for (entryIndex = 0 ; + (rc == 0) && (entryIndex < tpm_nv_index_entries->nvIndexCount) ; + entryIndex++) { + + tpm_nv_data_public = &(tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo); + printf(" TPM_NVIndexEntries_LoadVolatile: Loading index %08x\n", + tpm_nv_data_public->nvIndex); + /* load bReadSTClear */ + if (rc == 0) { + rc = TPM_LoadBool(&(tpm_nv_data_public->bReadSTClear), stream, stream_size); + } + /* load bWriteSTClear */ + if (rc == 0) { + rc = TPM_LoadBool(&(tpm_nv_data_public->bWriteSTClear), stream, stream_size); + } + } + return rc; +} + +/* TPM_NVIndexEntries_StoreVolatile() serializes the volatile members of the + TPM_NV_INDEX_ENTRIES array into the TPM_STORE_BUFFER. +*/ + +TPM_RESULT TPM_NVIndexEntries_StoreVolatile(TPM_STORE_BUFFER *sbuffer, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + TPM_RESULT rc = 0; + uint32_t usedCount; + uint32_t entryIndex; + TPM_NV_DATA_PUBLIC *tpm_nv_data_public; + + printf(" TPM_NVIndexEntries_StoreVolatile: %u slots\n", tpm_nv_index_entries->nvIndexCount); + /* store tag */ + if (rc == 0) { + rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_NV_INDEX_ENTRIES_VOLATILE_V1); + } + /* Get the number of used slots. If indexes were deleted since the last TPM_Init, there can be + some unused slots. */ + if (rc == 0) { + rc = TPM_NVIndexEntries_GetUsedCount(&usedCount, tpm_nv_index_entries); + } + /* store usedCount */ + if (rc == 0) { + printf(" TPM_NVIndexEntries_StoreVolatile: usedCount %u\n", usedCount); + rc = TPM_Sbuffer_Append32(sbuffer, usedCount); + } + /* save entries into the array */ + for (entryIndex = 0 ; + (rc == 0) && (entryIndex < tpm_nv_index_entries->nvIndexCount) ; + entryIndex++) { + /* Only save used slots. During a rollback, slots are deleted and recreated. At that time, + unused slots will be reclaimed. */ + if (tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo.nvIndex != + TPM_NV_INDEX_LOCK) { + + tpm_nv_data_public = &(tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo); + printf(" TPM_NVIndexEntries_StoreVolatile: Storing index %08x\n", + tpm_nv_data_public->nvIndex); + /* store bReadSTClear */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, + &(tpm_nv_data_public->bReadSTClear), sizeof(TPM_BOOL)); + } + /* store bWriteSTClear */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, + &(tpm_nv_data_public->bWriteSTClear), sizeof(TPM_BOOL)); + } + } + } + return rc; +} + +/* TPM_NVIndexEntries_GetVolatile() saves an array of the NV defined space volatile flags. + + The array is used during a rollback, since the volatile flags are not stored in NVRAM +*/ + +TPM_RESULT TPM_NVIndexEntries_GetVolatile(TPM_NV_DATA_ST **tpm_nv_data_st, /* freed by caller */ + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + TPM_RESULT rc = 0; + uint32_t usedCount; + uint32_t entryIndex; + uint32_t usedIndex; + + printf(" TPM_NVIndexEntries_GetVolatile: %u slots\n", tpm_nv_index_entries->nvIndexCount); + /* Get the number of used slots. If indexes were deleted since the last TPM_Init, there can be + some unused slots. */ + if (rc == 0) { + rc = TPM_NVIndexEntries_GetUsedCount(&usedCount, tpm_nv_index_entries); + } + /* allocate memory for the array, nvIndexCount TPM_NV_DATA_SENSITIVE structures */ + if ((rc == 0) && (usedCount > 0)) { + printf(" TPM_NVIndexEntries_GetVolatile: Aloocating for %u used slots\n", usedCount); + rc = TPM_Malloc((unsigned char **)tpm_nv_data_st, + sizeof(TPM_NV_DATA_ST) * usedCount); + } + /* save entries into the array */ + for (entryIndex = 0 , usedIndex = 0 ; + (rc == 0) && (entryIndex < tpm_nv_index_entries->nvIndexCount) && (usedCount > 0) ; + entryIndex++) { + /* Only save used slots. During a rollback, slots are deleted and recreated. At that time, + unused slots will be reclaimed. */ + if (tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo.nvIndex != + TPM_NV_INDEX_LOCK) { + + printf(" TPM_NVIndexEntries_GetVolatile: Saving slot %u at used %u NV index %08x\n", + entryIndex, usedIndex, + tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo.nvIndex); + + printf(" TPM_NVIndexEntries_GetVolatile: bReadSTClear %u bWriteSTClear %u\n", + tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo.bReadSTClear, + tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo.bWriteSTClear); + (*tpm_nv_data_st)[usedIndex].nvIndex = + tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo.nvIndex; + (*tpm_nv_data_st)[usedIndex].bReadSTClear = + tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo.bReadSTClear; + (*tpm_nv_data_st)[usedIndex].bWriteSTClear = + tpm_nv_index_entries->tpm_nvindex_entry[entryIndex].pubInfo.bWriteSTClear; + usedIndex++; + } + } + return rc; +} + +/* TPM_NVIndexEntries_SetVolatile() restores an array of the NV defined space volatile flags. + + The array is used during a rollback, since the volatile flags are not stored in NVRAM +*/ + +TPM_RESULT TPM_NVIndexEntries_SetVolatile(TPM_NV_DATA_ST *tpm_nv_data_st, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + TPM_RESULT rc = 0; + uint32_t usedCount; + uint32_t i; + + printf(" TPM_NVIndexEntries_SetVolatile: %u slots\n", tpm_nv_index_entries->nvIndexCount); + /* Get the number of used slots. This should be equal to the total number of slots. */ + if (rc == 0) { + rc = TPM_NVIndexEntries_GetUsedCount(&usedCount, tpm_nv_index_entries); + } + if (rc == 0) { + if (usedCount != tpm_nv_index_entries->nvIndexCount) { + printf("TPM_NVIndexEntries_SetVolatile: Error (fatal), " + "usedCount %u does not equal slot count %u\n", + usedCount, tpm_nv_index_entries->nvIndexCount); + rc = TPM_FAIL; + } + } + /* if the used count is non-zero, the volatile array should not be NULL */ + if (rc == 0) { + if ((usedCount > 0) && (tpm_nv_data_st == NULL)) { + printf("TPM_NVIndexEntries_SetVolatile: Error (fatal), " + "usedCount %u unconsistant with volatile array NULL\n", usedCount); + rc = TPM_FAIL; + } + } + /* copy entries into the array */ + for (i = 0 ; (rc == 0) && (i < tpm_nv_index_entries->nvIndexCount) ; i++) { + printf(" TPM_NVIndexEntries_SetVolatile: slot %u index %08x\n", + i, tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex); + /* sanity check on a mismatch of entries between the save and restore */ + if (tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex != + tpm_nv_data_st[i].nvIndex) { + + printf("TPM_NVIndexEntries_SetVolatile: Error (fatal), " + "mismatch NV entry %08x, saved %08x\n", + tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex, + tpm_nv_data_st[i].nvIndex); + rc = TPM_FAIL; + } + /* restore entries from the array */ + else { + printf(" TPM_NVIndexEntries_SetVolatile: bReadSTClear %u bWriteSTClear %u\n", + tpm_nv_data_st[i].bReadSTClear, tpm_nv_data_st[i].bWriteSTClear); + tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.bReadSTClear = + tpm_nv_data_st[i].bReadSTClear; + tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.bWriteSTClear = + tpm_nv_data_st[i].bWriteSTClear; + } + } + return rc; +} + +/* TPM_NVIndexEntries_GetFreeEntry() gets a free entry in the TPM_NV_INDEX_ENTRIES array. + + If a free entry exists, it it returned. It should already be initialized. + + If a free entry does not exist, it it created and initialized. + + If a slot cannot be created, tpm_nv_data_sensitive returns NULL, so a subsequent free is safe. +*/ + +TPM_RESULT TPM_NVIndexEntries_GetFreeEntry(TPM_NV_DATA_SENSITIVE **tpm_nv_data_sensitive, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + TPM_RESULT rc = 0; + TPM_BOOL done = FALSE; + size_t i; + + printf(" TPM_NVIndexEntries_GetFreeEntry: Searching %u slots\n", + tpm_nv_index_entries->nvIndexCount); + /* for debug - trace the entire TPM_NV_INDEX_ENTRIES array */ + for (i = 0 ; i < tpm_nv_index_entries->nvIndexCount ; i++) { + *tpm_nv_data_sensitive = &(tpm_nv_index_entries->tpm_nvindex_entry[i]); + printf(" TPM_NVIndexEntries_GetFreeEntry: slot %lu entry %08x\n", + (unsigned long)i, (*tpm_nv_data_sensitive)->pubInfo.nvIndex); + } + /* search the existing array for a free entry */ + for (i = 0 ; (rc == 0) && (i < tpm_nv_index_entries->nvIndexCount) && !done ; i++) { + *tpm_nv_data_sensitive = &(tpm_nv_index_entries->tpm_nvindex_entry[i]); + /* if the entry is not used */ + if ((*tpm_nv_data_sensitive)->pubInfo.nvIndex == TPM_NV_INDEX_LOCK) { + printf(" TPM_NVIndexEntries_GetFreeEntry: Found free slot %lu\n", (unsigned long)i); + done = TRUE; + } + } + /* need to expand the array */ + if ((rc == 0) && !done) { + *tpm_nv_data_sensitive = NULL; + rc = TPM_Realloc((unsigned char **)&(tpm_nv_index_entries->tpm_nvindex_entry), + sizeof(TPM_NV_DATA_SENSITIVE) * (i + 1)); + } + /* initialize the new entry in the array */ + if ((rc == 0) && !done) { + printf(" TPM_NVIndexEntries_GetFreeEntry: Created new slot at index %lu\n", + (unsigned long)i); + *tpm_nv_data_sensitive = &(tpm_nv_index_entries->tpm_nvindex_entry[i]); + TPM_NVDataSensitive_Init(*tpm_nv_data_sensitive); + tpm_nv_index_entries->nvIndexCount++; + } + return rc; +} + +/* TPM_NVIndexEntries_GetEntry() gets the TPM_NV_DATA_SENSITIVE entry corresponding to nvIndex. + + Returns TPM_BADINDEX on non-existent nvIndex +*/ + +TPM_RESULT TPM_NVIndexEntries_GetEntry(TPM_NV_DATA_SENSITIVE **tpm_nv_data_sensitive, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries, + TPM_NV_INDEX nvIndex) +{ + TPM_RESULT rc = 0; + size_t i; + TPM_BOOL found; + + printf(" TPM_NVIndexEntries_GetEntry: Getting NV index %08x in %u slots\n", + nvIndex, tpm_nv_index_entries->nvIndexCount); + /* for debug tracing */ + for (i = 0 ; i < tpm_nv_index_entries->nvIndexCount ; i++) { + *tpm_nv_data_sensitive = &(tpm_nv_index_entries->tpm_nvindex_entry[i]); + printf(" TPM_NVIndexEntries_GetEntry: slot %lu entry %08x\n", + (unsigned long)i, (*tpm_nv_data_sensitive)->pubInfo.nvIndex); + } + /* check for the special index that indicates an empty entry */ + if (rc == 0) { + if (nvIndex == TPM_NV_INDEX_LOCK) { + rc = TPM_BADINDEX; + } + } + for (i = 0 , found = FALSE ; + (rc == 0) && (i < tpm_nv_index_entries->nvIndexCount) && !found ; + i++) { + + *tpm_nv_data_sensitive = &(tpm_nv_index_entries->tpm_nvindex_entry[i]); + if ((*tpm_nv_data_sensitive)->pubInfo.nvIndex == nvIndex) { + printf(" TPM_NVIndexEntries_GetEntry: Found NV index at slot %lu\n", (unsigned long)i); + printf(" TPM_NVIndexEntries_GetEntry: permission %08x dataSize %u\n", + (*tpm_nv_data_sensitive)->pubInfo.permission.attributes, + (*tpm_nv_data_sensitive)->pubInfo.dataSize); + printf(" TPM_NVIndexEntries_GetEntry: " + "bReadSTClear %02x bWriteSTClear %02x bWriteDefine %02x\n", + (*tpm_nv_data_sensitive)->pubInfo.bReadSTClear, + (*tpm_nv_data_sensitive)->pubInfo.bWriteSTClear, + (*tpm_nv_data_sensitive)->pubInfo.bWriteDefine); + found = TRUE; + } + } + if (rc == 0) { + if (!found) { + printf(" TPM_NVIndexEntries_GetEntry: NV index not found\n"); + rc = TPM_BADINDEX; + } + } + return rc; +} + +/* TPM_NVIndexEntries_GetUsedCount() returns the number of used entries in the TPM_NV_INDEX_ENTRIES + array. + + At startup, all entries will be used. If an NV index is deleted, the entryis marked unused, but + the TPM_NV_INDEX_ENTRIES space is not reclaimed until the next startup. +*/ + +TPM_RESULT TPM_NVIndexEntries_GetUsedCount(uint32_t *count, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + TPM_RESULT rc = 0; + size_t i; + + *count = 0; + for (i = 0 ; (rc == 0) && (i < tpm_nv_index_entries->nvIndexCount) ; i++) { + /* if the entry is used */ + if (tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex != TPM_NV_INDEX_LOCK) { + (*count)++; + } + } + printf(" TPM_NVIndexEntries_GetUsedCount: Used count %d in %u slots\n", + *count, tpm_nv_index_entries->nvIndexCount); + return rc; +} + +/* TPM_NVIndexEntries_GetNVList() serializes a list of the used NV indexes into the + TPM_STORE_BUFFER +*/ + +TPM_RESULT TPM_NVIndexEntries_GetNVList(TPM_STORE_BUFFER *sbuffer, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + TPM_RESULT rc = 0; + size_t i; + + printf(" TPM_NVIndexEntries_GetNVList: Creating list from %u slots\n", + tpm_nv_index_entries->nvIndexCount); + + for (i = 0 ; (rc == 0) && (i < tpm_nv_index_entries->nvIndexCount) ; i++) { + /* if the entry is used */ + if (tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex != TPM_NV_INDEX_LOCK) { + rc = TPM_Sbuffer_Append32(sbuffer, + tpm_nv_index_entries->tpm_nvindex_entry[i].pubInfo.nvIndex); + } + } + return rc; +} + +/* TPM_NVIndexEntries_GetUsedSpace() gets the NV space consumed by NV defined space indexes. + + It does it inefficiently but reliably by serializing the structure with the same function used + when writing to NV storage. +*/ + +TPM_RESULT TPM_NVIndexEntries_GetUsedSpace(uint32_t *usedSpace, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + TPM_RESULT rc = 0; + TPM_STORE_BUFFER sbuffer; + const unsigned char *buffer; + + printf(" TPM_NVIndexEntries_GetUsedSpace:\n"); + TPM_Sbuffer_Init(&sbuffer); /* freed @1 */ + /* serialize NV defined space */ + if (rc == 0) { + rc = TPM_NVIndexEntries_Store(&sbuffer, tpm_nv_index_entries); + } + /* get the serialized buffer and its length */ + if (rc == 0) { + TPM_Sbuffer_Get(&sbuffer, &buffer, usedSpace); + printf(" TPM_NVIndexEntries_GetUsedSpace: Used space %u\n", *usedSpace); + } + TPM_Sbuffer_Delete(&sbuffer); /* @1 */ + return rc; +} + +/* TPM_NVIndexEntries_GetFreeSpace() gets the total free NV defined space. + + When defining an index, not all can be used for data, as some is consumed by metadata such as + authorization and the index number. +*/ + +TPM_RESULT TPM_NVIndexEntries_GetFreeSpace(uint32_t *freeSpace, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries) +{ + TPM_RESULT rc = 0; + uint32_t usedSpace; + + printf(" TPM_NVIndexEntries_GetFreeSpace:\n"); + /* get the used space */ + if (rc == 0) { + rc = TPM_NVIndexEntries_GetUsedSpace(&usedSpace, tpm_nv_index_entries); + } + /* sanity check */ + if (rc == 0) { + if (usedSpace > TPM_MAX_NV_DEFINED_SIZE) { + printf("TPM_NVIndexEntries_GetFreeSpace: used %u greater than max %u\n", + usedSpace, TPM_MAX_NV_DEFINED_SIZE); + rc = TPM_NOSPACE; + } + } + /* calculate the free space */ + if (rc == 0) { + *freeSpace = TPM_MAX_NV_DEFINED_SIZE - usedSpace; + printf(" TPM_NVIndexEntries_GetFreeSpace: Free space %u\n", *freeSpace); + } + return rc; +} + +/* TPM_OwnerClear: rev 99 + 12. The TPM MUST deallocate all defined NV storage areas where + a. TPM_NV_PER_OWNERWRITE is TRUE if nvIndex does not have the "D" bit set + b. TPM_NV_PER_OWNERREAD is TRUE if nvIndex does not have the "D" bit set + c. The TPM MUST NOT deallocate any other currently defined NV storage areas. + + TPM_RevokeTrust: a. NV items with the pubInfo -> nvIndex D value set MUST be deleted. This + changes the TPM_OwnerClear handling of the same NV areas + + If deleteAllNvram is TRUE, all NVRAM is deleted. If it is FALSE, indexes with the D bit set are + not cleared. + + The write to NV space is done bu the caller. +*/ + +TPM_RESULT TPM_NVIndexEntries_DeleteOwnerAuthorized(TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries, + TPM_BOOL deleteAllNvram) +{ + TPM_RESULT rc = 0; + size_t i; + TPM_NV_DATA_SENSITIVE *tpm_nv_data_sensitive; /* an entry in the array */ + + printf(" TPM_NVIndexEntries_DeleteOwnerAuthorized: Deleting from %u slots\n", + tpm_nv_index_entries->nvIndexCount); + for (i = 0 ; i < tpm_nv_index_entries->nvIndexCount ; i++) { + /* get an entry in the array */ + tpm_nv_data_sensitive = &(tpm_nv_index_entries->tpm_nvindex_entry[i]); + + /* if the index is in use */ + if (tpm_nv_data_sensitive->pubInfo.nvIndex != TPM_NV_INDEX_LOCK) { + /* if TPM_NV_PER_OWNERWRITE or TPM_NV_PER_OWNERREAD and nvIndex does not have the "D" + bit set */ + if ((tpm_nv_data_sensitive->pubInfo.permission.attributes & TPM_NV_PER_OWNERWRITE) || + (tpm_nv_data_sensitive->pubInfo.permission.attributes & TPM_NV_PER_OWNERREAD)) { + if (!(tpm_nv_data_sensitive->pubInfo.nvIndex & TPM_NV_INDEX_D_BIT) || + deleteAllNvram) { + /* delete the index */ + printf(" TPM_NVIndexEntries_DeleteOwnerAuthorized: Deleting NV index %08x\n", + tpm_nv_data_sensitive->pubInfo.nvIndex); + TPM_NVDataSensitive_Delete(tpm_nv_data_sensitive); + } + } + } + } + return rc; +} + +/* TPM_NVIndexEntries_GetDataPublic() returns the TPM_NV_DATA_PUBLIC corresponding to the nvIndex + */ + +TPM_RESULT TPM_NVIndexEntries_GetDataPublic(TPM_NV_DATA_PUBLIC **tpm_nv_data_public, + TPM_NV_INDEX_ENTRIES *tpm_nv_index_entries, + TPM_NV_INDEX nvIndex) +{ + TPM_RESULT rc = 0; + TPM_NV_DATA_SENSITIVE *tpm_nv_data_sensitive; + + printf(" TPM_NVIndexEntries_GetDataPublic: Getting data at NV index %08x\n", nvIndex); + if (rc == 0) { + rc = TPM_NVIndexEntries_GetEntry(&tpm_nv_data_sensitive, + tpm_nv_index_entries, + nvIndex); + } + if (rc == 0) { + *tpm_nv_data_public = &(tpm_nv_data_sensitive->pubInfo); + } + return rc; +} + +/* + Command Processing Functions +*/ + +/* 20.4 TPM_NV_ReadValue rev 114 + + Read a value from the NV store. This command uses optional owner authorization. + + Action 1 indicates that if the NV area is not locked then reading of the NV area continues + without ANY authorization. This is intentional, and allows a platform manufacturer to set the NV + areas, read them back, and then lock them all without having to install a TPM owner. +*/ + +TPM_RESULT TPM_Process_NVReadValue(tpm_state_t *tpm_state, + TPM_STORE_BUFFER *response, + TPM_TAG tag, + uint32_t paramSize, + TPM_COMMAND_CODE ordinal, + unsigned char *command, + TPM_TRANSPORT_INTERNAL *transportInternal) +{ + TPM_RESULT rcf = 0; /* fatal error precluding response */ + TPM_RESULT returnCode = TPM_SUCCESS; /* command return code */ + + /* input parameters */ + TPM_NV_INDEX nvIndex; /* The index of the area to set */ + uint32_t offset = 0; /* The offset into the area */ + uint32_t dataSize = 0; /* The size of the data area */ + TPM_AUTHHANDLE authHandle; /* The authorization handle used for TPM Owner + authorization */ + TPM_NONCE nonceOdd; /* Nonce generated by caller */ + TPM_BOOL continueAuthSession = TRUE; /* The continue use flag for the authorization + handle */ + TPM_AUTHDATA ownerAuth; /* HMAC key: TPM Owner authorization */ + + /* processing parameters */ + unsigned char * inParamStart; /* starting point of inParam's */ + unsigned char * inParamEnd; /* ending point of inParam's */ + TPM_DIGEST inParamDigest; + TPM_BOOL auditStatus; /* audit the ordinal */ + TPM_BOOL transportEncrypt; /* wrapped in encrypted transport session */ + TPM_BOOL authHandleValid = FALSE; + TPM_SECRET *hmacKey; + TPM_AUTH_SESSION_DATA *auth_session_data = NULL; /* session data for authHandle */ + TPM_BOOL ignore_auth = FALSE; + TPM_BOOL dir = FALSE; + TPM_BOOL physicalPresence; + TPM_BOOL isGPIO = FALSE; + BYTE *gpioData = NULL; + TPM_NV_DATA_SENSITIVE *d1NvdataSensitive; + uint32_t s1Last; + + /* output parameters */ + uint32_t outParamStart; /* starting point of outParam's */ + uint32_t outParamEnd; /* ending point of outParam's */ + TPM_DIGEST outParamDigest; + TPM_SIZED_BUFFER data; /* The data to set the area to */ + + printf("TPM_Process_NVReadValue: Ordinal Entry\n"); + TPM_SizedBuffer_Init(&data); /* freed @1 */ + /* + get inputs + */ + /* save the starting point of inParam's for authorization and auditing */ + inParamStart = command; + /* get nvIndex parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&nvIndex, &command, ¶mSize); + } + /* get offset parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&offset, &command, ¶mSize); + } + /* get dataSize parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&dataSize, &command, ¶mSize); + } + /* save the ending point of inParam's for authorization and auditing */ + inParamEnd = command; + /* digest the input parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetInParamDigest(inParamDigest, /* output */ + &auditStatus, /* output */ + &transportEncrypt, /* output */ + tpm_state, + tag, + ordinal, + inParamStart, + inParamEnd, + transportInternal); + } + /* check state */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckState(tpm_state, tag, (TPM_CHECK_NOT_SHUTDOWN | + TPM_CHECK_NO_LOCKOUT | + TPM_CHECK_NV_NOAUTH)); + } + /* check tag */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckRequestTag10(tag); + } + /* get the optional 'below the line' authorization parameters */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_AuthParams_Get(&authHandle, + &authHandleValid, + nonceOdd, + &continueAuthSession, + ownerAuth, + &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + if (paramSize != 0) { + printf("TPM_Process_NVReadValue: Error, command has %u extra bytes\n", + paramSize); + returnCode = TPM_BAD_PARAM_SIZE; + } + } + /* do not terminate sessions if the command did not parse correctly */ + if (returnCode != TPM_SUCCESS) { + authHandleValid = FALSE; + } + /* + Processing + */ + /* 1. If TPM_PERMANENT_FLAGS -> nvLocked is FALSE then all authorization checks are + ignored */ + /* a. Ignored checks include physical presence, owner authorization, PCR, bReadSTClear, + locality, TPM_NV_PER_OWNERREAD, disabled and deactivated */ + /* b. TPM_NV_PER_AUTHREAD is not ignored. */ + /* c. If ownerAuth is present, the TPM MAY check the authorization HMAC. */ + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_NVReadValue: index %08x offset %u dataSize %u\n", + nvIndex, offset, dataSize); + if (!(tpm_state->tpm_permanent_flags.nvLocked)) { + printf("TPM_Process_NVReadValue: nvLocked FALSE, ignoring authorization\n"); + ignore_auth = TRUE; + } + /* determine whether the nvIndex is legal GPIO space */ + if (returnCode == 0) { + returnCode = TPM_NVDataSensitive_IsGPIO(&isGPIO, nvIndex); + } + } + /* 2. Set D1 a TPM_NV_DATA_AREA structure to the area pointed to by nvIndex, if not found + return TPM_BADINDEX */ + if (returnCode == TPM_SUCCESS) { + /* a. If nvIndex = TPM_NV_INDEX_DIR, set D1 to TPM_PERMANENT_DATA -> authDir[0] */ + if (nvIndex == TPM_NV_INDEX_DIR) { + printf("TPM_Process_NVReadValue: Reading DIR\n"); + dir = TRUE; + } + else { + printf("TPM_Process_NVReadValue: Loading data from NVRAM\n"); + returnCode = TPM_NVIndexEntries_GetEntry(&d1NvdataSensitive, + &(tpm_state->tpm_nv_index_entries), + nvIndex); + if (returnCode != 0) { + printf("TPM_Process_NVReadValue: Error, NV index %08x not found\n", nvIndex); + } + } + } + /* Do not check permission for DIR, DIR is no-auth */ + if ((returnCode == TPM_SUCCESS) && !dir) { + /* 3. If TPM_PERMANENT_FLAGS -> nvLocked is TRUE */ + if (tpm_state->tpm_permanent_flags.nvLocked) { + /* a. If D1 -> permission -> TPM_NV_PER_OWNERREAD is TRUE */ + if (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_OWNERREAD) { + /* i. If TPM_PERMANENT_FLAGS -> disable is TRUE, return TPM_DISABLED */ + if (tpm_state->tpm_permanent_flags.disable) { + printf("TPM_Process_NVReadValue: Error, disabled\n"); + return TPM_DISABLED; + } + /* ii. If TPM_STCLEAR_FLAGS -> deactivated is TRUE, return TPM_DEACTIVATED */ + else if (tpm_state->tpm_stclear_flags.deactivated) { + printf("TPM_Process_NVReadValue: Error, deactivated\n"); + return TPM_DEACTIVATED;; + } + } + /* NOTE: Intel software requires NV access disabled and deactivated */ + /* b. If D1 -> permission -> TPM_NV_PER_OWNERREAD is FALSE */ + /* i. If TPM_PERMANENT_FLAGS -> disable is TRUE, the TPM MAY return TPM_DISABLED */ + /* ii. If TPM_STCLEAR_FLAGS -> deactivated is TRUE, the TPM MAY return + TPM_DEACTIVATED */ + } + } + /* 4. If tag = TPM_TAG_RQU_AUTH1_COMMAND then */ + /* NOTE: This is optional if ignore_auth is TRUE */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND) && !dir) { + /* a. If D1 -> TPM_NV_PER_OWNERREAD is FALSE return TPM_AUTH_CONFLICT */ + if (!(d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_OWNERREAD)) { + printf("TPM_Process_NVReadValue: Error, " + "owner authorization conflict, attributes %08x\n", + d1NvdataSensitive->pubInfo.permission.attributes); + returnCode = TPM_AUTH_CONFLICT; + } + } + /* b. Validate command and parameters using TPM Owners authorization on error return + TPM_AUTHFAIL */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_AuthSessions_GetData(&auth_session_data, + &hmacKey, + tpm_state, + authHandle, + TPM_PID_NONE, + TPM_ET_OWNER, + ordinal, + NULL, + &(tpm_state->tpm_permanent_data.ownerAuth), /* OIAP */ + tpm_state->tpm_permanent_data.ownerAuth); /* OSAP */ + } + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND) && !ignore_auth) { + returnCode = TPM_Authdata_Check(tpm_state, + *hmacKey, /* HMAC key */ + inParamDigest, + auth_session_data, /* authorization session */ + nonceOdd, /* Nonce generated by system + associated with authHandle */ + continueAuthSession, + ownerAuth); /* Authorization digest for input */ + } + /* 5. Else */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND) && !dir) { + /* a. If D1 -> TPM_NV_PER_AUTHREAD is TRUE return TPM_AUTH_CONFLICT */ + if (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_AUTHREAD) { + printf("TPM_Process_NVReadValue: Error, authorization conflict TPM_NV_PER_AUTHREAD\n"); + returnCode = TPM_AUTH_CONFLICT; + } + } + /* b. If D1 -> TPM_NV_PER_OWNERREAD is TRUE return TPM_AUTH_CONFLICT */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND) && !ignore_auth && !dir) { + if (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_OWNERREAD) { + printf("TPM_Process_NVReadValue: Error, authorization conflict TPM_NV_PER_OWNERREAD\n"); + returnCode = TPM_AUTH_CONFLICT; + } + } + /* 6. Check that D1 -> pcrInfoRead -> localityAtRelease for TPM_STANY_DATA -> localityModifier + is TRUE */ + /* a. For example if TPM_STANY_DATA -> localityModifier was 2 then D1 -> pcrInfo -> + localityAtRelease -> TPM_LOC_TWO would have to be TRUE */ + /* b. On error return TPM_BAD_LOCALITY */ + /* NOTE Done by TPM_PCRInfoShort_CheckDigest() */ + /* 7. If D1 -> attributes specifies TPM_NV_PER_PPREAD then validate physical presence is + asserted if not return TPM_BAD_PRESENCE */ + if ((returnCode == TPM_SUCCESS) && !ignore_auth && !dir) { + if (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_PPREAD) { + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Global_GetPhysicalPresence(&physicalPresence, tpm_state); + } + if (returnCode == TPM_SUCCESS) { + if (!physicalPresence) { + printf("TPM_Process_NVReadValue: Error, physicalPresence is FALSE\n"); + returnCode = TPM_BAD_PRESENCE; + } + } + } + } + if ((returnCode == TPM_SUCCESS) && !ignore_auth && !dir) { + /* 8. If D1 -> TPM_NV_PER_READ_STCLEAR then */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_READ_STCLEAR) && + /* a. If D1 -> bReadSTClear is TRUE return TPM_DISABLED_CMD */ + (d1NvdataSensitive->pubInfo.bReadSTClear)) { + printf("TPM_Process_NVReadValue: Error, area locked by bReadSTClear\n"); + returnCode = TPM_DISABLED_CMD; + } + } + /* 9. If D1 -> pcrInfoRead -> pcrSelection specifies a selection of PCR */ + /* a. Create P1 a composite hash of the PCR specified by D1 -> pcrInfoRead */ + /* b. Compare P1 to D1 -> pcrInfoRead -> digestAtRelease return TPM_WRONGPCRVAL on + mismatch */ + if ((returnCode == TPM_SUCCESS) && !ignore_auth && !dir) { + returnCode = TPM_PCRInfoShort_CheckDigest(&(d1NvdataSensitive->pubInfo.pcrInfoRead), + tpm_state->tpm_stclear_data.PCRS, + tpm_state->tpm_stany_flags.localityModifier); + } + if (returnCode == TPM_SUCCESS && !dir) { + /* 10. If dataSize is 0 then */ + if (dataSize == 0) { + printf("TPM_Process_NVReadValue: dataSize 0, setting bReadSTClear\n"); + /* a. Set D1 -> bReadSTClear to TRUE */ + d1NvdataSensitive->pubInfo.bReadSTClear = TRUE; + /* b. Set data to NULL (output parameter dataSize to 0) */ + /* NOTE Done by TPM_SizedBuffer_Init */ + } + /* 11. Else (if dataSize is not 0) */ + else { + if (returnCode == TPM_SUCCESS) { + /* a. Set S1 to offset + dataSize */ + s1Last = offset + dataSize; /* set to last data point */ + /* b. If S1 > D1 -> dataSize return TPM_NOSPACE */ + if (s1Last > d1NvdataSensitive->pubInfo.dataSize) { + printf("TPM_Process_NVReadValue: Error, NVRAM dataSize %u\n", + d1NvdataSensitive->pubInfo.dataSize); + returnCode = TPM_NOSPACE; + } + } + /* c. Set data to area pointed to by offset */ + if ((returnCode == TPM_SUCCESS) && !isGPIO) { + TPM_PrintFourLimit("TPM_Process_NVReadValue: read data", + d1NvdataSensitive->data + offset, dataSize); + returnCode = TPM_SizedBuffer_Set(&data, + dataSize, d1NvdataSensitive->data + offset); + } + /* GPIO */ + if ((returnCode == TPM_SUCCESS) && isGPIO) { + returnCode = TPM_Malloc(&gpioData, dataSize); /* freed @2 */ + } + if ((returnCode == TPM_SUCCESS) && isGPIO) { + printf("TPM_Process_NVReadValue: Reading GPIO\n"); + returnCode = TPM_IO_GPIO_Read(nvIndex, + dataSize, + gpioData, + tpm_state->tpm_number); + } + if ((returnCode == TPM_SUCCESS) && isGPIO) { + returnCode = TPM_SizedBuffer_Set(&data, + dataSize, gpioData); + } + } + } + /* DIR read */ + if (returnCode == TPM_SUCCESS && dir) { + /* DIR is hard coded as a TPM_DIRVALUE array */ + if (returnCode == TPM_SUCCESS) { + s1Last = offset + dataSize; /* set to last data point */ + if (s1Last > TPM_DIGEST_SIZE) { + printf("TPM_Process_NVReadValue: Error, NVRAM dataSize %u too small\n", + TPM_DIGEST_SIZE); + returnCode = TPM_NOSPACE; + } + } + /* i.This includes partial reads of TPM_NV_INDEX_DIR. */ + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_NVReadValue: Copying data\n"); + returnCode = TPM_SizedBuffer_Set(&data, dataSize, + tpm_state->tpm_permanent_data.authDIR + offset); + } + } + /* + response + */ + /* standard response: tag, (dummy) paramSize, returnCode. Failure is fatal. */ + if (rcf == 0) { + printf("TPM_Process_NVReadValue: Ordinal returnCode %08x %u\n", + returnCode, returnCode); + rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode); + } + /* success response, append the rest of the parameters. */ + if (rcf == 0) { + if (returnCode == TPM_SUCCESS) { + /* checkpoint the beginning of the outParam's */ + outParamStart = response->buffer_current - response->buffer; + /* return data */ + returnCode = TPM_SizedBuffer_Store(response, &data); + /* checkpoint the end of the outParam's */ + outParamEnd = response->buffer_current - response->buffer; + } + /* digest the above the line output parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetOutParamDigest(outParamDigest, /* output */ + auditStatus, /* input audit status */ + transportEncrypt, + tag, + returnCode, + ordinal, /* command ordinal */ + response->buffer + outParamStart, /* start */ + outParamEnd - outParamStart); /* length */ + } + /* calculate and set the below the line parameters */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_AuthParams_Set(response, + *hmacKey, /* owner HMAC key */ + auth_session_data, + outParamDigest, + nonceOdd, + continueAuthSession); + } + /* audit if required */ + if ((returnCode == TPM_SUCCESS) && auditStatus) { + returnCode = TPM_ProcessAudit(tpm_state, + transportEncrypt, + inParamDigest, + outParamDigest, + ordinal); + } + /* adjust the initial response */ + rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state); + } + /* if there was an error, or continueAuthSession is FALSE, terminate the session */ + if (((rcf != 0) || + ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) || + !continueAuthSession) && + authHandleValid) { + TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle); + } + /* + cleanup + */ + TPM_SizedBuffer_Delete(&data); /* @1 */ + free(gpioData); /* @2 */ + return rcf; +} + +/* 20.5 TPM_NV_ReadValueAuth rev 87 + + This command requires that the read be authorized by a value set with the blob. +*/ + +TPM_RESULT TPM_Process_NVReadValueAuth(tpm_state_t *tpm_state, + TPM_STORE_BUFFER *response, + TPM_TAG tag, + uint32_t paramSize, + TPM_COMMAND_CODE ordinal, + unsigned char *command, + TPM_TRANSPORT_INTERNAL *transportInternal) +{ + TPM_RESULT rcf = 0; /* fatal error precluding response */ + TPM_RESULT returnCode = TPM_SUCCESS; /* command return code */ + + /* input parameters */ + TPM_NV_INDEX nvIndex; /* The index of the area to set */ + uint32_t offset = 0; /* The offset from the data area */ + uint32_t dataSize = 0; /* The size of the data area */ + TPM_AUTHHANDLE authHandle; /* The auth handle for the NV element authorization */ + TPM_NONCE nonceOdd; /* Nonce generated by system associated with authHandle */ + TPM_BOOL continueAuthSession = TRUE; /* The continue use flag for the authorization + handle */ + TPM_AUTHDATA authHmac; /* HMAC key: nv element authorization */ + + /* processing parameters */ + unsigned char * inParamStart; /* starting point of inParam's */ + unsigned char * inParamEnd; /* ending point of inParam's */ + TPM_DIGEST inParamDigest; + TPM_BOOL auditStatus; /* audit the ordinal */ + TPM_BOOL transportEncrypt; /* wrapped in encrypted transport session */ + TPM_BOOL authHandleValid = FALSE; + TPM_AUTH_SESSION_DATA *auth_session_data = NULL; /* session data for authHandle */ + TPM_SECRET *hmacKey; + TPM_NV_DATA_SENSITIVE *d1NvdataSensitive; + uint32_t s1Last; + TPM_BOOL physicalPresence; + TPM_BOOL isGPIO; + BYTE *gpioData = NULL; + + /* output parameters */ + uint32_t outParamStart; /* starting point of outParam's */ + uint32_t outParamEnd; /* ending point of outParam's */ + TPM_DIGEST outParamDigest; + TPM_SIZED_BUFFER data; /* The data */ + + printf("TPM_Process_NVReadValueAuth: Ordinal Entry\n"); + TPM_SizedBuffer_Init(&data); /* freed @1 */ + /* + get inputs + */ + /* save the starting point of inParam's for authorization and auditing */ + inParamStart = command; + /* get nvIndex parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&nvIndex, &command, ¶mSize); + } + /* get offset parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&offset, &command, ¶mSize); + } + /* get dataSize parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&dataSize, &command, ¶mSize); + } + /* save the ending point of inParam's for authorization and auditing */ + inParamEnd = command; + /* digest the input parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetInParamDigest(inParamDigest, /* output */ + &auditStatus, /* output */ + &transportEncrypt, /* output */ + tpm_state, + tag, + ordinal, + inParamStart, + inParamEnd, + transportInternal); + } + /* check state */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL); + } + /* check tag */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckRequestTag1(tag); + } + /* get the 'below the line' authorization parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_AuthParams_Get(&authHandle, + &authHandleValid, + nonceOdd, + &continueAuthSession, + authHmac, + &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + if (paramSize != 0) { + printf("TPM_Process_NVReadValueAuth: Error, command has %u extra bytes\n", + paramSize); + returnCode = TPM_BAD_PARAM_SIZE; + } + } + /* do not terminate sessions if the command did not parse correctly */ + if (returnCode != TPM_SUCCESS) { + authHandleValid = FALSE; + } + /* + Processing + */ + /* determine whether the nvIndex is legal GPIO space */ + if (returnCode == 0) { + returnCode = TPM_NVDataSensitive_IsGPIO(&isGPIO, nvIndex); + } + /* 1. Locate and set D1 to the TPM_NV_DATA_AREA that corresponds to nvIndex, on error return + TPM_BAD_INDEX */ + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_NVReadValueAuth: index %08x offset %u dataSize %u\n", + nvIndex, offset, dataSize); + printf("TPM_Process_NVReadValueAuth: Loading data from NVRAM\n"); + returnCode = TPM_NVIndexEntries_GetEntry(&d1NvdataSensitive, + &(tpm_state->tpm_nv_index_entries), + nvIndex); + if (returnCode != 0) { + printf("TPM_Process_NVReadValueAuth: Error, NV index %08x not found\n", nvIndex); + } + } + /* 2. If D1 -> TPM_NV_PER_AUTHREAD is FALSE return TPM_AUTH_CONFLICT */ + if (returnCode == TPM_SUCCESS) { + if (!(d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_AUTHREAD)) { + printf("TPM_Process_NVReadValueAuth: Error, authorization conflict\n"); + returnCode = TPM_AUTH_CONFLICT; + } + } + /* 3. Validate authHmac using D1 -> authValue on error return TPM_AUTHFAIL */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_AuthSessions_GetData(&auth_session_data, + &hmacKey, + tpm_state, + authHandle, + TPM_PID_NONE, + TPM_ET_NV, + ordinal, + NULL, + &(d1NvdataSensitive->authValue), /* OIAP */ + d1NvdataSensitive->digest); /* OSAP */ + } + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Authdata_Check(tpm_state, + *hmacKey, /* HMAC key */ + inParamDigest, + auth_session_data, /* authorization session */ + nonceOdd, /* Nonce generated by system + associated with authHandle */ + continueAuthSession, + authHmac); /* Authorization digest for input */ + } + /* 4. If D1 -> attributes specifies TPM_NV_PER_PPREAD then validate physical presence is + asserted if not return TPM_BAD_PRESENCE */ + if (returnCode == TPM_SUCCESS) { + if (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_PPREAD) { + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Global_GetPhysicalPresence(&physicalPresence, tpm_state); + } + if (returnCode == TPM_SUCCESS) { + if (!physicalPresence) { + printf("TPM_Process_NVReadValueAuth: Error, physicalPresence is FALSE\n"); + returnCode = TPM_BAD_PRESENCE; + } + } + } + } + /* 5. Check that D1 -> pcrInfoRead -> localityAtRelease for TPM_STANY_DATA -> localityModifier + is TRUE */ + /* a. For example if TPM_STANY_DATA -> localityModifier was 2 then D1 -> pcrInfo -> + localityAtRelease -> TPM_LOC_TWO would have to be TRUE */ + /* b. On error return TPM_BAD_LOCALITY */ + /* 6. If D1 -> pcrInfoRead -> pcrSelection specifies a selection of PCR */ + /* a. Create P1 a composite hash of the PCR specified by D1 -> pcrInfoRead */ + /* b. Compare P1 to D1 -> pcrInfoRead -> digestAtRelease return TPM_WRONGPCRVAL on + mismatch */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_PCRInfoShort_CheckDigest(&(d1NvdataSensitive->pubInfo.pcrInfoRead), + tpm_state->tpm_stclear_data.PCRS, + tpm_state->tpm_stany_flags.localityModifier); + } + if (returnCode == TPM_SUCCESS) { + /* 7. If D1 specifies TPM_NV_PER_READ_STCLEAR then */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_READ_STCLEAR) && + /* a. If D1 -> bReadSTClear is TRUE return TPM_DISABLED_CMD */ + (d1NvdataSensitive->pubInfo.bReadSTClear)) { + printf("TPM_Process_NVReadValueAuth: Error, area locked by bReadSTClear\n"); + returnCode = TPM_DISABLED_CMD; + } + } + if (returnCode == TPM_SUCCESS) { + /* 8. If dataSize is 0 then */ + if (dataSize == 0) { + printf("TPM_Process_NVReadValueAuth: dataSize 0, setting bReadSTClear\n"); + /* a. Set D1 -> bReadSTClear to TRUE */ + d1NvdataSensitive->pubInfo.bReadSTClear = TRUE; + /* b. Set data to NULL */ + /* NOTE Done by TPM_SizedBuffer_Init */ + } + /* 9. Else (if dataSize is not 0) */ + else { + if (returnCode == TPM_SUCCESS) { + /* a. Set S1 to offset + dataSize */ + s1Last = offset + dataSize; /* set to last data point */ + /* b. If S1 > D1 -> dataSize return TPM_NOSPACE */ + if (s1Last > d1NvdataSensitive->pubInfo.dataSize) { + printf("TPM_Process_NVReadValueAuth: Error, NVRAM dataSize %u too small\n", + d1NvdataSensitive->pubInfo.dataSize); + returnCode = TPM_NOSPACE; + } + } + /* c. Set data to area pointed to by offset */ + if ((returnCode == TPM_SUCCESS) && !isGPIO) { + TPM_PrintFourLimit("TPM_Process_NVReadValueAuth: read data", + d1NvdataSensitive->data + offset, dataSize); + returnCode = TPM_SizedBuffer_Set(&data, dataSize, d1NvdataSensitive->data + offset); + } + /* GPIO */ + if ((returnCode == TPM_SUCCESS) && isGPIO) { + returnCode = TPM_Malloc(&gpioData, dataSize); /* freed @2 */ + } + if ((returnCode == TPM_SUCCESS) && isGPIO) { + printf("TPM_Process_NVReadValueAuth: Reading GPIO\n"); + returnCode = TPM_IO_GPIO_Read(nvIndex, + dataSize, + gpioData, + tpm_state->tpm_number); + } + if ((returnCode == TPM_SUCCESS) && isGPIO) { + returnCode = TPM_SizedBuffer_Set(&data, + dataSize, gpioData); + } + } + } + /* + response + */ + /* standard response: tag, (dummy) paramSize, returnCode. Failure is fatal. */ + if (rcf == 0) { + printf("TPM_Process_NVReadValueAuth: Ordinal returnCode %08x %u\n", + returnCode, returnCode); + rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode); + } + /* success response, append the rest of the parameters. */ + if (rcf == 0) { + if (returnCode == TPM_SUCCESS) { + /* checkpoint the beginning of the outParam's */ + outParamStart = response->buffer_current - response->buffer; + /* return data */ + returnCode = TPM_SizedBuffer_Store(response, &data); + /* checkpoint the end of the outParam's */ + outParamEnd = response->buffer_current - response->buffer; + } + /* digest the above the line output parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetOutParamDigest(outParamDigest, /* output */ + auditStatus, /* input audit status */ + transportEncrypt, + tag, + returnCode, + ordinal, /* command ordinal */ + response->buffer + outParamStart, /* start */ + outParamEnd - outParamStart); /* length */ + } + /* calculate and set the below the line parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_AuthParams_Set(response, + *hmacKey, /* HMAC key */ + auth_session_data, + outParamDigest, + nonceOdd, + continueAuthSession); + } + /* audit if required */ + if ((returnCode == TPM_SUCCESS) && auditStatus) { + returnCode = TPM_ProcessAudit(tpm_state, + transportEncrypt, + inParamDigest, + outParamDigest, + ordinal); + } + /* adjust the initial response */ + rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state); + } + /* if there was an error, or continueAuthSession is FALSE, terminate the session */ + if (((rcf != 0) || + ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) || + !continueAuthSession) && + authHandleValid) { + TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle); + } + /* + cleanup + */ + TPM_SizedBuffer_Delete(&data); /* @1 */ + return rcf; +} + +/* 20.2 TPM_NV_WriteValue rev 117 + + This command writes the value to a defined area. The write can be TPM Owner authorized or + unauthorized and protected by other attributes and will work when no TPM Owner is present. + + The action setting bGlobalLock to TRUE is intentionally before the action checking the + owner authorization. This allows code (e.g., a BIOS) to lock NVRAM without knowing the + owner authorization. + + The DIR (TPM_NV_INDEX_DIR) has the attributes TPM_NV_PER_OWNERWRITE and TPM_NV_WRITEALL. + + FIXME: A simpler way to do DIR might be to create the DIR as NV defined space at first + initialization and remove the special casing here. +*/ + +TPM_RESULT TPM_Process_NVWriteValue(tpm_state_t *tpm_state, + TPM_STORE_BUFFER *response, + TPM_TAG tag, + uint32_t paramSize, + TPM_COMMAND_CODE ordinal, + unsigned char *command, + TPM_TRANSPORT_INTERNAL *transportInternal) +{ + TPM_RESULT rcf = 0; /* fatal error precluding response */ + TPM_RESULT returnCode = TPM_SUCCESS; /* command return code */ + int irc; + + /* input parameters */ + TPM_NV_INDEX nvIndex; /* The index of the area to set */ + uint32_t offset = 0; /* The offset into the NV Area */ + TPM_SIZED_BUFFER data; /* The data to set the area to */ + TPM_AUTHHANDLE authHandle; /* The authorization handle used for TPM Owner */ + TPM_NONCE nonceOdd; /* Nonce generated by caller */ + TPM_BOOL continueAuthSession = TRUE; /* The continue use flag for the authorization + handle */ + TPM_AUTHDATA ownerAuth; /* The authorization digest HMAC key: TPM Owner auth */ + + /* processing parameters */ + unsigned char * inParamStart; /* starting point of inParam's */ + unsigned char * inParamEnd; /* ending point of inParam's */ + TPM_DIGEST inParamDigest; + TPM_BOOL auditStatus; /* audit the ordinal */ + TPM_BOOL transportEncrypt; /* wrapped in encrypted transport session */ + TPM_BOOL authHandleValid = FALSE; + TPM_AUTH_SESSION_DATA *auth_session_data = NULL; /* session data for authHandle */ + TPM_SECRET *hmacKey = NULL; + TPM_BOOL ignore_auth = FALSE; + TPM_BOOL index0 = FALSE; + TPM_BOOL done = FALSE; + TPM_BOOL dir = FALSE; + TPM_BOOL writeAllNV = FALSE; /* flag to write back NV */ + TPM_NV_DATA_SENSITIVE *d1NvdataSensitive = NULL; + uint32_t s1Last; + TPM_BOOL physicalPresence; + TPM_BOOL isGPIO = FALSE; + uint32_t nv1 = tpm_state->tpm_permanent_data.noOwnerNVWrite; + /* temp for noOwnerNVWrite, initialize to + silence compiler */ + TPM_BOOL nv1Incremented = FALSE; /* flag that nv1 was incremented */ + + /* output parameters */ + uint32_t outParamStart; /* starting point of outParam's */ + uint32_t outParamEnd; /* ending point of outParam's */ + TPM_DIGEST outParamDigest; + + printf("TPM_Process_NVWriteValue: Ordinal Entry\n"); + TPM_SizedBuffer_Init(&data); /* freed @1 */ + /* + get inputs + */ + /* save the starting point of inParam's for authorization and auditing */ + inParamStart = command; + /* get nvIndex parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&nvIndex, &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&offset, &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_SizedBuffer_Load(&data, &command, ¶mSize); + } + /* save the ending point of inParam's for authorization and auditing */ + inParamEnd = command; + /* digest the input parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetInParamDigest(inParamDigest, /* output */ + &auditStatus, /* output */ + &transportEncrypt, /* output */ + tpm_state, + tag, + ordinal, + inParamStart, + inParamEnd, + transportInternal); + } + /* check state */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckState(tpm_state, tag, (TPM_CHECK_NOT_SHUTDOWN | + TPM_CHECK_NO_LOCKOUT | + TPM_CHECK_NV_NOAUTH)); + } + /* check tag */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckRequestTag10(tag); + } + /* get the optional 'below the line' authorization parameters */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_AuthParams_Get(&authHandle, + &authHandleValid, + nonceOdd, + &continueAuthSession, + ownerAuth, + &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + if (paramSize != 0) { + printf("TPM_Process_NVWriteValue: Error, command has %u extra bytes\n", + paramSize); + returnCode = TPM_BAD_PARAM_SIZE; + } + } + /* do not terminate sessions if the command did not parse correctly */ + if (returnCode != TPM_SUCCESS) { + authHandleValid = FALSE; + } + /* + Processing + */ + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_NVWriteValue: index %08x offset %u dataSize %u\n", + nvIndex, offset, data.size); + TPM_PrintFourLimit("TPM_Process_NVWriteValue: data", data.buffer, data.size); + /* 1. If TPM_PERMANENT_FLAGS -> nvLocked is FALSE then all authorization checks except for + the max NV writes are ignored */ + /* a. Ignored checks include physical presence, owner authorization, TPM_NV_PER_OWNERWRITE, + PCR, bWriteDefine, bGlobalLock, bWriteSTClear, locality, disabled and deactivated */ + /* b. TPM_NV_PER_AUTHWRITE is not ignored. */ + /* a.If ownerAuth is present, the TPM MAY check the authorization HMAC. */ + if (!(tpm_state->tpm_permanent_flags.nvLocked)) { + printf("TPM_Process_NVWriteValue: nvLocked FALSE, ignoring authorization\n"); + ignore_auth = TRUE; + } + if (nvIndex == TPM_NV_INDEX0) { + index0 = TRUE; + } + /* determine whether the nvIndex is legal GPIO space */ + if (returnCode == 0) { + returnCode = TPM_NVDataSensitive_IsGPIO(&isGPIO, nvIndex); + } + } + /* 2. Locate and set D1 to the TPM_NV_DATA_AREA that corresponds to nvIndex, return TPM_BADINDEX + on error */ + if ((returnCode == TPM_SUCCESS) && !index0) { + /* a. If nvIndex = TPM_NV_INDEX_DIR, set D1 to TPM_PERMANENT_DATA -> authDir[0] */ + if (nvIndex == TPM_NV_INDEX_DIR) { + printf("TPM_Process_NVWriteValue: Writing DIR\n"); + dir = TRUE; + } + else { + printf("TPM_Process_NVWriteValue: Loading data space from NVRAM\n"); + returnCode = TPM_NVIndexEntries_GetEntry(&d1NvdataSensitive, + &(tpm_state->tpm_nv_index_entries), + nvIndex); + if (returnCode != 0) { + printf("TPM_Process_NVWriteValue: Error, NV index %08x not found\n", nvIndex); + } + } + } + if ((returnCode == TPM_SUCCESS) && !index0) { + /* 3. If TPM_PERMANENT_FLAGS -> nvLocked is TRUE */ + if (tpm_state->tpm_permanent_flags.nvLocked) { + /* a. If D1 -> permission -> TPM_NV_PER_OWNERWRITE is TRUE */ + if (dir || /* DIR always has TPM_NV_PER_OWNERWRITE */ + (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_OWNERWRITE)) { + /* i. If TPM_PERMANENT_FLAGS -> disable is TRUE, return TPM_DISABLED */ + if (tpm_state->tpm_permanent_flags.disable) { + printf("TPM_Process_NVWriteValue: Error, disabled\n"); + return TPM_DISABLED; + } + /* ii.If TPM_STCLEAR_FLAGS -> deactivated is TRUE, return TPM_DEACTIVATED */ + else if (tpm_state->tpm_stclear_flags.deactivated) { + printf("TPM_Process_NVWriteValue: Error, deactivated\n"); + return TPM_DEACTIVATED;; + } + } + /* NOTE: Intel software requires NV access disabled and deactivated */ + /* b. If D1 -> permission -> TPM_NV_PER_OWNERWRITE is FALSE */ + /* i. If TPM_PERMANENT_FLAGS -> disable is TRUE, the TPM MAY return TPM_DISABLED */ + /* ii. If TPM_STCLEAR_FLAGS -> deactivated is TRUE, the TPM MAY return + TPM_DEACTIVATED */ + } + } + /* 4. If tag = TPM_TAG_RQU_AUTH1_COMMAND then */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND) && !dir && !index0) { + /* a. If D1 -> permission -> TPM_NV_PER_OWNERWRITE is FALSE return TPM_AUTH_CONFLICT */ + /* i. This check is ignored if nvIndex is TPM_NV_INDEX0. */ + if (!(d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_OWNERWRITE)) { + printf("TPM_Process_NVWriteValue: Error, owner authorization conflict\n"); + returnCode = TPM_AUTH_CONFLICT; + } + } + /* b. Validate command and parameters using ownerAuth HMAC with TPM Owner authentication as the + secret, return TPM_AUTHFAIL on error */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_AuthSessions_GetData(&auth_session_data, + &hmacKey, + tpm_state, + authHandle, + TPM_PID_NONE, + TPM_ET_OWNER, + ordinal, + NULL, + &(tpm_state->tpm_permanent_data.ownerAuth), /* OIAP */ + tpm_state->tpm_permanent_data.ownerAuth); /* OSAP */ + } + /* NOTE: This is optional if ignore_auth is TRUE */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_Authdata_Check(tpm_state, + *hmacKey, /* HMAC key */ + inParamDigest, + auth_session_data, /* authorization session */ + nonceOdd, /* Nonce generated by system + associated with authHandle */ + continueAuthSession, + ownerAuth); /* Authorization digest for input */ + } + /* 5. Else */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND) && !ignore_auth && !index0) { + /* a. If D1 -> permission -> TPM_NV_PER_OWNERWRITE is TRUE return TPM_AUTH_CONFLICT */ + if (dir || /* DIR always has TPM_NV_PER_OWNERWRITE */ + (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_OWNERWRITE)) { + printf("TPM_Process_NVWriteValue: Error, no owner authorization conflict\n"); + returnCode = TPM_AUTH_CONFLICT; + } + } + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND) && !index0) { + /* b. If no TPM Owner validate max NV writes without an owner */ + /* i. Set NV1 to TPM_PERMANENT_DATA -> noOwnerNVWrite */ + nv1 = tpm_state->tpm_permanent_data.noOwnerNVWrite; + /* ii. Increment NV1 by 1 */ + nv1++; + /* iii. If NV1 > TPM_MAX_NV_WRITE_NOOWNER return TPM_MAXNVWRITES */ + if (nv1 > TPM_MAX_NV_WRITE_NOOWNER) { + printf("TPM_Process_NVWriteValue: Error, max NV writes %d w/o owner reached\n", + tpm_state->tpm_permanent_data.noOwnerNVWrite); + returnCode = TPM_MAXNVWRITES; + } + /* iv. Set NV1_INCREMENTED to TRUE */ + else { + nv1Incremented = TRUE; + } + } + if (returnCode == TPM_SUCCESS) { + /* 6. If nvIndex = 0 then */ + if (nvIndex == 0) { + /* a. If dataSize is not 0, the TPM MAY return TPM_BADINDEX. */ + if (data.size != 0) { + printf("TPM_Process_NVWriteValue: Error, index 0 size %u\n", data.size); + returnCode = TPM_BADINDEX; + } + else { + /* b. Set TPM_STCLEAR_FLAGS -> bGlobalLock to TRUE */ + printf("TPM_Process_NVWriteValue: nvIndex 0, setting bGlobalLock\n"); + tpm_state->tpm_stclear_flags.bGlobalLock = TRUE; + /* c. Return TPM_SUCCESS */ + done = TRUE; + } + } + } + /* 7. If D1 -> permission -> TPM_NV_PER_AUTHWRITE is TRUE return TPM_AUTH_CONFLICT */ + if ((returnCode == TPM_SUCCESS) && !done && !dir) { + if (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_AUTHWRITE) { + printf("TPM_Process_NVWriteValue: Error, authorization conflict, attributes %08x \n", + d1NvdataSensitive->pubInfo.permission.attributes); + returnCode = TPM_AUTH_CONFLICT; + } + } + /* 8. Check that D1 -> pcrInfoWrite -> localityAtRelease for TPM_STANY_DATA -> localityModifier + is TRUE */ + /* a. For example if TPM_STANY_DATA -> localityModifier was 2 then D1 -> pcrInfo -> + localityAtRelease -> TPM_LOC_TWO would have to be TRUE */ + /* b. On error return TPM_BAD_LOCALITY */ + /* NOTE Done by TPM_PCRInfoShort_CheckDigest() */ + /* 9. If D1 -> attributes specifies TPM_NV_PER_PPWRITE then validate physical presence is + asserted if not return TPM_BAD_PRESENCE */ + if ((returnCode == TPM_SUCCESS) && !done && !ignore_auth && !dir) { + if (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_PPWRITE) { + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Global_GetPhysicalPresence(&physicalPresence, tpm_state); + } + if (returnCode == TPM_SUCCESS) { + if (!physicalPresence) { + printf("TPM_Process_NVWriteValue: Error, physicalPresence is FALSE\n"); + returnCode = TPM_BAD_PRESENCE; + } + } + } + } + if ((returnCode == TPM_SUCCESS) && !done && !ignore_auth && !dir) { + /* 10. If D1 -> attributes specifies TPM_NV_PER_WRITEDEFINE */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_WRITEDEFINE) && + /* a. If D1 -> bWriteDefine is TRUE return TPM_AREA_LOCKED */ + (d1NvdataSensitive->pubInfo.bWriteDefine)) { + printf("TPM_Process_NVWriteValue: Error, area locked by bWriteDefine\n"); + returnCode = TPM_AREA_LOCKED; + } + } + if ((returnCode == TPM_SUCCESS) && !done && !ignore_auth && !dir) { + /* 11. If D1 -> attributes specifies TPM_NV_PER_GLOBALLOCK */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_GLOBALLOCK) && + /* a. If TPM_STCLEAR_FLAGS -> bGlobalLock is TRUE return TPM_AREA_LOCKED */ + (tpm_state->tpm_stclear_flags.bGlobalLock)) { + printf("TPM_Process_NVWriteValue: Error, area locked by bGlobalLock\n"); + returnCode = TPM_AREA_LOCKED; + } + } + if ((returnCode == TPM_SUCCESS) && !done && !ignore_auth && !dir) { + /* 12. If D1 -> attributes specifies TPM_NV_PER_WRITE_STCLEAR */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_WRITE_STCLEAR) && + /* a. If D1 ->bWriteSTClear is TRUE return TPM_AREA_LOCKED */ + (d1NvdataSensitive->pubInfo.bWriteSTClear)) { + printf("TPM_Process_NVWriteValue: Error, area locked by bWriteSTClear\n"); + returnCode = TPM_AREA_LOCKED; + } + } + /* 13. If D1 -> pcrInfoWrite -> pcrSelection specifies a selection of PCR */ + /* a. Create P1 a composite hash of the PCR specified by D1 -> pcrInfoWrite */ + /* b. Compare P1 to D1 -> pcrInfoWrite -> digestAtRelease return TPM_WRONGPCRVAL on mismatch + */ + if ((returnCode == TPM_SUCCESS) && !done && !ignore_auth && !dir) { + returnCode = TPM_PCRInfoShort_CheckDigest(&(d1NvdataSensitive->pubInfo.pcrInfoWrite), + tpm_state->tpm_stclear_data.PCRS, + tpm_state->tpm_stany_flags.localityModifier); + } + if ((returnCode == TPM_SUCCESS) && !done && !dir) { + /* 14. If dataSize = 0 then */ + if (data.size == 0) { + printf("TPM_Process_NVWriteValue: dataSize 0, setting bWriteSTClear, bWriteDefine\n"); + /* a. Set D1 -> bWriteSTClear to TRUE */ + d1NvdataSensitive->pubInfo.bWriteSTClear = TRUE; + /* b. Set D1 -> bWriteDefine */ + if (!d1NvdataSensitive->pubInfo.bWriteDefine) { /* save wearout, only write if + FALSE */ + d1NvdataSensitive->pubInfo.bWriteDefine = TRUE; + /* must write TPM_PERMANENT_DATA back to NVRAM, set this flag after structure is + written */ + writeAllNV = TRUE; + } + } + /* 15. Else (if dataSize is not 0) */ + else { + if (returnCode == TPM_SUCCESS) { + /* a. Set S1 to offset + dataSize */ + s1Last = offset + data.size; /* set to last data point */ + /* b. If S1 > D1 -> dataSize return TPM_NOSPACE */ + if (s1Last > d1NvdataSensitive->pubInfo.dataSize) { + printf("TPM_Process_NVWriteValue: Error, NVRAM dataSize %u too small\n", + d1NvdataSensitive->pubInfo.dataSize); + returnCode = TPM_NOSPACE; + } + } + if (returnCode == TPM_SUCCESS) { + /* c. If D1 -> attributes specifies TPM_NV_PER_WRITEALL */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_WRITEALL) && + /* i. If dataSize != D1 -> dataSize return TPM_NOT_FULLWRITE */ + (data.size != d1NvdataSensitive->pubInfo.dataSize)) { + printf("TPM_Process_NVWriteValue: Error, Must write full %u\n", + d1NvdataSensitive->pubInfo.dataSize); + returnCode = TPM_NOT_FULLWRITE; + } + } + if (returnCode == TPM_SUCCESS) { + /* not GPIO */ + if (!isGPIO) { + /* wearout optimization, don't write if the data is the same */ + irc = memcmp((d1NvdataSensitive->data) + offset, data.buffer, data.size); + if (irc != 0) { + printf("TPM_Process_NVWriteValue: Copying data\n"); + /* d. Write the new value into the NV storage area */ + memcpy((d1NvdataSensitive->data) + offset, data.buffer, data.size); + /* must write TPM_PERMANENT_DATA back to NVRAM, set this flag after + structure is written */ + writeAllNV = TRUE; + } + else { + printf("TPM_Process_NVWriteValue: Same data, no copy\n"); + } + } + /* GPIO */ + else { + printf("TPM_Process_NVWriteValue: Writing GPIO\n"); + returnCode = TPM_IO_GPIO_Write(nvIndex, + data.size, + data.buffer, + tpm_state->tpm_number); + } + } + } + } + /* DIR write */ + if ((returnCode == TPM_SUCCESS) && !done && dir) { + /* For TPM_NV_INDEX_DIR, the ordinal MUST NOT set an error code for the "if dataSize = 0" + action. However, the flags set in this case are not applicable to the DIR. */ + if (data.size != 0) { + /* DIR is hard coded as a TPM_DIRVALUE array, TPM_NV_WRITEALL is implied */ + if (returnCode == TPM_SUCCESS) { + if ((offset != 0) || (data.size != TPM_DIGEST_SIZE)) { + printf("TPM_Process_NVWriteValue: Error, Must write full DIR %u\n", + TPM_DIGEST_SIZE); + returnCode = TPM_NOT_FULLWRITE; + } + } + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_NVWriteValue: Copying data\n"); + memcpy(tpm_state->tpm_permanent_data.authDIR, data.buffer, TPM_DIGEST_SIZE); + writeAllNV = TRUE; + } + } + } + if ((returnCode == TPM_SUCCESS) && !done && !dir) { + /* 16. Set D1 -> bReadSTClear to FALSE (unlocked by a successful write) */ + d1NvdataSensitive->pubInfo.bReadSTClear = FALSE; + } + /* 15.d Write the new value into the NV storage area */ + if (writeAllNV) { + printf("TPM_Process_NVWriteValue: Writing data to NVRAM\n"); + /* NOTE Don't do this step until just before the serialization */ + /* e. If NV1_INCREMENTED is TRUE */ + if (nv1Incremented) { + /* i. Set TPM_PERMANENT_DATA -> noOwnerNVWrite to NV1 */ + tpm_state->tpm_permanent_data.noOwnerNVWrite = nv1; + } + } + returnCode = TPM_PermanentAll_NVStore(tpm_state, + writeAllNV, + returnCode); + /* + response + */ + /* standard response: tag, (dummy) paramSize, returnCode. Failure is fatal. */ + if (rcf == 0) { + printf("TPM_Process_NVWriteValue: Ordinal returnCode %08x %u\n", + returnCode, returnCode); + rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode); + } + /* success response, append the rest of the parameters. */ + if (rcf == 0) { + if (returnCode == TPM_SUCCESS) { + /* checkpoint the beginning of the outParam's */ + outParamStart = response->buffer_current - response->buffer; + /* checkpoint the end of the outParam's */ + outParamEnd = response->buffer_current - response->buffer; + } + /* digest the above the line output parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetOutParamDigest(outParamDigest, /* output */ + auditStatus, /* input audit status */ + transportEncrypt, + tag, + returnCode, + ordinal, /* command ordinal */ + response->buffer + outParamStart, /* start */ + outParamEnd - outParamStart); /* length */ + } + /* calculate and set the below the line parameters */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_AuthParams_Set(response, + *hmacKey, /* owner HMAC key */ + auth_session_data, + outParamDigest, + nonceOdd, + continueAuthSession); + } + /* audit if required */ + if ((returnCode == TPM_SUCCESS) && auditStatus) { + returnCode = TPM_ProcessAudit(tpm_state, + transportEncrypt, + inParamDigest, + outParamDigest, + ordinal); + } + /* adjust the initial response */ + rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state); + } + /* if there was an error, or continueAuthSession is FALSE, terminate the session */ + if (((rcf != 0) || + ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) || + !continueAuthSession) && + authHandleValid) { + TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle); + } + /* + cleanup + */ + TPM_SizedBuffer_Delete(&data); /* @1 */ + return rcf; +} + +/* 20.3 TPM_NV_WriteValueAuth rev 87 + + This command writes to a previously defined area. The area must require authorization to + write. This command is for using when authorization other than the owner authorization is to be + used. Otherwise, you should use TPM_NV_WriteValue +*/ + +TPM_RESULT TPM_Process_NVWriteValueAuth(tpm_state_t *tpm_state, + TPM_STORE_BUFFER *response, + TPM_TAG tag, + uint32_t paramSize, + TPM_COMMAND_CODE ordinal, + unsigned char *command, + TPM_TRANSPORT_INTERNAL *transportInternal) +{ + TPM_RESULT rcf = 0; /* fatal error precluding response */ + TPM_RESULT returnCode = TPM_SUCCESS; /* command return code */ + int irc; + + /* input parameters */ + TPM_NV_INDEX nvIndex; /* The index of the area to set */ + uint32_t offset = 0; /* The offset into the chunk */ + TPM_SIZED_BUFFER data; /* The data to set the area to */ + TPM_AUTHHANDLE authHandle; /* The authorization handle used for NV element + authorization */ + TPM_NONCE nonceOdd; /* Nonce generated by system associated with authHandle */ + TPM_BOOL continueAuthSession = TRUE; /* The continue use flag for the authorization + handle */ + TPM_AUTHDATA authValue; /* HMAC key: NV element auth value */ + + /* processing parameters */ + unsigned char * inParamStart; /* starting point of inParam's */ + unsigned char * inParamEnd; /* ending point of inParam's */ + TPM_DIGEST inParamDigest; + TPM_BOOL auditStatus = FALSE; /* audit the ordinal */ + TPM_BOOL transportEncrypt = FALSE; /* wrapped in encrypted transport + session */ + TPM_BOOL authHandleValid = FALSE; + TPM_AUTH_SESSION_DATA *auth_session_data = NULL; /* session data for authHandle */ + TPM_SECRET *hmacKey = NULL; + TPM_NV_DATA_SENSITIVE *d1NvdataSensitive; + uint32_t s1Last; + TPM_BOOL writeAllNV = FALSE; /* flag to write back NV */ + TPM_BOOL physicalPresence; + TPM_BOOL isGPIO; + + /* output parameters */ + uint32_t outParamStart; /* starting point of outParam's */ + uint32_t outParamEnd; /* ending point of outParam's */ + TPM_DIGEST outParamDigest; + + printf("TPM_Process_NVWriteValueAuth: Ordinal Entry\n"); + TPM_SizedBuffer_Init(&data); /* freed @1 */ + /* + get inputs + */ + /* save the starting point of inParam's for authorization and auditing */ + inParamStart = command; + /* get nvIndex parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&nvIndex, &command, ¶mSize); + } + /* get offset parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&offset, &command, ¶mSize); + } + /* get data parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_SizedBuffer_Load(&data, &command, ¶mSize); + } + /* save the ending point of inParam's for authorization and auditing */ + inParamEnd = command; + /* digest the input parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetInParamDigest(inParamDigest, /* output */ + &auditStatus, /* output */ + &transportEncrypt, /* output */ + tpm_state, + tag, + ordinal, + inParamStart, + inParamEnd, + transportInternal); + } + /* check state */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL); + } + /* check tag */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckRequestTag1(tag); + } + /* get the 'below the line' authorization parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_AuthParams_Get(&authHandle, + &authHandleValid, + nonceOdd, + &continueAuthSession, + authValue, + &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + if (paramSize != 0) { + printf("TPM_Process_NVWriteValueAuth: Error, command has %u extra bytes\n", + paramSize); + returnCode = TPM_BAD_PARAM_SIZE; + } + } + /* do not terminate sessions if the command did not parse correctly */ + if (returnCode != TPM_SUCCESS) { + authHandleValid = FALSE; + } + /* + Processing + */ + /* determine whether the nvIndex is legal GPIO space */ + if (returnCode == 0) { + returnCode = TPM_NVDataSensitive_IsGPIO(&isGPIO, nvIndex); + } + /* 1. Locate and set D1 to the TPM_NV_DATA_AREA that corresponds to nvIndex, return TPM_BADINDEX + on error */ + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_NVWriteValueAuth: index %08x offset %u dataSize %u\n", + nvIndex, offset, data.size); + TPM_PrintFourLimit("TPM_Process_NVWriteValueAuth: data", data.buffer, data.size); + printf("TPM_Process_NVWriteValueAuth: Loading data from NVRAM\n"); + returnCode = TPM_NVIndexEntries_GetEntry(&d1NvdataSensitive, + &(tpm_state->tpm_nv_index_entries), + nvIndex); + if (returnCode != 0) { + printf("TPM_Process_NVWriteValueAuth: Error, NV index %08x not found\n", nvIndex); + } + } + /* 2. If D1 -> attributes does not specify TPM_NV_PER_AUTHWRITE then return TPM_AUTH_CONFLICT */ + if (returnCode == TPM_SUCCESS) { + if (!(d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_AUTHWRITE)) { + printf("TPM_Process_NVWriteValueAuth: Error, authorization conflict\n"); + returnCode = TPM_AUTH_CONFLICT; + } + } + /* 3. Validate authValue using D1 -> authValue, return TPM_AUTHFAIL on error */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_AuthSessions_GetData(&auth_session_data, + &hmacKey, + tpm_state, + authHandle, + TPM_PID_NONE, + TPM_ET_NV, + ordinal, + NULL, + &(d1NvdataSensitive->authValue), /* OIAP */ + d1NvdataSensitive->digest); /* OSAP */ + } + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Authdata_Check(tpm_state, + *hmacKey, /* HMAC key */ + inParamDigest, + auth_session_data, /* authorization session */ + nonceOdd, /* Nonce generated by system + associated with authHandle */ + continueAuthSession, + authValue); /* Authorization digest for input */ + } + /* 4. Check that D1 -> pcrInfoWrite -> localityAtRelease for TPM_STANY_DATA -> localityModifier + is TRUE */ + /* a. For example if TPM_STANY_DATA -> localityModifier was 2 then D1 -> pcrInfo -> + localityAtRelease -> TPM_LOC_TWO would have to be TRUE */ + /* b. On error return TPM_BAD_LOCALITY */ + /* NOTE Done by TPM_PCRInfoShort_CheckDigest() */ + /* 5. If D1 -> attributes specifies TPM_NV_PER_PPWRITE then validate physical presence is + asserted if not return TPM_BAD_PRESENCE */ + if (returnCode == TPM_SUCCESS) { + if (d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_PPWRITE) { + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Global_GetPhysicalPresence(&physicalPresence, tpm_state); + } + if (returnCode == TPM_SUCCESS) { + if (!physicalPresence) { + printf("TPM_Process_NVWriteValueAuth: Error, physicalPresence is FALSE\n"); + returnCode = TPM_BAD_PRESENCE; + } + } + } + } + /* 6. If D1 -> pcrInfoWrite -> pcrSelection specifies a selection of PCR */ + /* a. Create P1 a composite hash of the PCR specified by D1 -> pcrInfoWrite */ + /* b. Compare P1 to digestAtRelease return TPM_WRONGPCRVAL on mismatch */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_PCRInfoShort_CheckDigest(&(d1NvdataSensitive->pubInfo.pcrInfoWrite), + tpm_state->tpm_stclear_data.PCRS, + tpm_state->tpm_stany_flags.localityModifier); + } + if (returnCode == TPM_SUCCESS) { + /* 7. If D1 -> attributes specifies TPM_NV_PER_WRITEDEFINE */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_WRITEDEFINE) && + /* a. If D1 -> bWriteDefine is TRUE return TPM_AREA_LOCKED */ + (d1NvdataSensitive->pubInfo.bWriteDefine)) { + printf("TPM_Process_NVWriteValueAuth: Error, area locked by bWriteDefine\n"); + returnCode = TPM_AREA_LOCKED; + } + } + if (returnCode == TPM_SUCCESS) { + /* 8. If D1 -> attributes specifies TPM_NV_PER_GLOBALLOCK */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_GLOBALLOCK) && + /* a. If TPM_STCLEAR_FLAGS -> bGlobalLock is TRUE return TPM_AREA_LOCKED */ + (tpm_state->tpm_stclear_flags.bGlobalLock)) { + printf("TPM_Process_NVWriteValueAuth: Error, area locked by bGlobalLock\n"); + returnCode = TPM_AREA_LOCKED; + } + } + if (returnCode == TPM_SUCCESS) { + /* 9. If D1 -> attributes specifies TPM_NV_PER_WRITE_STCLEAR */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_WRITE_STCLEAR) && + /* a. If D1 -> bWriteSTClear is TRUE return TPM_AREA_LOCKED */ + (d1NvdataSensitive->pubInfo.bWriteSTClear)) { + printf("TPM_Process_NVWriteValueAuth: Error, area locked by bWriteSTClear\n"); + returnCode = TPM_AREA_LOCKED; + } + } + if (returnCode == TPM_SUCCESS) { + /* 10. If dataSize = 0 then */ + if (data.size == 0) { + printf("TPM_Process_NVWriteValueAuth: " + "dataSize 0, setting bWriteSTClear, bWriteDefine\n"); + /* a. Set D1 -> bWriteSTClear to TRUE */ + d1NvdataSensitive->pubInfo.bWriteSTClear = TRUE; + /* b. Set D1 -> bWriteDefine to TRUE */ + if (!d1NvdataSensitive->pubInfo.bWriteDefine) { /* save wearout, only write if + FALSE */ + d1NvdataSensitive->pubInfo.bWriteDefine = TRUE; + /* must write TPM_PERMANENT_DATA back to NVRAM, set this flag after structure is + written */ + writeAllNV = TRUE; + } + } + /* 11. Else (if dataSize is not 0) */ + else { + if (returnCode == TPM_SUCCESS) { + /* a. Set S1 to offset + dataSize */ + s1Last = offset + data.size; /* set to last data point */ + /* b. If S1 > D1 -> dataSize return TPM_NOSPACE */ + if (s1Last > d1NvdataSensitive->pubInfo.dataSize) { + printf("TPM_Process_NVWriteValueAuth: Error, NVRAM dataSize %u\n", + d1NvdataSensitive->pubInfo.dataSize); + returnCode = TPM_NOSPACE; + } + } + if (returnCode == TPM_SUCCESS) { + /* c. If D1 -> attributes specifies TPM_PER_WRITEALL */ + if ((d1NvdataSensitive->pubInfo.permission.attributes & TPM_NV_PER_WRITEALL) && + /* i. If dataSize != D1 -> dataSize return TPM_NOT_FULLWRITE */ + (data.size != d1NvdataSensitive->pubInfo.dataSize)) { + printf("TPM_Process_NVWriteValueAuth: Error, Must write all %u\n", + d1NvdataSensitive->pubInfo.dataSize); + returnCode = TPM_NOT_FULLWRITE; + } + } + if (returnCode == TPM_SUCCESS) { + /* not GPIO */ + if (!isGPIO) { + /* wearout optimization, don't write if the data is the same */ + irc = memcmp((d1NvdataSensitive->data) + offset, data.buffer, data.size); + if (irc != 0) { + /* d. Write the new value into the NV storage area */ + printf("TPM_Process_NVWriteValueAuth: Copying data\n"); + memcpy((d1NvdataSensitive->data) + offset, data.buffer, data.size); + /* must write TPM_PERMANENT_DATA back to NVRAM, set this flag after + structure is written */ + writeAllNV = TRUE; + } + else { + printf("TPM_Process_NVWriteValueAuth: Same data, no copy\n"); + } + } + /* GPIO */ + else { + printf("TPM_Process_NVWriteValueAuth: Writing GPIO\n"); + returnCode = TPM_IO_GPIO_Write(nvIndex, + data.size, + data.buffer, + tpm_state->tpm_number); + } + } + } + } + /* 12. Set D1 -> bReadSTClear to FALSE */ + if (returnCode == TPM_SUCCESS) { + d1NvdataSensitive->pubInfo.bReadSTClear = FALSE; + printf("TPM_Process_NVWriteValueAuth: Writing data to NVRAM\n"); + } + /* write back TPM_PERMANENT_DATA if required */ + returnCode = TPM_PermanentAll_NVStore(tpm_state, + writeAllNV, + returnCode); + /* + response + */ + /* standard response: tag, (dummy) paramSize, returnCode. Failure is fatal. */ + if (rcf == 0) { + printf("TPM_Process_NVWriteValueAuth: Ordinal returnCode %08x %u\n", + returnCode, returnCode); + rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode); + } + /* success response, append the rest of the parameters. */ + if (rcf == 0) { + if (returnCode == TPM_SUCCESS) { + /* checkpoint the beginning of the outParam's */ + outParamStart = response->buffer_current - response->buffer; + /* checkpoint the end of the outParam's */ + outParamEnd = response->buffer_current - response->buffer; + } + /* digest the above the line output parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetOutParamDigest(outParamDigest, /* output */ + auditStatus, /* input audit status */ + transportEncrypt, + tag, + returnCode, + ordinal, /* command ordinal */ + response->buffer + outParamStart, /* start */ + outParamEnd - outParamStart); /* length */ + } + /* calculate and set the below the line parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_AuthParams_Set(response, + *hmacKey, /* HMAC key */ + auth_session_data, + outParamDigest, + nonceOdd, + continueAuthSession); + } + /* audit if required */ + if ((returnCode == TPM_SUCCESS) && auditStatus) { + returnCode = TPM_ProcessAudit(tpm_state, + transportEncrypt, + inParamDigest, + outParamDigest, + ordinal); + } + /* adjust the initial response */ + rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state); + } + /* if there was an error, or continueAuthSession is FALSE, terminate the session */ + if (((rcf != 0) || + ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) || + !continueAuthSession) && + authHandleValid) { + TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle); + } + /* + cleanup + */ + TPM_SizedBuffer_Delete(&data); /* @1 */ + return rcf; +} + +/* 20.1 TPM_NV_DefineSpace rev 109 + + This establishes the space necessary for the indicated index. The definition will include the + access requirements for writing and reading the area. + + Previously defined space at the index and new size is non-zero (and space is available, + etc.) -> redefine the index + + No previous space at the index and new size is non-zero (and space is available, etc.)-> + define the index + + Previously defined space at the index and new size is 0 -> delete the index + + No previous space at the index and new size is 0 -> error + + The space definition size does not include the area needed to manage the space. + + Setting TPM_PERMANENT_FLAGS -> nvLocked TRUE when it is already TRUE is not an error. + + For the case where pubInfo -> dataSize is 0, pubInfo -> pcrInfoRead and pubInfo -> pcrInfoWrite + are not used. However, since the general principle is to validate parameters before changing + state, the TPM SHOULD parse pubInfo completely before invalidating the data area. +*/ + +TPM_RESULT TPM_Process_NVDefineSpace(tpm_state_t *tpm_state, + TPM_STORE_BUFFER *response, + TPM_TAG tag, + uint32_t paramSize, + TPM_COMMAND_CODE ordinal, + unsigned char *command, + TPM_TRANSPORT_INTERNAL *transportInternal) +{ + TPM_RESULT rcf = 0; /* fatal error precluding response */ + TPM_RESULT returnCode = TPM_SUCCESS; /* command return code */ + + /* input parameters */ + TPM_NV_INDEX newNVIndex = TPM_NV_INDEX_LOCK; /* from input TPM_NV_DATA_PUBLIC, initialize + to silence compiler */ + TPM_ENCAUTH encAuth; /* The encrypted AuthData, only valid if the attributes + require subsequent authorization */ + TPM_AUTHHANDLE authHandle; /* The authorization session handle used for ownerAuth */ + TPM_NONCE nonceOdd; /* Nonce generated by system associated with authHandle */ + TPM_BOOL continueAuthSession = TRUE; /* The continue use flag for the authorization + session handle */ + TPM_AUTHDATA ownerAuth; /* The authorization session digest HMAC key: ownerAuth */ + + /* processing parameters */ + unsigned char * inParamStart; /* starting point of inParam's */ + unsigned char * inParamEnd; /* ending point of inParam's */ + TPM_DIGEST inParamDigest; + TPM_BOOL auditStatus; /* audit the ordinal */ + TPM_BOOL transportEncrypt; /* wrapped in encrypted transport session */ + TPM_BOOL authHandleValid = FALSE; + TPM_AUTH_SESSION_DATA *auth_session_data = NULL; /* session data for authHandle */ + TPM_SECRET *hmacKey = NULL; + TPM_BOOL ignore_auth = FALSE; + TPM_BOOL writeAllNV = FALSE; /* flag to write back NV */ + TPM_BOOL done = FALSE; /* processing is done */ + TPM_DIGEST a1Auth; + TPM_NV_DATA_SENSITIVE *d1_old; /* possibly old data */ + TPM_NV_DATA_SENSITIVE *d1_new = NULL; /* new data */ + TPM_NV_DATA_PUBLIC *pubInfo = NULL; /* new, initialize to silence + compiler */ + uint32_t freeSpace; /* free space after allocating new + index */ + TPM_BOOL writeLocalities = FALSE; + TPM_BOOL physicalPresence; + TPM_BOOL foundOld = TRUE; /* index already exists, initialize + to silence compiler */ + uint32_t nv1 = tpm_state->tpm_permanent_data.noOwnerNVWrite; + /* temp for noOwnerNVWrite, initialize to silence + compiler */ + TPM_BOOL nv1Incremented = FALSE; /* flag that nv1 was incremented */ + + /* output parameters */ + uint32_t outParamStart; /* starting point of outParam's */ + uint32_t outParamEnd; /* ending point of outParam's */ + TPM_DIGEST outParamDigest; + + printf("TPM_Process_NVDefineSpace: Ordinal Entry\n"); + /* This design gets a slot in the TPM_NV_INDEX_ENTRIES array, either an existing empty one or a + newly re'allocated one. The incoming parameters are deserialized directly into the slot. + + On success, the slot remains. On failure, the slot is deleted. There is no need to remove + the slot from the array. It can remain for the next call. + */ + /* + get inputs + */ + /* save the starting point of inParam's for authorization and auditing */ + inParamStart = command; + /* get or create a free index in the TPM_NV_INDEX_ENTRIES array */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_NVIndexEntries_GetFreeEntry(&d1_new, &(tpm_state->tpm_nv_index_entries)); + } + /* get pubInfo parameter */ + if (returnCode == TPM_SUCCESS) { + pubInfo = &(d1_new->pubInfo); /* pubInfo is an input parameter */ + returnCode = TPM_NVDataPublic_Load(pubInfo, + &command, ¶mSize, + FALSE); /* not optimized for digestAtRelease */ + /* The NV index cannot be immediately deserialized in the slot, or the function will think + that the index already exists. Therefore, the nvIndex parameter is saved and temporarily + set to empty until the old slot is deleted. */ + newNVIndex = pubInfo->nvIndex; /* save the possibly new index */ + pubInfo->nvIndex = TPM_NV_INDEX_LOCK; /* temporarily mark unused */ + } + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_NVDefineSpace: index %08x permission %08x dataSize %08x\n", + newNVIndex, pubInfo->permission.attributes, pubInfo->dataSize); + TPM_PCRInfo_Trace("TPM_Process_NVDefineSpace: pcrInfoRead", + pubInfo->pcrInfoRead.pcrSelection, + pubInfo->pcrInfoRead.digestAtRelease); + TPM_PCRInfo_Trace("TPM_Process_NVDefineSpace: pcrInfoWrite", + pubInfo->pcrInfoWrite.pcrSelection, + pubInfo->pcrInfoWrite.digestAtRelease); + /* get encAuth parameter */ + returnCode = TPM_Secret_Load(encAuth, &command, ¶mSize); + } + /* save the ending point of inParam's for authorization and auditing */ + inParamEnd = command; + /* digest the input parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetInParamDigest(inParamDigest, /* output */ + &auditStatus, /* output */ + &transportEncrypt, /* output */ + tpm_state, + tag, + ordinal, + inParamStart, + inParamEnd, + transportInternal); + } + /* check state */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALLOW_NO_OWNER | TPM_CHECK_NV_NOAUTH); + } + /* check tag */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckRequestTag10(tag); + } + /* get the optional 'below the line' authorization parameters */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_AuthParams_Get(&authHandle, + &authHandleValid, + nonceOdd, + &continueAuthSession, + ownerAuth, + &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + if (paramSize != 0) { + printf("TPM_Process_NVDefineSpace: Error, command has %u extra bytes\n", + paramSize); + returnCode = TPM_BAD_PARAM_SIZE; + } + } + /* do not terminate sessions if the command did not parse correctly */ + if (returnCode != TPM_SUCCESS) { + authHandleValid = FALSE; + } + /* + Processing + */ + /* 1. If pubInfo -> nvIndex == TPM_NV_INDEX_LOCK and tag = TPM_TAG_RQU_COMMAND */ + if ((returnCode == TPM_SUCCESS) && + (newNVIndex == TPM_NV_INDEX_LOCK) && + (tag == TPM_TAG_RQU_COMMAND)) { + /* a. If pubInfo -> dataSize is not 0, the command MAY return TPM_BADINDEX. */ + if (pubInfo->dataSize != 0) { + printf("TPM_Process_NVDefineSpace: Error, TPM_NV_INDEX_LOCK dataSize %u\n", + pubInfo->dataSize); + returnCode = TPM_BADINDEX; + } + else { + /* b. Set TPM_PERMANENT_FLAGS -> nvLocked to TRUE */ + /* writeAllNV set to TRUE if nvLocked is being set, not if already set */ + printf("TPM_Process_NVDefineSpace: Setting nvLocked\n"); + TPM_SetCapability_Flag(&writeAllNV, /* altered */ + &(tpm_state->tpm_permanent_flags.nvLocked ), /* flag */ + TRUE); /* value */ + } + /* c. Return TPM_SUCCESS */ + done = TRUE; + } + /* 2. If TPM_PERMANENT_FLAGS -> nvLocked is FALSE then all authorization checks except for the + Max NV writes are ignored */ + /* a. Ignored checks include physical presence, owner authorization, 'D' bit check, bGlobalLock, + no authorization with a TPM owner present, bWriteSTClear, the check that pubInfo -> dataSize + is 0 in Action 5.c. (the no-authorization case), disabled and deactivated. */ + /* NOTE: The disabled and deactivated flags are conditionally checked by TPM_CheckState() using + the TPM_CHECK_NV_NOAUTH flag */ + /* ii. The check that pubInfo -> dataSize is 0 is still enforced in Action 6.f. (returning after + deleting a previously defined storage area) and Action 9.f. (not allowing a space of size 0 + to be defined). */ + /* i.If ownerAuth is present, the TPM MAY check the authorization HMAC. */ + if (returnCode == TPM_SUCCESS) { + if (!(tpm_state->tpm_permanent_flags.nvLocked)) { + printf("TPM_Process_NVDefineSpace: nvLocked FALSE, ignoring authorization\n"); + ignore_auth = TRUE; + } + } + /* b.The check for pubInfo -> nvIndex == 0 in Action 3. is not ignored. */ + if ((returnCode == TPM_SUCCESS) && !done) { + if (newNVIndex == TPM_NV_INDEX0) { + printf("TPM_Process_NVDefineSpace: Error, bad index %08x\n", newNVIndex); + returnCode = TPM_BADINDEX; + } + } + /* 3. If pubInfo -> nvIndex has the D bit (bit 28) set to a 1 or pubInfo -> nvIndex == 0 then */ + if ((returnCode == TPM_SUCCESS) && !done && !ignore_auth) { + /* b. The D bit specifies an index value that is set in manufacturing and can never be + deleted or added to the TPM */ + if (newNVIndex & TPM_NV_INDEX_D_BIT) { + /* c. Index value of 0 is reserved and cannot be defined */ + /* a. Return TPM_BADINDEX */ + printf("TPM_Process_NVDefineSpace: Error, bad index %08x\n", newNVIndex); + returnCode = TPM_BADINDEX; + } + } + /* 4. If tag = TPM_TAG_RQU_AUTH1_COMMAND then */ + /* b. authHandle session type MUST be OSAP */ + /* must get the HMAC key for the response even if ignore_auth is TRUE */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_AuthSessions_GetData(&auth_session_data, + &hmacKey, + tpm_state, + authHandle, + TPM_PID_OSAP, + TPM_ET_OWNER, + ordinal, + NULL, + NULL, + tpm_state->tpm_permanent_data.ownerAuth); + } + /* a. The TPM MUST validate the command and parameters using the TPM Owner authentication and + ownerAuth, on error return TPM_AUTHFAIL */ + /* NOTE: This is optional if ignore_auth is TRUE */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND) && !done) { + returnCode = TPM_Authdata_Check(tpm_state, + *hmacKey, /* HMAC key */ + inParamDigest, + auth_session_data, /* authorization session */ + nonceOdd, /* Nonce generated by system + associated with authHandle */ + continueAuthSession, + ownerAuth); /* Authorization digest for input */ + } + /* c. Create A1 by decrypting encAuth according to the ADIP indicated by authHandle. */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND) && !done) { + returnCode = TPM_AuthSessionData_Decrypt(a1Auth, + NULL, + encAuth, + auth_session_data, + NULL, + NULL, + FALSE); /* even and odd */ + } + /* 5. else (not auth1) */ + /* a. Validate the assertion of physical presence. Return TPM_BAD_PRESENCE on error. */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND) && !done && !ignore_auth) { + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Global_GetPhysicalPresence(&physicalPresence, tpm_state); + } + if (returnCode == TPM_SUCCESS) { + if (!physicalPresence) { + printf("TPM_Process_NVDefineSpace: Error, physicalPresence is FALSE\n"); + returnCode = TPM_BAD_PRESENCE; + } + } + } + /* b. If TPM Owner is present then return TPM_OWNER_SET. */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND) && !done && !ignore_auth) { + if (tpm_state->tpm_permanent_data.ownerInstalled) { + printf("TPM_Process_NVDefineSpace: Error, no authorization, but owner installed\n"); + returnCode = TPM_OWNER_SET; + } + } + /* c. If pubInfo -> dataSize is 0 then return TPM_BAD_DATASIZE. Setting the size to 0 represents + an attempt to delete the value without TPM Owner authentication. */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND) && !done && !ignore_auth) { + if (pubInfo->dataSize == 0) { + printf("TPM_Process_NVDefineSpace: Error, no owner authorization and dataSize 0\n"); + returnCode = TPM_BAD_DATASIZE; + } + } + /* d. Validate max NV writes without an owner */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND) && !done) { + /* i. Set NV1 to TPM_PERMANENT_DATA -> noOwnerNVWrite */ + nv1 = tpm_state->tpm_permanent_data.noOwnerNVWrite; + /* ii. Increment NV1 by 1 */ + nv1++; + /* iii. If NV1 > TPM_MAX_NV_WRITE_NOOWNER return TPM_MAXNVWRITES */ + if (nv1 > TPM_MAX_NV_WRITE_NOOWNER) { + printf("TPM_Process_NVDefineSpace: Error, max NV writes %d w/o owner reached\n", + tpm_state->tpm_permanent_data.noOwnerNVWrite); + returnCode = TPM_MAXNVWRITES; + } + else { + /* iv. Set NV1_INCREMENTED to TRUE */ + nv1Incremented = TRUE; + } + } + /* e. Set A1 to encAuth. There is no nonce or authorization to create the encryption string, + hence the AuthData value is passed in the clear */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND) && !done) { + TPM_Digest_Copy(a1Auth, encAuth); + } + /* 6. If pubInfo -> nvIndex points to a valid previously defined storage area then */ + /* 6.a. Map D1 a TPM_NV_DATA_SENSITIVE to the storage area */ + if ((returnCode == TPM_SUCCESS) && !done) { + printf("TPM_Process_NVDefineSpace: Loading existing NV index %08x\n", newNVIndex); + returnCode = TPM_NVIndexEntries_GetEntry(&d1_old, + &(tpm_state->tpm_nv_index_entries), + newNVIndex); + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_NVDefineSpace: NV index %08x exists\n", newNVIndex); + foundOld = TRUE; + } + else if (returnCode == TPM_BADINDEX) { + returnCode = TPM_SUCCESS; /* non-existent index is not an error */ + foundOld = FALSE; + printf("TPM_Process_NVDefineSpace: Index %08x is new\n", newNVIndex); + } + } + if ((returnCode == TPM_SUCCESS) && !done && !ignore_auth && foundOld) { + /* 6.b. If D1 -> attributes specifies TPM_NV_PER_GLOBALLOCK then */ + if (d1_old->pubInfo.permission.attributes & TPM_NV_PER_GLOBALLOCK) { + /* i. If TPM_STCLEAR_FLAGS -> bGlobalLock is TRUE then return TPM_AREA_LOCKED */ + if (tpm_state->tpm_stclear_flags.bGlobalLock) { + printf("TPM_Process_NVDefineSpace: Error, index %08x (bGlobalLock) locked\n", + newNVIndex); + returnCode = TPM_AREA_LOCKED; + } + } + } + if ((returnCode == TPM_SUCCESS) && !done && !ignore_auth && foundOld) { + /* 6.c. If D1 -> attributes specifies TPM_NV_PER_WRITE_STCLEAR */ + if (d1_old->pubInfo.permission.attributes & TPM_NV_PER_WRITE_STCLEAR) { + /* i. If D1 -> pubInfo -> bWriteSTClear is TRUE then return TPM_AREA_LOCKED */ + if (d1_old->pubInfo.bWriteSTClear) { + printf("TPM_Process_NVDefineSpace: Error, area locked by bWriteSTClear\n"); + returnCode = TPM_AREA_LOCKED; + } + } + } + /* NOTE Changed the Action order. Must terminate auth sessions while the old index digest + still exists. + */ + /* 6.f. The TPM invalidates authorization sessions */ + /* i. MUST invalidate all authorization sessions associated with D1 */ + /* ii. MAY invalidate any other authorization session */ + if ((returnCode == TPM_SUCCESS) && !done && foundOld) { + TPM_AuthSessions_TerminateEntity(&continueAuthSession, + authHandle, + tpm_state->tpm_stclear_data.authSessions, + TPM_ET_NV, + &(d1_old->digest)); + } + if ((returnCode == TPM_SUCCESS) && !done && foundOld) { + /* 6.d. Invalidate the data area currently pointed to by D1 and ensure that if the area is + reallocated no residual information is left */ + printf("TPM_Process_NVDefineSpace: Deleting index %08x\n", newNVIndex); + TPM_NVDataSensitive_Delete(d1_old); + /* must write deleted space back to NVRAM */ + writeAllNV = TRUE; + /* 6.e. If NV1_INCREMENTED is TRUE */ + /* i. Set TPM_PERMANENT_DATA -> noOwnerNVWrite to NV1 */ + /* NOTE Don't do this step until just before the serialization */ + } + /* g. If pubInfo -> dataSize is 0 then return TPM_SUCCESS */ + if ((returnCode == TPM_SUCCESS) && !done && foundOld) { + if (pubInfo->dataSize == 0) { + printf("TPM_Process_NVDefineSpace: Size 0, done\n"); + done = TRUE; + } + } + /* 7. Parse pubInfo -> pcrInfoRead */ + /* a. Validate pcrInfoRead structure on error return TPM_INVALID_STRUCTURE */ + /* i. Validation includes proper PCR selections and locality selections */ + /* NOTE: Done by TPM_NVDataPublic_Load() */ + /* 8. Parse pubInfo -> pcrInfoWrite */ + /* a. Validate pcrInfoWrite structure on error return TPM_INVALID_STRUCTURE */ + /* i. Validation includes proper PCR selections and locality selections */ + /* NOTE: Done by TPM_NVDataPublic_Load() */ + if ((returnCode == TPM_SUCCESS) && !done) { + /* b. If pcrInfoWrite -> localityAtRelease disallows some localities */ + if (pubInfo->pcrInfoRead.localityAtRelease != TPM_LOC_ALL) { + /* i. Set writeLocalities to TRUE */ + writeLocalities = TRUE; + } + /* c. Else */ + else { + /* i. Set writeLocalities to FALSE */ + writeLocalities = FALSE; + } + } + /* 9. Validate that the attributes are consistent */ + /* a. The TPM SHALL ignore the bReadSTClear, bWriteSTClear and bWriteDefine attributes during + the execution of this command */ + /* b. If TPM_NV_PER_OWNERWRITE is TRUE and TPM_NV_PER_AUTHWRITE is TRUE return TPM_AUTH_CONFLICT + */ + if ((returnCode == TPM_SUCCESS) && !done) { + if ((pubInfo->permission.attributes & TPM_NV_PER_OWNERWRITE) && + (pubInfo->permission.attributes & TPM_NV_PER_AUTHWRITE)) { + printf("TPM_Process_NVDefineSpace: Error, write authorization conflict\n"); + returnCode = TPM_AUTH_CONFLICT; + } + } + /* c. If TPM_NV_PER_OWNERREAD is TRUE and TPM_NV_PER_AUTHREAD is TRUE return TPM_AUTH_CONFLICT + */ + if ((returnCode == TPM_SUCCESS) && !done) { + if ((pubInfo->permission.attributes & TPM_NV_PER_OWNERREAD) && + (pubInfo->permission.attributes & TPM_NV_PER_AUTHREAD)) { + printf("TPM_Process_NVDefineSpace: Error, read authorization conflict\n"); + returnCode = TPM_AUTH_CONFLICT; + } + } + /* d. If TPM_NV_PER_OWNERWRITE and TPM_NV_PER_AUTHWRITE and TPM_NV_PER_WRITEDEFINE and + TPM_NV_PER_PPWRITE and writeLocalities are all FALSE */ + if ((returnCode == TPM_SUCCESS) && !done) { + if (!(pubInfo->permission.attributes & TPM_NV_PER_OWNERWRITE) && + !(pubInfo->permission.attributes & TPM_NV_PER_AUTHWRITE) && + !(pubInfo->permission.attributes & TPM_NV_PER_WRITEDEFINE) && + !(pubInfo->permission.attributes & TPM_NV_PER_PPWRITE) && + !writeLocalities) { + /* i. Return TPM_PER_NOWRITE */ + printf("TPM_Process_NVDefineSpace: Error, no write\n"); + returnCode = TPM_PER_NOWRITE; + } + } + /* e. Validate pubInfo -> nvIndex */ + /* i. Make sure that the index is applicable for this TPM return TPM_BADINDEX on error */ + if ((returnCode == TPM_SUCCESS) && !done) { + returnCode = TPM_NVDataSensitive_IsValidIndex(newNVIndex); + } + /* f. If dataSize is 0 return TPM_BAD_PARAM_SIZE */ + if ((returnCode == TPM_SUCCESS) && !done) { + if (pubInfo->dataSize == 0) { + printf("TPM_Process_NVDefineSpace: Error, New index data size is zero\n"); + returnCode = TPM_BAD_PARAM_SIZE; + } + } + /* 10. Create D1 a TPM_NV_DATA_SENSITIVE structure */ + /* NOTE Created and initialized d1_new directly in the TPM_NV_INDEX_ENTRIES array */ + /* a. Set D1 -> pubInfo to pubInfo */ + /* NOTE deserialized in place */ + if ((returnCode == TPM_SUCCESS) && !done) { + /* b. Set D1 -> authValue to A1 */ + TPM_Digest_Copy(d1_new->authValue, a1Auth); + /* c. Set D1 -> pubInfo -> bReadSTClear to FALSE */ + /* d. Set D1 -> pubInfo -> bWriteSTClear to FALSE */ + /* e. Set D1 -> pubInfo -> bWriteDefine to FALSE */ + pubInfo->bReadSTClear = FALSE; + pubInfo->bWriteSTClear = FALSE; + pubInfo->bWriteDefine = FALSE; + } + if ((returnCode == TPM_SUCCESS) && !done) { + /* assign the empty slot to the index now so it will be counted as used space during the + serialization. */ + pubInfo->nvIndex = newNVIndex; + /* 12.a. Reserve NV space for pubInfo -> dataSize + + NOTE: Action is out or order. Must allocate data space now so that the serialization + inherent in TPM_NVIndexEntries_GetFreeSpace() is valid + */ + returnCode = TPM_Malloc(&(d1_new->data), pubInfo->dataSize); + } + /* 11. Validate that sufficient NV is available to store D1 and pubInfo -> dataSize bytes of + data*/ + /* a. return TPM_NOSPACE if pubInfo -> dataSize is not available in the TPM */ + if ((returnCode == TPM_SUCCESS) && !done) { + printf("TPM_Process_NVDefineSpace: Allocated %u data bytes at %p\n", + pubInfo->dataSize, d1_new->data); + printf("TPM_Process_NVDefineSpace: Checking for %u bytes free space\n", pubInfo->dataSize); + returnCode = TPM_NVIndexEntries_GetFreeSpace(&freeSpace, + &(tpm_state->tpm_nv_index_entries)); + if (returnCode != TPM_SUCCESS) { + printf("TPM_Process_NVDefineSpace: Error: No space\n"); + } + } + /* if there is no free space, free the NV index in-memory structure. This implicitly removes + the entry from tpm_nv_index_entries. If pubInfo -> nvIndex is TPM_NV_INDEX_TRIAL, the entry + should also be removed. */ + if ((returnCode != TPM_SUCCESS) || + (newNVIndex == TPM_NV_INDEX_TRIAL)) { + if (newNVIndex == TPM_NV_INDEX_TRIAL) { + printf("TPM_Process_NVDefineSpace: nvIndex is TPM_NV_INDEX_TRIAL, done\n"); + /* don't actually write, just return success or failure */ + done = TRUE; + } + TPM_NVDataSensitive_Delete(d1_new); + } + /* 12. If pubInfo -> nvIndex is not TPM_NV_INDEX_TRIAL */ + if ((returnCode == TPM_SUCCESS) && !done) { + printf("TPM_Process_NVDefineSpace: Creating index %08x\n", newNVIndex); + /* b. Set all bytes in the newly defined area to 0xFF */ + memset(d1_new->data, 0xff, pubInfo->dataSize); + /* must write newly defined space back to NVRAM */ + writeAllNV = TRUE; + } + if (returnCode == TPM_SUCCESS) { + /* c. If NV1_INCREMENTED is TRUE */ + if (nv1Incremented) { + /* i. Set TPM_PERMANENT_DATA -> noOwnerNVWrite to NV1 */ + tpm_state->tpm_permanent_data.noOwnerNVWrite = nv1; + } + /* 13. Ignore continueAuthSession on input and set to FALSE on output */ + continueAuthSession = FALSE; + } + /* write the file to NVRAM */ + /* write back TPM_PERMANENT_DATA and TPM_PERMANENT_FLAGS if required */ + returnCode = TPM_PermanentAll_NVStore(tpm_state, + writeAllNV, + returnCode); + /* + response + */ + /* standard response: tag, (dummy) paramSize, returnCode. Failure is fatal. */ + if (rcf == 0) { + printf("TPM_Process_NVDefineSpace: Ordinal returnCode %08x %u\n", + returnCode, returnCode); + rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode); + } + /* success response, append the rest of the parameters. */ + if (rcf == 0) { + if (returnCode == TPM_SUCCESS) { + /* checkpoint the beginning of the outParam's */ + outParamStart = response->buffer_current - response->buffer; + /* checkpoint the end of the outParam's */ + outParamEnd = response->buffer_current - response->buffer; + } + /* digest the above the line output parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetOutParamDigest(outParamDigest, /* output */ + auditStatus, /* input audit status */ + transportEncrypt, + tag, + returnCode, + ordinal, /* command ordinal */ + response->buffer + outParamStart, /* start */ + outParamEnd - outParamStart); /* length */ + } + /* calculate and set the below the line parameters */ + if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) { + returnCode = TPM_AuthParams_Set(response, + *hmacKey, /* owner HMAC key */ + auth_session_data, + outParamDigest, + nonceOdd, + continueAuthSession); + } + /* audit if required */ + if ((returnCode == TPM_SUCCESS) && auditStatus) { + returnCode = TPM_ProcessAudit(tpm_state, + transportEncrypt, + inParamDigest, + outParamDigest, + ordinal); + } + /* adjust the initial response */ + rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state); + } + /* if there was an error, or continueAuthSession is FALSE, terminate the session */ + if (((rcf != 0) || + ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) || + !continueAuthSession) && + authHandleValid) { + TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle); + } + /* + cleanup + */ + return rcf; +} + +/* 27.3 DIR commands rev 87 + + The DIR commands are replaced by the NV storage commands. + + The DIR [0] in 1.1 is now TPM_PERMANENT_DATA -> authDIR[0] and is always available for the TPM to + use. It is accessed by DIR commands using dirIndex 0 and by NV commands using nvIndex + TPM_NV_INDEX_DIR. + + If the TPM vendor supports additional DIR registers, the TPM vendor may return errors or provide + vendor specific mappings for those DIR registers to NV storage locations. + + 1. A dirIndex value of 0 MUST corresponds to an NV storage nvIndex value TPM_NV_INDEX_DIR. + + 2. The TPM vendor MAY return errors or MAY provide vendor specific mappings for DIR dirIndex + values greater than 0 to NV storage locations. +*/ + +/* 27.3.1 TPM_DirWriteAuth rev 87 + + The TPM_DirWriteAuth operation provides write access to the Data Integrity Registers. DIRs are + non-volatile memory registers held in a TPM-shielded location. Owner authentication is required + to authorize this action. + + Access is also provided through the NV commands with nvIndex TPM_NV_INDEX_DIR. Owner + authorization is not required when nvLocked is FALSE. + + Version 1.2 requires only one DIR. If the DIR named does not exist, the TPM_DirWriteAuth + operation returns TPM_BADINDEX. +*/ + +TPM_RESULT TPM_Process_DirWriteAuth(tpm_state_t *tpm_state, + TPM_STORE_BUFFER *response, + TPM_TAG tag, + uint32_t paramSize, + TPM_COMMAND_CODE ordinal, + unsigned char *command, + TPM_TRANSPORT_INTERNAL *transportInternal) +{ + TPM_RESULT rcf = 0; /* fatal error precluding response */ + TPM_RESULT returnCode = TPM_SUCCESS; /* command return code */ + + /* input parameters */ + TPM_DIRINDEX dirIndex; /* Index of the DIR */ + TPM_DIRVALUE newContents; /* New value to be stored in named DIR */ + TPM_AUTHHANDLE authHandle; /* The authorization session handle used for command. */ + TPM_NONCE nonceOdd; /* Nonce generated by system associated with authHandle */ + TPM_BOOL continueAuthSession = TRUE; /* The continue use flag for the authorization + session handle */ + TPM_AUTHDATA ownerAuth; /* The authorization session digest for inputs. HMAC key: + ownerAuth. */ + + /* processing parameters */ + unsigned char * inParamStart; /* starting point of inParam's */ + unsigned char * inParamEnd; /* ending point of inParam's */ + TPM_DIGEST inParamDigest; + TPM_BOOL auditStatus; /* audit the ordinal */ + TPM_BOOL transportEncrypt; /* wrapped in encrypted transport session */ + TPM_BOOL authHandleValid = FALSE; + TPM_SECRET *hmacKey; + TPM_AUTH_SESSION_DATA *auth_session_data = NULL; /* session data for authHandle */ + + /* output parameters */ + uint32_t outParamStart; /* starting point of outParam's */ + uint32_t outParamEnd; /* ending point of outParam's */ + TPM_DIGEST outParamDigest; + + printf("TPM_Process_DirWriteAuth: Ordinal Entry\n"); + /* + get inputs + */ + /* save the starting point of inParam's for authorization and auditing */ + inParamStart = command; + /* get dirIndex parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&dirIndex, &command, ¶mSize); + } + /* get newContents parameter */ + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_DirWriteAuth: dirIndex %08x\n", dirIndex); + returnCode = TPM_Digest_Load(newContents, &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + TPM_PrintFour("TPM_Process_DirWriteAuth: newContents", newContents); + } + /* save the ending point of inParam's for authorization and auditing */ + inParamEnd = command; + /* digest the input parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetInParamDigest(inParamDigest, /* output */ + &auditStatus, /* output */ + &transportEncrypt, /* output */ + tpm_state, + tag, + ordinal, + inParamStart, + inParamEnd, + transportInternal); + } + /* check state */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALLOW_NO_OWNER); + } + /* check tag */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckRequestTag1(tag); + } + /* get the 'below the line' authorization parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_AuthParams_Get(&authHandle, + &authHandleValid, + nonceOdd, + &continueAuthSession, + ownerAuth, + &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + if (paramSize != 0) { + printf("TPM_Process_DirWriteAuth: Error, command has %u extra bytes\n", + paramSize); + returnCode = TPM_BAD_PARAM_SIZE; + } + } + /* do not terminate sessions if the command did not parse correctly */ + if (returnCode != TPM_SUCCESS) { + authHandleValid = FALSE; + } + /* + Processing + */ + /* 1. Validate that authHandle contains a TPM Owner AuthData to execute the TPM_DirWriteAuth + command */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_AuthSessions_GetData(&auth_session_data, + &hmacKey, + tpm_state, + authHandle, + TPM_PID_NONE, + TPM_ET_OWNER, + ordinal, + NULL, + &(tpm_state->tpm_permanent_data.ownerAuth), /* OIAP */ + tpm_state->tpm_permanent_data.ownerAuth); /* OSAP */ + } + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Authdata_Check(tpm_state, + *hmacKey, /* HMAC key */ + inParamDigest, + auth_session_data, /* authorization session */ + nonceOdd, /* Nonce generated by system + associated with authHandle */ + continueAuthSession, + ownerAuth); /* Authorization digest for input */ + } + /* 2. Validate that dirIndex points to a valid DIR on this TPM */ + if (returnCode == TPM_SUCCESS) { + if (dirIndex != 0) { /* only one TPM_PERMANENT_DATA -> authDIR */ + printf("TPM_Process_DirWriteAuth: Error, Invalid index %08x\n", dirIndex); + returnCode = TPM_BADINDEX; + } + } + /* 3. Write newContents into the DIR pointed to by dirIndex */ + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_DirWriteAuth: Writing data\n"); + TPM_Digest_Copy(tpm_state->tpm_permanent_data.authDIR, newContents); + /* write back TPM_PERMANENT_DATA */ + returnCode = TPM_PermanentAll_NVStore(tpm_state, + TRUE, + returnCode); + } + /* + response + */ + /* standard response: tag, (dummy) paramSize, returnCode. Failure is fatal. */ + if (rcf == 0) { + printf("TPM_Process_DirWriteAuth: Ordinal returnCode %08x %u\n", + returnCode, returnCode); + rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode); + } + /* success response, append the rest of the parameters. */ + if (rcf == 0) { + if (returnCode == TPM_SUCCESS) { + /* checkpoint the beginning of the outParam's */ + outParamStart = response->buffer_current - response->buffer; + /* checkpoint the end of the outParam's */ + outParamEnd = response->buffer_current - response->buffer; + } + /* digest the above the line output parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetOutParamDigest(outParamDigest, /* output */ + auditStatus, /* input audit status */ + transportEncrypt, + tag, + returnCode, + ordinal, /* command ordinal */ + response->buffer + outParamStart, /* start */ + outParamEnd - outParamStart); /* length */ + } + /* calculate and set the below the line parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_AuthParams_Set(response, + *hmacKey, /* owner HMAC key */ + auth_session_data, + outParamDigest, + nonceOdd, + continueAuthSession); + } + /* audit if required */ + if ((returnCode == TPM_SUCCESS) && auditStatus) { + returnCode = TPM_ProcessAudit(tpm_state, + transportEncrypt, + inParamDigest, + outParamDigest, + ordinal); + } + /* adjust the initial response */ + rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state); + } + /* if there was an error, or continueAuthSession is FALSE, terminate the session */ + if (((rcf != 0) || + ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) || + !continueAuthSession) && + authHandleValid) { + TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle); + } + /* + cleanup + */ + return rcf; +} + +/* 27.3.2 TPM_DirRead rev 87 + + The TPM_DirRead operation provides read access to the DIRs. No authentication is required to + perform this action because typically no cryptographically useful AuthData is available early in + boot. TSS implementors may choose to provide other means of authorizing this action. Version 1.2 + requires only one DIR. If the DIR named does not exist, the TPM_DirRead operation returns + TPM_BADINDEX. +*/ + +TPM_RESULT TPM_Process_DirRead(tpm_state_t *tpm_state, + TPM_STORE_BUFFER *response, + TPM_TAG tag, + uint32_t paramSize, + TPM_COMMAND_CODE ordinal, + unsigned char *command, + TPM_TRANSPORT_INTERNAL *transportInternal) +{ + TPM_RESULT rcf = 0; /* fatal error precluding response */ + TPM_RESULT returnCode = TPM_SUCCESS; /* command return code */ + + /* input parameters */ + TPM_DIRINDEX dirIndex; /* Index of the DIR to be read */ + + /* processing parameters */ + unsigned char * inParamStart; /* starting point of inParam's */ + unsigned char * inParamEnd; /* ending point of inParam's */ + TPM_DIGEST inParamDigest; + TPM_BOOL auditStatus; /* audit the ordinal */ + TPM_BOOL transportEncrypt; /* wrapped in encrypted transport session */ + + /* output parameters */ + uint32_t outParamStart; /* starting point of outParam's */ + uint32_t outParamEnd; /* ending point of outParam's */ + TPM_DIGEST outParamDigest; + + printf("TPM_Process_DirRead: Ordinal Entry\n"); + /* + get inputs + */ + /* save the starting point of inParam's for authorization and auditing */ + inParamStart = command; + /* get dirIndex parameter */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_Load32(&dirIndex, &command, ¶mSize); + } + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_DirRead: dirIndex %08x\n", dirIndex); + } + /* save the ending point of inParam's for authorization and auditing */ + inParamEnd = command; + /* digest the input parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetInParamDigest(inParamDigest, /* output */ + &auditStatus, /* output */ + &transportEncrypt, /* output */ + tpm_state, + tag, + ordinal, + inParamStart, + inParamEnd, + transportInternal); + } + /* check state */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALLOW_NO_OWNER); + } + /* check tag */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_CheckRequestTag0(tag); + } + if (returnCode == TPM_SUCCESS) { + if (paramSize != 0) { + printf("TPM_Process_DirRead: Error, command has %u extra bytes\n", + paramSize); + returnCode = TPM_BAD_PARAM_SIZE; + } + } + /* + Processing + */ + /* 1. Validate that dirIndex points to a valid DIR on this TPM */ + if (returnCode == TPM_SUCCESS) { + if (dirIndex != 0) { /* only one TPM_PERMANENT_DATA -> authDIR */ + printf("TPM_Process_DirRead: Error, Invalid index %08x\n", dirIndex); + returnCode = TPM_BADINDEX; + } + } + /* 2. Return the contents of the DIR in dirContents */ + if (returnCode == TPM_SUCCESS) { + printf("TPM_Process_DirRead: Reading data\n"); + TPM_PrintFour("TPM_Process_DirRead:", tpm_state->tpm_permanent_data.authDIR); + } + /* + response + */ + /* standard response: tag, (dummy) paramSize, returnCode. Failure is fatal. */ + if (rcf == 0) { + printf("TPM_Process_DirRead: Ordinal returnCode %08x %u\n", + returnCode, returnCode); + rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode); + } + /* success response, append the rest of the parameters. */ + if (rcf == 0) { + if (returnCode == TPM_SUCCESS) { + /* checkpoint the beginning of the outParam's */ + outParamStart = response->buffer_current - response->buffer; + /* append dirContents */ + returnCode = TPM_Digest_Store(response, tpm_state->tpm_permanent_data.authDIR); + /* checkpoint the end of the outParam's */ + outParamEnd = response->buffer_current - response->buffer; + } + /* digest the above the line output parameters */ + if (returnCode == TPM_SUCCESS) { + returnCode = TPM_GetOutParamDigest(outParamDigest, /* output */ + auditStatus, /* input audit status */ + transportEncrypt, + tag, + returnCode, + ordinal, /* command ordinal */ + response->buffer + outParamStart, /* start */ + outParamEnd - outParamStart); /* length */ + } + /* audit if required */ + if ((returnCode == TPM_SUCCESS) && auditStatus) { + returnCode = TPM_ProcessAudit(tpm_state, + transportEncrypt, + inParamDigest, + outParamDigest, + ordinal); + } + /* adjust the initial response */ + rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state); + } + /* + cleanup + */ + return rcf; +} |