Index: qemu-git/hw/tpm_builtin.c
===================================================================
--- qemu-git.orig/hw/tpm_builtin.c
+++ qemu-git/hw/tpm_builtin.c
@@ -27,6 +27,7 @@
 #include "hw/pc.h"
 #include "migration.h"
 #include "sysemu.h"
+#include "aes.h"
 
 #include <libtpms/tpm_library.h>
 #include <libtpms/tpm_error.h>
@@ -110,14 +111,27 @@ typedef struct BSDir {
     uint16_t  rev;
     uint32_t  checksum;
     uint32_t  num_entries;
-    uint32_t  reserved[10];
+    uint8_t   enctype;
+    uint8_t   reserved1[3];
+    uint32_t  reserved[8];
     BSEntry   entries[BS_DIR_MAX_NUM_ENTRIES];
 } __attribute__((packed)) BSDir;
 
 
 #define BS_DIR_REV1         1
+/* rev 2 added encryption */
+#define BS_DIR_REV2         2
 
-#define BS_DIR_REV_CURRENT  BS_DIR_REV1
+
+#define BS_DIR_REV_CURRENT  BS_DIR_REV2
+
+/* above enctype */
+enum BSEnctype {
+    BS_DIR_ENCTYPE_NONE = 0,
+    BS_DIR_ENCTYPE_AES_CBC,
+
+    BS_DIR_ENCTYPE_LAST,
+};
 
 /* local variables */
 
@@ -150,6 +164,11 @@ static const unsigned char tpm_std_fatal
 
 static char dev_description[80];
 
