基于文件的加密

Android 7.0 及更高版本支持基于文件的加密 (FBE)。基于文件的加密允许使用不同的密钥加密不同的文件,这些密钥可以独立解锁。

本文介绍了如何在新的设备上启用基于文件的加密,以及系统应用如何使用“直接启动”API 为用户提供尽可能最佳、最安全的体验。

所有搭载 Android 10 及更高版本的设备都必须使用基于文件的加密。

直接启动

基于文件的加密启用了一项在 Android 7.0 中引入的新功能,称为“直接启动”。“直接启动”允许加密设备直接启动到锁屏界面。以前,在使用全盘加密 (FDE) 的加密设备上,用户需要在访问任何数据之前提供凭据,从而阻止手机执行除最基本操作以外的所有操作。例如,闹钟无法运行,无障碍功能服务不可用,并且手机无法接听电话,但仅限于基本的紧急拨号操作。

随着基于文件的加密 (FBE) 的引入以及使应用了解加密情况的新 API 的出现,这些应用有可能在有限的上下文中运行。这可以在用户提供凭据之前发生,同时仍然保护用户的私密信息。

在启用 FBE 的设备上,设备的每个用户都有两个可供应用使用的存储位置

  • 凭据加密 (CE) 存储,这是默认存储位置,仅在用户解锁设备后才可用。
  • 设备加密 (DE) 存储,这是一个在“直接启动”模式期间和用户解锁设备后都可用的存储位置。

这种分离使工作资料更加安全,因为它允许一次保护多个用户,因为加密不再仅仅基于启动时间密码。

“直接启动”API 允许支持加密的应用访问这些区域中的每一个。应用生命周期发生了更改,以适应在用户首次在锁屏界面输入凭据后,或在工作资料提供工作质询的情况下,需要通知应用用户的 CE 存储何时解锁。运行 Android 7.0 的设备必须支持这些新 API 和生命周期,无论它们是否实现 FBE。但是,如果没有 FBE,DE 和 CE 存储始终处于解锁状态。

Android 开放源代码项目 (AOSP) 中提供了在 Ext4 和 F2FS 文件系统上完全实现基于文件的加密,并且只需在满足要求的设备上启用即可。选择使用 FBE 的制造商可以探索基于所用片上系统 (SoC) 优化该功能的方法。

AOSP 中的所有必要软件包都已更新为支持“直接启动”。但是,如果设备制造商使用这些应用的自定义版本,他们希望至少确保有支持“直接启动”的软件包提供以下服务

  • 电话服务和拨号器
  • 用于在锁屏界面中输入密码的输入法

示例和源代码

Android 提供了一个基于文件的加密的参考实现,其中 vold (system/vold) 提供了用于管理 Android 上的存储设备和卷的功能。FBE 的添加为 vold 提供了几个新命令,以支持多个用户的 CE 和 DE 密钥的密钥管理。除了使用内核中基于文件的加密功能的核心更改之外,包括锁屏界面和 SystemUI 在内的许多系统软件包都已修改为支持 FBE 和“直接启动”功能。这些包括

  • AOSP 拨号器 (packages/apps/Dialer)
  • 桌面时钟 (packages/apps/DeskClock)
  • LatinIME (packages/inputmethods/LatinIME)*
  • 设置应用 (packages/apps/Settings)*
  • SystemUI (frameworks/base/packages/SystemUI)*

* 使用 defaultToDeviceProtectedStorage 清单属性的系统应用

如需查找更多支持加密的应用和服务的示例,请在 AOSP 源代码树的 frameworks 或 packages 目录中运行命令 mangrep directBootAware

依赖项

要安全地使用 AOSP 的 FBE 实现,设备需要满足以下依赖项

  • 内核支持 Ext4 加密或 F2FS 加密。
  • Keymaster 支持,HAL 版本为 1.0 或更高版本。不支持 Keymaster 0.3,因为它不提供必要的功能或确保对加密密钥进行充分保护。
  • Keymaster/Keystore 和 Gatekeeper 必须在 可信执行环境 (TEE) 中实现,以保护 DE 密钥,以便未经授权的操作系统(刷新到设备上的自定义操作系统)不能简单地请求 DE 密钥。
  • 需要绑定到 Keymaster 初始化硬件信任根验证启动,以确保未经授权的操作系统无法访问 DE 密钥。

