元数据加密

Android 7.0 及更高版本支持基于文件的加密 (FBE)。FBE 允许使用不同的密钥加密不同的文件,这些密钥可以独立解锁。这些密钥用于加密文件内容和文件名。使用 FBE 时,其他信息(例如目录布局、文件大小、权限以及创建/修改时间)不会加密。统称为文件系统元数据,即指这些其他信息。

Android 9 引入了对元数据加密的支持。借助元数据加密,启动时存在的单个密钥会对 FBE 未加密的所有内容进行加密。此密钥受 Keymaster 保护,而 Keymaster 又受 Verified Boot 保护。

只要启用了 FBE,可采纳存储设备上始终启用元数据加密。元数据加密也可以在内部存储设备上启用。搭载 Android 11 或更高版本的设备必须在内部存储设备上启用元数据加密。

在内部存储设备上实现

您可以通过设置 metadata 文件系统、更改 init 序列以及在设备的 fstab 文件中启用元数据加密,在新设备的内部存储设备上设置元数据加密。

前提条件

元数据加密只能在首次格式化数据分区时设置。因此,此功能仅适用于新设备;OTA 不应更改此设置。

元数据加密要求在您的内核中启用 dm-default-key 模块。在 Android 11 及更高版本中,Android 通用内核(版本 4.14 及更高版本)支持 dm-default-key。此版本的 dm-default-key 使用名为 blk-crypto 的硬件和供应商独立的加密框架。

要启用 dm-default-key,请使用

CONFIG_BLK_INLINE_ENCRYPTION=y
CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y
CONFIG_DM_DEFAULT_KEY=y

dm-default-key 在内联加密硬件(在数据传入/传出存储设备时对其进行加密/解密的硬件)可用时使用该硬件。如果您没有使用内联加密硬件,则还需要启用回退到内核的加密 API

CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y

当不使用内联加密硬件时,您还应启用任何可用的基于 CPU 的加速,如FBE 文档中所建议的那样。

在 Android 10 及更低版本中,Android 通用内核不支持 dm-default-key。因此,供应商需要自行实现 dm-default-key

设置元数据文件系统

由于在元数据加密密钥存在之前,userdata 分区中的任何内容都无法读取,因此分区表必须预留一个单独的分区(称为“元数据分区”)以存储保护此密钥的密钥库 blob。元数据分区应为 16MB。

fstab.hardware 必须包含元数据文件系统的条目,该文件系统位于该分区上,并将其挂载到 /metadata,包括 formattable 标志,以确保在启动时对其进行格式化。f2fs 文件系统不适用于较小的分区;我们建议改用 ext4。例如

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

为了确保 /metadata 挂载点存在,请将以下行添加到 BoardConfig-common.mk

BOARD_USES_METADATA_PARTITION := true

对 init 序列的更改

当使用元数据加密时,必须先运行 vold,然后才能挂载 /data。为了确保它尽早启动,请将以下节添加到 init.hardware.rc

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

Keymaster 必须在运行并准备就绪后,init 才能尝试挂载 /data

init.hardware.rc 应已包含 mount_all 指令,该指令在 on late-fs 节中挂载 /data 本身。在此行之前,添加指令以执行 wait_for_keymaster 服务

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

开启元数据加密

最后,将 keydirectory=/metadata/vold/metadata_encryption 添加到 userdatafstab 条目的 fs_mgr_flags 列。例如,完整的 fstab 行可能如下所示

/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

默认情况下,内部存储设备上的元数据加密算法为 AES-256-XTS。可以通过设置 metadata_encryption 选项(也在 fs_mgr_flags 列中)来覆盖此设置

  • 在缺少 AES 加速的设备上,可以通过设置 metadata_encryption=adiantum 来启用 Adiantum 加密
  • 在支持硬件封装密钥的设备上,可以通过设置 metadata_encryption=aes-256-xts:wrappedkey_v0(或等效地 metadata_encryption=:wrappedkey_v0,因为 aes-256-xts 是默认算法)来使元数据加密密钥成为硬件封装密钥。

由于 Android 11 中 dm-default-key 的内核接口已更改,因此您还需要确保在 device.mk 中设置了正确的 PRODUCT_SHIPPING_API_LEVEL 值。例如,如果您的设备搭载 Android 11(API 级别 30),则 device.mk 应包含

PRODUCT_SHIPPING_API_LEVEL := 30

您还可以设置以下系统属性,以强制使用新的 dm-default-key API,而与发布 API 级别无关

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

验证

要验证元数据加密是否已启用且工作正常,请运行下述测试。还要注意下述常见问题

测试

