diff options
Diffstat (limited to 'stubdom/vtpmmgr/vtpm_storage.c')
-rw-r--r-- | stubdom/vtpmmgr/vtpm_storage.c | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/stubdom/vtpmmgr/vtpm_storage.c b/stubdom/vtpmmgr/vtpm_storage.c new file mode 100644 index 0000000000..abb0dba297 --- /dev/null +++ b/stubdom/vtpmmgr/vtpm_storage.c @@ -0,0 +1,794 @@ +/* + * Copyright (c) 2010-2012 United States Government, as represented by + * the Secretary of Defense. All rights reserved. + * + * THIS SOFTWARE AND ITS DOCUMENTATION ARE PROVIDED AS IS AND WITHOUT + * ANY EXPRESS OR IMPLIED WARRANTIES WHATSOEVER. ALL WARRANTIES + * INCLUDING, BUT NOT LIMITED TO, PERFORMANCE, MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT ARE HEREBY + * DISCLAIMED. USERS ASSUME THE ENTIRE RISK AND LIABILITY OF USING THE + * SOFTWARE. + */ + +/*************************************************************** + * DISK IMAGE LAYOUT + * ************************************************************* + * All data is stored in BIG ENDIAN format + * ************************************************************* + * Section 1: Header + * + * 10 bytes id ID String "VTPMMGRDOM" + * uint32_t version Disk Image version number (current == 1) + * uint32_t storage_key_len Length of the storage Key + * TPM_KEY storage_key Marshalled TPM_KEY structure (See TPM spec v2) + * RSA_BLOCK aes_crypto Encrypted aes key data (RSA_CIPHER_SIZE bytes), bound by the storage_key + * BYTE[32] aes_key Aes key for encrypting the uuid table + * uint32_t cipher_sz Encrypted size of the uuid table + * + * ************************************************************* + * Section 2: Uuid Table + * + * This table is encrypted by the aes_key in the header. The cipher text size is just + * large enough to hold all of the entries plus required padding. + * + * Each entry is as follows + * BYTE[16] uuid Uuid of a vtpm that is stored on this disk + * uint32_t offset Disk offset where the vtpm data is stored + * + * ************************************************************* + * Section 3: Vtpm Table + * + * The rest of the disk stores vtpms. Each vtpm is an RSA_BLOCK encrypted + * by the storage key. Each vtpm must exist on an RSA_BLOCK aligned boundary, + * starting at the first RSA_BLOCK aligned offset after the uuid table. + * As the uuid table grows, vtpms may be relocated. + * + * RSA_BLOCK vtpm_crypto Vtpm data encrypted by storage_key + * BYTE[20] hash Sha1 hash of vtpm encrypted data + * BYTE[16] vtpm_aes_key Encryption key for vtpm data + * + ************************************************************* + */ +#define DISKVERS 1 +#define IDSTR "VTPMMGRDOM" +#define IDSTRLEN 10 +#define AES_BLOCK_SIZE 16 +#define AES_KEY_BITS 256 +#define AES_KEY_SIZE (AES_KEY_BITS/8) +#define BUF_SIZE 4096 + +#define UUID_TBL_ENT_SIZE (sizeof(uuid_t) + sizeof(uint32_t)) + +#define HEADERSZ (10 + 4 + 4) + +#define TRY_READ(buf, size, msg) do {\ + int rc; \ + if((rc = read(blkfront_fd, buf, (size))) != (size)) { \ + vtpmlogerror(VTPM_LOG_VTPM, "read() failed! " msg " : rc=(%d/%d), error=(%s)\n", rc, (int)(size), strerror(errno)); \ + status = TPM_IOERROR;\ + goto abort_egress;\ + } \ +} while(0) + +#define TRY_WRITE(buf, size, msg) do {\ + int rc; \ + if((rc = write(blkfront_fd, buf, (size))) != (size)) { \ + vtpmlogerror(VTPM_LOG_VTPM, "write() failed! " msg " : rc=(%d/%d), error=(%s)\n", rc, (int)(size), strerror(errno)); \ + status = TPM_IOERROR;\ + goto abort_egress;\ + } \ +} while(0) + +#include <blkfront.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <inttypes.h> +#include <stdlib.h> +#include <stdbool.h> +#include <mini-os/byteorder.h> +#include <polarssl/aes.h> + +#include "vtpm_manager.h" +#include "log.h" +#include "marshal.h" +#include "tpm.h" +#include "uuid.h" + +#include "vtpmmgr.h" +#include "vtpm_storage.h" + +#define MAX(a,b) ( ((a) > (b)) ? (a) : (b) ) +#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) + +/* blkfront device objets */ +static struct blkfront_dev* blkdev = NULL; +static int blkfront_fd = -1; + +struct Vtpm { + uuid_t uuid; + int offset; +}; +struct Storage { + int aes_offset; + int uuid_offset; + int end_offset; + + int num_vtpms; + int num_vtpms_alloced; + struct Vtpm* vtpms; +}; + +/* Global storage data */ +static struct Storage g_store = { + .vtpms = NULL, +}; + +static int get_offset(void) { + return lseek(blkfront_fd, 0, SEEK_CUR); +} + +static void reset_store(void) { + g_store.aes_offset = 0; + g_store.uuid_offset = 0; + g_store.end_offset = 0; + + g_store.num_vtpms = 0; + g_store.num_vtpms_alloced = 0; + free(g_store.vtpms); + g_store.vtpms = NULL; +} + +static int vtpm_get_index(const uuid_t uuid) { + int st = 0; + int ed = g_store.num_vtpms-1; + while(st <= ed) { + int mid = ((unsigned int)st + (unsigned int)ed) >> 1; //avoid overflow + int c = memcmp(uuid, &g_store.vtpms[mid].uuid, sizeof(uuid_t)); + if(c == 0) { + return mid; + } else if(c > 0) { + st = mid + 1; + } else { + ed = mid - 1; + } + } + return -(st + 1); +} + +static void vtpm_add(const uuid_t uuid, int offset, int index) { + /* Realloc more space if needed */ + if(g_store.num_vtpms >= g_store.num_vtpms_alloced) { + g_store.num_vtpms_alloced += 16; + g_store.vtpms = realloc( + g_store.vtpms, + sizeof(struct Vtpm) * g_store.num_vtpms_alloced); + } + + /* Move everybody after the new guy */ + for(int i = g_store.num_vtpms; i > index; --i) { + g_store.vtpms[i] = g_store.vtpms[i-1]; + } + + vtpmloginfo(VTPM_LOG_VTPM, "Registered vtpm " UUID_FMT "\n", UUID_BYTES(uuid)); + + /* Finally add new one */ + memcpy(g_store.vtpms[index].uuid, uuid, sizeof(uuid_t)); + g_store.vtpms[index].offset = offset; + ++g_store.num_vtpms; +} + +#if 0 +static void vtpm_remove(int index) { + for(i = index; i < g_store.num_vtpms; ++i) { + g_store.vtpms[i] = g_store.vtpms[i+1]; + } + --g_store.num_vtpms; +} +#endif + +static int pack_uuid_table(uint8_t* table, int size, int* nvtpms) { + uint8_t* ptr = table; + while(*nvtpms < g_store.num_vtpms && size >= 0) + { + /* Pack the uuid */ + memcpy(ptr, (uint8_t*)g_store.vtpms[*nvtpms].uuid, sizeof(uuid_t)); + ptr+= sizeof(uuid_t); + + + /* Pack the offset */ + ptr = pack_UINT32(ptr, g_store.vtpms[*nvtpms].offset); + + ++*nvtpms; + size -= UUID_TBL_ENT_SIZE; + } + return ptr - table; +} + +/* Extract the uuids */ +static int extract_uuid_table(uint8_t* table, int size) { + uint8_t* ptr = table; + for(;size >= UUID_TBL_ENT_SIZE; size -= UUID_TBL_ENT_SIZE) { + int index; + uint32_t v32; + + /*uuid_t is just an array of bytes, so we can do a direct cast here */ + uint8_t* uuid = ptr; + ptr += sizeof(uuid_t); + + /* Get the offset of the key */ + ptr = unpack_UINT32(ptr, &v32); + + /* Insert the new vtpm in sorted order */ + if((index = vtpm_get_index(uuid)) >= 0) { + vtpmlogerror(VTPM_LOG_VTPM, "Vtpm (" UUID_FMT ") exists multiple times! ignoring...\n", UUID_BYTES(uuid)); + continue; + } + index = -index -1; + + vtpm_add(uuid, v32, index); + + } + return ptr - table; +} + +static void vtpm_decrypt_block(aes_context* aes, + uint8_t* iv, + uint8_t* cipher, + uint8_t* plain, + int cipher_sz, + int* overlap) +{ + int bytes_ext; + /* Decrypt */ + aes_crypt_cbc(aes, AES_DECRYPT, + cipher_sz, + iv, cipher, plain + *overlap); + + /* Extract */ + bytes_ext = extract_uuid_table(plain, cipher_sz + *overlap); + + /* Copy left overs to the beginning */ + *overlap = cipher_sz + *overlap - bytes_ext; + memcpy(plain, plain + bytes_ext, *overlap); +} + +static int vtpm_encrypt_block(aes_context* aes, + uint8_t* iv, + uint8_t* plain, + uint8_t* cipher, + int block_sz, + int* overlap, + int* num_vtpms) +{ + int bytes_to_crypt; + int bytes_packed; + + /* Pack the uuid table */ + bytes_packed = *overlap + pack_uuid_table(plain + *overlap, block_sz - *overlap, num_vtpms); + bytes_to_crypt = MIN(bytes_packed, block_sz); + + /* Add padding if we aren't on a multiple of the block size */ + if(bytes_to_crypt & (AES_BLOCK_SIZE-1)) { + int oldsz = bytes_to_crypt; + //add padding + bytes_to_crypt += AES_BLOCK_SIZE - (bytes_to_crypt & (AES_BLOCK_SIZE-1)); + //fill padding with random bytes + vtpmmgr_rand(plain + oldsz, bytes_to_crypt - oldsz); + *overlap = 0; + } else { + *overlap = bytes_packed - bytes_to_crypt; + } + + /* Encrypt this chunk */ + aes_crypt_cbc(aes, AES_ENCRYPT, + bytes_to_crypt, + iv, plain, cipher); + + /* Copy the left over partials to the beginning */ + memcpy(plain, plain + bytes_to_crypt, *overlap); + + return bytes_to_crypt; +} + +static TPM_RESULT vtpm_storage_new_vtpm(const uuid_t uuid, int index) { + TPM_RESULT status = TPM_SUCCESS; + uint8_t plain[BUF_SIZE + AES_BLOCK_SIZE]; + uint8_t buf[BUF_SIZE]; + uint8_t* ptr; + int cipher_sz; + aes_context aes; + + /* Add new vtpm to the table */ + vtpm_add(uuid, g_store.end_offset, index); + g_store.end_offset += RSA_CIPHER_SIZE; + + /* Compute the new end location of the encrypted uuid table */ + cipher_sz = AES_BLOCK_SIZE; //IV + cipher_sz += g_store.num_vtpms * UUID_TBL_ENT_SIZE; //uuid table + cipher_sz += (AES_BLOCK_SIZE - (cipher_sz & (AES_BLOCK_SIZE -1))) & (AES_BLOCK_SIZE-1); //aes padding + + /* Does this overlap any key data? If so they need to be relocated */ + int uuid_end = (g_store.uuid_offset + cipher_sz + RSA_CIPHER_SIZE) & ~(RSA_CIPHER_SIZE -1); + for(int i = 0; i < g_store.num_vtpms; ++i) { + if(g_store.vtpms[i].offset < uuid_end) { + + vtpmloginfo(VTPM_LOG_VTPM, "Relocating vtpm data\n"); + + //Read the hashkey cipher text + lseek(blkfront_fd, g_store.vtpms[i].offset, SEEK_SET); + TRY_READ(buf, RSA_CIPHER_SIZE, "vtpm hashkey relocate"); + + //Write the cipher text to new offset + lseek(blkfront_fd, g_store.end_offset, SEEK_SET); + TRY_WRITE(buf, RSA_CIPHER_SIZE, "vtpm hashkey relocate"); + + //Save new offset + g_store.vtpms[i].offset = g_store.end_offset; + g_store.end_offset += RSA_CIPHER_SIZE; + } + } + + vtpmloginfo(VTPM_LOG_VTPM, "Generating a new symmetric key\n"); + + /* Generate an aes key */ + TPMTRYRETURN(vtpmmgr_rand(plain, AES_KEY_SIZE)); + aes_setkey_enc(&aes, plain, AES_KEY_BITS); + ptr = plain + AES_KEY_SIZE; + + /* Pack the crypted size */ + ptr = pack_UINT32(ptr, cipher_sz); + + vtpmloginfo(VTPM_LOG_VTPM, "Binding encrypted key\n"); + + /* Seal the key and size */ + TPMTRYRETURN(TPM_Bind(&vtpm_globals.storage_key, + plain, + ptr - plain, + buf)); + + /* Write the sealed key to disk */ + lseek(blkfront_fd, g_store.aes_offset, SEEK_SET); + TRY_WRITE(buf, RSA_CIPHER_SIZE, "vtpm aes key"); + + /* ENCRYPT AND WRITE UUID TABLE */ + + vtpmloginfo(VTPM_LOG_VTPM, "Encrypting the uuid table\n"); + + int num_vtpms = 0; + int overlap = 0; + int bytes_crypted; + uint8_t iv[AES_BLOCK_SIZE]; + + /* Generate the iv for the first block */ + TPMTRYRETURN(vtpmmgr_rand(iv, AES_BLOCK_SIZE)); + + /* Copy the iv to the cipher text buffer to be written to disk */ + memcpy(buf, iv, AES_BLOCK_SIZE); + ptr = buf + AES_BLOCK_SIZE; + + /* Encrypt the first block of the uuid table */ + bytes_crypted = vtpm_encrypt_block(&aes, + iv, //iv + plain, //plaintext + ptr, //cipher text + BUF_SIZE - AES_BLOCK_SIZE, + &overlap, + &num_vtpms); + + /* Write the iv followed by the crypted table*/ + TRY_WRITE(buf, bytes_crypted + AES_BLOCK_SIZE, "vtpm uuid table"); + + /* Decrement the number of bytes encrypted */ + cipher_sz -= bytes_crypted + AES_BLOCK_SIZE; + + /* If there are more vtpms, encrypt and write them block by block */ + while(cipher_sz > 0) { + /* Encrypt the next block of the uuid table */ + bytes_crypted = vtpm_encrypt_block(&aes, + iv, + plain, + buf, + BUF_SIZE, + &overlap, + &num_vtpms); + + /* Write the cipher text to disk */ + TRY_WRITE(buf, bytes_crypted, "vtpm uuid table"); + + cipher_sz -= bytes_crypted; + } + + goto egress; +abort_egress: +egress: + return status; +} + + +/************************************** + * PUBLIC FUNCTIONS + * ***********************************/ + +int vtpm_storage_init(void) { + struct blkfront_info info; + if((blkdev = init_blkfront(NULL, &info)) == NULL) { + return -1; + } + if((blkfront_fd = blkfront_open(blkdev)) < 0) { + return -1; + } + return 0; +} + +void vtpm_storage_shutdown(void) { + reset_store(); + close(blkfront_fd); +} + +TPM_RESULT vtpm_storage_load_hashkey(const uuid_t uuid, uint8_t hashkey[HASHKEYSZ]) +{ + TPM_RESULT status = TPM_SUCCESS; + int index; + uint8_t cipher[RSA_CIPHER_SIZE]; + uint8_t clear[RSA_CIPHER_SIZE]; + UINT32 clear_size; + + /* Find the index of this uuid */ + if((index = vtpm_get_index(uuid)) < 0) { + index = -index-1; + vtpmlogerror(VTPM_LOG_VTPM, "LoadKey failure: Unrecognized uuid! " UUID_FMT "\n", UUID_BYTES(uuid)); + status = TPM_BAD_PARAMETER; + goto abort_egress; + } + + /* Read the table entry */ + lseek(blkfront_fd, g_store.vtpms[index].offset, SEEK_SET); + TRY_READ(cipher, RSA_CIPHER_SIZE, "vtpm hashkey data"); + + /* Decrypt the table entry */ + TPMTRYRETURN(TPM_UnBind( + vtpm_globals.storage_key_handle, + RSA_CIPHER_SIZE, + cipher, + &clear_size, + clear, + (const TPM_AUTHDATA*)&vtpm_globals.storage_key_usage_auth, + &vtpm_globals.oiap)); + + if(clear_size < HASHKEYSZ) { + vtpmloginfo(VTPM_LOG_VTPM, "Decrypted Hash key size (%" PRIu32 ") was too small!\n", clear_size); + status = TPM_RESOURCES; + goto abort_egress; + } + + memcpy(hashkey, clear, HASHKEYSZ); + + vtpmloginfo(VTPM_LOG_VTPM, "Loaded hash and key for vtpm " UUID_FMT "\n", UUID_BYTES(uuid)); + goto egress; +abort_egress: + vtpmlogerror(VTPM_LOG_VTPM, "Failed to load key\n"); +egress: + return status; +} + +TPM_RESULT vtpm_storage_save_hashkey(const uuid_t uuid, uint8_t hashkey[HASHKEYSZ]) +{ + TPM_RESULT status = TPM_SUCCESS; + int index; + uint8_t buf[RSA_CIPHER_SIZE]; + + /* Find the index of this uuid */ + if((index = vtpm_get_index(uuid)) < 0) { + index = -index-1; + /* Create a new vtpm */ + TPMTRYRETURN( vtpm_storage_new_vtpm(uuid, index) ); + } + + /* Encrypt the hash and key */ + TPMTRYRETURN( TPM_Bind(&vtpm_globals.storage_key, + hashkey, + HASHKEYSZ, + buf)); + + /* Write to disk */ + lseek(blkfront_fd, g_store.vtpms[index].offset, SEEK_SET); + TRY_WRITE(buf, RSA_CIPHER_SIZE, "vtpm hashkey data"); + + vtpmloginfo(VTPM_LOG_VTPM, "Saved hash and key for vtpm " UUID_FMT "\n", UUID_BYTES(uuid)); + goto egress; +abort_egress: + vtpmlogerror(VTPM_LOG_VTPM, "Failed to save key\n"); +egress: + return status; +} + +TPM_RESULT vtpm_storage_new_header() +{ + TPM_RESULT status = TPM_SUCCESS; + uint8_t buf[BUF_SIZE]; + uint8_t keybuf[AES_KEY_SIZE + sizeof(uint32_t)]; + uint8_t* ptr = buf; + uint8_t* sptr; + + /* Clear everything first */ + reset_store(); + + vtpmloginfo(VTPM_LOG_VTPM, "Creating new disk image header\n"); + + /*Copy the ID string */ + memcpy(ptr, IDSTR, IDSTRLEN); + ptr += IDSTRLEN; + + /*Copy the version */ + ptr = pack_UINT32(ptr, DISKVERS); + + /*Save the location of the key size */ + sptr = ptr; + ptr += sizeof(UINT32); + + vtpmloginfo(VTPM_LOG_VTPM, "Saving root storage key..\n"); + + /* Copy the storage key */ + ptr = pack_TPM_KEY(ptr, &vtpm_globals.storage_key); + + /* Now save the size */ + pack_UINT32(sptr, ptr - (sptr + 4)); + + /* Create a fake aes key and set cipher text size to 0 */ + memset(keybuf, 0, sizeof(keybuf)); + + vtpmloginfo(VTPM_LOG_VTPM, "Binding uuid table symmetric key..\n"); + + /* Save the location of the aes key */ + g_store.aes_offset = ptr - buf; + + /* Store the fake aes key and vtpm count */ + TPMTRYRETURN(TPM_Bind(&vtpm_globals.storage_key, + keybuf, + sizeof(keybuf), + ptr)); + ptr+= RSA_CIPHER_SIZE; + + /* Write the header to disk */ + lseek(blkfront_fd, 0, SEEK_SET); + TRY_WRITE(buf, ptr-buf, "vtpm header"); + + /* Save the location of the uuid table */ + g_store.uuid_offset = get_offset(); + + /* Save the end offset */ + g_store.end_offset = (g_store.uuid_offset + RSA_CIPHER_SIZE) & ~(RSA_CIPHER_SIZE -1); + + vtpmloginfo(VTPM_LOG_VTPM, "Saved new manager disk header.\n"); + + goto egress; +abort_egress: +egress: + return status; +} + + +TPM_RESULT vtpm_storage_load_header(void) +{ + TPM_RESULT status = TPM_SUCCESS; + uint32_t v32; + uint8_t buf[BUF_SIZE]; + uint8_t* ptr = buf; + aes_context aes; + + /* Clear everything first */ + reset_store(); + + /* Read the header from disk */ + lseek(blkfront_fd, 0, SEEK_SET); + TRY_READ(buf, IDSTRLEN + sizeof(UINT32) + sizeof(UINT32), "vtpm header"); + + vtpmloginfo(VTPM_LOG_VTPM, "Loading disk image header\n"); + + /* Verify the ID string */ + if(memcmp(ptr, IDSTR, IDSTRLEN)) { + vtpmlogerror(VTPM_LOG_VTPM, "Invalid ID string in disk image!\n"); + status = TPM_FAIL; + goto abort_egress; + } + ptr+=IDSTRLEN; + + /* Unpack the version */ + ptr = unpack_UINT32(ptr, &v32); + + /* Verify the version */ + if(v32 != DISKVERS) { + vtpmlogerror(VTPM_LOG_VTPM, "Unsupported disk image version number %" PRIu32 "\n", v32); + status = TPM_FAIL; + goto abort_egress; + } + + /* Size of the storage key */ + ptr = unpack_UINT32(ptr, &v32); + + /* Sanity check */ + if(v32 > BUF_SIZE) { + vtpmlogerror(VTPM_LOG_VTPM, "Size of storage key (%" PRIu32 ") is too large!\n", v32); + status = TPM_IOERROR; + goto abort_egress; + } + + /* read the storage key */ + TRY_READ(buf, v32, "storage pub key"); + + vtpmloginfo(VTPM_LOG_VTPM, "Unpacking storage key\n"); + + /* unpack the storage key */ + ptr = unpack_TPM_KEY(buf, &vtpm_globals.storage_key, UNPACK_ALLOC); + + /* Load Storage Key into the TPM */ + TPMTRYRETURN( TPM_LoadKey( + TPM_SRK_KEYHANDLE, + &vtpm_globals.storage_key, + &vtpm_globals.storage_key_handle, + (const TPM_AUTHDATA*)&vtpm_globals.srk_auth, + &vtpm_globals.oiap)); + + /* Initialize the storage key auth */ + memset(vtpm_globals.storage_key_usage_auth, 0, sizeof(TPM_AUTHDATA)); + + /* Store the offset of the aes key */ + g_store.aes_offset = get_offset(); + + /* Read the rsa cipher text for the aes key */ + TRY_READ(buf, RSA_CIPHER_SIZE, "aes key"); + ptr = buf + RSA_CIPHER_SIZE; + + vtpmloginfo(VTPM_LOG_VTPM, "Unbinding uuid table symmetric key\n"); + + /* Decrypt the aes key protecting the uuid table */ + UINT32 datalen; + TPMTRYRETURN(TPM_UnBind( + vtpm_globals.storage_key_handle, + RSA_CIPHER_SIZE, + buf, + &datalen, + ptr, + (const TPM_AUTHDATA*)&vtpm_globals.storage_key_usage_auth, + &vtpm_globals.oiap)); + + /* Validate the length of the output buffer */ + if(datalen < AES_KEY_SIZE + sizeof(UINT32)) { + vtpmlogerror(VTPM_LOG_VTPM, "Unbound AES key size (%d) was too small! expected (%ld)\n", datalen, AES_KEY_SIZE + sizeof(UINT32)); + status = TPM_IOERROR; + goto abort_egress; + } + + /* Extract the aes key */ + aes_setkey_dec(&aes, ptr, AES_KEY_BITS); + ptr+= AES_KEY_SIZE; + + /* Extract the ciphertext size */ + ptr = unpack_UINT32(ptr, &v32); + int cipher_size = v32; + + /* Sanity check */ + if(cipher_size & (AES_BLOCK_SIZE-1)) { + vtpmlogerror(VTPM_LOG_VTPM, "Cipher text size (%" PRIu32 ") is not a multiple of the aes block size! (%d)\n", v32, AES_BLOCK_SIZE); + status = TPM_IOERROR; + goto abort_egress; + } + + /* Save the location of the uuid table */ + g_store.uuid_offset = get_offset(); + + /* Only decrypt the table if there are vtpms to decrypt */ + if(cipher_size > 0) { + int rbytes; + int overlap = 0; + uint8_t plain[BUF_SIZE + AES_BLOCK_SIZE]; + uint8_t iv[AES_BLOCK_SIZE]; + + vtpmloginfo(VTPM_LOG_VTPM, "Decrypting uuid table\n"); + + /* Pre allocate the vtpm array */ + g_store.num_vtpms_alloced = cipher_size / UUID_TBL_ENT_SIZE; + g_store.vtpms = malloc(sizeof(struct Vtpm) * g_store.num_vtpms_alloced); + + /* Read the iv and the first chunk of cipher text */ + rbytes = MIN(cipher_size, BUF_SIZE); + TRY_READ(buf, rbytes, "vtpm uuid table\n"); + cipher_size -= rbytes; + + /* Copy the iv */ + memcpy(iv, buf, AES_BLOCK_SIZE); + ptr = buf + AES_BLOCK_SIZE; + + /* Remove the iv from the number of bytes to decrypt */ + rbytes -= AES_BLOCK_SIZE; + + /* Decrypt and extract vtpms */ + vtpm_decrypt_block(&aes, + iv, ptr, plain, + rbytes, &overlap); + + /* Read the rest of the table if there is more */ + while(cipher_size > 0) { + /* Read next chunk of cipher text */ + rbytes = MIN(cipher_size, BUF_SIZE); + TRY_READ(buf, rbytes, "vtpm uuid table"); + cipher_size -= rbytes; + + /* Decrypt a block of text */ + vtpm_decrypt_block(&aes, + iv, buf, plain, + rbytes, &overlap); + + } + vtpmloginfo(VTPM_LOG_VTPM, "Loaded %d vtpms!\n", g_store.num_vtpms); + } + + /* The end of the key table, new vtpms go here */ + int uuid_end = (get_offset() + RSA_CIPHER_SIZE) & ~(RSA_CIPHER_SIZE -1); + g_store.end_offset = uuid_end; + + /* Compute the end offset while validating vtpms*/ + for(int i = 0; i < g_store.num_vtpms; ++i) { + /* offset must not collide with previous data */ + if(g_store.vtpms[i].offset < uuid_end) { + vtpmlogerror(VTPM_LOG_VTPM, "vtpm: " UUID_FMT + " offset (%d) is before end of uuid table (%d)!\n", + UUID_BYTES(g_store.vtpms[i].uuid), + g_store.vtpms[i].offset, uuid_end); + status = TPM_IOERROR; + goto abort_egress; + } + /* offset must be at a multiple of cipher size */ + if(g_store.vtpms[i].offset & (RSA_CIPHER_SIZE-1)) { + vtpmlogerror(VTPM_LOG_VTPM, "vtpm: " UUID_FMT + " offset(%d) is not at a multiple of the rsa cipher text size (%d)!\n", + UUID_BYTES(g_store.vtpms[i].uuid), + g_store.vtpms[i].offset, RSA_CIPHER_SIZE); + status = TPM_IOERROR; + goto abort_egress; + } + /* Save the last offset */ + if(g_store.vtpms[i].offset >= g_store.end_offset) { + g_store.end_offset = g_store.vtpms[i].offset + RSA_CIPHER_SIZE; + } + } + + goto egress; +abort_egress: + //An error occured somewhere + vtpmlogerror(VTPM_LOG_VTPM, "Failed to load manager data!\n"); + + //Clear the data store + reset_store(); + + //Reset the storage key structure + free_TPM_KEY(&vtpm_globals.storage_key); + { + TPM_KEY key = TPM_KEY_INIT; + vtpm_globals.storage_key = key; + } + + //Reset the storage key handle + TPM_EvictKey(vtpm_globals.storage_key_handle); + vtpm_globals.storage_key_handle = 0; +egress: + return status; +} + +#if 0 +/* For testing disk IO */ +void add_fake_vtpms(int num) { + for(int i = 0; i < num; ++i) { + uint32_t ind = cpu_to_be32(i); + + uuid_t uuid; + memset(uuid, 0, sizeof(uuid_t)); + memcpy(uuid, &ind, sizeof(ind)); + int index = vtpm_get_index(uuid); + index = -index-1; + + vtpm_storage_new_vtpm(uuid, index); + } +} +#endif |