实施

首先,应根据“直接启动”开发者文档,使闹钟、电话和无障碍功能等应用支持 android:directBootAware。

内核支持

Android 通用内核版本 3.18 及更高版本中提供了对 Ext4 和 F2FS 加密的内核支持。要在版本为 5.1 或更高版本的内核中启用它,请使用

CONFIG_FS_ENCRYPTION=y

对于旧内核,如果设备的 userdata 文件系统是 Ext4,请使用 CONFIG_EXT4_ENCRYPTION=y;如果设备的 userdata 文件系统是 F2FS,请使用 CONFIG_F2FS_FS_ENCRYPTION=y

如果您的设备支持可采纳存储设备或在内部存储设备上使用元数据加密,另请启用元数据加密文档中所述的元数据加密所需的内核配置选项。

除了对 Ext4 或 F2FS 加密的功能支持之外,设备制造商还应启用加密加速以加快基于文件的加密速度并改善用户体验。例如,在基于 ARM64 的设备上,可以通过设置以下内核配置选项来启用 ARMv8 CE(加密扩展)加速

CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
CONFIG_CRYPTO_SHA2_ARM64_CE=y

为了进一步提高性能并降低功耗,设备制造商还可以考虑实施内联加密硬件,该硬件在数据往返存储设备的过程中对其进行加密/解密。Android 通用内核(版本 4.14 及更高版本)包含一个框架,该框架允许在硬件和供应商驱动程序支持可用时使用内联加密。可以通过设置以下内核配置选项来启用内联加密框架

CONFIG_BLK_INLINE_ENCRYPTION=y
CONFIG_FS_ENCRYPTION=y
CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y

如果您的设备使用基于 UFS 的存储设备,另请启用

CONFIG_SCSI_UFS_CRYPTO=y

如果您的设备使用基于 eMMC 的存储设备,另请启用

CONFIG_MMC_CRYPTO=y

启用基于文件的加密

要在设备上启用 FBE,需要在内部存储设备 (userdata) 上启用它。这也将自动在可采纳存储设备上启用 FBE;但是,如有必要,可以替换可采纳存储设备上的加密格式。

内部存储设备

通过将选项 fileencryption=contents_encryption_mode[:filenames_encryption_mode[:flags]] 添加到 userdatafstab 行的 fs_mgr_flags 列来启用 FBE。此选项定义内部存储设备上的加密格式。它包含最多三个以冒号分隔的参数

  • contents_encryption_mode 参数定义用于加密文件内容的加密算法。它可以是 aes-256-xtsadiantum。自 Android 11 起,它也可以留空以指定默认算法,即 aes-256-xts
  • filenames_encryption_mode 参数定义用于加密文件名的加密算法。它可以是 aes-256-ctsaes-256-hehadiantumaes-256-hctr2。如果未指定,则当 contents_encryption_modeaes-256-xts 时,它默认为 aes-256-cts;当 contents_encryption_modeadiantum 时,它默认为 adiantum
  • flags 参数是 Android 11 中的新参数,是以 + 字符分隔的标志列表。支持以下标志
    • v1 标志选择版本 1 加密政策;v2 标志选择版本 2 加密政策。版本 2 加密政策使用更安全、更灵活的密钥派生函数。默认值为 v2(如果设备在 Android 11 或更高版本上启动,由 ro.product.first_api_level 确定),或者 v1(如果设备在 Android 10 或更低版本上启动)。
    • inlinecrypt_optimized 标志选择一种针对内联加密硬件优化的加密格式,该硬件无法高效处理大量密钥。它通过为每个 CE 或 DE 密钥(而不是每个文件)仅派生一个文件内容加密密钥来实现此目的。IV(初始化向量)的生成会进行相应的调整。
    • emmc_optimized 标志类似于 inlinecrypt_optimized,但它也选择一种将 IV 限制为 32 位的 IV 生成方法。此标志仅应在符合 JEDEC eMMC v5.2 规范的内联加密硬件上使用,因此仅支持 32 位 IV。在其他内联加密硬件上,请改用 inlinecrypt_optimized。此标志绝不应在基于 UFS 的存储设备上使用;UFS 规范允许使用 64 位 IV。
    • 在支持硬件封装的密钥的设备上,wrappedkey_v0 标志允许将硬件封装的密钥用于 FBE。这只能与 inlinecrypt 挂载选项以及 inlinecrypt_optimizedemmc_optimized 标志结合使用。
    • dusize_4k 标志强制加密数据单元大小为 4096 字节,即使文件系统块大小不是 4096 字节也是如此。加密数据单元大小是文件内容加密的粒度。此标志自 Android 15 起可用。此标志仅应用于在页面大小大于 4096 字节且使用 f2fs 文件系统的设备上,启用不支持大于 4096 字节数据单元的内联加密硬件。

