概述

简介

FDE(Full Disk Encryption,完全磁盘加密)是指在 Linux 操作系统上使用加密技术对整个硬盘或存储设备进行加密的过程。通过对整个硬盘进行加密,FDE 能够保护存储在磁盘上的所有数据,即使磁盘被物理盗窃或非法访问,未经授权的用户也无法读取数据。

FDE 原理

FDE 加密的核心思想是使用加密算法对硬盘的所有数据进行加密,通常在操作系统层级之前就对数据进行加密和解密。这意味着每次读写磁盘时,数据会被透明地加密和解密,而用户几乎不会感知到这一过程。

FDE 数据流如下

fde_data_stream

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。

cryptsetup_intro

dm-crypt

dm-crypt 是 Linux 系统中一个基于 Device Mapper 技术的加密模块,运行在内核空间。用于对存储设备(如硬盘、分区、LVM 卷等)进行加密。它提供了一种透明的加密机制,使得操作系统可以在不暴露加密细节的情况下,安全地处理数据。简而言之,dm-crypt 允许用户加密存储设备上的数据,确保数据的机密性,即使设备被盗或丢失,数据也无法被轻易访问。

工作原理

dm-crypt 是基于 Device Mapper(设备映射器)技术实现的,它通过创建一个新的虚拟设备,将加密层与存储层分离。操作系统通过虚拟设备进行所有的读写操作,而加密和解密的过程在后台自动完成。

当你对加密设备进行读写时,dm-crypt 会自动将数据加密(写入)或解密(读取)。这种加密/解密过程是透明的,应用程序或用户无需了解加密细节。

dm-crypt_read_write

X5 FDE用户密码管理

在全盘加密方案中,用户需要为加密的磁盘设置一个密码或密钥,只有提供正确的密码才能访问数据。这些密码和密钥管理的安全性直接决定了整个加密系统的强度。 X5 关于 FDE 密钥管理,提供了如下两种方案

  1. Cryptsetup 官方方案,使用 user password 为外部输入,例如保存在文件系统中的 user password 文件。

  2. 本文介绍的方案 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 分区加密

方案原理

该方案实现参考了 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,默认为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.bin

  • options:其他参数设置。

    • 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:

normal_fde_res

查看启动 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-plain

  • size : 密钥长度,支持 256/512

  • mntext4 : 使用 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)