1. 概述
文件系统加密常见两个方式,FDE(full disk encrypt),FBE(file baseed encrypt)和ME(metadata encrypt)。文件加密常用于用户数据保护场景,避免用户数据泄露。本文以ext4文件系统的读取流程为切入点,介绍其FBE的实现机制,以及与Keyring和fscypt的交互流程。
2. ext4文件加密
ext4支持对指定目录或者文件设置为加密目录或文件。
2.1 设置指定目录或文件加密
用户可以ioctl设置指定目录或者文件加密,相关调用链如下:
ioctl(EXT4_IOC_SET_ENCRYPTION_POLICY) -> ext4_ioctl -> fscrypt_ioctl_set_policy->create_encryption_context_from_policy -> ext4_set_context
1)fscrypt_ioctl_set_policy
ext4调用fscypt接口设置inode的加密上下文。
2)create_encryption_context_from_policy
此处会使用生成一个随机数作为派生秘钥的加密秘钥。每文件的加密秘钥如何生成,后续小节会详细介绍
3)ext4_set_context
ext4文件系统中使用inode的xattr保存加密上下文。
2.2 顶层加密目录节点的秘钥派生
上节介绍ioctl仅设置目录节点的fscypt_context并写入inode的xattr中。但是并未涉及派生秘钥的生成。当目录第一次open时,将inode->i_crypt_info初始化,并设置加密算法和派生秘钥
ext4_readdir/ext4_dir_open -> fscrypt_get_encryption_info -> find_and_derive_key -> derive_key_aes
1) fscrypt_get_encryption_info
当加密目录第一次打开时,会在inode中加载crypt_info数据,其中包含加密算法和加密秘钥的派生。
2)find_and_derive_key
秘钥派生前,需要获取当前进程的主密钥。进程的主密钥导入是基于keyring实现,本文不展开介绍。
3)秘钥派生derive_key_aes
秘钥派生实际上是使用ioctl流程生成的随机数对进程主密钥进行aes加密的过程。
4)进程主密钥,派生秘钥和生成随机数的关系
- 进程主秘钥作为输入数据
- ioctl设置加密目录时生成的随机数作为对称秘钥
- 主秘钥加密后即作为派生秘钥
- 由于目录的随机数是未知,因此即使同一进程的不同目录生成派生秘钥也不同
2.3 子目录或者文件节点的秘钥派生
当已设置某个目录为加密目录时,再此目录下创建的子目录和文件都将默认加密,且派生秘钥不同。从支持不同文件或目录使用不同秘钥进行加解密。
ext4_create/ext4_mkdir -> ext4_new_inode_start_handle -> __ext4_new_inode -> fscrypt_inherit_context
1)__ext4_new_inode
创建子节点时,如果是作为加密目录,需要设置加密上下文。
2)fscrypt_inherit_context
FBE的核心要求就是不同文件或者目录采用不同的加密秘钥,实际的体现就是不同inode的采用不同随机数用于秘钥派生,即ctx.noce重新生成。
2.4 写文件的相关流程
写文件操作涉及发起写请求和写回调处理。
2.4.1 写请求
当inode已加载crypt_info,用户调用write系统调用写文件时,ext4将调用fscypt接口,对page进行加密,然后将加密后的page提交bio。以writepage流程举例
写io发起流程:
sys_write-> ... -> ext4_writepage -> ext4_bio_write_page
1)ext4_bio_write_page
作为发起写io的核心流程,涉及加密节点相关任务
- 待写入page的加密
- 发起bio
2)fscrypt_encrypt_page
此处对明文page进行加密操作,不是直接修改当前page,而是申请一个新page,用于存储加密后的数据。
原因是原明文page,属于page cache需要继续报错明文,从而被应用使用。
2.4.2 写回调end_io
当写io完成时,回收bio以及加密page,并设置page状态
ext4_end_io -> ext4_finish_bio
2.5 读文件的解密流程
读文件和写文件类似涉及读io发起和读回调流程
2.5.1 读请求
当inode已加载crypt_info,用户调用read系统调用读文件时,由于磁盘中实际的block为加密数据,ext4需要对读取page进行解密后返回给用户。以readpage流程举例
读io发起流程:
sys_read-> ... -> ext4_readpage -> ext4_mpage_readpages
其中ext4_mpage_readpages涉及的关键点如下
- 设置end_io回调的解密算法和秘钥
- 发起读bio
2.5.2 读回调
当读io完成时,对读取加密page进行解密
mpage_end_io -> fscrypt_enqueue_decrypt_bio -> completion_pages -> __fscrypt_decrypt_bio -> fscrypt_decrypt_page
1)fscrypt_enqueue_decrypt_bio
由于end_io属于软中断上下文,因此加解密等耗时操作,不能在此流程中进行。
2)fscrypt_decrypt_page
由于对密文page不需要保留,因此解密过程不会申请新page,直接覆盖原密文内容即可。
3. 总结
本文以用户进行FBE加密的操作过程为切入点,介绍ext4的FBE实现逻辑。其中涉及密码学部分,如对称秘钥等内容,需要读者扩展学习。