如果您不使用内联加密硬件,则大多数设备的建议设置为 fileencryption=aes-256-xts。如果您使用内联加密硬件,则大多数设备的建议设置为 fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized(或等效地 fileencryption=::inlinecrypt_optimized)。在没有任何形式的 AES 加速的设备上,可以使用 Adiantum 代替 AES,方法是将 fileencryption=adiantum 设置为 adiantum

自 Android 14 起,AES-HCTR2 是具有加速加密指令的设备首选的文件名加密模式。但是,只有较新的 Android 内核才支持 AES-HCTR2。在未来的 Android 版本中,计划将其作为文件名加密的默认模式。如果您的内核支持 AES-HCTR2,则可以通过将 filenames_encryption_mode 设置为 aes-256-hctr2 来为文件名加密启用它。在最简单的情况下,这将通过 fileencryption=aes-256-xts:aes-256-hctr2 完成。

在搭载 Android 10 或更低版本的设备上,fileencryption=ice 也被接受,以指定使用 FSCRYPT_MODE_PRIVATE 文件内容加密模式。Android 通用内核未实现此模式,但供应商可以使用自定义内核补丁来实现此模式。此模式生成的磁盘格式是供应商特定的。在搭载 Android 11 或更高版本的设备上,不再允许使用此模式,而必须使用标准加密格式。

默认情况下,文件内容加密是使用 Linux 内核的加密 API 完成的。如果您想改用内联加密硬件,另请添加 inlinecrypt 挂载选项。例如,完整的 fstab 行可能如下所示

/dev/block/by-name/userdata /data f2fs nodev,noatime,nosuid,errors=panic,inlinecrypt wait,fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized

可采纳存储设备

自 Android 9 起,FBE 和可采纳存储设备可以一起使用。

userdata 指定 fileencryption fstab 选项也会自动在可采纳存储设备上启用 FBE 和元数据加密。但是,您可以通过在 PRODUCT_PROPERTY_OVERRIDES 中设置属性来替换可采纳存储设备上的 FBE 或元数据加密格式。

在搭载 Android 11 或更高版本的设备上,使用以下属性

  • ro.crypto.volume.options(Android 11 中的新增功能)选择可采纳存储设备上的 FBE 加密格式。它具有与 fileencryption fstab 选项的参数相同的语法,并且使用相同的默认值。有关此处应使用的内容,请参阅上面的 fileencryption 建议。
  • ro.crypto.volume.metadata.encryption 选择可采纳存储设备上的元数据加密格式。请参阅元数据加密文档

在搭载 Android 10 或更低版本的设备上,使用以下属性

  • ro.crypto.volume.contents_mode 选择内容加密模式。这等效于 ro.crypto.volume.options 的第一个以冒号分隔的字段。
  • ro.crypto.volume.filenames_mode 选择文件名加密模式。这等效于 ro.crypto.volume.options 的第二个以冒号分隔的字段,但搭载 Android 10 或更低版本的设备上的默认值为 aes-256-heh。在大多数设备上,这需要显式替换为 aes-256-cts
  • ro.crypto.fde_algorithmro.crypto.fde_sector_size 选择可采纳存储设备上的元数据加密格式。请参阅元数据加密文档

与 Keymaster 集成

