blob: 7dbb8e8d36f7e08a1f1339f1cb9f6f0de63c5b86 [file] [log] [blame]
Block Storage Service
=====================
Overview
--------
The Block Storage service can be used to share a block-oriented storage device
such as a QSPI flash between a set of independent secure world clients. A block
storage service provider presents a block level interface for accessing an
underlying block storage device. The basic storage functionality provided by a
device can be extended by Stacked Block Stores, which add extra features, like
encryption or partitioning on top of a device.
The following diagram illustrates a firmware integration that uses a single block
storage service provider to control access to a dedicated flash device. In this
example StMM, OP-TEE, Update Agent and the Protected Storage SP act as clients of
the service. Each client independently manages its own filesystem and is presented
with its own logical partition, starting with a logical block address (LBA) of zero.
.. image:: ../image/block-storage-example-usage.svg
Project Directories
-------------------
Components within the Trusted Services project related to the Block Storage service
are located under the following directories:
.. list-table::
:header-rows: 1
* - Directory
- Contains
* - ``components/service/block_storage``
- Service specific code components.
* - ``components/service/block_storage/block_store``
- Client, devices, stacked block stores.
* - ``deployments/block-storage``
- Build files and deployment specific code for building alternative configurations
of the block storage service provider.
* - ``protocols/service/block_storage``
- Service access protocol definitions.
Design Description
------------------
The block storage service provider conforms to the same model as other service providers
within the TS project. Service requests from clients are received by a service provider
that is responsible for parameter deserialization/serialization and service level access
control. Block storage operations are delegated to a backend *block_store* that provides
block-level storage in some way. There is much flexibility to realize the backend block-level
storage in different ways, allowing platform integrators to use alternative *block_store*
realizations to provide storage solutions that meet specific product requirements.
The following class diagram illustrates the block storage service provider model:
.. uml:: ../uml/BlockStorageProvider.puml
Block Store
^^^^^^^^^^^
The *block_store* component defines a virtual interface for block IO operations. Alternative
concrete *block_store* implementations are supported. Some *block_store* components are stackable
over other *block_store* components to add features such as store partitioning or block
authentication. Separation of functionality into stackable *block_store* components gives
platform integrators the flexibility to create alternative storage solutions with different
security/cost tradeoffs. The base *block_store* interface is defined in::
components/service/block_storage/block_store/block_store.h
Components that implement the *block_store* interface are located in subdirectories beneath
``components/service/block_storage/block_store``. A *block_device* is class of *block_store*
that actually provides block-level storage. In a stack of *block_store* components, a
*block_device* will always live at the bottom. The following layer diagram illustrates a
typical block storage deployment where storage is provided by a stack of *block_store* components:
.. image:: ../image/block-storage-layers.svg
Service Interface
-----------------
The Block Storage service supports a conventional set of block-level operations that
can be adapted to different driver models by clients. The following table summarizes
supported operations:
.. list-table::
:header-rows: 1
* - Operation
- Description
* - Open
- Open a session - take the required *UniquePartitionGUID* as a parameter. Returns
a handle to be used as a qualifier for other requests made by a client.
* - Close
- Close a previously opened session.
* - GetPartitionInfo
- Returns information about the partition associated with an open session. Includes
the block size and the total number of blocks assigned to the partition.
* - Read
- Read data from the specified block.
* - Write
- Write data to the specified block.
* - Erase
- Erase a set of one or more blocks.
The service interface is realized by the block storage service provider. It delegates
storage operations to a backend *block_store*. The *block_store* defines a common
interface for components that realize block storage operations. Where an underlying storage
technology does not support an explicit erase operation (e.g. RPMB), the corresponding
concrete *block_store* should return success for a call to erase but perform no actual
operation (if the partition is writable and the LBA falls within the limits of the
partition).
Block Store Client
------------------
Communicates with a remote block storage service provider to provide storage.
Block Store Devices
-------------------
- **file_block_store** - stores blocks in file accessed using the standard C file (stdio.h) API.
The file represents a contiguous array of storage blocks. Designed to be used in a POSIX
environment as a virtual storage media.
- **fvb_block_store** - an adapter that uses a UEFI firmware volume block driver to access
storage. Can be used with drivers from the EDK2 project.
- **mock_block_store** - mocked block store for unit testing.
- **null_block_store** - a store with no real storage. Always accepts legal writes and returns
zeros for reads.
- **ram_block_store** - stores blocks in RAM. Intended for test purposes.
- **rpmb_block_store** - it is a Replay Protected Memory Block device
(see `SD Association home page`_) that uses the RPMB frontend to provide RPMB based storage.
- **semihosting_block_store** - it is a block device that can be used on emulators
(FVP, qemu, etc...) or on target platforms where the debugger can provide the file-system
semihosting features (See `this page`_.). Semihosting allows accessing files from the host
environment. This block store uses a single file to represent a contiguous array of storage
blocks.
Stacked Block Stores
--------------------
Partitioned Block Store
^^^^^^^^^^^^^^^^^^^^^^^
To allow multiple higher layer filesystems to share the same storage device,
logical block addresses are partitioned, based on configuration data provided
by a system integrator. The partition configuration data may be read from a
GUID Partition Table (GPT) or from the block storage SP manifest. The
configuration data restricts access to a storage partition to a defined owner.
Each owner is allocated a maximum number of blocks and is given exclusive access
to its own blocks, based on the client ID of the calling client.
Storage Partition Configuration
"""""""""""""""""""""""""""""""
The block storage service allows a block storage device to be presented as a single storage
partition or as a set of smaller storage partitions. The way that storage is presented is
determined by configuration data prepared by a platform integrator. Each storage partition
presented by a block storage service provider starts at LBA zero. The number of partitions
and their size are defined by configuration data. Configuration data assigns partitions
to owners to enable access to be controlled. If no partition configuration exists for a
requesting client or if an attempt is made to access a block outside of the configured LBA
range, access is denied. The set of storage partitions used for secure block storage will
not necessarily span the entire underlying storage device. A platform integrator is free to
limit the area used for secure block storage to allow the storage device to be used for other
purposes e.g. as a boot source, read by the boot loader during early boot stages.
Two partition configuration methods will be supported; one where partition configuration data
is read from an SP manifest and the other where configuration is defined by a GUID Partition
Table. Both methods may be used in combination if necessary. Initial implementations will
use the SP manifest configuration method.
Each partition configuration entry includes an attributes bitmap that conforms to the UEFI
GPT Partition Entry attributes definition (see section 5.3 of the UEFI specification). Bits
48-63 are reserved for GUID specific use. For partitions labelled with the Secure Block Store
GUID, bits will be defined for:
- **Read-only** - write and erase operations are forbidden.
A GPT partition entry includes the PartitionName property which normally holds a human readable
name for the partition. For secure block store partitions, the PartitionName property will
hold the canonical UUID string identifying the owner. An empty string is interpreted as
'no specific owner' and any client will be granted access.
Configuration via SP Manifest
"""""""""""""""""""""""""""""
For an SP deployment, the partition configuration may be read from a device tree blob (DTB),
passed as an initialization parameter. Per-partition configuration data comprises the following:
.. list-table::
:header-rows: 1
* - Config Value
- Description
* - UniquePartitionGUID
- GUID that is unique for a partition entry.
* - StartingLBA
- The storage block address corresponding to LBA zero.
* - EndingLBA
- The last storage block in the contiguous range of blocks.
* - Attributes
- See UEFI specification
* - Owner
- Holds canonical UUID string for owner.
The partition configuration is included as a sub-node of the block-dev node that includes
configuration values related to the block device. The following is an example of how a block
device and related partitions are defined within a DT based SP manifest::
block-dev {
compatible = "tforg,ts-block-dev"
disk-guid = "af9f72de-d71f-4492-b44b-a4b4d96000bf"
partitions {
compatible = "tforg,ts-block-partitions"
fwu-meta {
guid = "a6f99e90-7a75-4384-847a-29c9a86c6279"
start-lba = <0x00000000>
end-lba = <0x00000003>
attr = <0x00000000>
owner = "afb995cd-9354-4333-9ea2-bd62ccaedb22"
};
fip {
guid = "1eccc9bc-9a5f-43d0-bcd3-466fd21c9a92"
start-lba = <0x00000004>
end-lba = <0x00040003>
attr = <0x00000000>
owner = "afb995cd-9354-4333-9ea2-bd62ccaedb22"
};
uefi-var {
guid = "1022a92b-4b4a-47b4-94cb-35faf5a45dc2"
start-lba = <0x00040004>
end-lba = <0x00080003>
attr = <0x00000000>
owner = "ed32d533-99e6-4209-9cc0-2d72cdd998a7"
};
};
};
Configuration via GUID Partition Table (GPT)
""""""""""""""""""""""""""""""""""""""""""""
The UEFI specification defines a standard layout for physical storage devices where storage
partitions are described by partition entries within the GUID Partition Table. During
initialization, the Block Storage SP will read the GPT and iterate over partition entries,
identifying those with the secure block store partition type GUID. Each entry contains the
following:
.. list-table::
:header-rows: 1
* - Offset
- Length
- contents
* - 0
- 16 bytes
- PartitionTypeGUID - Secure Block Store GUID
* - 16
- 16 bytes
- UniquePartitionGUID
* - 32
- 8 bytes
- Starting LBA
* - 40
- 8 bytes
- Ending LBA
* - 48
- 8 bytes
- Attributes (e.g. read-only)
* - 56
- 72 bytes
- PartitionName - Holds canonical UUID string for owner.
Encrypted Block Store
^^^^^^^^^^^^^^^^^^^^^
To provide data in rest, and data in transit protection for the stored data using encryption.
The current implementation uses *AES-CBC with ESSIV* encryption, where the encryption key is
derived from the Encryption Root key (ERK).
This way a unique, deterministic, but unpredictable vector is generated for each sector, which
mitigates IV prediction based attacks, like watermarking attack.
To implement the algorithm two keys are derived from the root key and generated with the same
salt value, but with different info:
- **encryption key** - encryption and decryption of the data (AES with CBC block cipher mode)
- **essiv key** - generation of the IV (AES with ECB block cipher mode)
Encrypted Block Store Configuration
"""""""""""""""""""""""""""""""""""
- **ENCRYPTED_BLK_AES_KEY_BITS** - determines the size of the keys derived from the root key
supported values are 128, 192 and 256.
- **ENCRYPTED_BLK_BLOCK_ENCRYPTION_ROOT_KEY** - root key to be used to derive encryption
and ESSIV keys from.
- **ENCRYPTED_BLK_BLOCK_ENCRYPTION_SALT** - Salt value to make impossible for an attacker to
derive the same keys as the ones used for encryption without knowing this value.
Encrypted Block Store Limitations
"""""""""""""""""""""""""""""""""
- Block size of the store must be multiple of the AES block size (16 bytes).
- Encryption root key is currently a configurable vector in the future it should come from a
secure source, like from the Crypto SP or a separate SP responsible for root key storage and
key derivation, but in the current implementation
- AES with CBC block method encrypts a whole block, where the consecutive AES blocks are
interconnected. A drawback of this algorithm is that partial read or write does not
work. To mitigate this limitation at read request the whole block is read and only partial
data is returned, at write request the read-modify-write methodology is used.
--------------
.. _`SD Association home page`: https://www.sdcard.org/developers/boot-and-new-security-features/replay-protected-memory-block/
.. _`this page`: https://developer.arm.com/documentation/dui0203/j/semihosting?lang=en
*Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.*
SPDX-License-Identifier: BSD-3-Clause