[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen master] vtpmmgr: add TPM group support



commit 5c3705c900581af6f30be124ab8fb64603bdca03
Author:     Daniel De Graaf <dgdegra@xxxxxxxxxxxxx>
AuthorDate: Mon Apr 21 13:22:57 2014 -0400
Commit:     Ian Campbell <ian.campbell@xxxxxxxxxx>
CommitDate: Wed Apr 23 11:56:41 2014 +0100

    vtpmmgr: add TPM group support
    
    This is a complete rewrite of the disk format and key hierarchy for the
    TPM Manager. The new format supports multiple groups of vTPMs which
    define the permitted configurations where a given vTPM's keys are
    available, allowing upgrades of critical components while retaining the
    secrecy of cryptographic keys.
    
    New features of the TPM Manager are explained in the README and in the
    definitions of the management commands in vtpm_manager.h.
    
    New features for vTPMs:
    
    1. The size of the state blob for a vTPM is expanded from 52 to 64
    bytes in order to support future vTPMs using SHA-2/3 instead of SHA-1.
    
    2. vTPMs can obtain a quote from the physical TPM with certain
    resettable PCRs set to include information about the vTPM. This can be
    used by a vTPM to provide evidence of its integrity, including the
    secrecy of its EK, and for deep quotes.
    
    Some additional changes made by this rewrite that may impact existing
    users:
    
    1. The value of WELLKNOWN_OWNER_AUTH was incorrect for the physical TPM;
    the convention is to use all zero bits for well-known authentication
    values, not all one bits.
    
    2. Randomly generating the owner auth value for the physical TPM is no
    longer supported, as it prevents later creation or certification of
    AIKs (which the old manager did not support).
    
    3. The vTPM Manager needs to be provisioned with a PCR composite and an
    upgrade authority's public key before it will save data across boots.
    
    The current implementation still has some limitations:
     * 5 valid system PCR selections per group
     * The vTPM Manager's disk can use at most 2MB of space
     * The vTPM domain's build hash is always set to null/zero
    
    Most of the code relating to upgrade and rollback protection is
    currently stubbed out, but future versions can add:
     * Support for using the TPM's monotonic counter to prevent rollback
       of vTPM data by taking and restoring disk snapshots
     * Masking the master disk encryption key using a value stored in the
       TPM's NVRAM so that revocation of old data is possible without
       relying on all previously authorized software stacks to respect the
       monotonic counter's value
    
    Signed-off-by: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx>
    Acked-by: Ian Campbell <ian.campbell@xxxxxxxxxx>
---
 docs/misc/vtpm-platforms.txt       |  127 ++++
 docs/misc/vtpm.txt                 |  247 ++++---
 docs/misc/vtpmmgr.txt              |  163 +++++
 stubdom/vtpm/README                |   75 --
 stubdom/vtpmmgr/Makefile           |    4 +-
 stubdom/vtpmmgr/README             |   75 --
 stubdom/vtpmmgr/disk_crypto.c      |  231 ++++++
 stubdom/vtpmmgr/disk_crypto.h      |   17 +
 stubdom/vtpmmgr/disk_format.h      |  193 +++++
 stubdom/vtpmmgr/disk_io.c          |  125 ++++
 stubdom/vtpmmgr/disk_io.h          |   25 +
 stubdom/vtpmmgr/disk_read.c        |  606 ++++++++++++++++
 stubdom/vtpmmgr/disk_tpm.c         |  238 +++++++
 stubdom/vtpmmgr/disk_tpm.h         |   25 +
 stubdom/vtpmmgr/disk_write.c       |  410 +++++++++++
 stubdom/vtpmmgr/endian_int.h       |   72 ++
 stubdom/vtpmmgr/init.c             |  104 +--
 stubdom/vtpmmgr/log.h              |    8 +-
 stubdom/vtpmmgr/marshal.h          |  824 ++++++++++++++--------
 stubdom/vtpmmgr/mgmt_authority.c   |  323 +++++++++
 stubdom/vtpmmgr/mgmt_authority.h   |   11 +
 stubdom/vtpmmgr/tcg.h              |    7 +
 stubdom/vtpmmgr/tpm.c              | 1360 ++++++++++++++++++------------------
 stubdom/vtpmmgr/tpm.h              |  104 +--
 stubdom/vtpmmgr/tpmrsa.c           |   25 +
 stubdom/vtpmmgr/tpmrsa.h           |    3 +
 stubdom/vtpmmgr/vtpm_cmd_handler.c |  858 ++++++++++++++++++++---
 stubdom/vtpmmgr/vtpm_disk.c        |  237 +++++++
 stubdom/vtpmmgr/vtpm_disk.h        |  233 ++++++
 stubdom/vtpmmgr/vtpm_manager.h     |  391 ++++++++++-
 stubdom/vtpmmgr/vtpm_storage.c     |  794 ---------------------
 stubdom/vtpmmgr/vtpm_storage.h     |   68 --
 stubdom/vtpmmgr/vtpmmgr.h          |   12 +-
 33 files changed, 5639 insertions(+), 2356 deletions(-)

diff --git a/docs/misc/vtpm-platforms.txt b/docs/misc/vtpm-platforms.txt
new file mode 100644
index 0000000..fe35fb6
--- /dev/null
+++ b/docs/misc/vtpm-platforms.txt
@@ -0,0 +1,127 @@
+Author: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx>
+
+================================================================================
+Overview
+================================================================================
+
+This document describes example platforms which use virtual TPMs to provide
+security properties for guests running on the platforms.  There are several
+tradeoffs between flexibility and trust which must be considered when
+implementing a platform containing vTPMs.
+
+================================================================================
+Example 1: Trusted Domain 0
+================================================================================
+
+This is the simplest example and provides maximal flexibility for testing the
+vTPM Manager and vTPMs.  The vtpmmgr, vtpm, and guest domains are created using
+xl from the command line on domain 0.
+
+Provisioning on domain 0:
+# dd if=/dev/zero of=/images/vtpmmgr-stubdom.img bs=2M count=1
+# dd if=/dev/zero of=/images/vtpm-guest1.img bs=2M count=1
+# dd if=/dev/zero of=/images/vtpm-guest2.img bs=2M count=1
+
+The vtpmmgr configuration file (vtpmmgr.cfg):
+
+name="vtpmmgr"
+kernel="/usr/lib/xen/boot/vtpmmgr-stubdom.gz"
+extra="tpmlocality=2"
+memory=8
+disk=["file:/images/vtpmmgr-stubdom.img,hda,w"]
+iomem=["fed42,1"]
+
+The vtpm configuration files (vtpm-guest1.cfg, vtpm-guest2.cfg):
+
+name="vtpm-guest1"
+kernel="/usr/lib/xen/boot/vtpm-stubdom.gz"
+extra="loglevel=debug"
+memory=8
+disk=["file:/images/vtpm-guest1.img,hda,w"]
+vtpm=["backend=vtpmmgr,uuid=ac0a5b9e-cbe2-4c07-b43b-1d69e46fb839"]
+
+name="vtpm-guest2"
+kernel="/usr/lib/xen/boot/vtpm-stubdom.gz"
+extra="loglevel=debug"
+memory=8
+disk=["file:/images/vtpm-guest2.img,hda,w"]
+vtpm=["backend=vtpmmgr,uuid=6c3ff5f1-8d58-4fed-b00d-a5ea9a817f7f"]
+
+The guest configuration files (guest1.cfg, guest2.cfg):
+
+name="guest1"
+kernel="/usr/lib/xen/boot/pv-grub-x86_64.gz"
+memory=1024
+disk=["file:/images/guest1.img,xvda,w"]
+vif=['mac=00:01:02:03:04:05,bridge=br0']
+vtpm=["backend=vtpm-guest1"]
+
+name="guest2"
+kernel="/usr/lib/xen/boot/pv-grub-x86_64.gz"
+memory=1024
+disk=["file:/images/guest2.img,xvda,w"]
+vif=['mac=00:01:02:03:04:06,bridge=br0']
+vtpm=["backend=vtpm-guest2"]
+
+Starting domains:
+
+# xl create vtpmmgr.cfg
+# xl create vtpm-guest1.cfg
+# xl create guest1.cfg
+
+================================================================================
+Example 2: Domain Builder with Static vTPMs
+================================================================================
+
+This example uses the domain builder to construct a TPM Manager and vTPM which
+do not require trusting the hardware domain with the vTPM's secrets.  However,
+it is not possible to construct additional vTPMs after the system is booted, 
and
+the guests with access to vTPMs may not be rebooted without rebooting the 
entire
+platform.
+
+The domain builder (dom0) constructs:
+       dom1 - xenstore    system_u:system_r:xenstore_t
+       dom2 - hardware    system_u:system_r:hwdom_t
+       dom3 - vtpmmgr     system_u:system_r:vtpmmgr_t
+       dom4 - vtpm-hw     system_u:system_r:vtpm_t
+       dom5 - vtpm-g1     guest1_u:vm_r:vtpm_t
+       dom6 - vtpm-g2     guest2_u:vm_r:vtpm_t
+       dom7 - guest1      guest1_u:vm_r:guest_t
+       dom8 - guest2      guest2_u:vm_r:guest_t
+
+It unpauses dom1 and dom2 after setting up Xenstore. The hardware domain is not
+permitted access to IO memory at 0xfed42; this IO memory is accessible to the
+vtpmmgr domain.  The two guest domains may be instantiated using pv-grub or
+using the same kernel as the hardware domain to conserve space in the domain
+builder's initrd.
+
+Once the hardware domain boots, it runs:
+
+# xl block-attach vtpmmgr 
'backendtype=phy,backend=hardware,vdev=hda,access=w,target=/dev/lvm/vtpmmgr'
+# xl block-attach vtpm-hw 
'backendtype=phy,backend=hardware,vdev=hda,access=w,target=/dev/lvm/vtpm-hw'
+# xl block-attach vtpm-g1 
'backendtype=phy,backend=hardware,vdev=hda,access=w,target=/dev/lvm/vtpm-g1'
+# xl block-attach vtpm-g2 
'backendtype=phy,backend=hardware,vdev=hda,access=w,target=/dev/lvm/vtpm-g2'
+# xl block-attach guest1 
'backendtype=phy,backend=hardware,vdev=xvda,access=w,target=/dev/lvm/guest1'
+# xl block-attach guest2 
'backendtype=phy,backend=hardware,vdev=xvda,access=w,target=/dev/lvm/guest2'
+# xl vtpm-attach vtpm-hw uuid=062b6416-ed46-492a-9e65-a2f92dc07f7f 
backend=vtpmmgr
+# xl vtpm-attach vtpm-g1 uuid=e9aa9d0f-ece5-4b84-b129-93004ba61a5f 
backend=vtpmmgr
+# xl vtpm-attach vtpm-g2 uuid=3fb2caf0-d305-4516-96c7-420618d98efb 
backend=vtpmmgr
+# xl vtpm-attach hardware uuid=062b6416-ed46-492a-9e65-a2f92dc07f7f 
backend=vtpm-hw
+# xl vtpm-attach guest1 uuid=e9aa9d0f-ece5-4b84-b129-93004ba61a5f 
backend=vtpm-g1
+# xl vtpm-attach guest2 uuid=3fb2caf0-d305-4516-96c7-420618d98efb 
backend=vtpm-g2
+
+Once these commands are complete, the domains are unpaused and may boot. The 
XSM
+policy must be configured to not allow any of the domain types named above to 
be
+created by any domain except the domain builder; guests created by the hardware
+domain or one of the primary guests acting as a control domain must have a
+different type. The type vtpmmgr_t may only map grants from vtpm_t; vtpm_t may
+only map grants from a domain of type guest_t or hwdom_t with the same user
+field.
+
+This example may be extended to allow dynamic creation of domains by using a
+domain builder that accepts build requests.  A single build request would 
create
+a pair of domains using an unused XSM user field: a vTPM and a pv-grub domain
+which requires the presence of a vTPM.  To bind the configuration of the guest
+to the vTPM, the guest may use full-disk encryption which can be unlocked using
+an unseal operation; using the wrong vTPM will then yield a non-functioning
+guest.
diff --git a/docs/misc/vtpm.txt b/docs/misc/vtpm.txt
index d20b424..1887d40 100644
--- a/docs/misc/vtpm.txt
+++ b/docs/misc/vtpm.txt
@@ -22,8 +22,8 @@ major component of vTPM is implemented as a separate domain, 
providing secure
 separation guaranteed by the hypervisor. The vTPM domains are implemented in
 mini-os to reduce memory and processor overhead.
  
-This mini-os vTPM subsystem was built on top of the previous vTPM
-work done by IBM and Intel corporation.
+This mini-os vTPM subsystem was built on top of the previous vTPM work done by
+IBM and Intel corporation.
  
 ------------------------------
 DESIGN OVERVIEW
@@ -106,16 +106,15 @@ INSTALLATION
 
 Prerequisites:
 --------------
-You must have an x86 machine with a TPM on the motherboard.
-The only software requirement to compiling vTPM is cmake.
-You must use libxl to manage domains with vTPMs. 'xm' is
-deprecated and does not support vTPM.
+You must have an x86 machine with a TPM on the motherboard.  The only extra
+software requirement for compiling vTPM is cmake.  You must use libxl to manage
+domains with vTPMs; 'xm' is deprecated and does not support vTPMs.
 
-Compiling the XEN tree:
+Compiling the Xen tree:
 -----------------------
 
-Compile and install the XEN tree as usual. Be sure to build and install
-the stubdom tree.
+Compile and install the Xen tree as usual; be sure that the vTPM domains are
+enabled when you run configure.
 
 Compiling the LINUX dom0 kernel:
 --------------------------------
@@ -127,16 +126,15 @@ blacklisting the module.  If dom0 needs a TPM but does 
not need to use it during
 the boot process (i.e. it is not using IMA), a virtual TPM can be attached to
 dom0 after the system is booted.
 
-Because the TPM manager does not yet accept requests for deep quotes, if a 
quote
-or other request needs to be fulfilled by the physical TPM, dom0 will need to
-access the physical TPM.  In order to prevent interference, the TPM Manager and
-dom0 should use different values for the TPM's locality; since Linux always 
uses
-locality 0, using locality 2 for the TPM Manager is recommended.  If both Linux
-and the TPM Manager attempt to access the TPM at the same time, the TPM device
-will return a busy status; some applications will consider this a fatal error
-instead of retrying the command at a later time.  If a vTPM gets an error when
-loading its key, it will currently generate a fresh vTPM image (with a new EK,
-SRK, and blank NVRAM).
+Access to the physical TPM may be required in order to manage the NVRAM or to
+perform other advanced operations where the vTPM is insufficient.  In order to
+prevent interference, the TPM Manager and dom0 should use different values for
+the TPM's locality; since Linux always uses locality 0, using locality 2 for 
the
+TPM Manager is recommended.  If both Linux and the TPM Manager attempt to 
access
+the TPM at the same time, the TPM device will return a busy status; some
+applications will consider this a fatal error instead of retrying the command 
at
+a later time.  If a vTPM gets an error when loading its key, it will currently
+generate a fresh vTPM image (with a new EK, SRK, and blank NVRAM).
 
 
 Compiling the LINUX domU kernel:
