概述
简介
FDE(Full Disk Encryption,完全磁盘加密)是指在 Linux 操作系统上使用加密技术对整个硬盘或存储设备进行加密的过程。通过对整个硬盘进行加密,FDE 能够保护存储在磁盘上的所有数据,即使磁盘被物理盗窃或非法访问,未经授权的用户也无法读取数据。
FDE 原理
FDE 加密的核心思想是使用加密算法对硬盘的所有数据进行加密,通常在操作系统层级之前就对数据进行加密和解密。这意味着每次读写磁盘时,数据会被透明地加密和解密,而用户几乎不会感知到这一过程。
FDE 数据流如下

FDE 的实现上依赖以下两个部分
名词解释
| 缩写 | 全称/解释 |
|---|---|
| FDE | Full Disk Encrypt,完全磁盘加密 |
| LUKS | Linux Unified Key Setup,Linux 统一密钥设置 |
| TEE | Trusted Execution Environment,可信执行环境 |
| AVB | Android Verified Boot,安卓验证启动 |
| Superblock | 超级块,用于存储文件系统的元数据和控制信息 |
| salt | 盐值,一段随机数据,对密码进行加盐处理能有效防止暴力破解 |
FDE 工具
Cryptsetup
Cryptsetup 是一个用于管理磁盘加密的工具,专门用于加密磁盘分区和管理 LUKS 加密卷。它提供了多种加密算法和灵活的密钥管理功能,广泛应用于保护敏感数据。通过 Cryptsetup,用户可以安全地创建和管理加密磁盘,并防止数据被未经授权的访问,尤其是在物理设备被盗或丢失的情况下。
工作原理
当创建一个 LUKS 加密卷时,会对目标分区生成一个luks header用来存储 key 和 salt 等,并重新格式化除了 luks header 外的部分,生成新的加密后的 superblock。

dm-crypt
dm-crypt 是 Linux 系统中一个基于 Device Mapper 技术的加密模块,运行在内核空间。用于对存储设备(如硬盘、分区、LVM 卷等)进行加密。它提供了一种透明的加密机制,使得操作系统可以在不暴露加密细节的情况下,安全地处理数据。简而言之,dm-crypt 允许用户加密存储设备上的数据,确保数据的机密性,即使设备被盗或丢失,数据也无法被轻易访问。
工作原理
dm-crypt 是基于 Device Mapper(设备映射器)技术实现的,它通过创建一个新的虚拟设备,将加密层与存储层分离。操作系统通过虚拟设备进行所有的读写操作,而加密和解密的过程在后台自动完成。
当你对加密设备进行读写时,dm-crypt 会自动将数据加密(写入)或解密(读取)。这种加密/解密过程是透明的,应用程序或用户无需了解加密细节。

