跳转到内容

AISeal 的 CE 存储:宿主只保管密文,密钥在 VM 里解开

CE 是 Credential Encrypted 的缩写,指用户第一次解锁后才能解密的存储;对应的 DE(Device Encrypted)开机就能解密。

情境GMS-VSR 要求声明 FEATURE_AISEAL 的设备把 AiSeal VM 跑成受保护 VM(pVM),并且支持 CE 存储。
冲突用户的锁屏凭据只在宿主 Android 上,而 AISeal 的前提是宿主可能被攻破,不能碰到 VM 里的明文数据。
问题VM 怎样做到"用户解锁之后才能读数据",同时不让宿主拿到解密用的密钥?

1 总体架构:CE 存储横跨宿主、VM、pKVM 和 TEE 四块

Section titled “1 总体架构:CE 存储横跨宿主、VM、pKVM 和 TEE 四块”

宿主提供"用户已解锁"这个信号和一个只有解锁后才能读的位置,VM 负责所有密钥运算,pKVM 保证宿主看不到 VM 内存,TEE 和启动链提供 VM 自己的根密钥。

总体架构:AISeal CE 存储在 Arm 软件栈中的位置

CE 存储和用户凭据之间的绑定,靠的是宿主自己的 FBE(文件级加密):kek 文件放在宿主的 CE 目录里,宿主用户不解锁,VM 也拿不到这个文件。

2 逻辑视图:宿主只做转发和保管,密钥逻辑全在 VM 里

Section titled “2 逻辑视图:宿主只做转发和保管,密钥逻辑全在 VM 里”

宿主侧三个模块只负责转发用户事件、替 VM 读写文件和管理 VM;生成密钥、包裹、解包、给目录加锁都在 VM 里的 microdroid_manager 完成。

逻辑视图:宿主和 VM 里的模块及调用关系

模块源码位置
AiSealSystemServiceframeworks/base/services/aiseal/java/com/android/server/aiseal/AiSealSystemService.java
aisealhostserviceframeworks/native/services/aisealhostservice/src/main.rs(AiSealInternalService、CEStoreKEK)、instance_data.rs、config.rs
GuestAgentpackages/modules/Virtualization/guest/microdroid_manager/src/main.rs:1446 起
encrypted_foldersguest/microdroid_manager/src/encrypted_folders.rs
encrypted_store_kekguest/microdroid_manager/src/encrypted_store_kek.rs
VmSecretguest/microdroid_manager/src/vm_secret.rs
encryptedstorepackages/modules/Virtualization/guest/encryptedstore/src/main.rs

2.1 密钥层次:三把密钥都由 VM secret 派生,宿主只拿到密文

Section titled “2.1 密钥层次:三把密钥都由 VM secret 派生,宿主只拿到密文”

下图展开逻辑视图里的 VmSecret 和 encrypted_store_kek。真正加密 CE 数据的是每个用户一把的用户密钥;它被 KEK 包裹以后才交给宿主保存。

密钥层次:从 DICE 和 Secretkeeper 到每个文件的密钥

密钥代码
VM secretvm_secret.rs:210 get_vm_secret()
外层盘密钥vm_secret.rs:230 derive_encryptedstore_key()
包裹密钥 KEKvm_secret.rs:235 derive_encryptedstore_key_encryption_key()
用户密钥(首次)vm_secret.rs:244 derive_random_key()
包裹和解包encrypted_store_kek.rs:22 encrypt_kek()、:30 decrypt_kek()

2.2 存储分层:一次写入在 VM 里加密,宿主只看到密文

Section titled “2.2 存储分层:一次写入在 VM 里加密,宿主只看到密文”

下图展开逻辑视图里 VM 的磁盘部分:CE 数据先由 fscrypt 加密,元数据由外层 dm-default-key 加密,落到宿主时已全是密文。

存储分层:从 tenant 写文件到 UFS

  • 外层的参数来自 AppSearch 的 VM 配置 AppSearchAiSealConfig/assets/vm_config.json:"dm_default_key": true、"hugepages": true。
  • dm-default-key 跳过已加密文件的规则在内核 include/linux/fscrypt.h 的 fscrypt_inode_should_skip_dm_default_key(),条件是 IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode)。
  • 外层的建立和挂载在 encryptedstore/src/main.rs::327 enable_dm_default_key()、:351 format_ext4()、:455 mount()。

3 运行视图:宿主 7 个进程、VM 里 3 个进程协作

Section titled “3 运行视图:宿主 7 个进程、VM 里 3 个进程协作”

宿主和 VM 之间只有一条 vsock 上的 RPC binder 会话,用户事件(IGuestAgent)和读写 kek 文件的回调(ICEStoreKEK)都走这条会话;Secretkeeper 的请求经宿主转发,但内容是 VM 和 TA 之间加密的。

运行视图:进程和通信通道

aisealhostservice 的启动条件写在 aisealhostservice.rc 里:

service aisealhostservice /system/bin/aisealhostservice
user system
group system
task_profiles MaxPerformance
interface aidl aiseal_host
disabled
on property:sys.boot_completed=1 && property:service.aiseal.enable=1
enable aisealhostservice

运行参数由系统属性控制(config.rs:32-39、device/google/cuttlefish/shared/aiseal/device_vendor.mk):

属性默认值作用
service.aiseal.enable无设为 1 才启动服务
service.aiseal.protected_vmtrue是否跑成受保护 VM;Cuttlefish 不支持 pVM,设为 0
service.aiseal.memory_bytes300 MiBVM 内存上限
service.aiseal.encrypted_storage_bytes16 GiBstorage.img 上限,已有磁盘启动时按它调整
service.aiseal.tenant_config_package无配置包,AOSP 里是 com.android.appsearch.aiseal.config
service.aiseal.tenant_config_path、aiseal_config_path无配置包里的 assets/vm_config.json、assets/aiseal_config.json