应将 Keymaster HAL 作为 early_hal 类的一部分启动。这是因为 FBE 要求 Keymaster 准备好处理 post-fs-data 启动阶段的请求,这是 vold 设置初始密钥的时间。

排除目录

init系统 DE 密钥应用于 /data 的所有顶层目录,但必须保持未加密的目录除外,例如包含系统 DE 密钥本身的目录以及包含用户 CE 或 DE 目录的目录。加密密钥以递归方式应用,并且不能被子目录覆盖。

在 Android 11 及更高版本中,init 应用于目录的密钥可以由 init 脚本中 mkdir 命令的 encryption=<action> 参数控制。<action> 的可能值记录在 Android init 语言的自述文件中。

在 Android 10 中,init 加密操作被硬编码到以下位置

/system/extras/libfscrypt/fscrypt_init_extensions.cpp

在 Android 9 及更早版本中,该位置为

/system/extras/ext4_utils/ext4_crypt_init_extensions.cpp

可以添加例外情况以防止某些目录被完全加密。如果进行了此类修改,则设备制造商应包含 SELinux 政策,这些政策仅授予需要使用未加密目录的应用访问权限。这应排除所有不受信任的应用。

此操作唯一已知的可接受用例是支持旧版 OTA 功能。

在系统应用中支持“直接启动”

使应用支持“直接启动”

为了促进系统应用的快速迁移,可以在应用级别设置两个新属性。defaultToDeviceProtectedStorage 属性仅对系统应用可用。directBootAware 属性对所有应用均可用。

<application
    android:directBootAware="true"
    android:defaultToDeviceProtectedStorage="true">

应用级别的 directBootAware 属性是用于将应用中的所有组件标记为支持加密的简写。

defaultToDeviceProtectedStorage 属性会将默认应用存储位置重定向为指向 DE 存储,而不是指向 CE 存储。使用此标志的系统应用必须仔细审核存储在默认位置中的所有数据,并将敏感数据的路径更改为使用 CE 存储。使用此选项的设备制造商应仔细检查他们正在存储的数据,以确保其中不包含个人信息。

在这种模式下运行时,以下系统 API 可用于显式管理由 CE 存储支持的 Context(在需要时),这等效于其“设备保护”对应项。

  • Context.createCredentialProtectedStorageContext()
  • Context.isCredentialProtectedStorage()

支持多用户

多用户环境中的每个用户都获得一个单独的加密密钥。每个用户都获得两个密钥:DE 密钥和 CE 密钥。用户 0 必须首先登录设备,因为它是一个特殊用户。这与设备管理用例相关。

支持加密的应用通过以下方式跨用户交互:INTERACT_ACROSS_USERSINTERACT_ACROSS_USERS_FULL 允许应用跨设备上的所有用户执行操作。但是,这些应用只能访问已解锁用户的 CE 加密目录。

应用可能能够跨 DE 区域自由交互,但一个用户解锁并不意味着设备上的所有用户都已解锁。应用应先检查此状态,然后再尝试访问这些区域。

每个工作资料用户 ID 也获得两个密钥:DE 和 CE。当工作质询得到满足时,资料用户被解锁,并且 Keymaster(在 TEE 中)可以提供资料的 TEE 密钥。

处理更新

恢复分区无法访问 userdata 分区上受 DE 保护的存储设备。强烈建议实施 FBE 的设备使用A/B 系统更新来支持 OTA。由于 OTA 可以在正常操作期间应用,因此无需恢复访问加密驱动器上的数据。

当使用旧版 OTA 解决方案(需要恢复访问 userdata 分区上的 OTA 文件)时

  1. userdata 分区中创建一个顶层目录(例如 misc_ne)。
  2. 将此顶层目录配置为未加密(请参阅排除目录)。
  3. 在顶层目录中创建一个目录以保存 OTA 软件包。
  4. 添加 SELinux 规则和文件上下文以控制对此目录及其内容的访问权限。只有接收 OTA 更新的进程或应用才能读取和写入此目录。任何其他应用或进程都不应具有对此目录的访问权限。

验证

测试

为确保此功能的已实施版本按预期工作,请首先运行许多 CTS 加密测试,例如 DirectBootHostTestEncryptionTest

