| # Lockbox |
| |
| Device Management manages a tamper-evident file which is meant to contain |
| install-lifetime system attributes. |
| |
| ## Overview |
| |
| Tamper-evident storage can be accessed by consumers over D-Bus using the |
| InstallAttributes* calls. Essentially, this provides a name-value storage |
| interface for Get()ing and Set()ing values during install time. The datastore is made |
| `tamper-evident` by serializing it to a bytestream and persisting it to the |
| filesystem via the `Lockbox` class. This is done when |
| `InstallAttributes::Finalize()` is called. After finalization, the data becomes |
| `read-only`. |
| |
| The Lockbox class provides a clear interface for implemented TPM NVRAM-backed, |
| tamper-evident data storage. It allows for the creation, destruction, storage, |
| and retrieval of tamper-evident data. InstallAttributes stores its serialized |
| data via this mechanism and reloads it similarly to ensure data integrity. |
| |
| Each lockbox assures tamper-evidence cryptographically by storing a SHA256 |
| digest of the supplied data blob in a TPM NVRAM space. The space itself is |
| logically defined as follows: |
| |
| ```c |
| struct { |
| uint32_t data_size; |
| uint8_t flags; |
| uint8_t salt[SHA256_DIGEST_LENGTH]; |
| uint8_t hash[SHA256_DIGEST_LENGTH]; |
| } __attribute__((packed)); |
| ``` |
| |
| * The data size is the expected size of the "locked" data. This provides a simple |
| validity check and ensures collision attacks against the stored data are |
| size-limited. |
| |
| * Flags is reserved for future use, primarily in anticipation of future digest |
| changes or data serialization changes (encrypted versus not). At present, flags |
| is always 0. |
| |
| * The salt is 32 bytes of randomly generated data sourced from the TPM itself. |
| This also aids in collision attack deterrence. Many devices will likely share |
| the same file. If a collision is found for the hash of that file, arbitrary |
| replacement attacks would be feasible. The added salt serves to increase the |
| difficulty of these forms of attack. |
| |
| * The hash is computed as `SHA256(data||salt)`. |
| |
| Once the struct is filled, it is written to the NVRAM space. A subsequent 0 byte |
| write locks the NVRAM space (bWriteDefine) from future modification without |
| redefinition using the TPM Owner passphrase or by asserting physical presence. |
| |
| See `device_management/dbus_bindings/org.chromium.DeviceManagement.xml` |
| for the exact D-Bus API calls. See `lockbox.h` and |
| `install_attributes.h` for more information on what they provide. |
| |
| ## Device Integration |
| |
| InstallAttributes and Lockbox are initialized by default whenever the |
| device_management service is started. On any system that is upgraded to support |
| InstallAttributes, it will treat the InstallAttributes as an empty-locked store |
| by observing that no NVRAM space was defined nor backing lockbox file created. |
| |
| On a fresh installation, cryptohomed will start and immediately begin the TPM |
| ownership process. Upon completion, InstallAttributes from device_management |
| service will perform one-time initialization that creates its Lockbox and destroys |
| any existing Lockbox data. cryptohomed will then "forget" the TPM owner password |
| when told to by Chrome |
| (such as at the end of the EULA view or at the login screen). Chrome will |
| populate the InstallAttributes during the system's first boot and call |
| Finalize() upon completion. In addition, device_managementd will call Finalize() any |
| time a (Async)Mount request is received over D-Bus. This is to ensure that the |
| InstallAttributes are fully populated prior to the system going into general |
| use. On first login, a very fast user may perform the first Mount call prior to |
| the TPM being owned. In that case, the InstallAttributes will remain freshly |
| initialized and unlocked until the next Mount attempt occurs (after TPM |
| ownership has completed). |
| |
| Recovery from a corrupt Lockbox requires the TPM to be cleared of a TPM owner. |
| This can be triggered by toggling the developer mode switch and rebooting, or by |
| going through the system recovery flow. |
| |
| ### Frontend APIs |
| - Fetches the install-time attributes from the given attribute name (if any): |
| ```bash |
| device_management_client --action=install_attributes_get --name=<attribute_name> |
| ``` |
| - Sets the attribute value to the given attribute name: |
| ```bash |
| device_management_client --action=install_attributes_set --name=<attribute_name> \ |
| --value=<attribute_value> |
| ``` |
| - Finalizes the install-time attributes: |
| ```bash |
| device_management_client --action=install_attributes_finalize |
| ``` |
| - Fetches the current status of install-time attributes (`UNKNOWN` / `TPM_NOT_OWNED`/ `FIRST_INSTALL` / `VALID` / `INVALID`): |
| ```bash |
| device_management_client --action=install_attributes_get_status |
| ``` |
| - Fetches the total number of install-time attributes stored in the storage: |
| ```bash |
| device_management_client --action=install_attributes_count |
| ``` |
| - Fetches the current status and returns `1` if the install-time attributes storage |
| is ready, `0` otherwise: |
| ```bash |
| device_management_client --action=install_attributes_is_ready |
| ``` |
| - Fetches the current status and returns `1` if the install-time attributes storage |
| is secure, `0` otherwise: |
| ```bash |
| device_management_client --action=install_attributes_is_secure |
| ``` |
| - Fetches the current status and returns `1` if the install-time attributes storage |
| is invalid, `0` otherwise: |
| ```bash |
| device_management_client --action=install_attributes_is_invalid |
| ``` |
| - Fetches the current status and returns `1` if the status is `FIRST_INSTALL`, |
| `0` otherwise: |
| ```bash |
| device_management_client --action=install_attributes_is_first_install |
| ``` |
| ## NVRAM Index Selection |
| |
| This value was picked by reviewing the related specification documents. The |
| index is a managed namespace as laid out below: |
| |
| ``` |
| 3 2 1 |
| 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |T|P|U|D| resvd | Purview | Index | |
| (T=TPM, P=platform maker, U=platform user, D=pre-defined) |
| ``` |
| |
| (from TCG TPM Structures rev 103) |
| |
| Some ranges have been declared reserved in other specs: |
| |
| * `0xFFFFFFFF` is the NVRAM lock (permanent). |
| * `0x50010000` is reserved for BIOS use. |
| * `0x0000Fxxx` preallocated/reserved by for locality use. |
| * `0x0001xxxx` are a handful of TSS reserved ranges. |
| * `0x10000001` is the deprecated NV_INDEX_DIR. |
| * `0x00000000` is the bGlobalLock index. |
| |
| In addition, there is at least one other major user. For tboot, Intel uses: |
| |
| * `0x20000001`: `00100000000000000000000000000001` |
| * `0x20000002`: `00100000000000000000000000000010` |
| |
| in the Platform User range. It's unclear if they'll expand further out in the |
| future, so we leave room for them to add a single space. |
| |
| * `0x20000004`: `00100000000000000000000000000100` |
| |
| Arguably, we could use a 'P' space, but that would complicate Chromium OS use on |
| non-Chrome hardware. |