X5 FDE用户密码管理
在全盘加密方案中,用户需要为加密的磁盘设置一个密码或密钥,只有提供正确的密码才能访问数据。这些密码和密钥管理的安全性直接决定了整个加密系统的强度。 X5 关于 FDE 密钥管理,提供了如下两种方案
Cryptsetup 官方方案,使用 user password 为外部输入,例如保存在文件系统中的 user password 文件。
本文介绍的方案 user passwd 由 TEE 派生出来,不保存在存储介质。
两者主要区别是 user password 来源不同
注意: X5 推荐使用方案2
方案一: Cryptsetup 官方方案
cryptsetup -c aes-xts-plain64 -s 256 -h sha256 -q luksFormat /dev/mmcblk0p1 -d /etc/fde_default.bin
cryptsetup -c aes-xts-plain64 -s 256 luksOpen /dev/mmcblk0p1 luksdata -d /etc/fde_default.bin
方案二: 通过 TEE 派生 user passwd
X5 通过修改 Cryptsetup 源码,其方案特性如下
user passwd 由TEE派生出来
user passwd 不保存在存储介质,只是在 Cryptsetup 初始化的时候使用
getdmkey --km | cryptsetup -c aes-xts-plain64 -s 256 -h sha256 -q luksFormat /dev/mmcblk0p1
getdmkey --km | cryptsetup -c aes-xts-plain64 -s 256 -h sha256 luksOpen /dev/mmcblk0p1 luksdata
cryptsetup 命令参数说明
-c, --cipher: 指定加密算法,例如aes-xts-plain64是 dm-crypt 中常用的一种加密算法配置,它指定了以下内容aes: 使用 AES 对称加密算法,xts: 使用 XTS 加密模式,适用于磁盘加密等场景plain: 表示没有额外的额外层次加密或包装
-s, --key-size: 指定密钥长度(单位: 比特),如 128 位或 256 位。通常建议使用 256 位的密钥大小--hash: 指定哈希算法,如 sha256 或 sha512,用于生成加密密钥的哈希值。-d, --key-file: 指定密钥文件的路径-q, --batch-mode: 批处理模式下运行 cryptsetup 命令,操作将不会进行任何交互式提示,所有的确认和用户输入都被自动跳过luksFormat: 用于格式化一个磁盘分区或设备为 LUKS 加密格式luksOpen: 解锁 LUKS 加密卷,将其映射为一个设备luksdata: 指定 device mapper 映射的节点名,用户可以指定为任意名称,此处使用luksdata仅作为演示
X5 FDE 使用方法
当前 X5 支持 system 分区和用户分区加密
system 分区加密可参考如下方案
用户分区加密支持 key-hobot 和 key-file 方案
system 分区加密
方案原理
该方案实现参考了 system 的 dm-verity 实现流程:
编译阶段: 在编译 system FDE 镜像时,FDE 相关的配置信息保存到了 vbmeta 分区
uboot 阶段: uboot 从 vbmeta 分区获取 system FDE 信息,并把此信息通过 bootargs 传递到 kernel
kernel 阶段: 由 kernel 的 dm-crypt 框架解析 bootargs,完成 system 分区的解密
在 bootargs 中的 FDE 信息示例如下:
dm-mod.create="dm-crypt,,0,ro,0 510976 crypt aes-cbc-essiv:sha256 bf05fb0da6bc94f6b550e23a093d5fd2 0 $(SYSTEM_PART) 0 1 allow_discards
使用方法
把 system 分区的验证方式改为 crypt
根据项目修改配置文件,以 device/horizon/x5/board_x5_evb_debug_config.mk 为例
# system 配置
# 指定根文件系统类型和预编译的文件系统路径
export HR_SYSTEM_TYPE="buildroot"
export HR_SYSTEM_DIR=${HR_TOP_DIR}/system/buildroot/prebuilt
# 根文件系统的分区名,需要和分区表配置对应
export HR_SYSTEM_PART_NAME="system"
# system verify method: dm-verity, crypt
export HR_SYSTEM_VERIFY="crypt"
注意: 修改配置后,需重新执行 ./bd.sh lunch 选择该配置文件,使选项生效
制作 system FDE 镜像
当前FDE key由编译时随机产生,不落盘, 长度为128 bit。
注意: 如何防止 system FDE 信息泄露
在开发板上通过 bootargs 传递 system FDE 信息,因此可以通过启动 log 查看 cmdline 获取 system FDE的信息。
因此为了防止 FDE key 的泄露,在 bootargs 中的 key 是由 efuse 中 user root key 加密后的 FDE key (其加密方式为 AES-128-ECB,nopad)
在 kernel 的 dm-crypt 框架中添加了对 bootargs 的解密 key 流程,解密流程在 TEE 侧完成。
要触发 key 的解密,需要 bootargs 中的 key 添加前缀 dr-fde:,如下所示:
dm-mod.create="dm-crypt,,0,ro,0 510976 crypt aes-cbc-essiv:sha256 dr-fde:bf05fb0da6bc94f6b550e23a093d5fd2 0 $(SYSTEM_PART) 0 1 allow_discards
user root key
user root key位于 device/horizon/x5/board_cfg/soc/bl2_cfg/user_root.key ,长度16字节
生成用户 key 用户可以更换user root key,例如:随机生成新的 key,操作如下
cd device/horizon/x5/board_cfg/soc/bl2_cfg/
dd if=/dev/random of=./user_root.key bs=1 count=16
烧写 key
user root key的烧写请参考 烧写efuse
注意: 没烧写 user root key,默认为16个字节的0x0
编译与启动
执行 ./bd.sh 重新编译 disk 镜像并且烧录到主板启动。
其中,system FDE镜像的制作脚本是 build/tools/partition_tools/pack_avb_img.sh
注意:
system FDE镜像的制作需要sudo权限
必须为 secure boot 启动,且开启 AVB 验证,否则不会触发 system 分区的解密而导致根文件系统挂载失败
system的加密信息保存在vbmeta分区,因此必须OTA升级或烧录时必须同时升级vbmeta/boot/system分区,并且这三个分区镜像是在同一次编译出产生的
用户分区加密: key-hobot 方案
实现普通分区的 FDE 方案,以分区 data 为例,操作步骤如下
新增分区
在分区表中新增 data 分区,以 device/horizon/x5/board_cfg/soc/x5-soc-debug-gpt.json 为例(用户以实际分区表文件为准)
diff --git a/board_cfg/soc/x5-soc-debug-gpt.json b/board_cfg/soc/x5-soc-debug-gpt.json
index 10500f3..22a3421 100644
--- a/board_cfg/soc/x5-soc-debug-gpt.json
+++ b/board_cfg/soc/x5-soc-debug-gpt.json
@@ -39,6 +39,10 @@
"part_type": "GOLDEN",
"size": "700m"
},
+ "data": {
+ "size": "50m"
+ },
"userdata": {
"fs_type": "ext4",
"size": "50m"
注意:
若新增包含内容的分区,则该分区在分区表中的前一个分区必须为非空(即实际会生成 .img 镜像文件)。如果是新增空分区,则不受此限制。
新增的分区名称中不得包含下划线
_,否则分区名称将在编译脚本中被截断,从而导致异常。如果把原有分区改成 FDE,需要在
system/buildroot/prebuilt/boot-utils-runtime/etc/hb-fstab中删除对应分区的 mount 规则,否则会 mount 失败。
在 hb-crypttab 中添加 FDE 相关配置
路径 system/buildroot/prebuilt/boot-utils-runtime/etc/hb-crypttab 添加 data 分区的 FDE 配置,如下
# <target name> <source device> <key file> <options>
data /dev/block/platform/by-name/data --key-hobot cipher=aes-xts-plain64,size=512,ext4=y
配置格式为 <target name> <source device> <key file> <options>, 参数说明
target name: 加密分区名,也是在/dev/mapper/下的名字source device: 设备路径,当前会对分区设备自动创建 by-name 链接,by-name 路径都在/dev/block/platform/by-name下,例如对应 eMMC 分区设备,会链接到/dev/mmcblk\*p\*key file:--key-hobot表示使用TEE派生出来的 user passwd;另外也可以指定 key 路径, 例如/etc/fde_default.binoptions:其他参数设置。cipher=aes-xts-plain64: 表示指定加密算法,注意需要和 cryptsetup 的-c参数匹配size=512: 表示key size,注意需要和 cryptsetup 的-s参数匹配hash=sha512: 表示对key做HASH,对应 cryptsetup 的-h参数ext4=y: 表示加密分区的文件系统格式,加密初始化之后需要对分区格式化之后才能挂载
注意: 如果要替换默认的 FDE key,编译时需要替换 system/buildroot/prebuilt/boot-utils-runtime/etc/fde_default.bin
在根文件系统中创建挂载节点目录
修改 system 编译脚本 build/mk_system.sh,增加 data 分区的挂载点目录
function build_unpack()
{
...(省略)...
# 创建分区挂载目录
mkdir -p ${SYSTEM_BUILD_DIR}/{app,log,userdata,usr/hobot,data,private}
...(省略)...
}
重新编译
编译与启动
执行 ./bd.sh 重新编译 disk 镜像并且烧录到主板启动。
如下图,开启成功后在 /dev/mapper 下将会出现 data:

查看启动 log
[ 27.455427] CRYPTSETUP: cipher value: aes-xts-plain64
[ 27.460926] CRYPTSETUP: /dev/block/platform/by-name/data
[ 27.485172] INFO: fde luksFormat /dev/block/platform/by-name/data starting
[ 27.505462] CRYPTSETUP: key:--key-hobot src:/dev/block/platform/by-name/data dst:data
[ 40.135874] CRYPTSETUP: data
[ 40.142366] mount -t ext4 /dev/mapper/data /data
[ 40.235217] ext2fs_open2:
[ 40.235270] Bad magic number in super-block
[ 40.243783] fsck.ext4: Superblock invalid, trying backup blocks...
[ 40.267239]
The
[ 40.267259] super
[ 40.271303] block
[ 40.273973] udevd[1265]: conflicting device node '/dev/mapper/data' found, link to '/dev/dm-0' will not be created
[ 40.278746] could not be read or does not describe a valid ext2/ext3/ext4
[ 40.298703] filesystem
[ 40.298709] . If the
[ 40.302940] printk: fsck.ext4: 14 output lines suppressed due to ratelimiting
[ 40.332572] Creating filesystem with 34816 1k blocks and 8720 inodes
[ 40.338968] Filesystem UUID: e57ae31f-6f33-4c02-a2c0-6d714aeebb57
[ 40.345097] Superblock backups stored on blocks:
[ 40.345105]
[ 40.349845] 8193
[ 40.352905] ,
[ 40.354750] 24577
[ 40.361360] Allocating group tables:
[ 41.841061] printk: mkfs.ext4: 22 output lines suppressed due to ratelimiting
[ 41.885513] Pass 1: Checking
[ 41.885533] inode
[ 41.888517] s,
[ 41.890479] block
[ 41.892236] s, and sizes
[ 41.904447] Pass 2: Checking
[ 41.904464] directory
[ 41.907447] structure
[ 41.914925] Pass 3: Checking
[ 41.914939] directory
[ 41.949896] printk: fsck.ext4: 6 output lines suppressed due to ratelimiting
[ 41.966145] EXT4-fs (dm-0): mounted filesystem with ordered data mode. Quota mode: disabled.
用户分区加密: key-file 方案
方案原理
该方案实现了在编译阶段使用 key 文件加密用户指定分区,并在启动阶段使用相同的 key 文件对用户分区进行解密并挂载
下面以新增用户分区 fdedata 为例,介绍其使用方法
编译阶段: 使用指定 key 文件对用户分区镜像进行加密
启动阶段: 启动脚本根据配置文件,对用户分区解密,并挂载到指定目录
使用方法
1. 新增分区与编译脚本
在分区表中新增 fdedata 分区,以 device/horizon/x5/board_cfg/soc/x5-soc-debug-gpt.json 为例(用户以实际分区表文件为准)
"fdedata": {
"fs_type": "ext4",
"part_type": "GOLDEN",
"size": "32m",
"fde_type": "key-file"
},
参数说明
fs_type: 文件系统格式,目前仅支持对 ext4 格式分区加密size: 分区大小fde_type: 用于在编译阶段指定分区加密类型,目前仅支持指定key-file方式
注意: 如果挂载点不存在,启动脚本将自动创建,需注意如果挂载点在 /,由于 rootfs 默认为只读,需要在编译阶段提前创建目录,避免目录创建失败
2. 加解密 key 文件
默认加解密使用的 key 文件路径 system/buildroot/prebuilt/boot-utils-runtime/etc/fde_default.bin,用户可替换为实际使用 key 文件。
示例: 通过随机数生成 key 文件
注意: key 文件大小需要与加密算法密钥长度匹配,目前用户分区加密 key 长度支持 256/512
以 AES-128 对应 key 长度 16字节为例
dd if=/dev/urandom of=system/buildroot/prebuilt/boot-utils-runtime/etc/fde_default.bin bs=1 count=16
新增 fdedata 分区的解密挂载配置,路径 system/buildroot/prebuilt/boot-utils-runtime/etc/hb-crypttab。
# <target name> <source device> <key file> <options>
fdedata /dev/block/platform/by-name/fdedata /etc/fde_default.bin dm_crypt,cipher=aes-xts-plain,size=256,mntext4=/userdata/test
配置文件的格式为: [分区名] [分区路径] [key文件路径] [参数]
参数支持多个选项,每个选项以逗号隔开,选项说明如下
dm_crypt: 表示使用 dm-crypt 加密用户分区cipher: 加密算法模式,注意此处应与编译脚本一致,X5 SDK 默认使用aes-xts-plainsize: 密钥长度,支持 256/512mntext4: 使用 ext4 格式挂载点路径
3. 新增加密分区编译脚本
为了让用户更好的控制编译逻辑,X5 SDK 对每个分区都使用了独立的编译脚本,针对用户加密分区编译,X5 SDK 提供了脚本模板 build/mk_fde.sh,用户需复制并修改为用户分区名,格式为 mk_[分区名].sh,以新增分区 fdedata 为例,需复制并重命名脚本为 mk_fdedata.sh。
注意:脚本文件名必须严格按照以上格式重命名,否则将导致编译报错
用户可以在编译脚本中添加自定义操作,如下
#...(省略代码)...
function build_all()
{
echo -e "\033[33m[INFO]: Starting Build User FDE Part [${PART_NAME}].\033[0m"
echo "Part Name: ${PART_NAME}"
echo "SRC Folder: ${user_part_dir}"
echo "Fs Type: ${user_fs_type}"
echo "FDE Type: ${fde_type}"
# The user adds custom actions here. Begin
# 用户在此添加自定义操作
# End
# Build EXT4 image
if [ "${user_fs_type}" = "ext4" ]; then
[ ! -d $user_part_dir ] && mkdir -p ${user_part_dir}
${HR_PARTITION_TOOL_PATH}/mk_avb_fs.sh ${PART_NAME} ${user_part_dir}
fi
#...(省略代码)...
在 xbuild.sh 增加 fdedata 的编译入口
+++ b/xbuild.sh
@@ -126,6 +126,11 @@ function build_app
build_component "app" "${HR_LOCAL_DIR}/mk_app.sh" "$@"
}
+function build_fdedata
+{
+ build_component "fdedata" "${HR_LOCAL_DIR}/mk_fdedata.sh" "$@"
+}
+
function truncate_fill_image
{
part_size=$(get_part_attr "${1}" "size")
@@ -408,7 +413,7 @@ function build_all
build_pack "$opt"
}
-avail_func=("all" "lunch" "miniboot" "uboot" "factory" "boot" "hbre" "system" "app" "pack" "otapackage")
+avail_func=("all" "lunch" "miniboot" "uboot" "factory" "boot" "hbre" "system" "app" "pack" "otapackage" "fdedata")
if [ $# -eq 0 ];then
build_all all
4. 用户加密分区源文件
用户分区源目录位于 SDK 根目录的 custom/[分区名]
注意: X5 SDK 默认并不包含此目录,需用户自行创建
以新增分区 fdedata 为例,用户需新增目录 custom/fdedata,并存放用户数据文件
custom/
└── fdedata
├── a.txt
├── b.txt
├── c.txt
├── d.txt
└── f.txt
5. 编译
全编译
在 SDK 根目录执行 sudo ./bd.sh,将自动编译并生成分区加密镜像,其原理步骤如下
将
custom/fdedata目录打包为 ext4 格式镜像使用 key 文件对 ext4 镜像镜像加密,生成
out/product/fdedata.img将
out/product/fdedata.img打包进固件镜像emmc_disk.simg
分区编译
用户也可以对加密分区镜像进行单独编译,编译命令格式为 sudo ./bd.sh [分区名]
注意: 编译加密分区需要获得 root 权限
启动验证
系统启动脚本 /etc/init.d/S95forcefde.sh,将读取配置 /etc/hb-crypttab,使用其参数选项解密分区,以分区 fdedata 为例:
其配置如下:
# <target name> <source device> <key file> <options>
fdedata /dev/block/platform/by-name/fdedata /etc/fde_default.bin dm_crypt,cipher=aes-xts-plain,size=256,mntext4=/userdata/test
启动log如下:
[ 4.799535] udevd[302]: conflicting device node '/dev/mapper/fdedata' found, link to '/dev/dm-1' will not be created
[ 4.857102] INSECURE MODE FOR /etc/fde_default.bin
[ 4.863015] CRYPTSETUP: cipher value: aes-xts-plain
[ 4.863393] Mount Point: /userdata/test
[ 4.866874] CRYPTSETUP: /dev/block/platform/by-name/fdedata
[ 4.914790] CRYPTSETUP: dm_crypt params --type plain -c aes-xts-plain -s 256
[ 4.914908] CRYPTSETUP: dm_crypt key -d /etc/fde_default.bin
[ 4.976727] CRYPTSETUP: fdedata
系统将分区 fdedata 解密并生成节点 /dev/mapper/fdedata。最后将解密分区自动挂载到目录 /userdata/test
root@buildroot:~# mount | grep fdedata
/dev/mapper/fdedata on /userdata/test type ext4 (rw,relatime)