File-Based Encryption文件级加密
- 基础知识
- Direct Boot
- FBE实现条件
- FBE实现方法
- kernel支持Ext4加密或F2FS加密
- 启用FBE
- 内存(internal storage,userdata)上启用FBE
- 可合并的存储设备
- 与主密钥(Keymaster)集成
- 添加例外目录
- 在系统应用中支持Direct Boot
- 将应用设为Direct Boot感知型应用
- 支持多用户
- 处理更新/升级
- FBE验证
- FBE实现思路
- fscrypt 加密
- 关于各存储的加密
- 密钥存储及其保护措施
Android 7.0 及以上版本支持FBE。采用FBE时,可以使用不同的密钥对不同的文件进行加密。
Android 7.0-8.1 中,FBE与adoptable storage不兼容,并且外部存储媒介(e.g. SD)只能用作traditional storage。
Android 9 及以上版本FBE与adoptable storage兼容。
谷歌规定Android 10 及以上版本必须使用FBE。
AOSP上提供了Ext4和F2FS文件系统的FBE完整实现。在满足相关要求的设备上,只需启用FBE即可。
基础知识
AOSP = Android Open Source Project
SoC = system on chip
FBE = File-Based Encryption 基于文件的加密
fscrypt = Filesystem-level encryption 文件级加密
FDE = full-disk encryption 全盘加密
CE = Credential Encrypted 凭据加密
DE = Device Encrypted 设备加密
存储分类:
traditional storage 传统存储
portable storage 便携存储 : SD卡或USB设备。
emulated storage 模拟存储 :将部分内部存储暴露于模拟层来实现存储。
adoptable storage 可合并的存储设备 :可以像内部存储设备那样进行加密和格式化的物理介质,SD卡或USB设备
Direct Boot
Android 7.0上FBE的实现引出了一个新的功能——Direct Boot。
在使用FDE的已加密设备上,用户在访问任何数据前都需要凭据,以致于设备只能执行最基本的操作,类似紧急拨号等。
Direct Boot允许已加密的设备在启动后直接进入锁屏界面。FBE和新的API让应用可感知是否加密,这意味着应用可以在受限环境下运行,即可以在用户提供凭据之前运行,同时系统仍能保护用户隐私。
在启用了 FBE 的设备上,每位用户均有两个可供应用使用的存储位置:
CE存储:默认存储位置,只能在用户解锁设备后使用
DE存储:在Direct Boot模式下和用户解锁设备后均可使用
但是我们需要将绝大多数的应用放到CE存储空间内,仅留个别应用在DE存储空间
如果没有启用FBE,DE和CE存储空间将始终处于解锁状态。启用FBE时,这种分离的存储方式让加密不再基于设备启动时的密码,从而能够同时保护多位用户的安全。
Direct Boot API 允许加密感知型应用访问CE和DE存储空间。应用的生命周期会发生一些变化:当用户在锁屏界面输入凭据解锁CE存储空间时,或者解锁应用加密锁(也是解锁CE存储空间)时,需要系统通知应用。
AOSP 中的所有必要包均已更新为可Direct Boot。定制的应用还需要确保包可提供通话服务、拨号盘、可在锁屏界面输入密码的输入法。
最重要的一点是,将诸如闹钟、电话、无障碍功能等应用设为 android:directBootAware。
FBE实现条件
设备需要满足以下条件:
1.kernel支持Ext4加密或F2FS加密
[android kernel 3.18及以上提供对Ext4加密和F2FS加密的支持]
2.HAL版本在1.0及上,支持主密钥(Keymaster)
[不支持0.3版本的主密钥,因为它不能提供必要的功能及加密密钥的安全性]
3.在可信执行环境 (TEE:Trusted Execution Environment) 中必须实现主密钥/密钥库(Keystore)和守门人(Gatekeeper),以便保护DE密钥,从而使未授权的操作系统无法直接请求 DE 密钥(即刷到设备上的定制的操作系统)
4.硬件授权和启动验证需要绑定到主密钥的初始化,使未授权的操作系统无法获取DE 密钥
[硬件授权:Hardware Root of Trust 启动验证:Verified Boot]
FBE实现方法
kernel支持Ext4加密或F2FS加密
android kernel 3.18及以上提供对Ext4加密和F2FS加密的支持。
android kernel 3.18 ~ android kernel 5.1启用Ext4加密或F2FS加密:
//userdata 文件系统为 Ext4
CONFIG_EXT4_ENCRYPTION=y
//userdata 文件系统为 F2FS
CONFIG_F2FS_FS_ENCRYPTION=y
android kernel 5.1及以上版本启用Ext4加密或F2FS加密:
CONFIG_FS_ENCRYPTION=y
此外,如果设备支持可合并的存储设备,或者对内部存储设备使用元数据加密,需要启用元数据加密所需的内核配置选项。
【https://source.android.com/docs/security/features/encryption/metadata?hl=zh-cn】
一般来说,加解密的耗时难以忽略,为了让用户有更好的产品体验 ,建议启用加密加速。
在arm64设备上,启用ARMv8 CE(Cryptography Extensions,加密扩展)加速:
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
CONFIG_CRYPTO_SHA2_ARM64_CE=y
为了进一步提高性能、降低功耗,可以内嵌加密硬件,对存储设备上的数据流(the data while it is on the way to/from the storage device)进行加/解密。android kernel 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
如果需要在设备上启用FBE,就必须在内存(internal storage,userdata)上启用FBE。在内存上启用FBE时,会自动对可合并的存储设备(adoptable storage)启用FBE,如果需要禁用可合并的存储设备的FBE,可以对其进行override.
内存(internal storage,userdata)上启用FBE
定义内部存储设备的加密格式:
在userdata的fstab行的fs_mgr_flags列添加
fileencryption=contents_encryption_mode[:filenames_encryption_mode[:flags]]
/*1.参数contents_encryption_mode指定文件内容的加密算法,可选aes-256-xts或adiantum
* android 11及以上默认aes-256-xts
*2.参数filenames_encryption_mode指定文件名的加密算法,可选aes-256-cts、aes-256-heh、adiantum 或 aes-256-hctr2
* 当contents_encryption_mode为aes-256-xts 时,默认为aes-256-cts
* 当contents_encryption_mode为adiantum 时,默认为adiantum
*3.参数flags,android 11新增,flag 以 + 作为分隔符
* 支持以下标记:
* (1)v1/v2:加密策略,v2使用更安全的密钥生成函数(key derivation function)。
* android 11及以上版本默认v2,android 10及以下版本默认v1
* (2)inlinecrypt_optimized:对无法高效处理大量密钥的内嵌加密硬件进行了优化的加密格式进行标记。
* 通过每个CE或DE密钥只生成一个文件内容加密密钥,为所有文件共享,而不是为每一个文件分别生成一个密钥。IV(initialization vectors,初始化向量)的生成会进行相应地进行调整。
* (3)emmc_optimized:对无法高效处理大量密钥的内嵌加密硬件进行了优化的加密格式进行标记,并对IV生产方法进行标记(IV限制为32bit)
* 该标记只能在符合 JEDEC eMMC v5.2 规范的内嵌加密硬件上使用,其它内嵌加密硬件上需要用inlinecrypt_optimized。
* 该标记不能在基于 UFS 的存储设备上使用(UFS允许用64bit的IV)
* (4)wrappedkey_v0:标记设备可支持FBE使用硬件封装密钥(hardware-wrapped keys)
* 该标记须要与inlinecrypt装载选项以及inlinecrypt_optimized或emmc_optimized标记结合使用。
*/
如果不使用内嵌加密硬件,建议fileencryption=aes-256-xts
如果使用内嵌加密硬件,建议fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized(或等效的fileencryption=::inlinecrypt_optimized)
如果没有使用任何形式加速的AES,建议fileencryption=adiantum
Android 10及以下版本,用 fileencryption=ice 来指定文件内容加密方式为FSCRYPT_MODE_PRIVATE,可使用自定义内核补丁程序实现。在Android 11及以上版本不再允许使用该模式,必现使用标准加密格式。(由于fileencryption选项指定了磁盘格式,因此无法通过OTA升级进行更改)
从Android 14(AOSP 实验版)开始,采用加速加密指令设备的文件名加密方式将首选AES-HCTR2。未来的 Android 版本计划将AES-HCTR2作为文件名加密的默认方式。
android 默认使用 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
注意:
1. inlinecrypt 和 fileencryption 选项位于不同的列中,inlinecrypt 是文件系统装载选项,而 fileencryption 是 Android 用户空间的标记。
2. 如果内嵌加密硬件正常工作,且未设置 wrappedkey_v0 标记,则可以随时添加或移除 inlinecrypt 装载选项,而无需擦除设备。当启用了硬件封装的密钥时,会影响磁盘格式,在添加或移除 inlinecrypt 装载选项后,刷版本时需要全擦。
3. 可以在开发过程中对移除 inlinecrypt 进行测试,以验证内嵌加密硬件是否正常工作。
可合并的存储设备
android 9 及以上版本,FBE和可合并的存储设备可以一起使用。
为 userdata 指定 fileencryption fstab 选项时,会自动为可合并的存储设备启用 FBE 和元数据加密。当然,可以通过设置 PRODUCT_PROPERTY_OVERRIDES 中的属性来覆盖可合并的存储设备上的 FBE 和/或元数据加密格式。
Android 11及以上版本,新增属性ro.crypto.volume.options,用于为可合并的存储设备选择 FBE 加密格式(与 fileencryption fstab 选项的参数相同,并且使用相同的默认值)。属性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,因为Android 10及以下版本中,aes-256-heh对大部分设备无效且与内部存储设备上的默认模式不同)
ro.crypto.fde_algorithm 和 ro.crypto.fde_sector_size :为可合并的存储设备选择元数据加密格式。
与主密钥(Keymaster)集成
Keymaster HAL 的启动应该在 early_hal 部分,因为 FBE 要求主密钥在 post-fs-data 启动阶段(即 vold 设置初始密钥时)。
添加例外目录
init 将系统 DE 密钥应用于 /data 的所有顶级目录(all top-level directories),除了必须不加密的目录(包含系统 DE 密钥的目录,包含用户 CE 或 DE 的目录)
加密密钥以递归方式应用,且无法由子目录替换。
Android 11 及以上版本,init 应用于目录的密钥可以通过 init 脚本中 mkdir 命令的 encryption=< action > 来控制。
Android 10 中,init 加密操作被硬编码到/system/extras/libfscrypt/fscrypt_init_extensions.cpp
Android 9 及以下版本,nit 加密操作被硬编码到/system/extras/ext4_utils/ext4_crypt_init_extensions.cpp
可以添加例外,以防止某些目录被加密。在添加例外时,还需添加 SELinux 策略(SELinux policies),仅对需要使用未加密目录的应用授予访问权限(建议排除所有不可信的应用)。
在系统应用中支持Direct Boot
将应用设为Direct Boot感知型应用
为了实现系统应用的快速迁移,新增了两个可在应用级别设置的属性。
defaultToDeviceProtectedStorage 属性仅适用于系统应用,用于将默认的应用存储位置重定向到 DE 存储空间(建议将该应用的敏感数据的路径更改为使用 CE 存储空间)。
directBootAware 属性适用于所有应用,将该应用中的所有组件均标记为加密感知型组件。
<application
android:directBootAware="true"
android:defaultToDeviceProtectedStorage="true">
当运行在Direct Boot模式下,系统 API :
Context.createCredentialProtectedStorageContext()
Context.isCredentialProtectedStorage()
可在需要时用于管理由 CE 存储空间支持的 Context
支持多用户
多用户场景中,每个用户都有其自己的DE加密密钥和CE加密密钥。
特殊用户User 0必须先登录设备,用以管理设备。
多用户之间的互动(加密感知型应用):INTERACT_ACROSS_USERS 和 INTERACT_ACROSS_USERS_FULL 允许应用在所有用户间互动,但这些应用只能访问已解锁用户的 CE 加密目录。
应用有可能在 DE 区域间自由互动,但应用在尝试访问这些区域之前,应先检查对应用户的解锁状态。
处理更新/升级
recovery 分区无法访问 userdata 分区中采用 DE 保护的存储空间。建议实现 FBE 的设备使用 A/B 系统更新来支持 OTA 机制。由于可以在正常操作期间安装 OTA升级,因此recovery 分区无需访问已加密存储卷中的数据。
如果使用之前的 OTA (即需要recovery 分区访问 userdata 分区中的 OTA 文件),则需要执行以下操作:
1. 在 userdata 分区中创建一个顶级目录(例如 misc_ne)。
2. 将该顶级目录配置为非加密(添加例外目录)。
3. 在顶级目录中创建一个用于存放 OTA 升级包的目录。
4. 添加 SELinux 策略和文件环境,以便控制对该目录及其内容的访问。应当只有接收 OTA 更新的进程或应用能够对该目录进行读取和写入操作。任何其他应用或进程都不应具有访问该目录的权限。
FBE验证
可以运行多个 CTS 加密测试来验证FBE功能,例如 DirectBootHostTest 和 EncryptionTest。
Android 11及以上版本需要同时运行vts_kernel_encryption_test:
atest vts_kernel_encryption_test
或者
vts-tradefed run vts -m vts_kernel_encryption_test
还需进行如下确认:
1. 检查 ro.crypto.state 是否存在,确认 ro.crypto.state 是否已加密
2. 检查 ro.crypto.type 是否存在,确认 ro.crypto.type 是否已设置为 file
实例测试:
1.主用户设置锁定屏幕
2.启动一个 userdebug 实例
3. adb shell
4. su 获得 root 权限
5.确认 /data/data 中是否包含加密的文件名(需包含)
FBE实现思路
fscrypt 加密
fscrypt 是一个库,文件系统可以挂载fscrypt。fscrypt 用于支持文件和目录的透明加密。
AOSP 实现FBE时使用内核中的“fscrypt”加密(需要支持 ext4 和 f2fs ),通常情况下配置如下:
在 XTS 模式下,加密文件内容使用 AES-256 算法
在 CBC-CTS 模式下,加密文件名使用 AES-256 算法
当然,也支持Adiantum 加密,可以参考https://source.android.com/docs/security/features/encryption/adiantum
,是一种运行在android 9 及以上版本中的加密方法,当设备CPU缺乏AES加密时可以使用Adiantum 加密。启用 Adiantum 加密后,文件内容和文件名都会使用 Adiantum 进行加密。
fscrypt 支持两种版本的加密政策:
version 1 :已弃用。
version 2 :使用用户空间提供的密钥,通过 HKDF-SHA512 算法,来获得实际的加密密钥。
android 11 及以上版本的设备,其CDD(Central Data Display)要求仅与version 2 兼容。
fscrypt 加密可以参考https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html
关于各存储的加密
存储类别 | 描述 | 目录 |
System DE | Device-encrypted data not tied to a particular user | /data/system, /data/app, and various other subdirectories of /data |
Per-boot | Ephemeral system files that don’t need to survive a reboot | /data/per_boot |
User CE (internal) | Per-user credential-encrypted data on internal storage | /data/data (alias of /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 (internal) | Per-user device-encrypted data on internal storage | /data/misc_de/ |
User CE (adoptable) | Per-user credential-encrypted data on adoptable storage | /mnt/expand/$ {volume_uuid}/media/$ {user_id} , /mnt/expand/$ {volume_uuid}/misc_ce/$ {user_id} , /mnt/expand/$ {volume_uuid}/user/${user_id} |
User DE (adoptable) | Per-user device-encrypted data on adoptable storage | /mnt/expand/$ {volume_uuid}/misc_de/$ {user_id} , /mnt/expand/$ {volume_uuid}/user_de/$ {user_id} |
密钥存储及其保护措施
系统不会存储 per-boot key ,除此之外其他所有的 FBE key 将被vold
管理,并且存储在磁盘上。FBE key存储的位置如下表所示:
Key type | Key location | Storage class of key location |
System DE key | /data/unencrypted | Unencrypted |
User CE (internal) keys | /data/misc/vold/user_keys/ce/${user_id} | System DE |
User DE (internal) keys | /data/misc/vold/user_keys/de/${user_id} | System DE |
User CE (adoptable) keys | /data/misc_ce/$ {user_id}/vold/volume_keys/$ {volume_uuid} | User CE (internal) |
User DE (adoptable) keys | /data/misc_de/$ {user_id}/vold/volume_keys/$ {volume_uuid} | User DE (internal) |
vold
对所有的FBE key都有一层加密。
除了用于内部存储的 CE 密钥之外,每个密钥都由自己的 Keystore key(该密钥不在 TEE 外部公开)和 AES-256-GCM 算法来加密。这确保FBE key无法被解锁,除非可信操作系统已启动(就像开机校验强制执行,验证下一阶段的完整性和真实性)。
此外,Keystore key 需要设置抗回滚,以便 Keymaster 在支持抗回滚的设备上也能安全地删除 FBE 密钥。抗回滚:一旦使用 deleteKey 或者 deleteAllKeys 来删除密钥,安全硬件将保证该密钥不再可用,即删除的密钥无法恢复。
为了在抗回滚不可用时能够尽可能地回退,16384 个随机字节的 SHA-512 哈希值存储在与密钥一起存储的 secdiscardable
文件中,作为 Keystore key中的app ID tag。只有将这些字节全部恢复,才能恢复 FBE key。
用于内部存储的 CE key 将获得更高级别的保护,以确保在未获取用户的LSKF (LSKF:Lock Screen Knowledge Factor,即PIN 码 personal identification、图案pattern 或 口令password)、安全密码重置令牌(secure passcode reset token)或在重启恢复操作后的客户端密钥及服务器端密钥的情况下,CE key 无法被解锁。只允许为工作资料和完全受管设备(for work profiles and fully managed devices)创建密码重置令牌。
为此,vold
用AES-256-GCM算法加密用于内部存储的CE key,其加密密钥源自用户合成的口令。该合成口令是为每个用户随机生成的不可变的高级无序的(high-entropy,高熵)加密的密码。system_server
中的 LockSettingsService
用于管理合成口令及其保护方式。
用 LSKF 保护合成口令时,LockSettingsService
首先会扩展 LSKF(可以通过 scrypt
传递 LSKF,目标时间约为 25 毫秒且内存用量约为 2 MiB)。
由于 LSKF 通常较短,因此该步骤通常无法提供太多安全性。主要的安全保障是SE (SE: Secure Element安全元素) 或者由 TEE 强制执行的速率限制。
如果设备有SE, LockSettingsService
使用 Weaver HAL
将经过扩展的 LSKF 映射到存储在 SE 中的高级无序随机密码(secret)。
然后,LockSettingsService
将对合成口令进行双重加密:
第一次加密:使用扩展的 LSKF 和 Weaver secret派生软件密钥;
第二次加密:使用未经身份验证绑定(non-auth-bound)的 Keystore key。
这样即可对 LSKF 猜测行为施加 SE 强制速率限制。
Weaver HAL代码路径:android / platform / hardware / interfaces / refs/heads/master / . / weaver / 1.0 / IWeaver.hal
如果设备没有 SE,则 LockSettingsService
使用扩展的 LSKF 作为 Gatekeeper
口令。
然后,LockSettingsService
将对合成口令进行双重加密:
第一次加密:使用扩展的 LSKF 和 secdiscardable 文件的哈希值派生软件密钥;
第二次加密:使用经过身份验证绑定(auth-bound)到Gatekeeper的 Keystore key。
这样即可对 LSKF 猜测行为施加 SE 强制速率限制。
Gatekeeper:
TEE(Trusted Execution Environment,可信执行环境)中,使用硬件支持的Keystore key通过HMAC注册并验证设备的图案或者口令(用TEE派生的共享密钥来进行身份验证)。
在连续数次验证失败时会限制验证尝试,拒绝为请求提供服务。
当LSKF改变时,LockSettingsService
会删除所有旧的LSKF以及合成口令绑定的相关信息。在支持 Weaver 或者抗回滚的 Keystore key 的设备上,这样做可以保证安全地删除旧绑定。因此,即使用户没有设置 LSKF,系统也会进行上述保护措施。