Metadata encryption

Android 7.0 and higher supports file-based encryption (FBE). FBE allows different files to be encrypted with different keys that can be unlocked independently. These keys are used to encrypt both file contents and file names. When FBE is used, other information, such as directory layouts, file sizes, permissions, and creation/modification times, is not encrypted. Collectively, this other information is known as filesystem metadata.

Android 9 introduced support for metadata encryption. With metadata encryption, a single key present at boot time encrypts whatever content is not encrypted by FBE. This key is protected by Keymaster, which in turn is protected by verified boot.

Metadata encryption is always enabled on adoptable storage whenever FBE is enabled. Metadata encryption can also be enabled on internal storage. Devices launched with Android 11 or higher must have metadata encryption on internal storage enabled.

Implementation on internal storage

You can set up metadata encryption on the internal storage of new devices by setting up the metadata filesystem, changing the init sequence, and enabling metadata encryption in the device's fstab file.

Prerequisites

Metadata encryption can only be set up when the data partition is first formatted. As a result, this feature is only for new devices; this is not something an OTA should change.

Metadata encryption requires that the dm-default-key module be enabled in your kernel. In Android 11 and higher, dm-default-key is supported by the Android common kernels, version 4.14 and higher. This version of dm-default-key uses a hardware and vendor-independent encryption framework called blk-crypto.

To enable dm-default-key, use:

CONFIG_BLK_INLINE_ENCRYPTION=y
CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y
CONFIG_DM_DEFAULT_KEY=y

dm-default-key uses inline encryption hardware (hardware that encrypts/decrypts data while it is on the way to/from the storage device) when available. If you are not using inline encryption hardware, it's also necessary to enable a fallback to the kernel's cryptography API:

CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y

When not using inline encryption hardware you should also enable any available CPU-based acceleration as recommended in the FBE documentation.

In Android 10 and lower, dm-default-key was not supported by the Android common kernel. It was therefore up to vendors to implement dm-default-key.

Set up metadata filesystem

Because nothing in the userdata partition can be read until the metadata encryption key is present, the partition table must set aside a separate partition called the "metadata partition" for storing the keymaster blobs that protect this key. The metadata partition should be 16MB.

fstab.hardware must include an entry for the metadata filesystem that lives on that partition mounting it at /metadata, including the formattable flag to ensure it is formatted at boot time. The f2fs filesystem does not work on smaller partitions; we recommend using ext4 instead. For example:

/dev/block/bootdevice/by-name/metadata              /metadata          ext4        noatime,nosuid,nodev,discard                          wait,check,formattable

To ensure the /metadata mount point exists, add the following line to BoardConfig-common.mk:

BOARD_USES_METADATA_PARTITION := true

Changes to the init sequence

When metadata encryption is used, vold must be running before /data is mounted. To ensure that it is started early enough, add the following stanza to init.hardware.rc:

# We need vold early for metadata encryption
on early-fs
    start vold

Keymaster must be running and ready before init attempts to mount /data.

init.hardware.rc should already contain a mount_all instruction which mounts /data itself in the on late-fs stanza. Before this line, add the directive to exec the wait_for_keymaster service:

on late-fs
   … 
    # Wait for keymaster
    exec_start wait_for_keymaster

    # Mount RW partitions which need run fsck
    mount_all /vendor/etc/fstab.${ro.boot.hardware.platform} --late

Switch on metadata encryption

Finally add keydirectory=/metadata/vold/metadata_encryption to the fs_mgr_flags column of the fstab entry for userdata. For example, a full fstab line might look like:

/dev/block/bootdevice/by-name/userdata              /data              f2fs        noatime,nosuid,nodev,discard,inlinecrypt latemount,wait,check,fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized,keydirectory=/metadata/vold/metadata_encryption,quota,formattable

By default, the metadata encryption algorithm on internal storage is AES-256-XTS. This can be overridden by setting the metadata_encryption option, also in the fs_mgr_flags column:

  • On devices that lack AES acceleration, Adiantum encryption can be enabled by setting metadata_encryption=adiantum.
  • On devices that support hardware-wrapped keys, the metadata encryption key can be made hardware-wrapped by setting metadata_encryption=aes-256-xts:wrappedkey_v0 (or equivalently metadata_encryption=:wrappedkey_v0, as aes-256-xts is the default algorithm).

Because the kernel interface to dm-default-key changed in Android 11, you also need to ensure that you have set the correct value for PRODUCT_SHIPPING_API_LEVEL in device.mk. For example, if your device launches with Android 11 (API level 30), device.mk should contain:

PRODUCT_SHIPPING_API_LEVEL := 30

You can also set the following system property to force the use of the new dm-default-key API regardless of shipping API level:

PRODUCT_PROPERTY_OVERRIDES += \
    ro.crypto.dm_default_key.options_format.version=2

Validation

To verify that metadata encryption is enabled and is working correctly, run the tests described below. Also be mindful of the common issues described below.

Tests

Start by running the following command to verify that metadata encryption is enabled on internal storage:

adb root
adb shell dmctl table userdata

The output should be similar to:

Targets in the device-mapper table for userdata:
0-4194304: default-key, aes-xts-plain64 - 0 252:2 0 3 allow_discards sector_size:4096 iv_large_sectors

If you overrode the default encryption settings by setting the metadata_encryption option in the device's fstab, then the output differs slightly from the above. For example, if you enabled Adiantum encryption, then the third field is xchacha12,aes-adiantum-plain64 instead of aes-xts-plain64.

Next, run vts_kernel_encryption_test to verify the correctness of metadata encryption and FBE:

atest vts_kernel_encryption_test

or:

vts-tradefed run vts -m vts_kernel_encryption_test

Common issues

During the call to mount_all, which mounts the metadata-encrypted /data partition, init executes the vdc tool. The vdc tool connects to vold over binder to set up the metadata-encrypted device and mount the partition. For the duration of this call, init is blocked, and attempts to either read or set init properties block until mount_all finishes. If, at this stage, any part of vold's work is directly or indirectly blocked on reading or setting a property, deadlock results. It is important to ensure that vold can complete the work of reading the keys, interacting with Keymaster, and mounting the data directory without interacting further with init.

If Keymaster is not fully started when mount_all runs, it doesn't respond to vold until it has read certain properties from init, resulting in exactly the deadlock described. Placing exec_start wait_for_keymaster above the relevant mount_all invocation as set out ensures that Keymaster is fully running in advance and so avoids this deadlock.

Configuration on adoptable storage

Since Android 9, a form of metadata encryption is always enabled on adoptable storage whenever FBE is enabled, even when metadata encryption is not enabled on internal storage.

In AOSP, there are two implementations of metadata encryption on adoptable storage: a deprecated one based on dm-crypt, and a newer one based on dm-default-key. To ensure that the correct implementation is selected for your device, ensure that you have set the correct value for PRODUCT_SHIPPING_API_LEVEL in device.mk. For example, if your device launches with Android 11 (API level 30), device.mk should contain:

PRODUCT_SHIPPING_API_LEVEL := 30

You can also set the following system properties to force the use of the new volume metadata encryption method (and the new default FBE policy version) regardless of shipping API level:

PRODUCT_PROPERTY_OVERRIDES += \
    ro.crypto.volume.metadata.method=dm-default-key \
    ro.crypto.dm_default_key.options_format.version=2 \
    ro.crypto.volume.options=::v2

Current method

On devices launching with Android 11 or higher, metadata encryption on adoptable storage uses the dm-default-key kernel module, just like on internal storage. See the prerequisites above for which kernel configuration options to enable. Note that inline encryption hardware that works on the device's internal storage might be unavailable on adoptable storage, and thus CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y might be required.

By default, the dm-default-key volume metadata encryption method uses the AES-256-XTS encryption algorithm with 4096-byte crypto sectors. The algorithm can be overridden by setting the ro.crypto.volume.metadata.encryption system property. This property's value has the same syntax as the metadata_encryption fstab option described above. For example, on devices that lack AES acceleration, Adiantum encryption can be enabled by setting ro.crypto.volume.metadata.encryption=adiantum.

Legacy method

On devices launching with Android 10 or lower, metadata encryption on adoptable storage uses the dm-crypt kernel module rather than dm-default-key:

CONFIG_DM_CRYPT=y

Unlike the dm-default-key method, the dm-crypt method causes file contents to be encrypted twice: once with a FBE key and once with the metadata encryption key. This double encryption reduces performance and is not required to achieve the security goals of metadata encryption, since Android ensures that FBE keys are at least as hard to compromise as the metadata encryption key. Vendors can make kernel customizations to avoid the double encryption, in particular by implementing the allow_encrypt_override option which Android passes to dm-crypt when the system property ro.crypto.allow_encrypt_override is set to true. These customizations are not supported by the Android common kernel.

By default, the dm-crypt volume metadata encryption method uses the AES-128-CBC encryption algorithm with ESSIV and 512-byte crypto sectors. This can be overridden by setting the following system properties (which are also used for FDE):

  • ro.crypto.fde_algorithm selects the metadata encryption algorithm. The choices are aes-128-cbc and adiantum. Adiantum can be used only if the device lacks AES acceleration.
  • ro.crypto.fde_sector_size selects the crypto sector size. The choices are 512, 1024, 2048, and 4096. For Adiantum encryption, use 4096.