@@ -156,130 +154,100 @@ VTPM MANAGER SETUP
 Manager disk image setup:
 -------------------------
 
-The vTPM Manager requires a disk image to store its
-encrypted data. The image does not require a filesystem
-and can live anywhere on the host disk. The image does not need
-to be large. 8 to 16 Mb should be sufficient.
-
-# dd if=/dev/zero of=/var/vtpmmgr-stubdom.img bs=16M count=1
+The vTPM Manager requires a disk image to store its encrypted data. The image
+does not require a filesystem and can live anywhere on the host disk. The image
+is not large; the Xen 4.5 vtpmmgr is limited to using the first 2MB of the 
image
+but can support more than 20,000 vTPMs.
 
 Manager config file:
 --------------------
 
-The vTPM Manager domain (vtpmmgr-stubdom) must be started like
-any other Xen virtual machine and requires a config file.
-The manager requires a disk image for storage and permission
-to access the hardware memory pages for the TPM. An
-example configuration looks like the following.
-
-kernel="/usr/lib/xen/boot/vtpmmgr-stubdom.gz"
-memory=16
-disk=["file:/var/vtpmmgr-stubdom.img,hda,w"]
-name="vtpmmgr"
-iomem=["fed40,5"]
-
-The iomem line tells xl to allow access to all of the TPM IO memory
-pages, which are 5 pages (one per locality) that start at 0xfed40000. By
-default, the TPM manager uses locality 0 (so only the page at 0xfed40 is
-needed); this can be changed on the domain's command line.
+The vTPM Manager domain (vtpmmgr-stubdom) must be started like any other Xen
+virtual machine and requires a config file.  The manager requires a disk image
+for storage and permission to access the hardware memory pages for the TPM. The
+disk must be presented as "hda", and the TPM memory pages are passed using the
+iomem configuration parameter. The TPM TIS uses 5 pages of IO memory (one per
+locality) that start at physical address 0xfed40000. By default, the TPM 
manager
+uses locality 0 (so only the page at 0xfed40 is needed); this can be changed on
+the domain's command line.  For full functionality in deep quotes, using
+locality 2 is required to manipulate PCR 20-22.
 
 Starting and stopping the manager:
 ----------------------------------
 
-The vTPM manager should be started at boot, you may wish to
-create an init script to do this.
-
-# xl create -c vtpmmgr-stubdom.cfg
+The vTPM manager should be started at boot; you may wish to create an init
+script to do this.  If a domain builder is used, the TPM Manager should be
+started by the domain builder to minimize the trusted computing base for the
+vTPM manager's secrets.
 
 Once initialization is complete you should see the following:
 INFO[VTPM]: Waiting for commands from vTPM's:
 
-To shutdown the manager you must destroy it. To avoid data corruption,
-only destroy the manager when you see the above "Waiting for commands"
-message. This ensures the disk is in a consistent state.
-
-# xl destroy vtpmmgr-stubdom
+The TPM Manager does not respond to shutdown requests; use the destroy command
+to shut it down.
 
 ------------------------------
 VTPM AND LINUX PVM SETUP
 ------------------------------
 
-In the following examples we will assume we have Linux
-guest named "domu" with its associated configuration
-located at /home/user/domu. It's vtpm will be named
-domu-vtpm.
-
 vTPM disk image setup:
 ----------------------
 
-The vTPM requires a disk image to store its persistent
-data. The image does not require a filesystem. The image
-does not need to be large. 8 Mb should be sufficient.
-
-# dd if=/dev/zero of=/home/user/domu/vtpm.img bs=8M count=1
+The vTPM requires a disk image to store its persistent data (RSA keys, NVRAM,
+etc). The image does not require a filesystem. The image does not need to be
+large; 2 Mb should be sufficient.
 
 vTPM config file:
 -----------------
 
-The vTPM domain requires a configuration file like
-any other domain. The vTPM requires a disk image for
-storage and a TPM frontend driver to communicate
-with the manager. An example configuration is given:
-
-kernel="/usr/lib/xen/boot/vtpm-stubdom.gz"
-memory=8
-disk=["file:/home/user/domu/vtpm.img,hda,w"]
-name="domu-vtpm"
-vtpm=["backend=vtpmmgr,uuid=ac0a5b9e-cbe2-4c07-b43b-1d69e46fb839"]
+The vTPM domain requires a configuration file like any other domain. The vTPM
+requires a disk image for storage and a TPM frontend driver to communicate with
+the manager.  You are required to generate a uuid for this vtpm, which is
+specified on the "vtpm=" line that describes its connection to the vTPM 
Manager.
+The uuidgen application may be used to generate a uuid, or one from the output
+of the "manage-vtpmmgr.pl vtpm-add" command may be used to create a vTPM
+belonging to a specific group.
 
-The vtpm= line sets up the tpm frontend driver. The backend must set
-to vtpmmgr. You are required to generate a uuid for this vtpm.
-You can use the uuidgen unix program or some other method to create a
-uuid. The uuid uniquely identifies this vtpm to manager.
-
-If you wish to clear the vTPM data you can either recreate the
-disk image or change the uuid.
+If you wish to clear the vTPM data you can either recreate the disk image or
+change the uuid.
 
 Linux Guest config file:
 ------------------------
 
-The Linux guest config file needs to be modified to include
-the Linux tpmfront driver. Add the following line:
+The Linux guest config file needs to be modified to include the Linux tpmfront
+driver. Add the following line:
 
 vtpm=["backend=domu-vtpm"]
 
 Currently only Linux guests are supported (PV or HVM with PV drivers).
 
-Launching and shut down:
-------------------------
-
-To launch a Linux guest with a vTPM we first have to start the vTPM domain.
-
-# xl create -c /home/user/domu/vtpm.cfg
+While attaching a vTPM after a guest is booted (using xl vtpm-attach) is
+supported, the attached vTPM will not have a record of the boot of the attached
+guest.  Furthermore, if the vTPM has been freshly created, a malicious guest
+could then extend any values into PCRs, potentially forging its boot
+configuration.  Attaching a vTPM to a running domain should only be used for
+trusted domains or when measurements have already been sent to the vTPM from
+another source.
 
-After initialization is complete, you should see the following:
-Info: Waiting for frontend domain to connect..
+Using the vTPM in the guest:
+----------------------------
 
-Next, launch the Linux guest
-
-# xl create -c /home/user/domu/domu.cfg
-
-If xen-tpmfront was compiled as a module, be sure to load it
-in the guest.
+If xen-tpmfront was compiled as a module, it must be loaded it in the guest.
 
 # modprobe xen-tpmfront
 
-After the Linux domain boots and the xen-tpmfront driver is loaded,
-you should see the following on the vtpm console:
+After the Linux domain boots and the xen-tpmfront driver is loaded, you should
+see the following on the vtpm console:
 
 Info: VTPM attached to Frontend X/Y
 
-If you have trousers and tpm_tools installed on the guest, you can test the
-vtpm.
+You can quickly test the vTPM by using the sysfs interface:
 
-On guest:
-# tcsd (if tcsd is not running already)
-# tpm_version
+# cat /sys/devices/vtpm-0/pubek
+# cat /sys/devices/vtpm-0/pcrs
+
+If you have trousers and tpm_tools installed on the guest, the tpm_version
+command should return the following:
 
 The version command should return the following:
   TPM 1.2 Version Info:
@@ -290,12 +258,12 @@ The version command should return the following:
   TPM Version:         01010000
   Manufacturer Info:   4554485a
 
-You should also see the command being sent to the vtpm console as well
-as the vtpm saving its state. You should see the vtpm key being
-encrypted and stored on the vtpmmgr console.
+You should also see the command being sent to the vtpm console as well as the
+vtpm saving its state. You should see the vtpm key being encrypted and stored 
on
+the vtpmmgr console.
 
-You may wish to write a script to start your vtpm and guest together and
-to destroy the vtpm when the guest shuts down.
+You may wish to write a script to start your vtpm and guest together and to
+destroy the vtpm when the guest shuts down.
 
 ------------------------------
 INTEGRATION WITH PV-GRUB
@@ -319,10 +287,69 @@ way can attest to its early boot state.
 MORE INFORMATION
 ------------------------------
 
-See stubdom/vtpmmgr/README for more details about how
-the manager domain works, how to use it, and its command line
-parameters.
+See vtpmmgr.txt for more details about how the manager domain works, how to use
+it, and its command line parameters.
+
+------------------------------
+VTPM DOMAIN OPERATION
+------------------------------
+
+The vtpm-stubdom is a mini-OS domain that emulates a TPM for the guest OS to
+use. It is a small wrapper around the Berlios TPM emulator version 0.7.4.
+Commands are passed from the linux guest via the mini-os TPM backend driver.
+vTPM data is encrypted and stored via a disk image provided to the virtual
+machine. The key used to encrypt the data along with a hash of the vTPM's data
+is sent to the vTPM manager for secure storage and later retrieval.  The vTPM
+domain communicates with the manager using a mini-os tpm front/back device 
pair.
+
+------------------------------------
+VTPM DOMAIN COMMAND LINE ARGUMENTS
+------------------------------------
+
+Command line arguments are passed to the domain via the 'extra' parameter in 
the
+VM config file. Each parameter is separated by white space. For example:
+
+extra="foo=bar baz"
+
+List of Arguments:
+------------------
+
+loglevel=<LOG>: Controls the amount of logging printed to the console.
+       The possible values for <LOG> are:
+        error
+        info (default)
+        debug
 
-See stubdom/vtpm/README for more specifics about how vtpm-stubdom
-operates and the command line options it accepts.
+clear: Start the Berlios emulator in "clear" mode. (default)
+
+save: Start the Berlios emulator in "save" mode.
+
+deactivated: Start the Berlios emulator in "deactivated" mode.
+       See the Berlios TPM emulator documentation for details
+       about the startup mode. For all normal use, always use clear
+       which is the default. You should not need to specify any of these.
+
+maintcmds=<1|0>: Enable to disable the TPM maintenance commands.
+       These commands are used by tpm manufacturers and thus
+       open a security hole. They are disabled by default.
+
+hwinitpcr=<PCRSPEC>: Initialize the virtual Platform Configuration Registers
+       (PCRs) with PCR values from the hardware TPM. Each pcr specified by
+       <PCRSPEC> will be initialized with the value of that same PCR in TPM
+       once at startup. By default all PCRs are zero initialized.
+       Value values of <PCRSPEC> are:
+        all: copy all pcrs
+        none: copy no pcrs (default)
+        <N>: copy pcr n
+        <X-Y>: copy pcrs x to y (inclusive)
+
+       These can also be combined by comma separation, for example:
+        hwinitpcrs=5,12-16
+       will copy pcrs 5, 12, 13, 14, 15, and 16.
+
+------------------------------
+REFERENCES
+------------------------------
 
