mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Add the code base for vtpmmgrdom. Makefile changes next patch. Signed-off-by: Matthew Fioravante <matthew.fioravante@jhuapl.edu> Acked-by: Ian Campbell <ian.campbell@citrix.com> Committed-by: Ian Campbell <ian.campbell@citrix.com>
795 lines
23 KiB
C
795 lines
23 KiB
C
/*
|
|
* 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
|