atest vts_kernel_encryption_test

如果设备运行的是 Android 11 或更高版本,另请运行 vts_kernel_encryption_test

vts-tradefed run vts -m vts_kernel_encryption_test

  • 此外,设备制造商可以执行以下手动测试。在启用 FBE 的设备上
    • 检查 ro.crypto.state 是否存在
  • 确保 ro.crypto.state 已加密
    • 检查 ro.crypto.type 是否存在

确保 ro.crypto.type 设置为 file

adb root; adb shell ls /data/user/10

此外,测试人员可以验证在设备自启动以来首次解锁之前,CE 存储是否已锁定。为此,请使用 userdebugeng build,在主用户上设置 PIN 码、图案或密码,然后重启设备。在解锁设备之前,运行以下命令以检查主用户的 CE 存储。如果设备使用无头系统用户模式(大多数汽车设备),则主用户为用户 10,因此运行

adb root; adb shell ls /data/user/0

验证列出的文件名是否为 Base64 编码,这表示文件名已加密,并且用于解密的密钥尚不可用。如果文件名以明文形式列出,则表示存在问题。

设备制造商也应考虑在其设备或内核上运行 上游 Linux fscrypt 测试。这些测试是 xfstests 文件系统测试套件的一部分。但是,Android 官方不支持这些上游测试。

AOSP 实现详情

本节详细介绍 AOSP 的实现,并描述基于文件的加密的工作原理。设备制造商无需在此处进行任何更改即可在其设备上使用 FBE 和直接启动。

fscrypt 加密

AOSP 实现使用内核中的“fscrypt”加密(ext4 和 f2fs 支持),并且通常配置为

  • 使用 XTS 模式下的 AES-256 加密文件内容
  • 使用 CBC-CTS 模式下的 AES-256 加密文件名

还支持 Adiantum 加密。启用 Adiantum 加密后,文件内容和文件名都将使用 Adiantum 加密。

fscrypt 支持两个版本的加密策略:版本 1 和版本 2。版本 1 已弃用,并且针对搭载 Android 11 及更高版本推出的设备的 CDD 要求仅与版本 2 兼容。版本 2 加密策略使用 HKDF-SHA512 从用户空间提供的密钥派生实际的加密密钥。

有关 fscrypt 的更多信息,请参阅上游内核文档

存储类别

下表更详细地列出了 FBE 密钥及其保护的目录

存储类别 说明 目录
未加密 /data 中无法或不需要受 FBE 保护的目录。在使用元数据加密的设备上,这些目录并非真正未加密,而是受元数据加密密钥(等同于 System DE)保护。
  • /data/apex,不包括 /data/apex/decompressed/data/apex/ota_reserved,它们是 system DE
  • /data/lost+found
  • /data/preloads
  • /data/unencrypted
  • 所有子目录使用不同 FBE 密钥的目录。例如,由于每个 /data/user/${user_id} 目录都使用每个用户的密钥,因此 /data/user 本身不使用任何密钥。
System DE 未与特定用户关联的设备加密数据
  • /data/apex/decompressed
  • /data/apex/ota_reserved
  • /data/app
  • /data/misc
  • /data/system
  • /data/vendor
  • 此表未提及为具有不同类别的 /data 的所有其他子目录
每次启动 不需要在重启后保留的临时系统文件 /data/per_boot
User CE(内部) 内部存储上每个用户的凭据加密数据
  • /data/data/data/user/0 的别名)
  • /data/media/${user_id}
  • /data/misc_ce/${user_id}
  • /data/system_ce/${user_id}
  • /data/user/${user_id}
  • /data/vendor_ce/${user_id}
User DE(内部) 内部存储上每个用户的设备加密数据
  • /data/misc_de/${user_id}
  • /data/system_de/${user_id}
  • /data/user_de/${user_id}
  • /data/vendor_de/${user_id}
User CE(可采纳) 可采纳存储上每个用户的凭据加密数据
  • /mnt/expand/${volume_uuid}/media/${user_id}
  • /mnt/expand/${volume_uuid}/misc_ce/${user_id}
  • /mnt/expand/${volume_uuid}/user/${user_id}