+Berlios TPM Emulator:
+http://tpm-emulator.berlios.de/
diff --git a/docs/misc/vtpmmgr.txt b/docs/misc/vtpmmgr.txt
new file mode 100644
index 0000000..fe3d8a6
--- /dev/null
+++ b/docs/misc/vtpmmgr.txt
@@ -0,0 +1,163 @@
+Author: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx>
+
+This document describes the operation and command line interface of
+vtpmmgr-stubdom. See docs/misc/vtpm.txt for details on the vTPM subsystem as a
+whole.
+
+================================================================================
+Overview
+================================================================================
+
+The TPM Manager has three primary functions:
+
+1. Securely store the encryption keys for vTPMs
+2. Provide a single controlled path of access to the physical TPM
+3. Provide evidence (via TPM Quotes) of the current configuration
+
+When combined with a platform that provides a trusted method for creating
+domains, the TPM Manager provides assurance that the private keys in a vTPM are
+only available in specific trusted configurations.
+
+The manager accepts commands from the vtpm-stubdom domains via the mini-os TPM
+backend driver. The vTPM manager communicates directly with hardware TPM using
+the mini-os tpm_tis driver.
+
+================================================================================
+Boot Configurations and TPM Groups
+================================================================================
+
+The TPM Manager's data is secured by using the physical TPM's seal operation,
+which allows data to be bound to specific PCRs. These PCRs are populated in the
+physical TPM during the boot process, either by the firmware/BIOS or by a
+dynamic launch environment such as TBOOT. In order to provide assurance of the
+system's security, the PCRs used to seal the TPM manager's data must contain
+measurements for domains used to bootstrap the TPM Manager and vTPMs.
+
+Because these measurements are based on hashes, they will change any time that
+any component of the system is upgraded. Since it is not possible to construct 
a
+list of all possible future good measurements, the job of approving
+configurations is delegated to a third party, referred to here as the system
+approval agent (SAA). The SAA is identified by its public (RSA) signature key,
+which is used to sign lists of valid configurations. A single TPM manager can
+support multiple SAAs via the use of vTPM groups. Each group is associated with
+a single SAA; this allows the creation of a multi-tenant environment where
+tenants may not all choose to trust the same SAA.
+
+Each vTPM is bound to a vTPM group at the time of its creation. Each vTPM group
+has its own AIK in the physical TPM for quotes of the hardware TPM state; when
+used with a conforming Privacy CA, this allows each group on the system to form
+the basis of a distinct identity.
+
+================================================================================
+Initial Provisioning
+================================================================================
+
+When the TPM Manager first boots up, it will create a stub vTPM group along 
with
+entries for any vTPMs that communicate with it. This stub group must be
+provisioned with an SAA and a boot configuration in order to survive a reboot.
+
+When a vTPM is connected to the TPM Manager using a UUID that is not 
recognized,
+a slot will be created in group 0 for it. In the future, this auto-creation may
+be restricted to specific UUIDs (such as the all-zero UUID) to enforce the use
+of the TPM manager as the generator of the UUID. The first vTPM to be connected
+is given administrative privileges for the TPM Manager, and should be attached
+to dom0 or a control domain in order to send provisioning commands.
+
+Provisioning a vTPM group for the system requires the public key of the SAA and
+privacy CA data used to certify the AIK (see the TPM spec for details). Once 
the
+group is created, a signed list of boot measurements can be installed. The
+initial group controls the ability to boot the system as a whole, and cannot be
+deleted once provisioned.
+
+================================================================================
+Command Line Arguments
+================================================================================
+
+Command line arguments are passed to the domain via the 'extra' parameter in 
the
+VM config file. Each parameter is separated by white space. For example:
+
+extra="foo=bar baz"
+
+Valid arguments:
+
+owner_auth=<AUTHSPEC>
+srk_auth=<AUTHSPEC>
+       Set the owner and SRK authdata for the TPM. If not specified, the
+       default is 160 zero bits (the well-known auth value). Valid values of
+       <AUTHSPEC> are:
+               well-known   Use the well known auth (default)
+               hash:<HASH>  Use the given 40-character ASCII hex string
+               text:<STR>   Use sha1 hash of <STR>.
+
+tpmdriver=<DRIVER>
+       Choose the driver used for communication with the hardware TPM. Values
+       other than tpm_tis should only be used for testing.
+
+       The possible values of <DRIVER> are:
+               tpm_tis    Direct communication with a hardware TPM 1.2.  The
+                           domain must have access to TPM IO memory. (default)
+               tpmfront   Use the Xen tpmfront interface to talk to another
+                           domain which provides access to the TPM.
+
+The following options only apply to the tpm_tis driver:
+
+tpmiomem=<ADDR>: The base address of the hardware memory pages of the TPM.
+       The default is 0xfed40000, as defined by the TCG's PC Client spec.
+
+tpmirq=<IRQ>: The irq of the hardware TPM if using interrupts. A value of
+       "probe" can be set to probe for the irq. A value of 0 disables
+       interrupts and uses polling (default 0).
+
+tpmlocality=<LOC>: Attempt to use locality <LOC> of the hardware TPM.
+       For full functionality of the TPM Manager, this should be set to "2".
+
+================================================================================
+Platform Security Assumptions
+================================================================================
+
+While the TPM Manager has the ability to check the hash of the vTPM requesting 
a
+key, there is currently no trusted method to inform the TPM Manager of the hash
+of each new domain.  Because of this, the TPM Manager trusts the UUID key in
+Xenstore to identify a vTPM in a trusted manner.  The XSM policy may be used to
+strengthen this assumption if the creation of vTPM-labeled domains is more
+constrained (for example, only permitted to a domain builder service): the only
+grants mapped by the TPM Manager should belong to vTPM domains, so restricting
+the ability to map other domain's granted pages will prevent other domains from
+directly requesting keys from the TPM Manager.
+
+A domain with direct access to the hardware TPM will be able to decrypt the TPM
+Manager's disk image if the haredware TPM's PCR values are in a permitted
+configuration.  To protect the TPM Manager's data, the list of permitted
+configurations should be chosen to include PCRs that measure the hypervisor,
+domain 0, the TPM Manager, and other critical configuration such as the XSM
+policy.  If the TPM Manager is configured to use locality 2 as recommended, it
+is safe to permit the hardware domain to access locality 0 (the default in
+Linux), although concurrent use of the TPM should be avoided as it can result 
in
+unexpected busy errors from the TPM driver.  The ability to access locality 2 
of
+the TPM should be enforced using IO memory labeling in the XSM policy; the
+physical address 0xFED42xxx is always locality 2 for TPMs using the TIS driver.
+
+================================================================================
+Appendix: unsecured migration process for vtpmmgr domain upgrade
+================================================================================
+
+There is no direct upgrade supported from previous versions of the vtpmmgr
+domain due to changes in the on-disk format and the method used to seal data.
+If a vTPM domain supports migration, this feature should be used to migrate the
+vTPM's data; however, the vTPM packaged with Xen does not yet support 
migration.
+
+If adding migration support to the vTPM is not desired, a simpler migration
+domain usable only for local migration can be constructed. The migration 
process
+would look like the following:
+
+1. Start the old vtpmmgr
+2. Start the vTPM migration domain
+3. Attach the vTPM migration domain's vtpm/0 device to the old vtpmmgr
+4. Migration domain executes vtpmmgr_LoadHashKey on vtpm/0
+5. Start the new vtpmmgr, possibly shutting down the old one first
+6. Attach the vTPM migration domain's vtpm/1 device to the new vtpmmgr
+7. Migration domain executes vtpmmgr_SaveHashKey on vtpm/1
+
+This requires the migration domain must be added to the list of valid vTPM
+kernel hashes.  Because the TPM Manager currently does not verify vTPM kernel
+hashes, the control domain can initiate this operation at any time.
diff --git a/stubdom/vtpm/README b/stubdom/vtpm/README
deleted file mode 100644
index 11bdacb..0000000
--- a/stubdom/vtpm/README
+++ /dev/null
@@ -1,75 +0,0 @@
-Copyright (c) 2010-2012 United States Government, as represented by
-the Secretary of Defense.  All rights reserved.
-November 12 2012
-Authors: Matthew Fioravante (JHUAPL),
-
-This document describes the operation and command line interface
-of vtpm-stubdom. See docs/misc/vtpm.txt for details on the
-vTPM subsystem as a whole.
-
-
-------------------------------
-OPERATION
-------------------------------
-
-The vtpm-stubdom is a mini-OS domain that emulates a TPM for the guest OS to
-use. It is a small wrapper around the Berlios TPM emulator
-version 0.7.4. Commands are passed from the linux guest via the
-mini-os TPM backend driver. vTPM data is encrypted and stored via a disk image
-provided to the virtual machine. The key used to encrypt the data along
-with a hash of the vTPM's data is sent to the vTPM manager for secure storage
-and later retrieval.  The vTPM domain communicates with the manager using a
-mini-os tpm front/back device pair.
-
-------------------------------
-COMMAND LINE ARGUMENTS
-------------------------------
-
-Command line arguments are passed to the domain via the 'extra'
-parameter in the VM config file. Each parameter is separated
-by white space. For example:
-
-extra="foo=bar baz"
-
-List of Arguments:
-------------------
-
-loglevel=<LOG>: Controls the amount of logging printed to the console.
-       The possible values for <LOG> are:
-        error
-        info (default)
-        debug
-
-clear: Start the Berlios emulator in "clear" mode. (default)
-
-save: Start the Berlios emulator in "save" mode.
-
-deactivated: Start the Berlios emulator in "deactivated" mode.
-       See the Berlios TPM emulator documentation for details
-       about the startup mode. For all normal use, always use clear
-       which is the default. You should not need to specify any of these.
-
-maintcmds=<1|0>: Enable to disable the TPM maintenance commands.
-       These commands are used by tpm manufacturers and thus
-       open a security hole. They are disabled by default.
-
-hwinitpcr=<PCRSPEC>: Initialize the virtual Platform Configuration Registers
-       (PCRs) with PCR values from the hardware TPM. Each pcr specified by
-       <PCRSPEC> will be initialized with the value of that same PCR in TPM
-       once at startup. By default all PCRs are zero initialized.
-       Value values of <PCRSPEC> are:
-        all: copy all pcrs
-        none: copy no pcrs (default)
-        <N>: copy pcr n
-        <X-Y>: copy pcrs x to y (inclusive)
-
-       These can also be combined by comma separation, for example:
-        hwinitpcrs=5,12-16
-       will copy pcrs 5, 12, 13, 14, 15, and 16.
-
-------------------------------
-REFERENCES
-------------------------------
-
-Berlios TPM Emulator:
-http://tpm-emulator.berlios.de/
diff --git a/stubdom/vtpmmgr/Makefile b/stubdom/vtpmmgr/Makefile
index af13b39..a39a22c 100644
--- a/stubdom/vtpmmgr/Makefile
+++ b/stubdom/vtpmmgr/Makefile
@@ -12,7 +12,9 @@
 XEN_ROOT=../..
 
 TARGET=vtpmmgr.a
-OBJS=vtpmmgr.o vtpm_cmd_handler.o vtpm_storage.o init.o tpmrsa.o tpm.o log.o
+OBJS=vtpmmgr.o vtpm_cmd_handler.o init.o tpmrsa.o tpm.o log.o
+OBJS += vtpm_disk.o disk_tpm.o disk_io.o disk_crypto.o disk_read.o disk_write.o
+OBJS += mgmt_authority.o
 
 CFLAGS+=-Werror -Iutil -Icrypto -Itcs
 CFLAGS+=-Wno-declaration-after-statement -Wno-unused-label
