文件加密FBE&DirectBoot模式介绍
1. File based Encryption
Android 7.0及以上版本提供基于文件的加密方式(FBE). 这种方式允许使用不同的密钥对不同的文件进行加密, 并且可以独立进行解密.
Android 7.0为基于文件的加密引入了新的功能, 命名为Direct Boot. 它允许加密设备直接启动至锁定屏幕的状态. 在以前的版本中, 使用全局磁盘加密(FDE)的设备,
用户需要在任何数据能够被访问前提供认证凭证(图形/密码/指纹), 来预防设备可能被进行的任何基本操作.
如: 闹钟无法执行, 辅助功能服务无法使用, 电话无法接受通话请求, 除了最基本的紧急通话操作.
使用基于文件加密的设备, 应用可以了解当前加密的情况, 同时被允许执行限定范围内的操作. 这些操作能够在用户提供认证凭证前执行, 同时依然能够保护私有用户信息.
在使用FBE的设备上, 设备的每个用户对于设备应用均拥有两个存储空间:
- 凭证加密存储区间(
Credential Encrypted (CE)
), 这是缺省的存储空间, 并且只有当用户解锁设备后才可用. - 设备加密存储空间(
Device Encrypted (DE)
), 该存储空间在Direct Boot模式和用户解锁设备后均可用.
1.1. FDE Vs FBE:
- FBE: FILE BASED ENCRYPTION:
- 每一个用户一个CE key,CE key的加密信息存储在data分区的相应文件夹中。
- 每个用户对应一个DE key,除此之外,还有一个全局的DE key用来为与用户非强关联的文件夹设置加密。
- 设备未解锁之前,可以使用CE区域以外的文件夹。
- 加密针对文件夹级别,加密机制利用了Ext4 文件系统的加密特性。
- 这种区分能够使工作模型更为安全, 因为它允许在同一时间能够保护多个用户,并不是基于一个启动时密码。
- FDE(FULL DISK ENCTYPTION)
- 只有一个key,key的加密信息存储在磁盘尾部区域。
- 设备未解锁之前,data目录是以tmpfs格式挂载的data分区处于完全不可使用状态。
- 加密针对的是分区级别,加密机制利用了dm-crypt加密特性。
1.2. Dependencies
设备必须满足以下的要求来安全使用AOSP的FBE实现:
- Kernel支持ext4加密(相关的kernel config为: EXT4_FS_ENCRYPTION)
- Keymaster支持1.0/2.0的HAL版本. 0.3版本的keymaster hal并不提供相关需要的属性支持, 而且不能保证加密密钥的安全保护机制.
- Keymaster/Keystore 和 GateKeeper必须在可信任执行环境(TEE)中实现, 用以提供对DE密钥的保护, 该情况下, 未认证的系统OS(自行输入到设备上的OS)将不能简单的访问DE密钥.
- kernel中提供的加密性能必须达到使用AES-XTS时最低为50MB/s来保证良好的用户体验.
- Verified Boot机制下 Keymaster root of trust 必须绑定至keymaster的初始化过程中. 这将保证设备加密证书不会被未验证的操作系统访问到
- bootloader passes the following information to the TEE after boot/recovery partition verification and
TEE initialization to bind the Keymaster root of trust:- the public key that was used to sign the boot partition
1.3. 加密规则 Encryption policy
Ext4加密可以将加密规则应用到文件夹级别. 当设备的用户数据分区初次创建时, 由init脚本来赋值基本的结构和规则. 该脚本同时会触发来创建第一个用户(0用户)的CE/DE密钥, 并且定义那些目录需要由该密钥进行加密.
当其余的用户和工作模式被创建时, 相关所需的密钥会被创建, 相关的存储目录会被创建并且由加密规则将密钥和目录链接起来.
在当前的AOSP实现中, 加密规则被硬编码到以下的位置: /system/extras/ext4_utils/ext4_crypt_init_extensions.cpp
可以通过在这个文件中增加例外的规则来避免指定的目录被加密, 目录可以加入到directories_to_exclude列表中. 如果进行了该类的修改, 设备提供商必须包含增加SElinux的规则, 以使得只有必须使用未加密目录的应用才能获取访问权限, 其他未授权的应用必须被排除在外.
1.4. 打开文件加密特性
fstab中指定标志位 fileencryption 时默认开启文件加密。/dev/block/platform/soc.0/f9824900.sdhci/by-name/userdata /data ext4 noatime,nosuid,nodev,barrier=1,data=ordered,nomblk_io_submit,noauto_da_alloc,errors=panic wait,check,fileencryption
同时可以通过以下方法来测试设备上的FBE实现. 可以指定标志位:
forcefdeorfbe
开启该标志位后,在开发者选项中,会多出一个菜单convert encryption to file
。用户点击后,可以开启设备的文件加密特性。
fastboot模式中也可以转变FBE模式:$ fastboot --wipe-and-use-fbe
1.5. 文件加密相关流程
1.5.1. Implementation
- Setting中开启文件加密
- forcefdeorfbe
- forcefdeorfbe
==> covert to file encryption
MASTER_CLEAR-> wipe –reason=convert_fbe –> wipe_data –>
format –> make_ext4fs_directory(CONVERT_FBE_DIR)
- fs阶段 mount_all时判断是否进入文件加密模式
- MF_FILEENCRYPTION
- MF_FORCEFDEORFBE&data/convert_fbe 文件存在
- install_keyring 调用add_key 创建一个session keyring
- 密钥环创建完成后,设置属性:
- ro.crypto.state encrypted
- ro.crypto.type file
- post-fs-data (late-init阶段 trigger)installkey 前置条件:ro.crypto.type=file
- installkey
- 创建目录 /data/unencrypted
- 启动vdc service 对vold下发命令 cryptfs enablefilecrypto
- 创建/data/unencrypted/key 目录
- 读取随机密钥ikey1: /dev/urandom 读取 64字节作为文件加密的主密钥
- 读取密钥种子 /dev/urandom 读取1 << 14字节存储到 secdiscardable文件中
- 将scrypt扩展因子存储到stretching文件中 (ro.crypto.scrypt_params)
- 读取加密盐 /dev/urandom 读取1 << 4字节存储到salt文件中
- 根据auth token(验证令牌)、密钥种子、扩展因子、加密盐生成APPLICATION_ID
- TEE根据auth token和 APPLICATION_ID 生成
keymaster_key_blob
(加密数据块)数据保存在 keymaster_key_blob文件中。 - TEE根据auth token、APPLICATION_ID、keymaster_key_blob、随机密钥ikey1生成加密主键保存在encrypted_key文件中。
- 对ikey1 做两次Sha512算法处理,得到ikey1的摘要key_ref2
- 搜索keyring(找到前面fs阶段调用add_key建立的session keyring),返回keyring的序列号
- 将当前的ikey1经过封装后加入到该keyring中。
- ikey1封装成payload数据块。
- mode: EXT4_ENCRYPTION_MODE_AES_256_XTS
- ikey1
- ikey1->size
- add_key( “logon”, ext4:key_ref2, payload, session-keyring )
- ikey1封装成payload数据块。
- 将ikey1的摘要key_ref2存储到/data/unencrypted/key/ref 文件中
- init_user0 (ro.crypto.type=file)
- 创建目录:
- data/misc/vold/user_keys
- data/misc/vold/user_keys/ce
- data/misc/vold/user_keys/de
- data/misc/vold/user_keys/de/0
- data/misc/vold/user_keys/ce/0
- create_and_install_user_keys
- 为user0 创建其对应的ce/de key
- 读取/dev/urandom 创建 ce_ikey1, de_ikey1
- 在data/misc/vold/user_keys/ce/0下根据ce_ikey1计算存储各个加密过程的文件。 存储到data/misc/vold/user_keys/ce/0/current
- 在data/misc/vold/user_keys/de/0下根据de_ikey1计算存储各个加密过程的文件。 存储到data/misc/vold/user_keys/de/0/
- 加密过程文件: version、secdiscardable、stretching、salt、keymaster_key_blob
- 将ce_ikey1,de_ikey1添加到keyring中。
- 为user0 创建其对应的ce/de key
- load_all_de_keys将根据加密过程文件进行解密出对应的de_key并安装到session-keyring中。
- e4crypt_prepare_user_storage(DE) 准备DE区域文件夹,并设置加密策略. 以空token和seceret调用e4crypt_unlock_user_key 计算ce_key,或初始化ce_key
- e4crypt_prepare_user_storage
- /data/misc_de/0
- /data/system_de/0
- /data/user_de/0/
- 加密策略存储在文件系统级别,通过
ioctl (fd, EXT4_IOC_SET_ENCRYPTION_POLICY | EXT4_IOC_GET_ENCRYPTION_POLICY, & ext4_encryption_policy)
,主要是master_key_descriptor字段,保存了de_key的摘要信息。如果文件夹是空文件夹,则执行SET,非空则GET,并校验policy信息是否正确。- e4crypt_unlock_user_key
- 前置条件 ro.crypto.type=file persist.sys.emulate_fbe=false
- 解密的流程与前面create_and_install_user_keys的思路基本一致。在
/data/misc/vold/user_keys/<userid>/current
目录下读取version、secdiscardable、stretching、salt、keymaster_key_blob、encrypted_key及auth 解出加密键 ce_key。将解出的key(对应前面的随机key)进行两次Sha512计算得出摘要。 - 将计算出的ce_key,摘要作为descriptor,add logon key 到 session-keyring中。
- e4crypt_unlock_user_key
- e4crypt_prepare_user_storage
- 创建目录:
- installkey
1.6. ext4 加密
AOSP实现使用kernel中的ext4加密功能, kernel被配置为:
- 使用XTS模式的 AES-256 加密文件内容.
- 使用CBC-CTS模式的AES-256 加密文件名.
密钥管理: - 使用512位的AES-XTS密钥作为文件的加密密钥
- 它被保存在TEE中的另一份密钥进行加密,加密之后保存到encrypt_key文件中
- 如果需要使用TEE密钥, 必须满足以下三个条件:
- 验证令牌. The auth token
Auth token是由GateKeeper在用户成功登录时产生的加密令牌. TEE只有在正确的加密令牌被提供时才能够对AES-XTS密钥进行处理. 如果当前用户并没有证书, 那么验证的令牌将不被使用。 - 扩展证书. The stretched credential
Stretched credential是用户的证书, 同时经过了加密算法和加盐的处理. 并且在由lock setting服务传递给vold进行加密运算前, 还进行过hash处理. 该证书同所有通过APPLICATION_ID进行的提权行为绑定, 并通过TEE中的密钥进行加密. 如果用户没有证书, 该扩展证书将不存在或者没有必要使用. - 随机hash. The “secdiscardable hash”
Secdiscardable hash
是一个随机的16KB单独存放的文件, 用来重建密钥, 密码种子. 这份文件将在密钥删除时同样被安全删除掉, 或者被以其他方式再加密. 这个额外的保护将限制攻击者必须使用这个文件重建密钥. 这同样通过APPLICATION_ID同TEE中的密钥进行绑定.所有的DE KEY、 默认密码的CE KEY及global key,对应空的token、空的secret、空的用户证书,保证了不需要用户参与即可解锁。APPLICATION_ID可以理解为同TEE的一次会话连接ID。
- 验证令牌. The auth token
1.7. 密钥管理
1.8. FBE开机流程
1.9. 解锁流程
1.10. 加解密流程图
2. Linux key retention service
在 Linux 内核中缓存身份验证数据。远程文件系统和其他内核服务可以使用这个服务来管理密码学、身份验证标记、跨域用户映射和其他安全问题。它还使 Linux 内核能够快速访问所需的密钥,并可以用来将密钥操作(比如添加、更新和删除)委托给用户空间。
密钥的属性:
- 序列号(Serial number):一个惟一的 32 位非零正数
- 类型(Type):Linux 密钥保留服务定义两个标准密钥类型:user 和 keyring。要添加新的密钥类型,必须由一个内核服务注册它。用户空间程序不允许创建新的密钥类型
- 描述(Description):一个描述密钥的可输出字符串。这个属性还可以用来执行搜索操作
- 访问控制信息(Access control information):每个密钥有一个所有者 ID、一个 GID 和一个权限掩码,权限掩码表示如何响应用户级或内核级程序。权限掩码给四个可能的密钥访问者类型各分配 8 位:所有者、用户、组和其他。
- 密钥类型 (预定义的密钥类型)
- keyring
- user
- logon
2.1. 访问控制信息
访问控制信息 | UID | GID | Other | Permission Granted |
---|---|---|---|---|
01000000 | 00010000 | 00000100 | 00000001 | View |
02000000 | 00020000 | 00000200 | 00000002 | Read |
04000000 | 00040000 | 00000400 | 00000004 | Write |
08000000 | 00080000 | 00000800 | 00000008 | Search |
10000000 | 00100000 | 00001000 | 00000010 | Link |
20000000 | 00200000 | 00002000 | 00000020 | Set Attribute |
2.1.1. keyring
Keyrings are special key types that may contain links to sequences of other keys of any type. If this interface is used to create a keyring.
keyring 包含一组到其他密钥或 keyring 的链接。
- 线程特有的
- 进程特有的
- 会话特有的
- 用户特有的会话
- 用户默认的会话
用户特有的会话 keyring 通常会链接到一个会话特有的 keyring。
登录进程将绑定到用户默认的会话 keyring,直到创建另一个会话为止。
2.1.1.1. SESSION-KEYRING
进程的session-keyring能够被其fork clone的进程保留。新进程可以通过设置uid、gid继承session
当指向这个session-keyring的最后一个进程终止后,该session会被杀掉。
进程可以创建新的session,也可以重新绑定已经存在的session。
2.1.2. Linux key相关的系统调用
add_key 在用户空间中操作密钥
add_key 系统调用用来创建类型为 type、长度为 plen 的密钥。密钥描述由 desc 定义,它的有效内容由 payload 指定。密钥链接到 keyring。密钥类型可以是 user 或 keyring或logon(新增,与user类型的区别是不允许读payload的内容)。其他任何密钥类型必须已经通过内核服务向内核注册,然后才能使用。request_key 由ext4 文件系统层调用
request_key 系统调用搜索一个进程 keyring,寻找一个密钥是否匹配查询的type和description。
查询不到返回ENOKEY(android中 Required key not available)
1 | |-exam_dir |
3. 在应用中支持Direct Boot
1 | <application |
应用级别的directBootAware作用是表明该应用中的所有组件都是已经和加密相关的.
当DE区域解锁以后,应用可以收到 LOCKED_BOOT_COMPLETED
的广播,定义了directBootAware
的组件可以使用
即DE区域解锁以后,CE区域解锁之前,只有定义directBootAware
的组件可以使用Context.createDeviceProtectedStorageContext()
使用此上下文执行的所有存储 API 调用均访问设备加密存储(DE)Context.createCredentialProtectedStorageContext()
使用此上下文执行的所有存储 API 调用均访问凭证加密存储(CE)
Android7.0 上默认的应用存储空间指向CE区间。defaultToDeviceProtectedStorag
e属性将缺省的应用存储空间由原来的指向CE区间重新连接指向DE区间。
使用该属性标志的应用必须仔细调整所有存储于缺省空间的敏感数据,将其中的敏感数据修改路径存储至CE空间. 使用标志的设备提供商应用也必须仔细考虑,
在存储相关数据时, 必须确保在DE空间内不包含用户信息相关的内容.Context.isCredentialProtectedStorage() Context.isDeviceProtectedStorage()
此处 CE目录 DE目录 目前生效的只有/data/user|user_ce/
目录
重启后一旦用户解锁了设备,您的应用即可切换至访问凭据加密存储,并使用依赖用户凭据的常规系统服务。
应用需要监听 ACTION_USER_UNLOCKED
广播,来确定用户已经解锁了CE
区域。
或者可以接收现有 ACTION_BOOT_COMPLETED
消息,该消息现在表明设备已启动,且用户已解锁设备。
可以通过调用 UserManager.isUserUnlocked()
直接查询用户是否已解锁设备
当设备解锁后,可能需要将现有数据迁移到设备加密存储。Context.moveSharedPreferencesFrom(context,filename)
Context.moveDatabaseFrom(context, filename)
根据应用自身情况决定哪些数据需要迁移,不应该将用户的私密信息迁移到DE区域
默认情况下,应用不会在“直接启动”模式下运行。如果应用需要在“直接启动”模式下进行操作,您可以注册在此模式期间应运行的应用组件。 对于需要在“直接启动”模式下运行的应用,常见的一些用例包括:
- 已安排通知的应用,如闹钟应用。
- 提供重要用户通知的应用,如短信应用。
- 提供无障碍服务的应用,如 Talkback。
如果应用在“直接启动”模式下运行时需要访问数据,则使用设备加密存储。
设备加密存储包含使用密钥加密的数据,仅在设备已成功执行验证启动后密钥才可用。
对于应使用与用户凭据(如 PIN 或密码)关联的密钥加密的数据,使用凭据加密存储。凭据加密存储仅在用户已成功解锁设备后可用,直到用户再次重启设备。 如果用户在解锁设备后启用锁定屏幕,则不会锁定凭据加密存储。
3.1. 支持DirectBoot的相关应用:
vold负责提供android中管理存储设备和卷标的功能. FBE向vold提供了额外的新命令, 以支持在多用户情况下对于CE/DE的密钥进行管理的功能.
使用kernel支持的基于ext4 encryption的加密属性, 很多系统应用包括锁屏界面和SystemUI都被修改以支持FBE和Direct Boot功能.