User DE(可采纳) 可采纳存储上每个用户的设备加密数据
  • /mnt/expand/${volume_uuid}/misc_de/${user_id}
  • /mnt/expand/${volume_uuid}/user_de/${user_id}

密钥存储和保护

所有 FBE 密钥均由 vold 管理,并以加密形式存储在磁盘上,但每次启动密钥除外,后者根本不存储。下表列出了各种 FBE 密钥的存储位置

密钥类型 密钥位置 密钥位置的存储类别
System DE 密钥 /data/unencrypted 未加密
User CE(内部)密钥 /data/misc/vold/user_keys/ce/${user_id} System DE
User DE(内部)密钥 /data/misc/vold/user_keys/de/${user_id} System DE
User CE(可采纳)密钥 /data/misc_ce/${user_id}/vold/volume_keys/${volume_uuid} User CE(内部)
User DE(可采纳)密钥 /data/misc_de/${user_id}/vold/volume_keys/${volume_uuid} User DE(内部)

如上表所示,大多数 FBE 密钥都存储在由另一个 FBE 密钥加密的目录中。在包含密钥的存储类别解锁之前,密钥无法解锁。

vold 还对所有 FBE 密钥应用了一层加密。内部存储的 CE 密钥以外的每个密钥都使用 AES-256-GCM 加密,并使用其自己的 Keystore 密钥,该密钥在 TEE 之外不可见。这确保了除非受信任的操作系统已启动(由 Verified Boot 强制执行),否则无法解锁 FBE 密钥。Keystore 密钥还请求回滚保护,这允许在 Keymaster 支持回滚保护的设备上安全删除 FBE 密钥。作为回滚保护不可用时的尽力而为的后备方案,存储在密钥旁边的 secdiscardable 文件中的 16384 个随机字节的 SHA-512 哈希用作 Keystore 密钥的 Tag::APPLICATION_ID。需要恢复所有这些字节才能恢复 FBE 密钥。

内部存储的 CE 密钥受到更高级别的保护,以确保在不知道用户的锁屏知识因子 (LSKF)(PIN 码、图案或密码)、安全密码重置令牌或用于 Resume-on-Reboot 操作的客户端和服务器端密钥的情况下,无法解锁这些密钥。密码重置令牌仅允许为工作资料完全受管理设备创建。

为了实现此目的,vold 使用从用户的合成密码派生的 AES-256-GCM 密钥加密内部存储的每个 CE 密钥。合成密码是为每个用户随机生成的不可变高熵加密密钥。LockSettingsServicesystem_server 中管理合成密码及其保护方式。

为了使用 LSKF 保护合成密码,LockSettingsService 首先通过 scrypt 传递 LSKF 来拉伸 LSKF,目标时间约为 25 毫秒,内存使用量约为 2 MiB。由于 LSKF 通常很短,因此此步骤通常不会提供太多安全性。主要的安全性层是下面描述的安全元件 (SE)或 TEE 强制执行的速率限制。

如果设备具有安全元件 (SE),则 LockSettingsService 使用 Weaver HAL 将拉伸的 LSKF 映射到存储在 SE 中的高熵随机密钥。LockSettingsService 然后对合成密码进行两次加密:第一次使用从拉伸的 LSKF 和 Weaver 密钥派生的软件密钥,第二次使用非身份验证绑定的 Keystore 密钥。这提供了 SE 强制执行的 LSKF 猜测速率限制。

如果设备没有 SE,则 LockSettingsService 改为将拉伸的 LSKF 用作 Gatekeeper 密码。LockSettingsService 然后对合成密码进行两次加密:第一次使用从拉伸的 LSKF 和 secdiscardable 文件的哈希派生的软件密钥,第二次使用绑定到 Gatekeeper 注册的 Keystore 密钥。这提供了 TEE 强制执行的 LSKF 猜测速率限制。

当 LSKF 更改时,LockSettingsService 会删除与合成密码绑定到旧 LSKF 的所有信息。在支持 Weaver 或回滚保护 Keystore 密钥的设备上,这保证了旧绑定的安全删除。因此,即使在用户没有 LSKF 的情况下,也会应用此处描述的保护措施。