diff --git a/stubdom/vtpmmgr/README b/stubdom/vtpmmgr/README
deleted file mode 100644
index a70c1cc..0000000
--- a/stubdom/vtpmmgr/README
+++ /dev/null
@@ -1,75 +0,0 @@
-Copyright (c) 2010-2012 United States Government, as represented by
-the Secretary of Defense.  All rights reserved.
-November 12 2012
-Authors: Matthew Fioravante (JHUAPL),
-
-This document describes the operation and command line interface
-of vtpmmgr-stubdom. See docs/misc/vtpm.txt for details on the
-vTPM subsystem as a whole.
-
-
-------------------------------
-OPERATION
-------------------------------
-
-The vtpmmgr-stubdom implements a vTPM manager who has two major functions:
-
- - Securely store encryption keys for each of the vTPMS
- - Regulate access to the hardware TPM for the entire system
-
-The manager accepts commands from the vtpm-stubdom domains via the mini-os
-TPM backend driver. The vTPM manager communicates directly with hardware TPM
-using the mini-os tpm_tis driver.
-
-
-When the manager starts for the first time it will check if the TPM
-has an owner. If the TPM is unowned, it will attempt to take ownership
-with the supplied owner_auth (see below) and then create a TPM
-storage key which will be used to secure vTPM key data. Currently the
-manager only binds vTPM keys to the disk. In the future support
-for sealing to PCRs should be added.
-
-------------------------------
-COMMAND LINE ARGUMENTS
-------------------------------
-
-Command line arguments are passed to the domain via the 'extra'
-parameter in the VM config file. Each parameter is separated
-by white space. For example:
-
-extra="foo=bar baz"
-
-List of Arguments:
-------------------
-
-owner_auth=<AUTHSPEC>: Set the owner auth of the TPM. The default
-       is the well known owner auth of all ones.
-
-srk_auth=<AUTHSPEC>: Set the SRK auth for the TPM. The default is
-       the well known srk auth of all zeroes.
-       The possible values of <AUTHSPEC> are:
-        well-known: Use the well known auth (default)
-        random: Randomly generate an auth
-        hash: <HASH>: Use the given 40 character ASCII hex string
-        text: <STR>: Use sha1 hash of <STR>.
-
-tpmdriver=<DRIVER>: Which driver to use to talk to the hardware TPM.
-       Don't change this unless you know what you're doing.
-       The possible values of <DRIVER> are:
-        tpm_tis: Use the tpm_tis driver to talk directly to the TPM.
-               The domain must have access to TPM IO memory.  (default)
-        tpmfront: Use tpmfront to talk to the TPM. The domain must have
-               a tpmfront device setup to talk to another domain
-               which provides access to the TPM.
-
-The following options only apply to the tpm_tis driver:
-
-tpmiomem=<ADDR>: The base address of the hardware memory pages of the
-       TPM (default 0xfed40000).
-
-tpmirq=<IRQ>: The irq of the hardware TPM if using interrupts. A value of
-       "probe" can be set to probe for the irq. A value of 0
-       disabled interrupts and uses polling (default 0).
-
-tpmlocality=<LOC>: Attempt to use locality <LOC> of the hardware TPM.
-       (default 0)
diff --git a/stubdom/vtpmmgr/disk_crypto.c b/stubdom/vtpmmgr/disk_crypto.c
new file mode 100644
index 0000000..18718d0
--- /dev/null
+++ b/stubdom/vtpmmgr/disk_crypto.c
@@ -0,0 +1,231 @@
+#include <inttypes.h>
+#include <mini-os/byteorder.h>
+#include <polarssl/aes.h>
+#include <polarssl/sha2.h>
+#include <polarssl/ctr_drbg.h>
+
+#include "log.h"
+#include "vtpmmgr.h"
+#include "vtpm_disk.h"
+#include "disk_io.h"
+#include "disk_crypto.h"
+
+// XXX defining this stubs out all disk encryption for easier debugging
+#undef DISK_IS_PLAINTEXT
+
+void do_random(void *buf, size_t size)
+{
+       int rc = ctr_drbg_random(&vtpm_globals.ctr_drbg, buf, size);
+       if (rc) abort();
+}
+
+void aes_setup(aes_context *ctx, const struct key128 *key)
+{
+       aes_setkey_enc(ctx, (void*)key, 128);
+}
+
+static void aes_encrypt_ecb(void *target, const void *src, const aes_context 
*key_e)
+{
+       aes_crypt_ecb((void*)key_e, AES_ENCRYPT, src, target);
+}
+
+void aes_encrypt_one(void *target, const void *src, const struct key128 *key)
+{
+       aes_context ctx;
+       aes_setkey_enc(&ctx, (void*)key, 128);
+       aes_crypt_ecb(&ctx, AES_ENCRYPT, src, target);
+}
+
+void aes_decrypt_one(void *target, const void *src, const struct key128 *key)
+{
+       aes_context ctx;
+       aes_setkey_dec(&ctx, (void*)key, 128);
+       aes_crypt_ecb(&ctx, AES_DECRYPT, src, target);
+}
+
+static void aes_ctr_one(uint64_t out[2], uint64_t ctr[2], const aes_context 
*key_e)
+{
+#ifdef DISK_IS_PLAINTEXT
+       memset(out, 0, 16);
+#else
+       aes_encrypt_ecb(out, ctr, key_e);
+#endif
+       ctr[1]++;
+}
+
+void aes_encrypt_ctr(void *target, size_t target_size, const void *srcv, 
size_t pt_size, const aes_context *key_e)
+{
+       uint64_t ctr[2];
+       uint64_t tmp[2];
+       uint64_t *dst = target;
+       const uint64_t *src = srcv;
+
+       do_random(ctr, sizeof(ctr));
+       dst[0] = ctr[0];
+       dst[1] = ctr[1];
+       dst += 2;
+       target_size -= 16;
+
+       if (pt_size > target_size)
+               abort(); // invalid argument: target too small for plaintext
+
+       while (pt_size >= 16) {
+               aes_ctr_one(tmp, ctr, key_e);
+
+               dst[0] = tmp[0] ^ src[0];
+               dst[1] = tmp[1] ^ src[1];
+
+               dst += 2;
+               src += 2;
+               pt_size -= 16;
+               target_size -= 16;
+       }
+       if (pt_size) {
+               uint64_t stmp[2];
+               uint64_t dtmp[2];
+               memset(stmp, 0, 16);
+               memcpy(stmp, src, pt_size);
+
+               aes_ctr_one(tmp, ctr, key_e);
+
+               dtmp[0] = tmp[0] ^ stmp[0];
+               dtmp[1] = tmp[1] ^ stmp[1];
+               if (target_size < 16) {
+                       memcpy(dst, dtmp, target_size);
+                       return;
+               } else {
+                       memcpy(dst, dtmp, 16);
+                       target_size -= 16;
+               }
+       }
+       while (target_size >= 16) {
+               aes_ctr_one(dst, ctr, key_e);
+
+               dst += 2;
+               target_size -= 16;
+       }
+       if (target_size)
+               abort(); // invalid argument: overlarge target size is not a 
full block
+}
+
+void aes_decrypt_ctr(void *target, size_t pt_size, const void *srcv, size_t 
src_size, const aes_context *key_e)
+{
+       uint64_t ctr[2];
+       uint64_t tmp[2];
+       uint64_t *dst = target;
+       const uint64_t *src = srcv;
+
+       ctr[0] = src[0];
+       ctr[1] = src[1];
+       src += 2;
+       src_size -= 16;
+
+       if (pt_size > src_size)
+               abort(); // invalid argument: source too small for plaintext
+       // we discard src_size now
+
+       while (pt_size >= 16) {
+               aes_ctr_one(tmp, ctr, key_e);
+               dst[0] = tmp[0] ^ src[0];
+               dst[1] = tmp[1] ^ src[1];
+
+               dst += 2;
+               src += 2;
+               pt_size -= 16;
+       }
+       if (pt_size) {
+               uint64_t stmp[2];
+               uint64_t dtmp[2];
+               memset(stmp, 0, 16);
+               memcpy(stmp, src, pt_size);
+
+               aes_ctr_one(tmp, ctr, key_e);
+
+               dtmp[0] = tmp[0] ^ stmp[0];
+               dtmp[1] = tmp[1] ^ stmp[1];
+               memcpy(dst, dtmp, pt_size);
+       }
+}
+
+static void shl_128_mod_hex87(struct mac128 *dst, const struct mac128 *src)
+{
+       int i;
+       int carry = 0x87 * !!(src->bits[0] & 0x80);
+       for(i=0; i < 15; i++)
+               dst->bits[i] = (src->bits[i] << 1) | (src->bits[i+1] >> 7);
+       dst->bits[15] = (src->bits[15] << 1) ^ carry;
+}
+
+static void xor128(struct mac128 *dst, const struct mac128 *s1, const struct 
mac128 *s2)
+{
+       int i;
+       for(i=0; i < 16; i++)
+               dst->bits[i] = s1->bits[i] ^ s2->bits[i];
+}
+
+void aes_cmac(struct mac128 *target, const void *src, size_t size, const 
aes_context *key)
+{
+       const struct mac128 *M = src;
+       struct mac128 x, y, L, K1, K2;
+       int i;
+       size_t bsize = (size - 1) / 16;
+
+       memset(&x, 0, sizeof(x));
+       aes_encrypt_ecb(&L, &x, key);
+       shl_128_mod_hex87(&K1, &L);
+       shl_128_mod_hex87(&K2, &K1);
+
+       for(i=0; i < bsize; i++) {
+               xor128(&y, &x, &M[i]);
+               aes_encrypt_ecb(&x, &y, key);
+       }
+       if (size & 0xF) {
+               struct mac128 z;
+               memset(&z, 0, sizeof(z));
+               memcpy(&z, M + bsize, size & 0xF);
+               xor128(&y, &x, &K2);
+               xor128(&x, &y, &z);
+       } else {
+               xor128(&y, &x, &K1);
+               xor128(&x, &y, M + bsize);
+       }
+       aes_encrypt_ecb(target, &x, key);
+}
+
+static int verify_128(const void *a, const void* b)
+{
+       const volatile uint64_t *x = a;
+       const volatile uint64_t *y = b;
+       if ((x[0] ^ y[0]) | (x[1] ^ y[1]))
+               return 1;
+       return 0;
+}
+
+int aes_cmac_verify(const struct mac128 *target, const void *src, size_t size, 
const aes_context *key)
+{
+       struct mac128 mac;
+       aes_cmac(&mac, src, size, key);
+       return verify_128(&mac, target);
+}
+
+static int verify_256(const void *a, const void* b)
+{
+       const volatile uint64_t *x = a;
+       const volatile uint64_t *y = b;
+       if ((x[0] ^ y[0]) | (x[1] ^ y[1]) | (x[2] ^ y[2]) | (x[3] ^ y[3]))
+               return 1;
+       return 0;
+}
+
+void sha256(struct hash256 *target, const void *src, size_t size)
+{
+       void* dst = target;
+       sha2(src, size, dst, 0);
+}
+
+int sha256_verify(const struct hash256 *targ, const void *data, size_t size)
+{
+       struct hash256 hash;
+       sha256(&hash, data, size);
+       return verify_256(&hash, targ);
+}
diff --git a/stubdom/vtpmmgr/disk_crypto.h b/stubdom/vtpmmgr/disk_crypto.h
new file mode 100644
index 0000000..faae9ab
--- /dev/null
+++ b/stubdom/vtpmmgr/disk_crypto.h
@@ -0,0 +1,17 @@
+#ifndef __VTPMMGR_DISK_CRYPTO_H
+#define __VTPMMGR_DISK_CRYPTO_H
+
+void do_random(void *buf, size_t size);
+void aes_encrypt_one(void *target, const void *src, const struct key128 *key);
+void aes_decrypt_one(void *target, const void *src, const struct key128 *key);
+
+void aes_setup(aes_context *ctx, const struct key128 *key);
+void aes_encrypt_ctr(void *target, size_t target_size, const void *srcv, 
size_t src_size, const aes_context *key_e);
+void aes_decrypt_ctr(void *target, size_t target_size, const void *srcv, 
size_t src_size, const aes_context *key_e);
+void aes_cmac(struct mac128 *target, const void *src, size_t size, const 
aes_context *key);
+int aes_cmac_verify(const struct mac128 *target, const void *src, size_t size, 
const aes_context *key);
+
+void sha256(struct hash256 *target, const void *src, size_t size);
+int sha256_verify(const struct hash256 *targ, const void *data, size_t size);
+
+#endif
diff --git a/stubdom/vtpmmgr/disk_format.h b/stubdom/vtpmmgr/disk_format.h
new file mode 100644
index 0000000..bc20fbb
--- /dev/null
+++ b/stubdom/vtpmmgr/disk_format.h
@@ -0,0 +1,193 @@
+#ifndef __VTPMMGR_DISK_FORMAT_H
+#define __VTPMMGR_DISK_FORMAT_H
+
+static const uint8_t TPM_MGR_MAGIC[12] = {
+       'T','P','M',0xfe,'M','G','R',0xdd,'D','O','M',0x00
+};
+
+/**
+ * Sector 0 on disk: stored in plaintext
+ */
+struct disk_header {
+       char magic[12];
+#define TPM_MGR_VERSION 0
+       be32_t version;
+};
+
+/**
+ * Raw contents of disk sectors that need both encryption and authentication
+ */
+struct disk_crypt_sector_plain {
+       struct mac128 mac;
+       union {
+               struct {
+                       uint8_t iv[16];
+                       char data[4096-32];
+               };
+               uint8_t iv_data[4096-16];
+       };
+};
+
+/**
+ * Contents of the sealed blob in the root seal list
+ */
+struct disk_root_sealed_data {
+#define DISK_ROOT_BOUND_MAGIC "Root"
+       char magic[4];
+       uuid_t tpm_manager_uuid;
+
+       be32_t nvram_slot;
+       struct tpm_authdata nvram_auth;
+       be32_t counter_index;
+       struct tpm_authdata counter_auth;
+
+       /* encrypted (AES-ECB) with key from NVRAM */
+       struct key128 tm_key;
+};
+
+/**
+ * Contents of the sealed blob in a group's seal list
+ */
+struct disk_group_sealed_data {
+#define DISK_GROUP_BOUND_MAGIC "TGrp"
+       char magic[4];
+       uuid_t tpm_manager_uuid;
+       struct tpm_authdata aik_authdata;
+
+       struct key128 group_key;
+       struct key128 rollback_mac_key;
+};
+
+/**
+ * Contents of the seal_list_N sectors on disk (plaintext, linked list)
+ *
+ * The hdr field is unused except in sector 0
+ */
+struct disk_seal_list {
+       struct disk_header hdr;
+       be32_t length;
+       sector_t next;
+#define SEALS_PER_ROOT_SEAL_LIST 13
+       struct disk_seal_entry entry[SEALS_PER_ROOT_SEAL_LIST];
+};
+
+/**
+ * TODO - overflow for struct disk_group_boot_config_list
+ */
+struct disk_group_seal_list {
+       sector_t next;
+#define SEALS_PER_GROUP_SEAL_LIST 13
+       struct disk_seal_entry entry[SEALS_PER_GROUP_SEAL_LIST];
+};
+
+/**
+ * Rollback detection MAC entry
+ */
+struct disk_rb_mac_entry {
+       be32_t id;
+       struct mac128 mac;
+};
+
+#define NR_ENTRIES_PER_ROOT 16
+/**
+ * The area of the root sector protected by rollback MACs
+ */
+struct disk_root_sector_mac1_area {
+       be64_t sequence;
+       be32_t tpm_counter_value;
+
+       be32_t nr_groups;
+       struct hash256 group_hash[NR_ENTRIES_PER_ROOT];
+};
+
+/**
+ * Decrypted contents of the root sector (sector 1 and 2) on disk
+ */
+struct disk_root_sector {
+       struct disk_root_sector_mac1_area v;
+
+       sector_t group_loc[NR_ENTRIES_PER_ROOT];
+
+       uint8_t pad[8];
+
+       /* Rollback detection MACs */
+       be32_t nr_rb_macs;
+       sector_t rb_next_loc;
+       /* used if rb_macs overflows */
+       struct hash256 rb_next_hash;
+
+#define NR_RB_MACS_PER_ROOT 128
+       struct disk_rb_mac_entry rb_macs[NR_RB_MACS_PER_ROOT];
+};
+
+/**
+ * Hash tree for list expansion. Used for the list of groups in the root and 
for
+ * the list of vTPMs in a group.
+ */
+struct disk_itree_sector {
+#define NR_ENTRIES_PER_ITREE 112
+       sector_t location[NR_ENTRIES_PER_ITREE];
+       /* SECTOR-HASH { */
+       struct hash256 hash[NR_ENTRIES_PER_ITREE];
+       /* SECTOR-HASH } */
+};
+
+#define NR_ENTRIES_PER_GROUP_BASE 16
+/**
+ * Data that must remain constant if a group is not open
+ */
+struct disk_group_sector_mac3_area {
+       struct group_id_data id_data; /* MAC2 */
+       struct group_details details;
+       struct disk_group_boot_config_list boot_configs;
+
+       be32_t nr_vtpms;
+       struct hash256 vtpm_hash[NR_ENTRIES_PER_GROUP_BASE];
+};
+
+/**
+ * Group metadata sector
+ *
+ * Encrypted with TM_KEY - takes 16 bytes for IV; integrity from parent.
+ */
+struct disk_group_sector {
+       /* SECTOR-HASH { */
+       struct disk_group_sector_mac3_area v;
+
+       /* MAC(MAC3, group_key) */
+       struct mac128 group_mac;
+       /* SECTOR-HASH } */
+
+       sector_t vtpm_location[NR_ENTRIES_PER_GROUP_BASE];
+       sector_t boot_configs_next;
+};
+
+/**
+ * Data on a vTPM which is available when its group is not open
+ */
+struct disk_vtpm_plain {
+       uuid_t uuid;
+       be32_t flags;
+};
+
+/**
+ * Data on a vTPM which is only available when its group is open
+ */
+struct disk_vtpm_secret {
+       uint8_t data[64];
+};
+
+/**
+ * Contents of a vTPM data disk sector
+ *
+ * Encrypted with TM_KEY - takes 16 bytes for IV
+ */
+struct disk_vtpm_sector {
+       /* SECTOR-HASH { */
+       struct disk_vtpm_plain header[VTPMS_PER_SECTOR];
+       struct mac128 iv;
+       struct disk_vtpm_secret data[VTPMS_PER_SECTOR];
+       /* SECTOR-HASH } */
+};
+
+#endif
diff --git a/stubdom/vtpmmgr/disk_io.c b/stubdom/vtpmmgr/disk_io.c
new file mode 100644
index 0000000..02b4fdd
--- /dev/null
+++ b/stubdom/vtpmmgr/disk_io.c
@@ -0,0 +1,125 @@
+#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 "vtpm_manager.h"
+#include "log.h"
+#include "uuid.h"
+
+#include "vtpmmgr.h"
+#include "vtpm_disk.h"
+#include "disk_tpm.h"
+#include "disk_io.h"
+
+static uint8_t disk_staging_buf[4096] __attribute__((aligned(4096)));
+
+static struct blkfront_dev* blkdev;
+static int blkfront_fd = -1;
+
+int vtpm_storage_init(void) {
+       struct blkfront_info info;
+       blkdev = init_blkfront(NULL, &info);
+       if (blkdev == NULL)
+               return -1;
+       blkfront_fd = blkfront_open(blkdev);
+       if (blkfront_fd < 0)
+               return -1;
+       return 0;
+}
+
+void* disk_read_sector(sector_t sector)
+{
+       uint32_t pos = be32_native(sector);
+       int rc;
+       vtpmloginfo(VTPM_LOG_VTPM, "disk_read_sector %x\n", pos);
+       lseek(blkfront_fd, pos * 4096, SEEK_SET);
+       rc = read(blkfront_fd, disk_staging_buf, 4096);
+       if (rc != 4096)
+               abort();
+       return disk_staging_buf;
+}
+
+void* disk_write_buf(void) { return disk_staging_buf; }
+
+void disk_write_sector(sector_t sector, void* buf, size_t siz)
+{
+       int rc;
+       uint32_t pos = be32_native(sector);
+       lseek(blkfront_fd, pos * 4096, SEEK_SET);
+       if (siz < 4096) {
+               if (buf != disk_staging_buf)
+                       memcpy(disk_staging_buf, buf, siz);
+               memset(disk_staging_buf + siz, 0, 4096 - siz);
+               buf = disk_staging_buf;
+       } else if (siz > 4096)
+               abort();
+
+       rc = write(blkfront_fd, buf, 4096);
+       if (rc != 4096)
+               abort();
+}
+
+void disk_write_barrier(void)
+{
+       blkfront_sync(blkdev);
+}
+
+enum inuse_value {
+       UNUSED,
+       SLOT_1,
+       SLOT_2,
+       SHARED
+};
+
+/* TODO make this dynamic to support using more than 2MB of disk */
+#define DISK_MAX_SECTOR 0x200
+
+/* The first 4 sectors are statically allocated:
+ *  0 - disk header (copy 1)
+ *  1 - disk header (copy 2)
+ *  2 - root sector (copy 1)
+ *  3 - root sector (copy 2)
+ */
+#define FIRST_DYNAMIC_SECTOR 4
+
+static uint8_t sector_inuse_map[DISK_MAX_SECTOR];
+
+static int active_slot(const struct mem_tpm_mgr *mgr)
+{
+       return 1 + mgr->active_root;
+}
+
+void disk_set_used(sector_t loc, const struct mem_tpm_mgr *mgr)
+{
+       uint32_t s = be32_native(loc);
+       if (s > DISK_MAX_SECTOR) {
+               printk("Attempted disk_set_used %x\n", s);
+               return;
+       }
+       sector_inuse_map[s] |= active_slot(mgr);
+}
+
+void disk_flush_slot(const struct mem_tpm_mgr *mgr)
+{
+       int i;
+       for(i = FIRST_DYNAMIC_SECTOR; i < DISK_MAX_SECTOR; i++)
+               sector_inuse_map[i] &= ~active_slot(mgr);
+}
+
+sector_t disk_find_free(const struct mem_tpm_mgr *mgr)
+{
+       int i;
+       for(i = FIRST_DYNAMIC_SECTOR; i < DISK_MAX_SECTOR; i++) {
+               if (sector_inuse_map[i])
+                       continue;
+               sector_inuse_map[i] = active_slot(mgr);
+               return native_be32(i);
+       }
+       // TODO more graceful error handling (in callers)
+       abort();
+}
diff --git a/stubdom/vtpmmgr/disk_io.h b/stubdom/vtpmmgr/disk_io.h
new file mode 100644
index 0000000..c0d35a5
--- /dev/null
+++ b/stubdom/vtpmmgr/disk_io.h
@@ -0,0 +1,25 @@
+#ifndef __VTPMMGR_DISK_IO_H
+#define __VTPMMGR_DISK_IO_H
+
+void* disk_read_sector(sector_t sector);
+void disk_write_sector(sector_t sector, void* buf, size_t siz);
+void* disk_write_buf(void);
+void disk_write_barrier(void);
+
+sector_t disk_find_free(const struct mem_tpm_mgr *mgr);
+void disk_flush_slot(const struct mem_tpm_mgr *mgr);
+void disk_set_used(sector_t loc, const struct mem_tpm_mgr *mgr);
+
+void disk_write_all(struct mem_tpm_mgr *mgr);
+
+static inline sector_t seal_loc(struct mem_tpm_mgr *mgr)
+{
+       return native_be32(mgr->active_root);
+}
+
+static inline sector_t root_loc(struct mem_tpm_mgr *mgr)
+{
+       return native_be32(2 + mgr->active_root);
+}
+
+#endif
diff --git a/stubdom/vtpmmgr/disk_read.c b/stubdom/vtpmmgr/disk_read.c
new file mode 100644
index 0000000..33aacdd
--- /dev/null
+++ b/stubdom/vtpmmgr/disk_read.c
@@ -0,0 +1,606 @@
+#include <console.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 "vtpm_manager.h"
+#include "log.h"
+#include "uuid.h"
+
+#include "vtpmmgr.h"
+#include "vtpm_disk.h"
+#include "disk_tpm.h"
+#include "disk_io.h"
+#include "disk_crypto.h"
+#include "disk_format.h"
+
+static int disk_read_crypt_sector(void *data, size_t size, sector_t block, 
const struct mem_tpm_mgr *mgr)
+{
+       struct disk_crypt_sector_plain *sector = disk_read_sector(block);
+       if (!sector)
+               return 2;
+
+       if (aes_cmac_verify(&sector->mac, sector->data, sizeof(sector->data), 
&mgr->tm_key_e))
+               return 2;
+
+       aes_decrypt_ctr(data, size, sector->iv_data, sizeof(sector->iv_data), 
&mgr->tm_key_e);
+       return 0;
+}
+
+static void group_free(struct mem_group *group)
+{
+       int i, j;
+       if (!group)
+               return;
+       if (group->data) {
+               for (i = 0; i < group->nr_pages; i++) {
+                       for (j = 0; j < group->data[i].size; j++) {
+                               free(group->data[i].vtpms[j]);
+                       }
+               }
+               free(group->data);
+       }
+       free(group->seals);
+       free(group);
+}
+
+static void mgr_free(struct mem_tpm_mgr *mgr)
+{
+       int i;
+       if (!mgr)
+               return;
+       if (mgr->groups) {
+               for(i=0; i < mgr->nr_groups; i++)
+                       group_free(mgr->groups[i].v);
+               free(mgr->groups);
+       }
+       free(mgr);
+}
+
+/* Open the group keys from one of the sealed strutures */
+static int find_group_key(struct mem_group *dst,
+               const struct disk_group_sector *group,
+               const struct mem_tpm_mgr *parent)
+{
+       int i, rc, rv = 1;
+       struct hash160 buf;
+       struct disk_group_sealed_data sealed;
+
+       dst->nr_seals = be32_native(group->v.boot_configs.nr_cfgs);
+       if (dst->nr_seals > NR_SEALS_PER_GROUP)
+               return 3; // TODO support spill to extra pages
+
+       dst->seals = calloc(dst->nr_seals, sizeof(dst->seals[0]));
+       if (!dst->seals) {
+               vtpmlogerror(VTPM_LOG_VTPM, "find_group_key alloc %x\n", 
dst->nr_seals);
+               return 2;
+       }
+
+       for(i=0; i < dst->nr_seals; i++) {
+               const struct disk_seal_entry *cfg = 
&group->v.boot_configs.entry[i];
+               dst->seals[i].pcr_selection = cfg->pcr_selection;
+               memcpy(&dst->seals[i].digest_release, &cfg->digest_release, 20);
+
+               TPM_pcr_digest(&buf, cfg->pcr_selection);
+               if (memcmp(&buf, &cfg->digest_release, 20))
+                       continue;
+               rc = TPM_disk_unseal(&sealed, sizeof(sealed), cfg);
+               if (rc)
+                       continue;
+               if (memcmp(&sealed.magic, DISK_GROUP_BOUND_MAGIC, 4))
+                       continue;
+               if (memcmp(sealed.tpm_manager_uuid, parent->uuid, 16))
+                       continue;
+
+               memcpy(&dst->rollback_mac_key, &sealed.rollback_mac_key, 16);
+               memcpy(&dst->group_key, &sealed.group_key, 16);
+               memcpy(&dst->aik_authdata, &sealed.aik_authdata, 20);
+               rv = 0;
+       }
+
+       // cache the list to allow writes without touching the TPM
+       memcpy(&dst->seal_bits, &group->v.boot_configs, sizeof(dst->seal_bits));
+       dst->flags |= MEM_GROUP_FLAG_SEAL_VALID;
+
+       return rv;
+}
+
+static int parse_root_key(struct mem_tpm_mgr *dst, struct disk_seal_entry *src)
+{
+       int rc;
+       struct disk_root_sealed_data sealed;
+
+       rc = TPM_disk_unseal(&sealed, sizeof(sealed), src);
+       if (rc)
+               return rc;
+
+       if (memcmp(&sealed.magic, DISK_ROOT_BOUND_MAGIC, 4))
+               return 1;
+
+       rc = TPM_disk_nvread(&dst->nv_key, 16, sealed.nvram_slot, 
sealed.nvram_auth);
+       if (rc)
+               return rc;
+
+       // TODO when an NV slot in the physical TPM is used to populate nv_key,
+       // that value should be used to mask the master key so that the value
+       // can be changed to revoke old disk state
+#if 0
+       aes_decrypt_one(&dst->tm_key, &sealed.tm_key, &dst->nv_key);
+#else
+       memcpy(&dst->tm_key, &sealed.tm_key, 16);
+#endif
+
+       memcpy(dst->uuid, sealed.tpm_manager_uuid, 16);
+       dst->nvram_slot = sealed.nvram_slot;
+       memcpy(&dst->nvram_auth, &sealed.nvram_auth, sizeof(struct 
tpm_authdata));
+       dst->counter_index = sealed.counter_index;
+       memcpy(&dst->counter_auth, &sealed.counter_auth, sizeof(struct 
tpm_authdata));
+
+       return 0;
+}
+
+static struct mem_tpm_mgr *find_root_key(int active_root)
+{
+       sector_t seal_list = native_be32(active_root);
+       struct disk_seal_list *seal = disk_read_sector(seal_list);
+       struct hash160 buf;
+       int i, rc, nr;
+       struct mem_tpm_mgr *dst;
+
+       if (memcmp(seal->hdr.magic, TPM_MGR_MAGIC, 12))
+               return NULL;
+
+       if (be32_native(seal->hdr.version) != TPM_MGR_VERSION)
+               return NULL;
+
+       dst = calloc(1, sizeof(*dst));
+       dst->active_root = active_root;
+
+       for (nr = 0; nr < 100; nr++) {
+               disk_set_used(seal_list, dst);
+               uint32_t nr_seals = be32_native(seal->length);
+               if (nr_seals > SEALS_PER_ROOT_SEAL_LIST)
+                       break;
+               for (i = 0; i < nr_seals; i++) {
+                       struct disk_seal_entry *src = &seal->entry[i];
+
+                       TPM_pcr_digest(&buf, src->pcr_selection);
+                       if (memcmp(&buf, &src->digest_release, 20))
+                               continue;
+
+                       rc = parse_root_key(dst, src);
+                       if (rc)
+                               continue;
+                       return dst;
+               }
+               seal_list = seal->next;
+               if (seal_list.value == 0)
+                       break;
+               seal = disk_read_sector(seal_list);
+       }
+       mgr_free(dst);
+       return NULL;
+}
+
+/* Load and verify one sector's worth of vTPMs. This loads all the vTPM entries
+ * and decrypts their state data into memory.
+ */
+static int load_verify_vtpm_page(struct mem_vtpm_page *dst, int base,
+               const struct mem_tpm_mgr *mgr, const aes_context *group_key)
+{
+       struct disk_vtpm_sector pt;
+       int i, rc;
+
+       disk_set_used(dst->disk_loc, mgr);
+
+       rc = disk_read_crypt_sector(&pt, sizeof(pt), dst->disk_loc, mgr);
+       if (rc) {
+               printk("Malformed sector %d\n", be32_native(dst->disk_loc));
+               return rc;
+       }
+       
+       rc = sha256_verify(&dst->disk_hash, &pt, sizeof(pt));
+       if (rc) {
+               printk("Hash mismatch in sector %d\n", 
be32_native(dst->disk_loc));
+               return rc;
+       }
+
+       if (!group_key)
+               return 0;
+
+       aes_decrypt_ctr(pt.data, sizeof(pt.data), &pt.iv, sizeof(pt.data) + 16, 
group_key);
+
+       for (i = 0; i < dst->size; i++) {
+               struct mem_vtpm *vtpm = calloc(1, sizeof(*vtpm));
+               dst->vtpms[i] = vtpm;
+               memcpy(vtpm->uuid, pt.header[i].uuid, 16);
+               memcpy(vtpm->data, pt.data[i].data, 64);
+               vtpm->flags = be32_native(pt.header[i].flags);
+               vtpm->index_in_parent = i + base;
+       }
+       return 0;
+}
+
+static int load_verify_vtpm_pages(struct mem_group *group, int base, int size,
+               const struct hash256 *hash, const sector_t *loc,
+               const struct mem_tpm_mgr *mgr, const aes_context *group_key)
+{
+       int i, rc;
+       struct mem_vtpm_page *page = group->data + base;
+
+       /* base was in terms of sectors; convert to vtpms */
+       base *= VTPMS_PER_SECTOR;
+
+       for (i = 0; i < size; i++) {
+               page->disk_hash = hash[i];
+               page->disk_loc = loc[i];
+               if (group->nr_vtpms - base > VTPMS_PER_SECTOR)
+                       page->size = VTPMS_PER_SECTOR;
+               else
+                       page->size = group->nr_vtpms - base;
+               rc = load_verify_vtpm_page(page, base, mgr, group_key);
+               if (rc)
+                       return rc;
+               base += VTPMS_PER_SECTOR;
+       }
+
+       return 0;
+}
+
+static int load_verify_vtpm_itree(struct mem_group_hdr *hdr, int base, int 
nr_entries,
+               const struct hash256 *hash, const sector_t *loc, int hsize,
+               const struct mem_tpm_mgr *mgr, const aes_context *group_key);
+
+static int load_verify_vtpm_itree(struct mem_group_hdr *hdr, int base, int 
nr_entries,
+               const struct hash256 *hash, const sector_t *loc, int hsize,
+               const struct mem_tpm_mgr *mgr, const aes_context *group_key)
+{
+       int i, rc, incr = 1, inuse_base = hdr->disk_nr_inuse, lsize;
+
+       // increase tree depth until all entries fit
+       while (nr_entries > incr * hsize)
+               incr *= NR_ENTRIES_PER_ITREE;
+
+       // save the list of used sectors (itree and vtpm) in the header
+       lsize = 1 + (nr_entries - 1) / incr;
+       hdr->disk_nr_inuse += lsize;
+       hdr->disk_inuse = realloc(hdr->disk_inuse, hdr->disk_nr_inuse * 
sizeof(sector_t));
+       memcpy(&hdr->disk_inuse[inuse_base], loc, lsize * sizeof(sector_t));
+
+       // if the entries already fit, process vtpm pages
+       if (nr_entries <= hsize)
+               return load_verify_vtpm_pages(hdr->v, base, nr_entries, hash, 
loc, mgr, group_key);
+
+       for (i = 0; i * incr < nr_entries; i++) {
+               struct disk_itree_sector pt;
+               int child_entries = incr;
+
+               // the last sector is not completely full
+               if (nr_entries - i * incr < incr)
+                       child_entries = nr_entries - i * incr;
+
+               disk_set_used(loc[i], mgr);
+               hdr->disk_inuse[inuse_base++] = loc[i];
+
+               rc = disk_read_crypt_sector(&pt, sizeof(pt), loc[i], mgr);
+               if (rc) {
+                       printk("Malformed sector %d\n", be32_native(loc[i]));
+                       return rc;
+               }
+
+               rc = sha256_verify(&hash[i], pt.hash, sizeof(pt.hash));
+               if (rc) {
+                       printk("Hash mismatch in sector %d\n", 
be32_native(loc[i]));
+                       return rc;
+               }
+
+               rc = load_verify_vtpm_itree(hdr, base, child_entries, pt.hash, 
pt.location,
+                               NR_ENTRIES_PER_ITREE, mgr, group_key);
+               if (rc)
+                       return rc;
+
+               base += incr;
+       }
+
+       return 0;
+}
+
+/* Load and verify one group's data structure, including its vTPMs.
+ */
+static int load_verify_group(struct mem_group_hdr *dst, const struct 
mem_tpm_mgr *mgr)
+{
+       struct mem_group *group;
+       struct disk_group_sector disk;
+       int rc;
+       aes_context key_e;
+       aes_context *opened_key = NULL;
+
+       disk_set_used(dst->disk_loc, mgr);
+
+       rc = disk_read_crypt_sector(&disk, sizeof(disk), dst->disk_loc, mgr);
+       if (rc) {
+               printk("Malformed sector %d\n", be32_native(dst->disk_loc));
+               return rc;
+       }
+       
+       rc = sha256_verify(&dst->disk_hash, &disk.v, sizeof(disk.v) + 
sizeof(disk.group_mac));
+       if (rc) {
+               printk("Hash mismatch in sector %d\n", 
be32_native(dst->disk_loc));
+               return rc;
+       }
+       
+       dst->v = group = calloc(1, sizeof(*group));
+
+       rc = find_group_key(group, &disk, mgr);
+       if (rc == 0) {
+               opened_key = &key_e;
+               /* Verify the group with the group's own key */
+               aes_setup(opened_key, &group->group_key);
+               if (aes_cmac_verify(&disk.group_mac, &disk.v, sizeof(disk.v), 
opened_key)) {
+                       printk("Group CMAC failed\n");
+                       return 2;
+               }
+
+               memcpy(&group->id_data, &disk.v.id_data, 
sizeof(group->id_data));
+               memcpy(&group->details, &disk.v.details, 
sizeof(group->details));
+       } else if (rc == 1) {
+               // still need to walk the vtpm list
+               rc = 0;
+       } else {
+               printk("Group key unsealing failed\n");
+               return rc;
+       }
+
+       group->nr_vtpms = be32_native(disk.v.nr_vtpms);
+       group->nr_pages = (group->nr_vtpms + VTPMS_PER_SECTOR - 1) / 
VTPMS_PER_SECTOR;
+
+       group->data = calloc(group->nr_pages, sizeof(group->data[0]));
+
+       rc = load_verify_vtpm_itree(dst, 0, group->nr_pages, disk.v.vtpm_hash,
+                       disk.vtpm_location, NR_ENTRIES_PER_GROUP_BASE, mgr, 
opened_key);
+
+       if (!opened_key) {
+               /* remove the struct */
+               free(group->data);
+               free(group->seals);
+               free(group);
+               dst->v = NULL;
+       }
+
+       return rc;
+}
+
+static int load_root_pre(struct disk_root_sector *root, struct mem_tpm_mgr 
*dst)
+{
+       int rc;
+
+       aes_setup(&dst->tm_key_e, &dst->tm_key);
+
+       rc = disk_read_crypt_sector(root, sizeof(*root), root_loc(dst), dst);
+
+       if (rc) {
+               vtpmloginfo(VTPM_LOG_VTPM, "root cmac verify failed in slot 
%d\n", dst->active_root);
+               return 2;
+       }
+
+       dst->root_seals_valid = 1 + dst->active_root;
+       dst->sequence = be64_native(root->v.sequence);
+
+       return 0;
+}
+
+static int load_verify_group_itree(struct mem_tpm_mgr *dst, int base, int 
nr_entries,
+               const struct hash256 *hash, const sector_t *loc, int hsize);
+
+static int load_verify_group_itree(struct mem_tpm_mgr *dst, int base, int 
nr_entries,
+               const struct hash256 *hash, const sector_t *loc, int hsize)
+{
+       int i, rc, incr = 1;
+
+       if (nr_entries <= hsize) {
+               for(i=0; i < nr_entries; i++) {
+                       struct mem_group_hdr *group = dst->groups + base + i;
+                       group->disk_loc = loc[i];
+                       memcpy(&group->disk_hash, &hash[i], 
sizeof(group->disk_hash));
+                       rc = load_verify_group(group, dst);
+                       if (rc) {
+                               printk("Error loading group %d\n", base + i);
+                               return rc;
+                       }
+               }
+               return 0;
+       }
+
+       // increase tree depth until all entries fit
+       while (nr_entries > incr * hsize)
+               incr *= NR_ENTRIES_PER_ITREE;
+
+       for (i = 0; i * incr < nr_entries; i++) {
+               struct disk_itree_sector pt;
+               int child_entries = incr;
+
+               // the last sector is not completely full
+               if (nr_entries - i * incr < incr)
+                       child_entries = nr_entries - i * incr;
+
+               disk_set_used(loc[i], dst);
+
+               rc = disk_read_crypt_sector(&pt, sizeof(pt), loc[i], dst);
+               if (rc) {
+                       printk("Malformed sector %d\n", be32_native(loc[i]));
+                       return rc;
+               }
+
+               rc = sha256_verify(&hash[i], pt.hash, sizeof(pt.hash));
+               if (rc) {
+                       printk("Hash mismatch in sector %d\n", 
be32_native(loc[i]));
+                       return rc;
+               }
+
+               rc = load_verify_group_itree(dst, base, child_entries, pt.hash, 
pt.location, NR_ENTRIES_PER_ITREE);
+               if (rc)
+                       return rc;
+
+               base += incr;
+       }
+
+       return 0;
+}
+
+static int load_root_post(struct mem_tpm_mgr *dst, const struct 
disk_root_sector *root)
+{
+       int rc, i, j;
+       uint32_t nr_disk_rbs = be32_native(root->nr_rb_macs);
+
+       rc = TPM_disk_check_counter(dst->counter_index, dst->counter_auth,
+                       root->v.tpm_counter_value);
+       if (rc)
+               return 2;
+       dst->counter_value = root->v.tpm_counter_value;
+
+       dst->nr_groups = be32_native(root->v.nr_groups);
+       dst->groups = calloc(sizeof(dst->groups[0]), dst->nr_groups);
+
+       if (!dst->groups) {
+               vtpmlogerror(VTPM_LOG_VTPM, "load_root_post alloc %x\n", 
dst->nr_groups);
+               return 2;
+       }
+
+       rc = load_verify_group_itree(dst, 0, dst->nr_groups,
+                       root->v.group_hash, root->group_loc, 
NR_ENTRIES_PER_ROOT);
+       if (rc)
+               return rc;
+
+       /* Sanity check: group0 must be open */
+       if (!dst->groups[0].v) {
+               printk("Error opening group 0\n");
+               return 2;
+       }
+
+       /* TODO support for spilling rollback list */
+       if (nr_disk_rbs > NR_RB_MACS_PER_ROOT)
+               return 3;
+
+       i = 0;
+       j = 0;
+       while (i < dst->nr_groups) {
+               aes_context key_e;
+               struct mem_group_hdr *group = &dst->groups[i];
+               struct mem_group *groupv = group->v;
+               const struct disk_rb_mac_entry *ent = &root->rb_macs[j];
+
+               if (!groupv) {
+                       i++;
+                       // this group is not open - no need to verify now
+                       continue;
+               }
+
+               if (be32_native(ent->id) < i) {
+                       // this entry is for a group that is not open
+                       j++;
+                       continue;
+               }
+
+               if (j >= nr_disk_rbs || be32_native(ent->id) != i) {
+                       // TODO allow delegation
+                       if (!(groupv->details.flags.value & 
FLAG_ROLLBACK_DETECTED)) {
+                               groupv->details.flags.value |= 
FLAG_ROLLBACK_DETECTED;
+                               group->disk_loc.value = 0;
+                       }
+                       i++;
+                       continue;
+               }
+
+               aes_setup(&key_e, &groupv->rollback_mac_key);
+               if (aes_cmac_verify(&ent->mac, &root->v, sizeof(root->v), 
&key_e)) {
+                       if (!(groupv->details.flags.value & 
FLAG_ROLLBACK_DETECTED)) {
+                               groupv->details.flags.value |= 
FLAG_ROLLBACK_DETECTED;
+                               group->disk_loc.value = 0;
+                       }
+               }
+               i++; j++;
+       }
+
+       return 0;
+}
+
+int vtpm_load_disk(void)
+{
+       struct disk_root_sector root1, root2;
+       int rc = 0;
+       TPM_read_pcrs();
+
+       printk("TPM Manager - disk format %d\n", TPM_MGR_VERSION);
+       printk(" root seal: %lu; sector of %d: %lu\n",
+               sizeof(struct disk_root_sealed_data), SEALS_PER_ROOT_SEAL_LIST, 
sizeof(struct disk_seal_list));
+       printk(" root: %lu v=%lu\n", sizeof(root1), sizeof(root1.v));
+       printk(" itree: %lu; sector of %d: %lu\n",
+               4 + 32, NR_ENTRIES_PER_ITREE, sizeof(struct disk_itree_sector));
+       printk(" group: %lu v=%lu id=%lu md=%lu\n",
+               sizeof(struct disk_group_sector), sizeof(struct 
disk_group_sector_mac3_area),
+               sizeof(struct group_id_data), sizeof(struct group_details));
+       printk(" group seal: %lu; %d in parent: %lu; sector of %d: %lu\n",
+               sizeof(struct disk_group_sealed_data), NR_SEALS_PER_GROUP, 
sizeof(struct disk_group_boot_config_list),
+               SEALS_PER_GROUP_SEAL_LIST, sizeof(struct disk_group_seal_list));
+       printk(" vtpm: %lu+%lu; sector of %d: %lu\n",
+               sizeof(struct disk_vtpm_plain), sizeof(struct disk_vtpm_secret),
+               VTPMS_PER_SECTOR, sizeof(struct disk_vtpm_sector));
+
+       struct mem_tpm_mgr *mgr1 = find_root_key(0);
+       struct mem_tpm_mgr *mgr2 = find_root_key(1);
+
+       rc = mgr1 ? load_root_pre(&root1, mgr1) : 0;
+       if (rc) {
+               mgr_free(mgr1);
+               mgr1 = NULL;
+       }
+
+       rc = mgr2 ? load_root_pre(&root2, mgr2) : 0;
+       if (rc) {
+               mgr_free(mgr2);
+               mgr2 = NULL;
+       }
+
+       printk("load_root_pre: %c/%c\n", mgr1 ? 'y' : 'n', mgr2 ? 'y' : 'n');
+
+       if (!mgr1 && !mgr2)
+               return 2;
+
+       if (mgr1 && mgr2 && mgr2->sequence > mgr1->sequence) {
+               rc = load_root_post(mgr2, &root2);
+               if (rc) {
+                       mgr_free(mgr2);
+                       mgr2 = NULL;
+               } else {
+                       mgr_free(mgr1);
+                       g_mgr = mgr2;
+                       return 0;
+               }
+       }
+       if (mgr1) {
+               rc = load_root_post(mgr1, &root1);
+               if (rc) {
+                       mgr_free(mgr1);
+               } else {
+                       mgr_free(mgr2);
+                       g_mgr = mgr1;
+                       return 0;
+               }
+       }
+       if (mgr2) {
+               rc = load_root_post(mgr2, &root2);
+               if (rc) {
+                       mgr_free(mgr2);
+               } else {
+                       g_mgr = mgr2;
+                       return 0;
+               }
+       }
+       printk("Could not read vTPM disk\n");
+
+       return 2;
+}
diff --git a/stubdom/vtpmmgr/disk_tpm.c b/stubdom/vtpmmgr/disk_tpm.c
new file mode 100644
index 0000000..6f5d556
--- /dev/null
+++ b/stubdom/vtpmmgr/disk_tpm.c
@@ -0,0 +1,238 @@
+/* TPM disk interface */
+#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 <polarssl/sha1.h>
+
+#include "tpm.h"
+#include "tcg.h"
+
+#include "vtpmmgr.h"
+#include "vtpm_disk.h"
+#include "disk_tpm.h"
+
+// Print out input/output of seal/unseal operations (includes keys)
+#undef DEBUG_SEAL_OPS
+
+#ifdef DEBUG_SEAL_OPS
+#include "marshal.h"
+#endif
+
+struct pcr_list {
+       TPM_DIGEST pcrs[24];
+};
+
+static struct pcr_list hwtpm;
+
+void TPM_read_pcrs(void)
+{
+       int i;
+       for(i=0; i < 24; i++)
+               TPM_PCR_Read(i, &hwtpm.pcrs[i]);
+}
+
+struct pcr_composite_3 {
+       be16_t sel_size;
+       uint8_t sel[3];
+       be32_t val_size;
+       uint8_t val[0];
+} __attribute__((packed));
+
+void TPM_pcr_digest(struct hash160 *buf, le32_t selection)
+{
+       int i;
+       int count = 0;
+       uint32_t sel = le32_native(selection);
+       struct pcr_composite_3 *v;
+       for(i=0; i < 24; i++) {
+               if (sel & (1 << i))
+                       count++;
+       }
+       v = alloca(sizeof(*v) + 20 * count);
+       v->sel_size = native_be16(3);
+       memcpy(v->sel, &selection, 3);
+       v->val_size = native_be32(20 * count);
+
+       count = 0;
+       for(i=0; i < 24; i++) {
+               if (sel & (1 << i)) {
+                       memcpy(v->val + 20 * count, &hwtpm.pcrs[i], 20);
+                       count++;
+               }
+       }
+
+       sha1((void*)v, sizeof(*v) + 20 * count, buf->bits);
+}
+
+
+int TPM_disk_seal(struct disk_seal_entry *dst, const void* src, size_t size)
+{
+       uint32_t rc;
+       TPM_PCR_INFO info;
+       TPM_STORED_DATA out;
+       TPM_AUTH_SESSION osap = TPM_AUTH_SESSION_INIT;
+       TPM_AUTHDATA sharedsecret;
+       TPM_AUTHDATA auth;
+
+       rc = TPM_OSAP(TPM_ET_KEYHANDLE, TPM_SRK_KEYHANDLE, 
(void*)&vtpm_globals.srk_auth,
+                       &sharedsecret, &osap);
+
+       if (rc) abort();
+
+#ifdef DEBUG_SEAL_OPS
+       int i;
+       printk("to-seal:");
+       for(i=0; i < size; i++)
+               printk(" %02x", ((uint8_t*)src)[i]);
+       printk("\n");
+#endif
+
+       memset(auth, 0, 20);
+       info.pcrSelection.sizeOfSelect = 3;
+       info.pcrSelection.pcrSelect = (void*)&dst->pcr_selection;
+       memcpy(&info.digestAtCreation, &dst->digest_at_seal, 20);
+       memcpy(&info.digestAtRelease, &dst->digest_release, 20);
+
+       rc = TPM_Seal(TPM_SRK_KEYHANDLE, 45, &info, size, src, &out,
+                       (void*)&sharedsecret, (void*)&auth, &osap);
+
+       TPM_TerminateHandle(osap.AuthHandle);
+
+#ifdef DEBUG_SEAL_OPS
+       printk("TPM_Seal rc=%d encDataSize=%d sealInfoSize=%d\n", rc, 
out.encDataSize, out.sealInfoSize);
+#endif
+       if (!rc)
+               memcpy(dst->sealed_data, out.encData, 256);
+
+#ifdef DEBUG_SEAL_OPS
+       uint8_t buf[512];
+       uint8_t *start = buf;
+       uint8_t *end = pack_TPM_STORED_DATA(buf, &out);
+       printk("stored_data:");
+       while (start != end) {
+               printk(" %02x", *start);
+               start++;
+       }
+       printk("\n");
+#endif
+
+       free_TPM_STORED_DATA(&out);
+       return rc;
+}
+
+int TPM_disk_unseal(void *dst, size_t size, const struct disk_seal_entry *src)
+{
+       uint32_t rc;
+       TPM_STORED_DATA in;
+       TPM_AUTH_SESSION oiap = TPM_AUTH_SESSION_INIT;
+       TPM_AUTHDATA auth;
+       uint32_t outSize = 0;
+       uint8_t *out = NULL;
+
+       rc = TPM_OIAP(&oiap);
+       if (rc) abort();
+
+       memset(auth, 0, 20);
+
+       in.ver = TPM_STRUCT_VER_1_1;
+       in.sealInfoSize = 45;
+       in.sealInfo.pcrSelection.sizeOfSelect = 3;
+       in.sealInfo.pcrSelection.pcrSelect = (void*)&src->pcr_selection;
+       memcpy(&in.sealInfo.digestAtCreation, &src->digest_at_seal, 20);
+       memcpy(&in.sealInfo.digestAtRelease, &src->digest_release, 20);
+       in.encDataSize = 256;
+       in.encData = (void*)src->sealed_data;
+
+#ifdef DEBUG_SEAL_OPS
+       uint8_t buf[512];
+       uint8_t *start = buf;
+       uint8_t *end = pack_TPM_STORED_DATA(buf, &in);
+       printk("stored_data:");
+       while (start != end) {
+               printk(" %02x", *start);
+               start++;
+       }
+       printk("\n");
+#endif
+
+       rc = TPM_Unseal(TPM_SRK_KEYHANDLE, &in, &outSize, &out,
+                       (void*)&vtpm_globals.srk_auth, (void*)&auth, 
&vtpm_globals.oiap, &oiap);
+
+       TPM_TerminateHandle(oiap.AuthHandle);
+
+#ifdef DEBUG_SEAL_OPS
+       printk("TPM_Unseal rc=%d outSize=%d size=%d\n", rc, outSize, size);
+#endif
+       if (!rc) {
+               memcpy(dst, out, size);
+#ifdef DEBUG_SEAL_OPS
+               printk("unsealed:");
+               int i;
+               for(i=0; i < size; i++)
+                       printk(" %02x", ((uint8_t*)dst)[i]);
+               printk("\n");
+#endif
+       }
+
+       free(out);
+
+       return rc;
+}
+
+int TPM_disk_nvalloc(be32_t *nvram_slot, struct tpm_authdata auth)
+{
+       // TODO-3
+       nvram_slot->value = 0;
+       return 0;
+}
+
+int TPM_disk_nvread(void *buf, size_t bufsiz, be32_t nvram_slot, struct 
tpm_authdata auth)
+{
+       // TODO-3
+       memset(buf, 0, bufsiz);
+       return 0;
+}
+
+int TPM_disk_nvwrite(void *buf, size_t bufsiz, be32_t nvram_slot, struct 
tpm_authdata auth)
+{
+       // TODO-3
+       return 0;
+}
+
+int TPM_disk_nvchange(be32_t nvram_slot, struct tpm_authdata old, struct 
tpm_authdata noo)
+{
+       // TODO-3
+       return 0;
+}
+
+int TPM_disk_alloc_counter(be32_t *slot, struct tpm_authdata auth, be32_t 
*value)
+{
+       // TODO-3
+       slot->value = 0;
+       value->value = 0;
+       return 0;
+}
+
+int TPM_disk_check_counter(be32_t slot, struct tpm_authdata auth, be32_t value)
+{
+       // TODO-3
+       return 0;
+}
+
+int TPM_disk_incr_counter(be32_t slot, struct tpm_authdata auth)
+{
+       // TODO-3
+       return 0;
+}
+
+int TPM_disk_change_counter(be32_t slot, struct tpm_authdata old, struct 
tpm_authdata noo)
+{
+       // TODO-3
+       return 0;
+}
diff --git a/stubdom/vtpmmgr/disk_tpm.h b/stubdom/vtpmmgr/disk_tpm.h
new file mode 100644
index 0000000..b235895
--- /dev/null
+++ b/stubdom/vtpmmgr/disk_tpm.h
@@ -0,0 +1,25 @@
+#ifndef __VTPMMGR_DISK_VTPM_H
+#define __VTPMMGR_DISK_VTPM_H
+#include "vtpm_disk.h"
+
+/* Read PCR values to determine which unseal to try */
+void TPM_read_pcrs(void);
+void TPM_pcr_digest(struct hash160 *buf, le32_t selection);
+
+/* Sealing for key storage */
+int TPM_disk_seal(struct disk_seal_entry *dst, const void* src, size_t size);
+int TPM_disk_unseal(void *dst, size_t size, const struct disk_seal_entry *src);
+
+/* NVRAM to allow revocation of TM-KEY */
+int TPM_disk_nvalloc(be32_t *nvram_slot, struct tpm_authdata auth);
+int TPM_disk_nvread(void *buf, size_t bufsiz, be32_t nvram_slot, struct 
tpm_authdata auth);
+int TPM_disk_nvwrite(void *buf, size_t bufsiz, be32_t nvram_slot, struct 
tpm_authdata auth);
+int TPM_disk_nvchange(be32_t nvram_slot, struct tpm_authdata old, struct 
tpm_authdata noo);
+
+/* Monotonic counters to detect rollback */
+int TPM_disk_alloc_counter(be32_t *slot, struct tpm_authdata auth, be32_t 
*value);
+int TPM_disk_check_counter(be32_t slot, struct tpm_authdata auth, be32_t 
value);
+int TPM_disk_incr_counter(be32_t slot, struct tpm_authdata auth);
+int TPM_disk_change_counter(be32_t slot, struct tpm_authdata old, struct 
tpm_authdata noo);
+
+#endif
diff --git a/stubdom/vtpmmgr/disk_write.c b/stubdom/vtpmmgr/disk_write.c
new file mode 100644
index 0000000..4c825c5
--- /dev/null
+++ b/stubdom/vtpmmgr/disk_write.c
@@ -0,0 +1,410 @@
+#include <console.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 "vtpm_manager.h"
+#include "log.h"
+#include "uuid.h"
+
+#include "vtpmmgr.h"
+#include "vtpm_disk.h"
+#include "disk_tpm.h"
+#include "disk_io.h"
+#include "disk_crypto.h"
+#include "disk_format.h"
+#include "mgmt_authority.h"
+
+static void disk_write_crypt_sector(sector_t *dst, const void *data, size_t 
size, const struct mem_tpm_mgr *mgr)
+{
+       struct disk_crypt_sector_plain *sector = disk_write_buf();
+       *dst = disk_find_free(mgr);
+       aes_encrypt_ctr(sector->iv_data, sizeof(sector->iv_data), data, size, 
&mgr->tm_key_e);
+       aes_cmac(&sector->mac, sector->data, sizeof(sector->data), 
&mgr->tm_key_e);
+       disk_write_sector(*dst, sector, sizeof(*sector));
+}
+
+/*
+ * Mark unchanged sectors on disk as being used
+ */
+static void disk_populate_used_vtpm(const struct mem_vtpm_page *src, const 
struct mem_tpm_mgr *mgr)
+{
+       if (be32_native(src->disk_loc) != 0)
+               disk_set_used(src->disk_loc, mgr);
+}
+
+/*
+ * Write out a vTPM page to disk, doing nothing if the existing copy is valid
+ */
+static void disk_write_vtpm_page(struct mem_vtpm_page *dst, const aes_context 
*auth_key,
+               const struct mem_tpm_mgr *mgr)
+{
+       struct disk_vtpm_sector pt;
+       int i;
+       memset(&pt, 0, sizeof(pt));
+       if (be32_native(dst->disk_loc) != 0)
+               return;
+
+       for(i=0; i < dst->size; i++) {
+               memcpy(pt.header[i].uuid, dst->vtpms[i]->uuid, 16);
+               memcpy(pt.data[i].data, dst->vtpms[i]->data, 64);
+               pt.header[i].flags = native_be32(dst->vtpms[i]->flags & 
VTPM_FLAG_DISK_MASK);
+       }
+       aes_encrypt_ctr(&pt.iv, sizeof(pt.data) + 16, &pt.data, 
sizeof(pt.data), auth_key);
+
+       sha256(&dst->disk_hash, &pt, sizeof(pt));
+
+       disk_write_crypt_sector(&dst->disk_loc, &pt, sizeof(pt), mgr);
+}
+
+/*
+ * Generate TPM seal blobs for a group's keys; do nothing if existing copy is 
valid
+ */
+static void generate_group_seals(struct mem_group *src, const struct 
mem_tpm_mgr *parent)
+{
+       int i;
+       struct disk_group_sealed_data sblob;
+
+       // previous seals are still valid, skip talking to the TPM
+       if (src->flags & MEM_GROUP_FLAG_SEAL_VALID)
+               return;
+
+       memcpy(&sblob.magic, DISK_GROUP_BOUND_MAGIC, 4);
+       memcpy(sblob.tpm_manager_uuid, parent->uuid, 16);
+       memcpy(&sblob.aik_authdata, &src->aik_authdata, 20);
+       memcpy(&sblob.group_key, &src->group_key, 16);
+       memcpy(&sblob.rollback_mac_key, &src->rollback_mac_key, 16);
+
+       /* TODO support for more than NR_SEALS_PER_GROUP seals */
+       if (src->nr_seals > NR_SEALS_PER_GROUP)
+               abort();
+
+       for(i=0; i < src->nr_seals; i++) {
+               struct disk_seal_entry *dst = &src->seal_bits.entry[i];
+               dst->pcr_selection = src->seals[i].pcr_selection;
+               memcpy(&dst->digest_release, &src->seals[i].digest_release, 20);
+               TPM_pcr_digest(&dst->digest_at_seal, dst->pcr_selection);
+               TPM_disk_seal(dst, &sblob, sizeof(sblob));
+       }
+       src->seal_bits.nr_cfgs = native_be32(src->nr_seals);
+
+       src->flags |= MEM_GROUP_FLAG_SEAL_VALID;
+}
+
+/*
+ * Mark unchanged sectors on disk as being used
+ */
+static void disk_populate_used_group(const struct mem_group_hdr *src, const 
struct mem_tpm_mgr *mgr)
+{
+       int i;
+       struct mem_group *group = src->v;
+       if (be32_native(src->disk_loc) != 0) {
+               // entire group is unchanged - mark group, itree, and vtpm 
sectors
+               // TODO mark other children (seal)
+               disk_set_used(src->disk_loc, mgr);
+               for(i = 0; i < src->disk_nr_inuse; i++)
+                       disk_set_used(src->disk_inuse[i], mgr);
+               return;
+       }
+
+       // unopened groups should never have been invalidated
+       if (!group)
+               abort();
+
+       for (i = 0; i < group->nr_pages; i++)
+               disk_populate_used_vtpm(&group->data[i], mgr);
+}
+
+static void disk_write_vtpm_itree(struct mem_group_hdr *hdr, int base, int 
nr_entries,
+               struct hash256 *hash, sector_t *loc, int hsize,
+               const aes_context *group_key, const struct mem_tpm_mgr *mgr);
+
+static void disk_write_vtpm_itree(struct mem_group_hdr *hdr, int base, int 
nr_entries,
+               struct hash256 *hash, sector_t *loc, int hsize,
+               const aes_context *group_key, const struct mem_tpm_mgr *mgr)
+{
+       int i, incr = 1, inuse_base, lsize;
+
+       while (nr_entries > incr * hsize)
+               incr *= NR_ENTRIES_PER_ITREE;
+
+       if (nr_entries <= hsize) {
+               struct mem_group *group = hdr->v;
+               for (i = 0; i < nr_entries; i++) {
+                       struct mem_vtpm_page *page = group->data + base + i;
+                       disk_write_vtpm_page(page, group_key, mgr);
+                       loc[i] = page->disk_loc;
+                       hash[i] = page->disk_hash;
+               }
+       } else {
+               for (i = 0; i * incr < nr_entries; i++) {
+                       struct disk_itree_sector pt;
+                       int child_entries = incr;
+
+                       // the last sector is not completely full
+                       if (nr_entries - i * incr < incr)
+                               child_entries = nr_entries - i * incr;
+
+                       disk_write_vtpm_itree(hdr, base, child_entries, 
pt.hash, pt.location,
+                                       NR_ENTRIES_PER_ITREE, group_key, mgr);
+
+                       sha256(&hash[i], &pt.hash, sizeof(pt.hash));
+                       disk_write_crypt_sector(&loc[i], &pt, sizeof(pt), mgr);
+
+                       base += incr;
+               }
+       }
+
+       // save the list of used sectors (itree and vtpm) in the header
+       inuse_base = hdr->disk_nr_inuse;
+       lsize = 1 + (nr_entries - 1) / incr;
+       hdr->disk_nr_inuse += lsize;
+       hdr->disk_inuse = realloc(hdr->disk_inuse, hdr->disk_nr_inuse * 
sizeof(sector_t));
+       memcpy(&hdr->disk_inuse[inuse_base], loc, lsize * sizeof(sector_t));
+}
+
+/*
+ * Write out a vTPM group sector and its children
+ */
+static void disk_write_group_sector(struct mem_group_hdr *src,
+               const struct mem_tpm_mgr *mgr)
+{
+       struct disk_group_sector disk;
+       struct mem_group *group = src->v;
+       aes_context key_e;
+
+       /* Don't write if the data hasn't changed */
+       if (be32_native(src->disk_loc) != 0)
+               return;
+
+       // if the group was not opened, it should not have been changed
+       if (!group)
+               abort();
+
+       memset(&disk, 0, sizeof(disk));
+       memcpy(&disk.v.id_data, &group->id_data, sizeof(disk.v.id_data));
+       memcpy(&disk.v.details, &group->details, sizeof(disk.v.details));
+
+       aes_setup(&key_e, &group->group_key);
+
+       disk.v.nr_vtpms = native_be32(group->nr_vtpms);
+
+       // regenerated
+       src->disk_nr_inuse = 0;
+
+       disk_write_vtpm_itree(src, 0, group->nr_pages, disk.v.vtpm_hash, 
disk.vtpm_location,
+                       NR_ENTRIES_PER_GROUP_BASE, &key_e, mgr);
+
+       generate_group_seals(group, mgr);
+       memcpy(&disk.v.boot_configs, &group->seal_bits, 
sizeof(group->seal_bits));
+
+       aes_cmac(&disk.group_mac, &disk.v, sizeof(disk.v), &key_e);
+       sha256(&src->disk_hash, &disk.v, sizeof(disk.v) + 
sizeof(disk.group_mac));
+       disk_write_crypt_sector(&src->disk_loc, &disk, sizeof(disk), mgr);
+}
+
+/*
+ * Write TPM seal blobs for the manager's keys, using the given group's list
+ * of valid configurations
+ */
+static void disk_write_seal_list(struct mem_tpm_mgr *mgr, struct mem_group 
*group)
+{
+       int i;
+       struct disk_seal_list *seal = disk_write_buf();
+       struct disk_root_sealed_data sblob;
+
+       if (mgr->root_seals_valid & (1 + mgr->active_root))
+               return;
+
+       memcpy(&sblob.magic, DISK_ROOT_BOUND_MAGIC, 4);
+       memcpy(sblob.tpm_manager_uuid, mgr->uuid, 16);
+       memcpy(&sblob.nvram_slot, &mgr->nvram_slot, 4);
+       memcpy(&sblob.nvram_auth, &mgr->nvram_auth, 20);
+       memcpy(&sblob.counter_index, &mgr->counter_index, 4);
+       memcpy(&sblob.counter_auth, &mgr->counter_auth, 20);
+
+       // TODO when an NV slot in the physical TPM is used to populate nv_key,
+       // that value should be used to mask the master key so that the value
+       // can be changed to revoke old disk state
+#if 0
+       aes_encrypt_one(&sblob.tm_key, &mgr->tm_key, &mgr->nv_key);
+#else
+       memcpy(&sblob.tm_key, &mgr->tm_key, 16);
+#endif
+
+       memset(seal, 0, sizeof(*seal));
+       seal->length = native_be32(group->nr_seals);
+
+       // TODO support for more entries
+       if (group->nr_seals > SEALS_PER_ROOT_SEAL_LIST)
+               abort();
+
+       for(i=0; i < group->nr_seals; i++) {
+               struct mem_seal *src = &group->seals[i];
+               struct disk_seal_entry *dst = &seal->entry[i];
+               dst->pcr_selection = src->pcr_selection;
+               memcpy(&dst->digest_release, &src->digest_release, 20);
+               TPM_pcr_digest(&dst->digest_at_seal, dst->pcr_selection);
+
+               TPM_disk_seal(dst, &sblob, sizeof(sblob));
+       }
+
+       memcpy(seal->hdr.magic, TPM_MGR_MAGIC, 12);
+       seal->hdr.version = native_be32(TPM_MGR_VERSION);
+
+       disk_write_sector(seal_loc(mgr), seal, sizeof(*seal));
+       mgr->root_seals_valid |= 1 + mgr->active_root;
+}
+
+/*
+ * Mark unchanged sectors on disk as being used
+ */
+static void disk_populate_used_mgr(const struct mem_tpm_mgr *mgr)
+{
+       int i;
+
+       // TODO walk the linked lists for seals, rb_macs here (when supported)
+
+       for(i=0; i < mgr->nr_groups; i++)
+               disk_populate_used_group(&mgr->groups[i], mgr);
+}
+
+static void disk_write_group_itree(struct mem_tpm_mgr *mgr, int base, int 
nr_entries,
+               struct hash256 *hash, sector_t *loc, int hsize);
+
+static void disk_write_group_itree(struct mem_tpm_mgr *mgr, int base, int 
nr_entries,
+               struct hash256 *hash, sector_t *loc, int hsize)
+{
+       int i, incr = 1;
+
+       if (nr_entries <= hsize) {
+               for(i=0; i < mgr->nr_groups; i++) {
+                       struct mem_group_hdr *group = mgr->groups + base + i;
+                       disk_write_group_sector(group, mgr);
+                       loc[i] = group->disk_loc;
+                       hash[i] = group->disk_hash;
+               }
+               return;
+       }
+
+       while (nr_entries > incr * hsize)
+               incr *= NR_ENTRIES_PER_ITREE;
+
+       for (i = 0; i * incr < nr_entries; i++) {
+               struct disk_itree_sector pt;
+               int child_entries = incr;
+
+               // the last sector is not completely full
+               if (nr_entries - i * incr < incr)
+                       child_entries = nr_entries - i * incr;
+
+               disk_write_group_itree(mgr, base, child_entries, pt.hash, 
pt.location, NR_ENTRIES_PER_ITREE);
+
+               sha256(&hash[i], &pt.hash, sizeof(pt.hash));
+               disk_write_crypt_sector(&loc[i], &pt, sizeof(pt), mgr);
+
+               base += incr;
+       }
+}
+
+/*
+ * Write out the root TPM Manager sector and its children
+ */
+static void disk_write_root_sector(struct mem_tpm_mgr *mgr)
+{
+       int i, j;
+       struct disk_root_sector root;
+       memset(&root, 0, sizeof(root));
+       root.v.sequence = native_be64(mgr->sequence);
+       root.v.tpm_counter_value = mgr->counter_value;
+
+       root.v.nr_groups = native_be32(mgr->nr_groups);
+
+       disk_write_group_itree(mgr, 0, mgr->nr_groups, root.v.group_hash, 
root.group_loc, NR_ENTRIES_PER_ROOT);
+
+       i = 0;
+       j = 0;
+       while (i < mgr->nr_groups) {
+               aes_context key_e;
+               struct mem_group_hdr *group = &mgr->groups[i];
+               struct mem_group *groupv = group->v;
+
+               if (!groupv) {
+                       i++;
+                       continue;
+               }
+               if (groupv->details.flags.value & FLAG_ROLLBACK_DETECTED) {
+                       i++;
+                       continue;
+               }
+               if (j >= NR_RB_MACS_PER_ROOT)
+                       break; // TODO support for nr_rb_macs > 128
+
+               aes_setup(&key_e, &groupv->rollback_mac_key);
+               root.rb_macs[j].id = native_be32(i);
+               aes_cmac(&root.rb_macs[j].mac, &root.v, sizeof(root.v), &key_e);
+               i++; j++;
+       }
+       root.nr_rb_macs = native_be32(j);
+
+       struct disk_crypt_sector_plain *root_sect = disk_write_buf();
+       aes_encrypt_ctr(root_sect->iv_data, sizeof(root_sect->iv_data), &root, 
sizeof(root), &mgr->tm_key_e);
+       aes_cmac(&root_sect->mac, &root_sect->data, sizeof(root_sect->data), 
&mgr->tm_key_e);
+       disk_write_sector(root_loc(mgr), root_sect, sizeof(*root_sect));
+}
+
+/*
+ * Write out changes to disk
+ */
+void disk_write_all(struct mem_tpm_mgr *mgr)
+{
+       disk_flush_slot(mgr);
+       disk_populate_used_mgr(mgr);
+       disk_write_root_sector(mgr);
+
+       disk_write_seal_list(mgr, mgr->groups[0].v);
+
+       disk_write_barrier();
+}
+
+/*
+ * Create a new (blank) TPM Manager disk image.
+ *
+ * Does not actually write anything to disk.
+ */
+int vtpm_new_disk(void)
+{
+       int rc;
+       struct mem_tpm_mgr *mgr = calloc(1, sizeof(*mgr));
+
+       do_random(mgr->uuid, 16);
+       do_random(&mgr->tm_key, 16);
+       do_random(&mgr->nvram_auth, 20);
+       do_random(&mgr->counter_auth, 20);
+       do_random(&mgr->nv_key, 16);
+
+       aes_setup(&mgr->tm_key_e, &mgr->tm_key);
+
+       // TODO postpone these allocs until first write?
+       rc = TPM_disk_nvalloc(&mgr->nvram_slot, mgr->nvram_auth);
+       if (rc)
+               return rc;
+
+       rc = TPM_disk_alloc_counter(&mgr->counter_index, mgr->counter_auth, 
&mgr->counter_value);
+       if (rc)
+               return rc;
+
+       mgr->nr_groups = 1;
+       mgr->groups = calloc(1, sizeof(mgr->groups[0]));
+       mgr->groups[0].v = vtpm_new_group(NULL);
+
+       TPM_disk_nvwrite(&mgr->nv_key, 16, mgr->nvram_slot, mgr->nvram_auth);
+
+       g_mgr = mgr;
+
+       return 0;
+}
diff --git a/stubdom/vtpmmgr/endian_int.h b/stubdom/vtpmmgr/endian_int.h
new file mode 100644
index 0000000..030f87b
--- /dev/null
+++ b/stubdom/vtpmmgr/endian_int.h
@@ -0,0 +1,72 @@
+#ifndef __VTPMMGR_ENDIAN_INT_H
+#define __VTPMMGR_ENDIAN_INT_H
+
+#include <mini-os/byteorder.h>
+
+/* These wrapper structs force the use of endian-to-CPU conversions */
+
+typedef struct be_int16 {
+       uint16_t value;
+} be16_t;
+
+typedef struct be_int32 {
+       uint32_t value;
+} be32_t;
+
+typedef struct le_int32 {
+       uint32_t value;
+} le32_t;
+
+typedef struct be_int64 {
+       uint64_t value;
+} be64_t;
+
+static inline uint16_t be16_native(be16_t v)
+{
+       return be16_to_cpu(v.value);
+}
+
+static inline uint32_t le32_native(le32_t v)
+{
+       return le32_to_cpu(v.value);
+}
+
+static inline uint32_t be32_native(be32_t v)
+{
+       return be32_to_cpu(v.value);
+}
+
+static inline uint64_t be64_native(be64_t v)
+{
+       return be64_to_cpu(v.value);
+}
+
+static inline be16_t native_be16(uint16_t v)
+{
+       be16_t rv;
+       rv.value = cpu_to_be16(v);
+       return rv;
+}
+
+static inline le32_t native_le32(uint32_t v)
+{
+       le32_t rv;
+       rv.value = cpu_to_le32(v);
+       return rv;
+}
+
+static inline be32_t native_be32(uint32_t v)
+{
+       be32_t rv;
+       rv.value = cpu_to_be32(v);
+       return rv;
+}
+
+static inline be64_t native_be64(uint64_t v)
+{
+       be64_t rv;
+       rv.value = cpu_to_be64(v);
+       return rv;
+}
+
+#endif
diff --git a/stubdom/vtpmmgr/init.c b/stubdom/vtpmmgr/init.c
index 33ac152..89643e7 100644
--- a/stubdom/vtpmmgr/init.c
+++ b/stubdom/vtpmmgr/init.c
@@ -48,7 +48,7 @@
 
 #include "log.h"
 #include "vtpmmgr.h"
-#include "vtpm_storage.h"
+#include "vtpm_disk.h"
 #include "tpm.h"
 #include "marshal.h"
 
@@ -64,16 +64,11 @@ struct Opts {
 };
 
 // --------------------------- Well Known Auths --------------------------
-const TPM_AUTHDATA WELLKNOWN_SRK_AUTH = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00,
+const TPM_AUTHDATA WELLKNOWN_AUTH = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
-const TPM_AUTHDATA WELLKNOWN_OWNER_AUTH = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff,
-   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-
 struct vtpm_globals vtpm_globals = {
    .tpm_fd = -1,
-   .storage_key = TPM_KEY_INIT,
-   .storage_key_handle = 0,
    .oiap = { .AuthHandle = 0 }
 };
 
@@ -247,42 +242,11 @@ abort_egress:
    return status;
 }
 