vCPU 数量没有属性可调,写死为 1(main.rs:171 CpuTopology::CpuCount(1))。

4 调用流程:VM 启动、用户解锁,以及停用、删除和重启

Section titled “4 调用流程:VM 启动、用户解锁,以及停用、删除和重启”

4.1 VM 启动:外层加密盘挂好,guest agent 注册到宿主

Section titled “4.1 VM 启动:外层加密盘挂好,guest agent 注册到宿主”

VM 启动阶段不涉及任何用户:VM 算出自己的 secret,挂好外层加密盘,再把 guest agent 注册给宿主,为后面的用户解锁做好准备。

调用流程一:VM 启动

步骤代码
1 等开机完成aisealhostservice main.rs:101 wait_for_boot_completed()
3~4 打开或新建磁盘instance_data.rs:60 load_existing()、:87 create()
5~6 建 VMmain.rs:127 VirtualizationService::new()、:157 VirtualMachineAppConfig
11~12 取 Secretkeeper 秘密vm_secret.rs:442 get_secretkeeper_service()
13~15 外层盘microdroid_manager main.rs:722、:1394 prepare_encryptedstore()
16 注册 guest agentmicrodroid_manager main.rs:548 registerGuestAgent()
19 注册 binder 服务aisealhostservice main.rs:221、:224

4.2 用户解锁:VM 取回用户密钥,给目录加上 fscrypt

Section titled “4.2 用户解锁:VM 取回用户密钥,给目录加上 fscrypt”

一次解锁走 17 步:宿主侧只做到第 5 步,之后由 VM 主导,宿主只在 VM 回调时读写一次文件。

调用流程二:用户解锁

步骤代码
1~3AiSealSystemService.java:80 onUserUnlocking()、:174 notifyUserUnlockingLocked()、:229 getKekFile()
4~6aisealhostservice main.rs:427 onUserUnlocking()
7~14microdroid_manager main.rs:1446 userUnlocked()、:1505 set_encrypted_store_per_user_key();宿主侧回调 main.rs:459 impl ICEStoreKEK
15~16encrypted_folders.rs:113 set_encryption_key()、:58 fs_ioc_set_encryption_policy()

加密策略是 fscrypt v2:内容 AES-256-XTS,文件名 AES-256-CTS,FSCRYPT_POLICY_FLAGS_PAD_32。设置策略只在两种情况下成功:目录是空的(第一次),或者目录已经用同一把密钥设过策略(之后每次)。

4.3 停用、删除和重启:宿主只发通知,加锁、删目录、补密钥都在 VM 里做

Section titled “4.3 停用、删除和重启:宿主只发通知,加锁、删目录、补密钥都在 VM 里做”

三种情况的入口不同,但宿主侧都只转发一次通知;真正改变数据可读状态的操作都发生在 VM 里。

调用流程三:停用、删除和重启

情况代码
用户停用AiSealSystemService.java:87;aisealhostservice main.rs:433;microdroid_manager main.rs:1486 userLocked()、encrypted_folders.rs:137
用户被删除AiSealSystemService.java:218;aisealhostservice main.rs:438;microdroid_manager main.rs:1477 userRemoved()
服务或 VM 重启aisealhostservice main.rs:228-239(VM 死亡后 exit(1));AiSealSystemService.java:100 重连、:114 binderDied、:144 补发

5 落地要做的:芯片侧提供两个可信来源,系统集成避开三个边界情况

Section titled “5 落地要做的:芯片侧提供两个可信来源,系统集成避开三个边界情况”

5.1 芯片侧:保证 VM secret 的两个来源可信

Section titled “5.1 芯片侧:保证 VM secret 的两个来源可信”

VM secret 决定所有 CE 密钥的安全性,它的两个来源都由我们的启动链和 TEE 提供。

要提供的做不到的后果
启动链逐级派生 DICE,把 DICE 链和 pvmfw 一起交给 pKVMsealing CDI 没有可信根,VM secret 可被伪造
TEE 里实现 Secretkeeper TA,数据放在防篡改存储里退回 V1,只用 DICE,失去镜像回滚保护
pKVM 的 Stage-2 和 SMMU 隔离受保护 VM 的内存宿主能直接读到 VM 内存里的明文密钥

另外,VM 里的 fscrypt 和 dm-default-key 都在软件里算(挂载选项没有 inlinecrypt),用不上 UFS 的 inline crypto,全部压在 1 个 vCPU 上。AppSearch 读写密集时要在我们的 CPU 核上实测 AES-XTS 的吞吐。

5.2 系统集成:避开三个边界情况

Section titled “5.2 系统集成:避开三个边界情况”

前两种是当前代码没有处理的情况,第三种是设计取舍;系统集成和安全测试时都要考虑。

情况原因怎么规避
宿主的 kek 文件丢失,这个用户的 AISeal 数据就永远打不开VM 会生成新密钥,但目录已用旧密钥设过策略,SET_ENCRYPTION_POLICY 失败;宿主只记一条 Slog.wtf备份恢复、清数据、OTA 迁移不能单独删或改 /data/system_ce/<u>/AiSeal/
用户停用后,还开着的文件仍可读REMOVE_ENCRYPTION_KEY 遇到打开的文件只做部分移除,代码没检查 removal_status_flags(encrypted_folders.rs:97)安全测试按"所有文件关闭后才算锁上"来验收
所有用户的密钥用同一把 KEK 包裹KEK 由固定 salt 和 identifier 派生,不区分用户用户之间的隔离依赖 VM 内部可信;tenant 只能用平台密钥签名、预装在只读分区(GMS-VSR 7.1-017)