Like most disk and file encryption software, Android's storage encryption traditionally relies on the raw encryption keys being present in system memory so that the encryption can be performed. Even when the encryption is performed by dedicated hardware rather than by software, software generally still needs to manage the raw encryption keys.
This traditionally isn't seen as a problem because the keys won't be present during an offline attack, which is the main type of attack that storage encryption is intended to protect against. However, there is a desire to provide increased protection against other types of attacks, such as cold boot attacks, and online attacks where an attacker might be able to leak system memory without fully compromising the device.
To solve this problem, Android 11 introduced support for hardware-wrapped keys, where hardware support is present. Hardware-wrapped keys are storage keys that are only known in raw form to dedicated hardware; software only sees and works with these keys in wrapped (encrypted) form. This hardware must be capable of generating and importing storage keys, wrapping storage keys in ephemeral and long-term forms, deriving subkeys, directly programming one subkey into an inline crypto engine, and returning a separate subkey to software.
Note: An inline crypto engine (or inline encryption hardware) refers to hardware that encrypts/decrypts data while it is on its way to/from the storage device. Usually this is a UFS or eMMC host controller that implements the crypto extensions defined by the corresponding JEDEC specification.
This section presents the design of the hardware-wrapped keys feature, including what hardware support is required for it. This discussion focuses on file-based encryption (FBE), but the solution applies to metadata encryption too.
One way to avoid needing the raw encryption keys in system memory would be to keep them only in the keyslots of an inline crypto engine. However, this approach runs into some problems:
- The number of encryption keys may exceed the number of keyslots.
- Inline crypto engines can only be used to encrypt/decrypt full blocks of data on-disk. However, in the case of FBE, software still needs to be able to do other cryptographic work such as filenames encryption and deriving key identifiers. Software would still need access to the raw FBE keys in order to do this other work.
To avoid these problems, the storage keys are instead made into hardware-wrapped keys, which can only be unwrapped and used by dedicated hardware. This allows an unlimited number of keys to be supported. In addition, the key hierarchy is modified and partially moved to this hardware, which allows a subkey to be returned to software for tasks which cannot use an inline crypto engine.
The following diagram depicts a typical key hierarchy for FBE when hardware-wrapped keys are not used:
The FBE class key is the raw encryption key which Android passes to the Linux kernel to unlock a particular set of encrypted directories, such as the credential-encrypted storage for a particular Android user. (In the kernel, this key is called an fscrypt master key.) From this key, the kernel derives the following subkeys:
- The key identifier. This is not used for encryption, but rather is a value used to identify the key with which a particular file or directory is protected.
- The file contents encryption key
- The filenames encryption key
In contrast, the following diagram depicts the key hierarchy for FBE when hardware-wrapped keys are used:
Compared to the earlier case, an additional level has been added to the key hierarchy, and the file contents encryption key has been relocated. The root node still represents the key which Android passes to Linux to unlock a set of encrypted directories. However, now that key is in ephemerally-wrapped form, and in order to be used it must be passed to dedicated hardware. This hardware must implement two interfaces that take an ephemerally-wrapped key:
- One interface to derive
inline_encryption_keyand directly program it into a keyslot of the inline crypto engine. This allows file contents to be encrypted/decrypted without software having access to the raw key. In the Android common kernels, this interface corresponds to the
blk_ksm_ll_ops::keyslot_programoperation, which must be implemented by the storage driver.
- One interface to derive and return
sw_secret("software secret" -- also called the "raw secret" in some places), which is the key that Linux uses to derive the subkeys for everything other than file contents encryption. In the Android common kernels, this interface corresponds to the
blk_ksm_ll_ops::derive_raw_secretoperation, which must be implemented by the storage driver.
sw_secret from the
raw storage key, the hardware must use a cryptographically strong KDF. This KDF
must follow cryptography best practices; it must have a security strength of at
least 256 bits, i.e. enough for any algorithm used later on. It also must use a
distinct label, context, and/or application-specific information string when
deriving each type of subkey in order to guarantee that the resulting subkeys
are cryptographically isolated, i.e. knowledge of one of them doesn't reveal any
other. Key stretching is not required, as the raw storage key is already a
uniformly random key.
Technically, any KDF that meets the security requirements could be used.
However, for testing purposes, it is necessary to re-implement the same KDF in
test code. Currently, one KDF has been reviewed and implemented; it can be found
in the source code for
It is recommended that hardware use this KDF, which uses NIST SP 800-108 "KDF in Counter Mode" with AES-256-CMAC as the PRF. Note that to be compatible, all
parts of the algorithm must be identical, including the choice of KDF contexts
and labels for each subkey.
To meet the security goals of hardware-wrapped keys, two types of key wrapping are defined:
- Ephemeral wrapping: the hardware encrypts the raw key using a key which is randomly generated at every boot and is not directly exposed outside the hardware.
- Long-term wrapping: the hardware encrypts the raw key using a unique, persistent key built into the hardware which is not directly exposed outside the hardware.
All keys passed to the Linux kernel to unlock the storage are ephemerally-wrapped. This ensures that if an attacker is able to extract an in-use key from system memory, then that key will be unusable not only off-device, but also on-device after a reboot.
At the same time, Android still needs to be able to store an encrypted version of the keys on-disk so that they can be unlocked in the first place. The raw keys would work for this purpose. However, it is desirable to never have the raw keys be present in system memory at all so that they can never be extracted to be used off-device, even if extracted at boot time. For this reason, the concept of long-term wrapping is defined.
To support managing keys wrapped in these two different ways, the hardware must implement the following interfaces:
- Interfaces to generate and import storage keys, returning them in
long-term wrapped form. These interfaces are accessed indirectly through
KeyMint, and they correspond to the
TAG_STORAGE_KEYKeyMint tag. The "generate" ability is used by
voldto generate new storage keys for use by Android, while the "import" ability is used by
vts_kernel_encryption_testto import test keys.
- An interface to convert a long-term wrapped storage key into an
ephemerally-wrapped storage key. This corresponds to the
convertStorageKeyToEphemeralKeyMint method. This method is used by both
vts_kernel_encryption_testin order to unlock the storage.
The key wrapping algorithm is an implementation detail, but it should use a strong AEAD such as AES-256-GCM with random IVs.
Software changes required
AOSP already has a basic framework for supporting hardware-wrapped keys. This
includes the support in userspace components such as
vold, as well
as the Linux kernel support in blk-crypto, fscrypt and
However, some implementation-specific changes are required.
The device's KeyMint implementation must be modified to support
TAG_STORAGE_KEY and implement the
exportKey was used instead of
Linux kernel changes
The Linux kernel driver for the device's inline crypto engine must be modified
BLK_CRYPTO_FEATURE_WRAPPED_KEYS, make the
support programming/evicting hardware-wrapped keys, and implement the
Although encryption with hardware-wrapped keys is harder to test than encryption
with standard keys, it is still possible to test by importing a test key and
re-implementing the key derivation that the hardware does. This is implemented
vts_kernel_encryption_test. To run this test,
atest -v vts_kernel_encryption_test
Read the test log and verify that the hardware-wrapped key test cases (e.g.
DmDefaultKeyTest.TestHwWrappedKey) were not skipped due to support
for hardware-wrapped keys not being detected, as the test results will still be
"passed" in that case.
Once the device's hardware-wrapped key support is working correctly, you can
make the following changes to the device's
fstab file to make
Android use it for FBE and metadata encryption:
- FBE: add the
wrappedkey_v0flag to the
fileencryptionparameter. For example, use
fileencryption=::inlinecrypt_optimized+wrappedkey_v0. For more details, see the FBE documentation.
- Metadata encryption: add the
wrappedkey_v0flag to the
metadata_encryptionparameter. For example, use
metadata_encryption=:wrappedkey_v0. For more details, see the metadata encryption documentation.