-static void init_storage_key(TPM_KEY* key) {
-   key->ver.major = 1;
-   key->ver.minor = 1;
-   key->ver.revMajor = 0;
-   key->ver.revMinor = 0;
-
-   key->keyUsage = TPM_KEY_BIND;
-   key->keyFlags = 0;
-   key->authDataUsage = TPM_AUTH_ALWAYS;
-
-   TPM_KEY_PARMS* p = &key->algorithmParms;
-   p->algorithmID = TPM_ALG_RSA;
-   p->encScheme = TPM_ES_RSAESOAEP_SHA1_MGF1;
-   p->sigScheme = TPM_SS_NONE;
-   p->parmSize = 12;
-
-   TPM_RSA_KEY_PARMS* r = &p->parms.rsa;
-   r->keyLength = RSA_KEY_SIZE;
-   r->numPrimes = 2;
-   r->exponentSize = 0;
-   r->exponent = NULL;
-
-   key->PCRInfoSize = 0;
-   key->encDataSize = 0;
-   key->encData = NULL;
-}
-
-static int parse_auth_string(char* authstr, BYTE* target, const TPM_AUTHDATA 
wellknown, int allowrandom) {
+static int parse_auth_string(char* authstr, BYTE* target) {
    int rc;
    /* well known owner auth */
    if(!strcmp(authstr, "well-known")) {
-      memcpy(target, wellknown, sizeof(TPM_AUTHDATA));
-   }
-   /* Create a randomly generated owner auth */
-   else if(allowrandom && !strcmp(authstr, "random")) {
-      return 1;
+          return 0;
    }
    /* owner auth is a raw hash */
    else if(!strncmp(authstr, "hash:", 5)) {
@@ -318,12 +282,12 @@ int parse_cmdline_opts(int argc, char** argv, struct 
Opts* opts)
    int i;
 
    //Set defaults
-   memcpy(vtpm_globals.owner_auth, WELLKNOWN_OWNER_AUTH, sizeof(TPM_AUTHDATA));
-   memcpy(vtpm_globals.srk_auth, WELLKNOWN_SRK_AUTH, sizeof(TPM_AUTHDATA));

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.