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.
Design
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.
Key hierarchy
Keys can be derived from other keys using a
KDF (key derivation function)
such as
HKDF
,
resulting in a
key hierarchy
.
The following diagram depicts a typical key hierarchy for FBE when
hardware-wrapped keys are
not
used:
Figure 1.
FBE key hierarchy (standard)
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:
Figure 2.
FBE key hierarchy (with hardware-wrapped key)
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_key
and 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_crypto_ll_ops::keyslot_program
operation, 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_crypto_ll_ops::derive_sw_secret
operation, which must be
implemented by the storage driver.
To derive
inline_encryption_key
and
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
vts_kernel_encryption_test
.
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.
Key wrapping
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_KEY
KeyMint tag.
The "generate" ability is used by
vold
to generate new storage
keys for use by Android, while the "import" ability is used by
vts_kernel_encryption_test
to import test keys.
- An interface to convert a long-term wrapped storage key into an
ephemerally-wrapped storage key. This corresponds to the
convertStorageKeyToEphemeral
KeyMint method. This method is used
by both
vold
and
vts_kernel_encryption_test
in 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
dm-default-key
.
However, some implementation-specific changes are required.
KeyMint changes
The device's KeyMint implementation must be modified to support
TAG_STORAGE_KEY
and implement the
convertStorageKeyToEphemeral
method.
In Keymaster,
exportKey
was used instead of
convertStorageKeyToEphemeral
.
Linux kernel changes
The Linux kernel driver for the device's inline crypto engine must be modified
to support hardware-wrapped keys.
For
android14
and higher kernels,
set
BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
in
blk_crypto_profile::key_types_supported
,
make
blk_crypto_ll_ops::keyslot_program
and
blk_crypto_ll_ops::keyslot_evict
support programming/evicting hardware-wrapped keys,
and implement
blk_crypto_ll_ops::derive_sw_secret
.
For
android12
and
android13
kernels,
set
BLK_CRYPTO_FEATURE_WRAPPED_KEYS
in
blk_keyslot_manager::features
,
make
blk_ksm_ll_ops::keyslot_program
and
blk_ksm_ll_ops::keyslot_evict
support programming/evicting hardware-wrapped keys,
and implement
blk_ksm_ll_ops::derive_raw_secret
.
For
android11
kernels,
set
BLK_CRYPTO_FEATURE_WRAPPED_KEYS
in
keyslot_manager::features
,
make
keyslot_mgmt_ll_ops::keyslot_program
and
keyslot_mgmt_ll_ops::keyslot_evict
support programming/evicting hardware-wrapped keys,
and implement
keyslot_mgmt_ll_ops::derive_raw_secret
.
Testing
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
in
vts_kernel_encryption_test
. To run this test,
run:
atest -v vts_kernel_encryption_test
Read the test log and verify that the hardware-wrapped key test cases (e.g.
FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
and
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.
Enabling
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_v0
flag to the
fileencryption
parameter. For example, use
fileencryption=::inlinecrypt_optimized+wrappedkey_v0
. For
more details, see the
FBE
documentation
.
- Metadata encryption: add the
wrappedkey_v0
flag to the
metadata_encryption
parameter. For example, use
metadata_encryption=:wrappedkey_v0
. For more details, see the
metadata
encryption documentation
.