+static struct enckey {
+    uint8_t enctype;
+    AES_KEY tpm_enc_key;
+    AES_KEY tpm_dec_key;
+} enckey;
 
 static int tpm_builtin_load_sized_data_from_bs(BlockDriverState *bs,
                                                enum BSEntryType be,
@@ -264,7 +283,7 @@ static uint32_t tpm_builtin_calc_dir_che
 
 static bool tpm_builtin_is_valid_bsdir(BSDir *dir)
 {
-    if (dir->rev != BS_DIR_REV_CURRENT ||
+    if (dir->rev > BS_DIR_REV_CURRENT ||
         dir->num_entries > BS_DIR_MAX_NUM_ENTRIES) {
         return false;
     }
@@ -295,6 +314,33 @@ static bool tpm_builtin_has_valid_conten
     return rc;
 }
 
+static bool tpm_builtin_supports_encryption(const BSDir *dir)
+{
+    return (dir->rev >= BS_DIR_REV2);
+}
+
+
+static bool tpm_builtin_has_missing_key(const BSDir *dir)
+{
+    return ((dir->enctype   != BS_DIR_ENCTYPE_NONE) &&
+            (enckey.enctype == BS_DIR_ENCTYPE_NONE));
+}
+
+
+static bool tpm_builtin_has_unnecessary_key(const BSDir *dir)
+{
+    return (((dir->enctype   == BS_DIR_ENCTYPE_NONE) &&
+             (enckey.enctype != BS_DIR_ENCTYPE_NONE)) ||
+            ((!tpm_builtin_supports_encryption(dir)) &&
+             (enckey.enctype != BS_DIR_ENCTYPE_NONE)));
+}
+
+
+static bool tpm_builtin_uses_unsupported_enctype(const BSDir *dir)
+{
+    return (dir->enctype >= BS_DIR_ENCTYPE_LAST);
+}
+
 
 static int tpm_builtin_create_blank_dir(BlockDriverState *bs)
 {
@@ -306,6 +352,7 @@ static int tpm_builtin_create_blank_dir(
     dir = (BSDir *)buf;
     dir->rev = BS_DIR_REV_CURRENT;
     dir->num_entries = 0;
+    dir->enctype = enckey.enctype;
 
     dir->checksum = tpm_builtin_calc_dir_checksum(dir);
 
@@ -407,6 +454,38 @@ static int tpm_builtin_startup_bs(BlockD
 
     tpm_builtin_dir_be_to_cpu(dir);
 
+    if (tpm_builtin_is_valid_bsdir(dir)) {
+        if (tpm_builtin_supports_encryption(dir) &&
+            tpm_builtin_has_missing_key(dir)) {
+            fprintf(stderr,
+                    "tpm: the data are encrypted but I am missing the key.\n");
+            rc = -EIO;
+            goto err_exit;
+        }
+        if (tpm_builtin_has_unnecessary_key(dir)) {
+            fprintf(stderr,
+                    "tpm: I have a key but the data are not encrypted.\n");
+            rc = -EIO;
+            goto err_exit;
+        }
+        if (tpm_builtin_supports_encryption(dir) &&
+            tpm_builtin_uses_unsupported_enctype(dir)) {
+            fprintf(stderr,
+                    "tpm: State is encrypted with an unsupported encryption "
+                    "scheme.\n");
+            rc = -EIO;
+            goto err_exit;
+        }
+        if (tpm_builtin_supports_encryption(dir) &&
+            (dir->enctype != BS_DIR_ENCTYPE_NONE) &&
+            !tpm_builtin_has_valid_content(dir)) {
+            fprintf(stderr, "tpm: cannot read the data - "
+                    "is this the wrong key?\n");
+            rc = -EIO;
+            goto err_exit;
+        }
+    }
+
     if (!tpm_builtin_is_valid_bsdir(dir) ||
         !tpm_builtin_has_valid_content(dir)) {
         /* if it's encrypted and has something else than null-content,
@@ -569,6 +648,105 @@ static int set_bs_entry_size_crc(BlockDr
 }
 
 
+static int tpm_builtin_blocksize_roundup(uint8_t enctype, int plainsize)
+{
+    switch (enctype) {
+    case BS_DIR_ENCTYPE_NONE:
+        return plainsize;
+    case BS_DIR_ENCTYPE_AES_CBC:
+        return ALIGN(plainsize, AES_BLOCK_SIZE);
+    default:
+        assert(false);
+        return 0;
+    }
+}
+
+
+static int tpm_builtin_bdrv_pread(BlockDriverState *bs, int64_t offset,
+                                  void *buf, int count,
+                                  enum BSEntryType type)
+{
+    int ret;
+    union {
+        uint64_t ll[2];
+        uint8_t b[16];
+    } ivec;
+    int toread = count;
+
+    toread = tpm_builtin_blocksize_roundup(enckey.enctype, count);
+
+    ret = bdrv_pread(bs, offset, buf, toread);
+
+    if (ret != toread) {
+        return ret;
+    }
+
+    switch (enckey.enctype) {
+    case BS_DIR_ENCTYPE_NONE:
+        break;
+    case BS_DIR_ENCTYPE_AES_CBC:
+        ivec.ll[0] = cpu_to_be64(type);
+        ivec.ll[1] = 0;
+
+        AES_cbc_encrypt(buf, buf, toread, &enckey.tpm_dec_key, ivec.b, 0);
+        break;
+    default:
+        assert(false);
+    }
+
+    return count;
+}
+
+
+static int tpm_builtin_bdrv_pwrite(BlockDriverState *bs, int64_t offset,
+                                   void *buf, int count,
+                                   enum BSEntryType type)
+{
+    int ret;
+    union {
+        uint64_t ll[2];
+        uint8_t b[16];
+    } ivec;
+    int towrite = count;
+    void *out_buf = buf;
+
+    switch (enckey.enctype) {
+    case BS_DIR_ENCTYPE_NONE:
+        break;
+    case BS_DIR_ENCTYPE_AES_CBC:
+        ivec.ll[0] = cpu_to_be64(type);
+        ivec.ll[1] = 0;
+
+        towrite = ALIGN(count, AES_BLOCK_SIZE);
+
+        if (towrite != count) {
+            out_buf = qemu_malloc(towrite);
+
+            if (out_buf == NULL) {
+                return -ENOMEM;
+            }
+        }
+
+        AES_cbc_encrypt(buf, out_buf, towrite, &enckey.tpm_enc_key, ivec.b, 1);
+        break;
+    default:
+        assert(false);
+    }
+
+    ret = bdrv_pwrite(bs, offset, out_buf, towrite);
+
+    if (out_buf != buf) {
+        qemu_free(out_buf);
+    }
+
+    if (ret == towrite) {
+        return count;
+    }
+
+    return ret;
+}
+
+
 static int tpm_builtin_load_sized_data_from_bs(BlockDriverState *bs,
                                                enum BSEntryType be,
                                                TPMSizedBuffer *tsb)
@@ -594,7 +772,7 @@ static int tpm_builtin_load_sized_data_f
         goto err_exit;
     }
 
-    tsb->buffer = qemu_malloc(entry.blobsize);
+    tsb->buffer = qemu_malloc(ALIGN(entry.blobsize, AES_BLOCK_SIZE));
     if (!tsb->buffer) {
         rc = -ENOMEM;
         goto err_exit;
@@ -602,7 +780,8 @@ static int tpm_builtin_load_sized_data_f
 
     tsb->size = entry.blobsize;
 
-    if (bdrv_pread(bs, entry.offset, tsb->buffer, tsb->size) != tsb->size) {
+    if (tpm_builtin_bdrv_pread(bs, entry.offset, tsb->buffer, tsb->size, be) !=
+        tsb->size) {
         clear_sized_buffer(tsb);
         fprintf(stderr, "tpm: Error while reading stored data!\n");
         rc = -EIO;
@@ -667,7 +846,8 @@ static int tpm_builtin_save_sized_data_t
     }
 
     if (data_len > 0) {
-        if (bdrv_pwrite(bs, entry.offset, data, data_len) != data_len) {
+        if (tpm_builtin_bdrv_pwrite(bs, entry.offset, data, data_len, be) !=
+            data_len) {
             rc = -EIO;
         }
     }
@@ -1492,11 +1672,77 @@ static const char *tpm_builtin_create_de
 }
 
 
+/*
+ * Convert a string of hex digits to its binary representation.
+ * The conversion stops once either the maximum size of the binary
+ * array has been reached or an non-hex digit was encountered.
+ */
+static size_t stream_to_bin(const char *stream,
+                            unsigned char *bin, size_t bin_size)
+{
+    size_t c = 0;
+    unsigned char nib = 0;
+
+    while (c < bin_size && stream[c] != 0) {
+        if (stream[c] >= '0' && stream[c] <= '9') {
+            nib |= stream[c] - '0';
+        } else if (stream[c] >= 'A' && stream[c] <= 'F') {
+            nib |= stream[c] - 'A' + 10;
+        } else if (stream[c] >= 'a' && stream[c] <= 'f') {
+            nib |= stream[c] - 'a' + 10;
+        } else {
+            break;
+        }
+
+        if ((c & 1) == 1) {
+            bin[c/2] = nib;
+            nib = 0;
+        } else {
+            nib <<= 4;
+            bin[c/2] = nib;
+        }
+
+        c++;
+    }
+
+    return c;
+}
+
+
+static bool tpm_builtin_parse_as_hexkey(const char *rawkey,
+                                        unsigned char *keyvalue,
+                                        int *keysize)
+{
+    size_t c = 0;
+
+    /* skip over leading '0x' */
+    if (!strncmp(rawkey, "0x", 2)) {
+        rawkey += 2;
+    }
+
+    c = stream_to_bin(rawkey, keyvalue, *keysize);
+
+    if (c == 256/4) {
+        *keysize = 256;
+    } else if (c >= 192/4) {
+        *keysize = 192;
+    } else if (c >= 128/4) {
+        *keysize = 128;
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+
 static TPMBackend *tpm_builtin_create(QemuOpts *opts, const char *id,
                                       const char *model)
 {
     TPMBackend *driver;
     const char *value;
+    unsigned char keyvalue[256/8];
+    int keysize = sizeof(keyvalue);
 
     driver = qemu_malloc(sizeof(TPMBackend));
     if (!driver) {
@@ -1523,6 +1769,33 @@ static TPMBackend *tpm_builtin_create(Qe
         goto err_exit;
     }
 
+    value = qemu_opt_get(opts, "key");
+    if (value) {
+        if (!strncasecmp(value, "aes-cbc:", 8)) {
+            memset(keyvalue, 0x0, sizeof(keyvalue));
+
+            if (!tpm_builtin_parse_as_hexkey(&value[8], keyvalue, &keysize)) {
+                keysize = 128;
+                strncpy((char *)keyvalue, value, 128/8);
+            }
+
+            if (AES_set_encrypt_key(keyvalue, keysize,
+                                    &enckey.tpm_enc_key) != 0 ||
+                AES_set_decrypt_key(keyvalue, keysize,
+                                    &enckey.tpm_dec_key) != 0) {
+                fprintf(stderr, "tpm: Error setting AES key.\n");
+                goto err_exit;
+            }
+            enckey.enctype = BS_DIR_ENCTYPE_AES_CBC;
+        } else {
+            fprintf(stderr, "tpm: Unknown encryption scheme. Known types are: "
+                            "aes-cbc.\n");
+            goto err_exit;
+        }
+    } else {
+        enckey.enctype = BS_DIR_ENCTYPE_NONE;
+    }
+
     return driver;
 
 err_exit:
Index: qemu-git/qemu-config.c
===================================================================
--- qemu-git.orig/qemu-config.c
+++ qemu-git/qemu-config.c
@@ -521,6 +521,11 @@ static QemuOptsList qemu_tpmdev_opts = {
             .type = QEMU_OPT_STRING,
             .help = "Persistent storage for TPM state",
         },
+        {
+            .name = "key",
+            .type = QEMU_OPT_STRING,
+            .help = "Data encryption key",
+        },
         { /* end of list */ }
     },
 };
@@ -545,6 +550,11 @@ static QemuOptsList qemu_tpm_opts = {
             .type = QEMU_OPT_STRING,
             .help = "Persistent storage for TPM state",
         },
+        {
+            .name = "key",
+            .type = QEMU_OPT_STRING,
+            .help = "Data encryption key",
+        },
         { /* end of list */ }
     },
 };
Index: qemu-git/tpm.c
===================================================================
--- qemu-git.orig/tpm.c
+++ qemu-git/tpm.c
@@ -245,6 +245,7 @@ void tpm_cleanup(void)
 void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
 {
     QemuOpts *opts;
+    char *key;
 
     if (strcmp("none", optarg) != 0) {
         if (*optarg == '?') {
@@ -255,6 +256,15 @@ void tpm_config_parse(QemuOptsList *opts
         if (!opts) {
             exit(1);
         }
+
+        /* if a key is provided, wipe it out so no one can see it with 'ps' */
+        key = strstr(optarg, "key=");
+        if (key) {
+            key += 4;
+            while (key[0] && key[0] != ',') {
+                *key++ = '-';
+            }
+        }
     }
 }
 
Index: qemu-git/qemu-options.hx
===================================================================
--- qemu-git.orig/qemu-options.hx
+++ qemu-git/qemu-options.hx
@@ -1762,8 +1762,9 @@ DEFHEADING(TPM device options:)
 # ifdef CONFIG_TPM
 DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
     "" \
-    "-tpm builtin,path=<path>[,model=<model>]\n" \
+    "-tpm builtin,path=<path>[,model=<model>][,key=<aes key>]\n" \
     "                enable a builtin TPM with state in file in path\n" \
+    "                and encrypt the TPM's state with the given AES key\n" \
     "-tpm model=?    to list available TPM device models\n" \
     "-tpm ?          to list available TPM backend types\n",
     QEMU_ARCH_I386)
@@ -1792,13 +1793,22 @@ Use ? to print all available TPM backend
 qemu -tpmdev ?
 @end example
 
-@item -tpmdev builtin ,id=@var{id}, path=@var{path}
+@item -tpmdev builtin ,id=@var{id}, path=@var{path} [,key=@var{key}]
 
 Creates an instance of the built-in TPM.
 
 @option{path} specifies the path to the QCoW2 image that will store
 the TPM's persistent data. @option{path} is required.
 
+@option{key} specifies the AES key to use to encrypt the TPM's persistent
+data. If encryption is to be used, the key must be provided the first
+time a Qemu VM with attached TPM is started and the same key must subsequently
+be used. The format of the key is the type of encryption to use, i.e.,
+@code{aes-cbc}, followed by a colon and then the actual key. The key can
+be a hex number with optional leading @code{0x}
+and 32, 48 or 64 hex digits for 128, 192 or 256 bit AES keys respectively.
+@option{key} is optional.
+
 To create a built-in TPM use the following two options:
 @example
 -tpmdev builtin,id=tpm0,path=<path_to_qcow2> -device tpm-tis,tpmdev=tpm0
@@ -1806,12 +1816,18 @@ To create a built-in TPM use the followi
 Not that the @code{-tpmdev} id is @code{tpm0} and is referenced by
 @code{tpmdev=tpm0} in the device option.
 
+
+To create a built-in TPM whose state is encrypted with a 128 bit AES key
+using AES-CBC encryption scheme supply the following two options:
+@example
+-tpmdev builtin,id=tpm0,path=<path_to_qcow2>,key=aes-cbc:0x1234567890abcdef01234567890abcdef -device tpm-tis,tpmdev=tpm0
+@end example
 @end table
 
 The short form of a TPM device option is:
 @table @option
 
-@item -tpm @var{backend-type}, path=@var{path} [,model=@var{model}]
+@item -tpm @var{backend-type}, path=@var{path} [,model=@var{model}] [,key=@var{key}]
 @findex -tpm
 
 @option{model} specifies the device model. The default device model is a