首先运行以下命令,以验证是否在内部存储设备上启用了元数据加密

adb root
adb shell dmctl table userdata

输出应类似于

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

如果您通过在设备的 fstab 中设置 metadata_encryption 选项来覆盖了默认加密设置,则输出与上述内容略有不同。例如,如果您启用了 Adiantum 加密,则第三个字段是 xchacha12,aes-adiantum-plain64 而不是 aes-xts-plain64

接下来,运行 vts_kernel_encryption_test 以验证元数据加密和 FBE 的正确性

atest vts_kernel_encryption_test

vts-tradefed run vts -m vts_kernel_encryption_test

常见问题

在调用 mount_all(用于挂载元数据加密的 /data 分区)期间,init 会执行 vdc 工具。vdc 工具通过 binder 连接到 vold 以设置元数据加密的设备并挂载分区。在此调用期间,init 会被阻止,并且尝试读取或设置 init 属性的操作都会被阻止,直到 mount_all 完成。如果在此阶段,vold 的任何部分工作直接或间接地被阻止读取或设置属性,则会导致死锁。务必确保 vold 可以完成读取密钥、与 Keymaster 交互以及挂载数据目录的工作,而无需进一步与 init 交互。

如果 Keymaster 在 mount_all 运行时未完全启动,则它不会响应 vold,直到它从 init 读取某些属性,从而导致完全如上所述的死锁。如上所述,将 exec_start wait_for_keymaster 放置在相关的 mount_all 调用之上,可确保 Keymaster 提前完全运行,从而避免这种死锁。

在可采纳存储设备上配置

自 Android 9 起,只要启用了 FBE,即使内部存储设备上未启用元数据加密,可采纳存储设备上也始终启用某种形式的元数据加密。

在 AOSP 中,可采纳存储设备上的元数据加密有两种实现方式:一种是已弃用的基于 dm-crypt 的实现方式,另一种是较新的基于 dm-default-key 的实现方式。为了确保为您的设备选择正确的实现方式,请确保在 device.mk 中设置了正确的 PRODUCT_SHIPPING_API_LEVEL 值。例如,如果您的设备搭载 Android 11(API 级别 30),则 device.mk 应包含

PRODUCT_SHIPPING_API_LEVEL := 30

您还可以设置以下系统属性,以强制使用新的卷元数据加密方法(以及新的默认 FBE 政策版本),而与发布 API 级别无关

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

当前方法

在搭载 Android 11 或更高版本启动的设备上,可采纳存储设备上的元数据加密使用 dm-default-key 内核模块,就像在内部存储设备上一样。有关要启用的内核配置选项,请参阅上面的前提条件。请注意,在设备内部存储设备上工作的内联加密硬件可能在可采纳存储设备上不可用,因此可能需要 CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y

默认情况下,dm-default-key 卷元数据加密方法使用 AES-256-XTS 加密算法和 4096 字节的加密扇区。可以通过设置 ro.crypto.volume.metadata.encryption 系统属性来覆盖该算法。此属性的值与上面描述的 metadata_encryption fstab 选项的语法相同。例如,在缺少 AES 加速的设备上,可以通过设置 ro.crypto.volume.metadata.encryption=adiantum 来启用 Adiantum 加密

旧方法

在搭载 Android 10 或更低版本启动的设备上,可采纳存储设备上的元数据加密使用 dm-crypt 内核模块,而不是 dm-default-key

CONFIG_DM_CRYPT=y

dm-default-key 方法不同,dm-crypt 方法会导致文件内容被加密两次:一次使用 FBE 密钥加密,一次使用元数据加密密钥加密。这种双重加密会降低性能,并且不是实现元数据加密的安全目标所必需的,因为 Android 确保 FBE 密钥至少与元数据加密密钥一样难以破解。供应商可以进行内核自定义以避免双重加密,特别是通过实现 allow_encrypt_override 选项,当系统属性 ro.crypto.allow_encrypt_override 设置为 true 时,Android 会将此选项传递给 dm-crypt。Android 通用内核不支持这些自定义。

默认情况下,dm-crypt 卷元数据加密方法使用 AES-128-CBC 加密算法以及 ESSIV 和 512 字节的加密扇区。可以通过设置以下系统属性(也用于 FDE)来覆盖此设置

  • ro.crypto.fde_algorithm 选择元数据加密算法。选项为 aes-128-cbcadiantumAdiantum 只能在设备缺少 AES 加速时使用。
  • ro.crypto.fde_sector_size 选择加密扇区大小。选项为 512、1024、2048 和 4096。对于 Adiantum 加密,请使用 4096。