Android 7.0 及更高版本支持文件级加密 (FBE)。采用 FBE 时,可以使用不同的密钥对不同的文件进行加密,也可以对加密文件单独解密。这些密钥会用于加密文件内容和文件名。 采用 FBE 时,其他信息(例如目录布局、文件大小、权限和创建/修改时间)不会被加密。这些其他信息统称为“文件系统元数据”。
Android 9 引入了对元数据加密的支持。借助元数据加密,启动时即可用的单个密钥会加密所有未通过 FBE 加密的内容。该密钥受到 Keymaster 的保护,而 Keymaster 受到启动时验证功能的保护。
每当启用 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
如果不使用内嵌加密硬件,您还应按照FBE 文档中的建议,启用任何基于 CPU 的可用加速。
在 Android 10 及更低版本中,Android 通用内核不支持 dm-default-key
。因此,应由供应商实现 dm-default-key
。
设置元数据文件系统
由于在元数据加密密钥可用之前,userdata 分区中的所有内容均无法读取,因此分区表必须留出一个名为“metadata 分区”的单独分区,用于存储保护该密钥的 Keymaster Blob。metadata 分区的大小应为 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 序列
在使用元数据加密时,必须在装载 /data
之前运行 vold
。为了确保其尽早开始运行,请将以下节添加到 init.hardware.rc
:
# We need vold early for metadata encryption on early-fs start vold
Keymaster 必须在 init 尝试装载 /data
之前运行并准备就绪。
init.hardware.rc
应该已经包含一个 mount_all
指令,用于将 /data
本身装载到 on
late-fs
节中。请在这行代码前面添加以下指令,以执行 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
添加到 userdata
对应 fstab
条目的 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
会被拦截,并且在 mount_all
完成之前,尝试读取或设置 init
属性的操作也会被拦截。
在此阶段,如果读取或设置某个属性时 vold
的任何一部分工作遭到直接或间接拦截,就会导致死锁。请务必确保 vold
能够完成读取密钥、与 Keymaster 交互以及装载数据目录的工作,而无需与 init
进一步交互。
Keymaster 如果在 mount_all
运行时没有完全启动,在从 init
读取到某些属性之前,都不会响应 vold
,从而导致上述死锁。按照相关规定将 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-cbc
和adiantum
。只有当设备没有采用 AES 加速时,才能使用 Adiantum。ro.crypto.fde_sector_size
,用于选择加密扇区大小。选项包括 512、1024、2048 和 4096。对于 Adiantum 加密,请使用 4096。