一、前言
用户数据加密是移动设备的重要功能,是使用对称加密算法对Android设备上的所有用户数据进行编码的过程,防止用户数据被未经授权的用户或应用程序访问。
本文是Android系统安全技术系列第二篇,主要介绍基于文件的加密技术。首先介绍Android保护用户隐私数据的技术方案,包括全盘加密FDE、文件加密FBE和元数据加密ME。其次介绍基于文件加密FBE的密钥管理,涉及HAL、Linux Kernel、TEE和Hardware。
1.1、说明
Google FBE是Android系统中较为复杂的模块之一。从硬件角度,Google FBE特性依赖ICE(Inline Crypto Engine)、硬件加速引擎或高安子系统。从软件角度,Google FBE特性涉及frameworks、HAL、Linux Kernel、ATF和TEEOS。本着以点带面的原则,本文以密钥管理为主线进行梳理,涉及VOLD、Linux Kernel Keyring、Linux Kernel Fscrypt、KSM、Keymaster和ICE,不涉及VOLD子系统存储管理、Fscrypt用户空间部分和F2FS文件系统IO处理等。
1.2、声明
本文档所有代码来自:
AndroidAOSPhttps://cs.android.com/android/platform/superproject
Linux AOSP https://elixir.bootlin.com/linux/v5.15.111/source
Google Trusty
https://github.com/haitao52198/TrustyOS/tree/master/AndroidTrustyOS
1.3、缩略语
二、用户隐私数据保护方案
下图是DCCI(互联网数据中心)联合360发布的《中国Android手机用户隐私安全认知调查报告》,调查发现,图片、联系人和各种账号/密码是三大Android用户认为属于隐私的信息,也是用户最担心泄露的三大隐私信息:
Android设备可以访问和存储用户的个人隐私数据,如果设备一旦丢失,会极大的增加用户数据泄露的风险。Google规避该风险的措施之一就是磁盘加密(Disk Encryption)。从Android 4.4开始,Google相继推出FDE全盘加密、FBE文件加密和ME元数据加密。如果设备丢失,加密可以将泄露用户隐私数据的风险降至最低。但必须认识到,无论哪种技术手段,主要解决Android设备丢失或被盗等场景下,无法有效地获取用户数据,即使将用户数据复制到其它Android设备,依然可以有效保护用户的隐私数据。而对于Android设备运行过程中,攻击者通过提权等手段获取用户的隐私数据,上述技术手段无能为力。
2.1、全盘加密FDE
Full-Disk Encryption,全盘加密,Android 5.0到Android 9.0版本支持,使用密钥(密钥本身也会被加密)对Android设备上的所有用户数据进行编码的过程。设备经过加密后,所有由用户创建的数据在存入磁盘前都会自动加密,并且所有读操作都会将数据返回给调用者之前自动解密。
从Google针对FDE这段描述可知,在系统启动过程中,会随机生成userdata partition加密密钥DEK(Disk Encryption Key),同时用户还需要输入Credentials来加密保护DEK。使用时,先要求用户输入Credentials并解密DEK,接下来OS才可以使用DEK解密userdata partition上的数据。所谓用户Credentials是开机启动过程中,在锁屏界面前,OS会提供单独的UI供用户输入密码。
从Google针对创建DEK这段描述可知,系统启动过程中产生16字节随机数作为DEK,并基于HBK和用户密钥创建KEK(Key Encryption Key)。将KEK作为输入密钥,采用AES_CBC算法加密DEK。
虽然全盘加密FDE可以保护整个userdata partition,但在启动时,用户必须先提供Credentials,然后才可以访问userdata partition,这意味着用户提供Credentials前,系统无法访问用户数据,导致闹钟、无障碍服务等无法运行,手机也无法接听电话,所以全盘加密FDE逐渐被文件加密FBE替代。
2.2、文件加密FBE
File-Based Encryption,基于文件加密,由于Android将userdata partition格式化为F2FS类型文件系统,所以也可以理解为基于F2FS类型系统的加密。
Android 7.0及更高的版本支持FBE。采用文件级加密时,允许使用不同密钥对不同文件进行加密,同时允许对加密文件进行单独解密。支持文件级加密的设备支持直接启动,当使能该功能时,已加密设备在启动后可以直接进入锁屏界面,从而使用户可以快速使用Android系统的闹钟和无障碍服务。这些系统服务主要包括:
总而言之,可以在用户解锁前使用系统服务,同时还可以保护用户的隐私数据。
2.3、元数据加密ME
Android 9引入对存在硬件支持的元数据加密的支持。采用Metadata Encryption时,启动时出现的单个密钥会加密未通过FBE进行加密的任何内容,包括目录布局、文件大小、权限和创建/修改时间。该密钥受Keymaster保护,而Keymaster受到启动时验证功能的保护。
三、FBE密钥框架
3.1、FBE TOP HW Architecture
Android FBE特性依赖UFS和TEE,如果芯片支持高安子系统,还依赖高安子系统。
TEE或高安子系统提供基于硬件环境的KMS(Key Management Service),该服务提供密钥操作,如密钥创建(Key Generation)、密钥派生(Key Derivation)、密钥编程(Key Programming)等。
UFS包括UFS Core和UFS Device,UFS Controller工作在UFS Core内部,用于接收来自AP的数据和命令。
3.2、FBE TOP SW Architecture
在不考虑平台高安子系统的前提下,可以从User space-Kernel space、REE-TEE等多个维度来划分FBE整体框架。
3.3、FBE设计思路
FBE将userdata partition划分为Uncrypted Storage、System DE Storage、User DE Storage和User CE Storage等4个区域:
不同的Storage使用不同密钥。其中,System DE Storage对应SYSTEM_DE_KEY,User DE Storage对应USER_DE_KEY,User CE Storage对应USER_CE_KEY。
3.4、挂载文件fstab
下图是Android AOSP的挂载文件device/linaro/dragonboard/fstab.common:
● fileencryption
格式"fileencryption=contents_encryption_mode[:filenames_encryption_mode[:flags]]",最多包含3个以英文冒号为分割参数。
● contents_encryption_mode
加密文件内容算法,可选值有"aes-256-xts"和"adiantum"。从Android 11开始,允许留空以默认加密算法,即"aes-256-xts"。
● filenames_encryption_mode
加密文件名算法,可选值有"aes-256-cts"、"aes-256-heh"和"adiantum"。如果不指定,当"contents_encryption_mode"为"aes-256-xts",该参数默认为"aes-256-cts"。如果"contents_encryption_mode"为"adiantum"时,该参数默认为"adiantum"。
● v1 & v2 flag
v2是第二版加密策略,且第二版加密策略使用更安全、更灵活的密钥派生函数。如果设备搭载Android 11或更高版本,默认选择第二版,如果设备搭载Android 10或更低版本,默认选择第一版。
● inlinecrypt_optimized
针对无法高效处理大量密钥的内嵌加密硬件进行优化的加密格式。仅为每个CE或DE密钥派生一个文件内容加密密钥,而不是为每个文件派生一个,且初始向量IV生成也会相应调整。
● emmc_optimized
与inlinecrypt_optimized类似,初始向量IV限制为32位,该标记仅在符合JEDEC eMMC v5.2规范的内嵌加密硬件上使用,因此仅支持32位IV。在其他内嵌加密硬件上,使用inlinecrypt_optimized,此标记一律不得在基于UFS的存储设备上使用(UFS规范允许使用64位IV)。
● wrappedkey_v0
在支持内嵌加密硬件的设备上,允许为FBE使用硬件封装的密钥。此标记只能与inlinecrypt挂载选项以及标记inlinecrypt_optimized或标记emmc_optimized结合使用。
四、VOLD对密钥的处理
VOLD子系统对密钥的处理主要可以分为4步,分别是"Mount userdata partition"、"Retrieve or Generate Key"、"Install key into Linux Kernel Keyring"和"Encryption Policy"。
4.1、Mount userdata partition
Init进程是Android系统启动的第一个进程,调用mount_all命令挂载userdata partition,该命令在system/core/init/builtins.cpp内实现,对应函数do_mount_all。该命令读取并解析fstab挂载文件,在挂载userdata partition后发送event。
4.2、Retrieve or Generate Key
Init进程是Android系统启动的第一个进程。类似mount的处理流程,调用installkey命令创建或提取FBE Class Key,该命令在system/core/init/builtins.cpp内实现,对应函数do_installkey:
该函数首先判断是否为文件加密方式,如果是,则执行vdc命令,从而正式进入加密流程。
/system/bin/vdc进程向VOLD发送参数"cryptfs"等,最终调用fscrypt_initialize_systemwide_keys函数,该函数是VOLD子系统处理密钥的入口函数。
首先调用get_data_file_encryption_options读取并解析挂载文件fstab,获取加密模式、加密算法和加密标志等。其次,函数retrieveOrGenerateKey的处理可以分为系统首次启动和非首次启动两种场景。当系统首次启动时,将FBE Class Key(encrypted_key)和Keymaster_key(KEK)以KeyBlob形式,并落盘到userdata partition。当系统非首次启动时,从userdata partition分别提取FBE Class Key和Keymaster_key对应KeyBlob到TEEOS的Keymaster TA进行解密。
4.2.1、解析挂载文件fstab
函数ParseOptionsForApiLevel读取并解析挂载文件fstab,并填充EncryptionOptions结构体:
4.2.2、Retrieve or Generate Key
当系统首次启动时,将FBE Class Key(encrypted_key)和Keymaster_key(KEK)以KeyBlob形式,并落盘到userdata partition。当系统非首次启动时,从userdata partition分别提取FBE Class Key和Keymaster_key对应KeyBlob到TEEOS的Keymaster TA进行解密。
4.3、Install key into Linux Kernel Keyring
将FBE Class Key安装到Linux Kernel Keyring的入口函数是install_storage_key:
VOLD子系统下发IOCTL命令FS_IOC_ADD_ENCRYPTION_KEY到Linux Kernel Keyring,返回长度为128bit的FBE Class Key Identifier:
函数fscrypt_ioctl_add_key首先接收来自User Space的VOLD子系统的参数。其次可以分为系统首次启动和非首次启动两种场景进行处理。当系统首次启动时,将Encrypted FBE Class Key保存在Linux Kernel Keyring。当系统非首次启动时,从Linux Kernel Keyring提取Encrypted FBE Class Key,最终返回FBE Class Key Identifier到VOLD子系统。这里,返回的Key Identifier使用fscrypt_key_specifier结构体来描述:
4.4、Encryption Policy
4.4.1、Encryption Policy Overview
Android FBE Encryption Policy与mkdir命令参数"encryption"的配置有关:
● "Encryption=Require"
强制设置和校验目录Encryption Policy,必须严格匹配。
● "Encryption=None"
不设置或不校验目录Encryption Policy,即该目录不需要加密。
● "Encryption=Attempt"
尝试设置/校验目录Encryption Policy,即使设置/校验失败,不进行任何处理。
● "Encryption=DeleteNecessary"
尝试设置/校验目录Encryption Policy,如果设置/校验失败,清空目录,并再次强制设置和校验。
4.4.2、Setup Encryption Policy
Init进程是Android系统启动的第一个进程,调用mkdir命令创建目录并设置加密策略,该命令在system/core/init/builtins.cpp内实现,对应函数do_mkdir。
最终调用到函数EnsurePolicy设置目录的加密策略:
从挂载文件fstab可知,Android FBE选择V2加密策略。接下来初始化文件内容加密模式、文件名加密模式、加密标志和密钥标识符(Key Identifier)。
VOLD子系统使用结构体fscrypt_policy_v2描述加密策略:
五、Linux Kernel对密钥的处理
5.1、内核对FBE Class Key处理框架
fscrypt用于Linux文件系统加密管理工具,负责管理元数据、密钥生成、密钥封装与 PAM 集成,并提供用于创建和修改加密目录的统一界面。fscrypt 内核部分已集成到诸如ext4文件系统中。
blk-mq是 Linux 块设备层多队列机制,它将Linux Kernel存储栈中请求层的单队列改成多队列以提升性能。如果blk-mq支持内联加密(Inline Encryption),它能够在存储栈中向下传递加密上下文。在Linux Kernel源码的commit中是这样解释的:
这段话的含义是,必须通过某种方式让存储设备驱动程序知道应该用于加密/解密请求的加密上下文,上层(如Filesystem or Fscrypt)知道情况并且管理加密上下文。当上层提交 BIO 到块层,该BIO最终到达的设备驱动程序支持内联加密,则设备驱动程序已经表明BIO加密上下文。回落到代码上具体改动是将结构体struct bio_crypt_ctx添加到struct bio中,用来表示加密上下文,同时引入各种用于操作 bio_crypt_ctx并使 bio/request合并逻辑知晓 bio_crypt_ctx 的函数。
● Fscrypt从Kernel Keyring提取FBE Class Key。
● F2FS将Wrapped key和Encryption Policy保存在encryption context。
● KSM负责管理ICE Keyslot,维护Keyslot状态机。
● TEEOS Keymaster TA负责对ICE Keyslot执行Program和Evict操作。
5.2、Fscrypt
下面这段话是Linux Kernel对fscrypt的描述:
Fscrypt是一个库,文件系统可以通过它以支持文件和目录的透明加密功能。fscrypt运行在文件系统级别,而不是块级别,这允许它使用不同的密钥加密不同的文件,并在同一文件系统上拥有未加密的文件。必须注意的是,除Filename外,fscrypt不会加密文件系统的元数据。
Fscrypt不作为本文档介绍的重点,仍然回到和FBE密钥处理相关的流程。接下来分析fscrypt如何处理目录/文件的加密策略。从4.4.2章节可知,VOLD下发IOCTL命令FS_IOC_SET_ENCRYPTION_POLICY到Linux Kernel,并调用函数fscrypt_ioctl_set_policy继续处理加密策略:
该函数的处理逻辑并不复杂,首先从VOLD子系统获取Encryption Policy,并将Encryption Policy保存到目录/文件的inode,保存前会先比较是否已经设置Encryption Policy,如果是,判断两次设置是否一致,不一致则报错。
5.3、F2FS filesystem
F2FS,Flash Friendly File System,专门为基于NAND存储设备设计的新型开源flash文件系统,特别针对NAND闪存存储介质进行了友好设计。
F2FS将整个卷切分成大量的Segments,每个Segment的大小固定为2MB。连续若干个Segments构成Section,连续若干个Section构成Zone。F2FS文件系统将整个卷切分成6个区域,除了超级块(Superblock,简称SB)外,其余每个区域都包含多个Segments,其结构如下图所示:
5.3.1、Segments
连续的Blocks集合成Segments,一个Segment的大小是512个Blocks(2MB),每个Segment都有一个Segment Summary Block元数据结构,描述了Segment 中的每个Block的所有者(该块所属的文件及块在文件内的偏移)。Segment Summary主要用于在执行Cleaning操作时识别哪些Blocks中的数据需要转移到新的位置,以及在转移之后如何更新Blocks的索引信息。一个Block就可以完全存储512个Blocks的summary信息,每个blocks都有一个1 bit的额外空间用于其它目的。
5.3.2、Superblock
F2FS 的 f2fs_super_block 存储在设备的第二个块中,仅包含只读数据,称为超级块 Superblock 。一旦文件系统创建,SB 的信息就不会再改变,SB 描述了文件系统有多大、Segment 有多大、Section有多大、Zone 有多大以及分配了多少空间给各个部分的“元数据”区域以及其他少量的细节信息。SB 位于文件系统分区的开头,有两个备份以避免文件系统 crash 无法恢复的情况发生。它包含基本的分区信息和默认的 F2FS 参数。
5.3.3、Main Area
Main Area被4KB大小的block所填充,这些block可以分配给文件的data或者文件的node,是F2FS文件系统的主要数据保存区域。
5.4、KSM
KeySlot Manager,密钥槽管理器,下面是Linux Kernel对KSM的描述:
由此可知,KSM是Linux kernel用于维护ICE(Inline Crypto Engine)内部keyslot硬件密钥槽的状态机,由结构体blk_keyslot_manager来描述:
该结构体的重点是成员struct blk_ksm_ll_ops ksm_ll_ops,定义对ICE Keyslot的操作函数集。成员函数keyslot_program用于对Keyslot执行Program操作,成员函数keyslot_evict用于对Keyslot执行Evict操作。
5.4.1、keyslot_program
以keyslot_program回调函数cqhci_crypto_keyslot_program@drivers/mmc/host/cqhci-crypto.c为例来说明如何对ICE Keyslot执行Program操作,且芯片平台厂商可以客制化。
该函数将参数key指向的加密上下文编程到处于idle状态的keyslot中,将编程成功keyslot index保存到参数slot。通过这种方式实现了"密钥可用不可见",即密钥被保存在ICE硬件Keyslot内,软件只可以获取保存密钥的keyslot index。
5.4.2、keyslot_evict
以keyslot_evict回调函数cqhci_crypto_keyslot_program@drivers/mmc/host/cqhci-crypto.c为例来说明如何对ICE Keyslot执行evict操作,且芯片平台厂商可以客制化。
根据参数key指向的加密上下文,获取在ICE硬件的keyslot index,并请求TEEOS Keymaster TA,根据keyslot index对ICE硬件内部keyslot执行evict操作。通过这种方式实现了"密钥可用不可见",即密钥被保存在ICE硬件Keyslot内,软件只可以根据密钥keyslot index执行evict操作。
六、TEEOS对密钥的处理
注:以Google Trusty来分析TEEOS对AES对称密钥的处理流程。
6.1、Trusty TEE Architecture
Google Trusty是一种安全OS,可为Android提供可信执行环境(TEE)。Trusty OS和Android OS在同一处理器上运行,通过硬件和软件与系统的其余组件进行隔离,且Trusty与Android彼此并行运行。Trusty可以访问设备主处理器和内存的全部功能,但完全隔离。隔离可以保护Trusty免受用户安装的恶意应用以及可能在Android中发现的潜在漏洞的侵害。
运行时,Trusty 应用在 Trusty 内核下以隔离进程的形式在非特权模式下运行。每个进程都会利用 TEE 处理器的内存管理单元功能在各自的虚拟内存沙盒中运行。
6.2、Trusty Keymaster Architecture
Android Keystore API和Keymaster HAL提供一组加密原语,允许使用访问受控、硬件支持的密钥来实现协议。Keymaster HAL是OEM提供的可动态加载的库,Keystore服务使用它来提供硬件支持的加密服务。为确保安全,HAL实现不会在用户空间甚至内核空间中执行任何敏感操作。敏感操作委托给通过某些内核接口达到的TEEOS,总的框架如下图所示:
Android设备中,Keymaster HAL的客户端包含App、frameworks、KeyStore daemon,其目的不是实现安全敏感型算法,而是对发送到TEEOS的请求进行编排和解排,真正实现安全敏感计算的是位于TEEOS的Keymaster TA。下图是Google对Keymaster架构的描述:
6.3、Keymaster Feature
下面是Google对Keymaster feature的定义:
可以看出,Keymaster功能强大。回归到FBE密钥框架领域,FBE密钥派生依赖Keymaster TA的Key generation特性,该特性与Key characteristics、Purpose、Client binding、Root of trust binding和version binding多重因素有关。
6.3.1、Key characteristics
无论是FBE Class key,还是加密FBE Class key的KEK,均采用AES对称算法,下面是Keymaster支持AES对称算法种类的描述:
所以,请求Keymaster派生密钥时,需要提供key characteristics:
6.3.2、Key Purpose
系统首次启动时需要创建FBE Class key和KEK,并以Keyblob形式落盘在userdata partition,这种场景下需要使用Keymaster加密功能。系统非首次启动时,从userdata partition提取Keyblob以获取FBE Class key和KEK,这种场景下需要使用Keymaster解密功能。所以,向Keymaster发送请求时,需要指定密钥目的:
6.3.3、Client binding
Keymaster要求产生的密钥需要和对应的应用进行绑定,绑定参数是APP_ID或ADD_DATA:
6.3.4、Root of trust binding
下图是Google对Root of trust的描述:
6.3.5、version binding
下图是Google对version binding的描述:
6.4、FBE key hierarchy
下图是Android FBE密钥体系:
在密钥派生系统中,主要包含"FileContens Encryption Key"、"FileName Encryption Key"和"Key Identifier"。
Linux Kernel KSM模块提供回调函数可以使用blk_crypto_ll_ops::keyslot_program将FileContents Encryption Key编程到ICE硬件Keyslot内,使用blk_crypto_ll_ops::derive_sw_secret进行密钥派生。
6.5、Keymaster派生FBE Class key
该函数处理逻辑并不复杂,最终FBE Class key以Keyblob形式落盘到userdata partition。
七、Inline Crypto Engine
基于软件的加密解决方案使用CPU来执行加密和解密任务,虽然这种方法提供了相当多的性能,但由于需要不断提高存储速度,因此存在不足。为了客服性能下降,JEDEC等标准机构向主机控制器内添加基于硬件的内联加密功能。内联意味着硬件加密引擎位于主机控制器内部,并动态加密和解密数据。使用内联硬件加密引擎处理大量安全数据。
就Android设备而言,内联加密引擎ICE位于UFS内部。当AP从DDR写数据到UFS Device时,需要对数据流进行加密。当AP从UFS Device读数据到DDR时,需要对数据流进行解密。下图是包含AES加解密引擎的UFS Controller内部逻辑框图:
八、总结
本文首先介绍了用户隐私数据保护方案,包括全盘加密FDE、文件加密FBE和元数据加密ME。其次本着以点带面的原则,以密钥管理为主线详细梳理了VOLD子系统、Linux Kernel和TEEOS的密钥处理流程,最后对内联加密引擎ICE进行了介绍。
希望读者可以通过这篇文档对Android文件加密FBE有整体认识,受限于篇幅,更受限于芯片厂商专利等诸多因素,很多技术细节和方案无法更详细展开,希望在后续文章中能够结合开源方案和代码可以更深入剖析FBE技术细节,如Linux Kernel Keyring、Fscrypt、文件IO等。
参考资料
Full Disk Encryption https://source.android.com/docs/security/features/encryption/full-disk
File-Based Encryption https://source.android.com/docs/security/features/encryption/file-based
Metadata Encryption https://source.android.com/docs/security/features/encryption/metadata
Hardware-Wrapped Keys https://source.android.com/docs/security/features/encryption/hw-wrapped-keys
Android AOSP https://cs.android.com/android/platform/superproject
Linux AOSP https://elixir.bootlin.com/linux/v5.15.111/source
Google Trusty https://source.android.com/docs/security/features/trusty
Google Keystore https://source.android.com/docs/security/features/keystore
Google Trusty Code https://github.com/haitao52198/TrustyOS/tree/master/AndroidTrustyOS
Inline Encryption https://www.synopsys.com/designware-ip/technical-bulletin/emmc-and-ufs-inline-encryption-2017q4.html