可信启动通过 TPM 的完整性度量机制扩展 UEFI 安全启动的签名验证能力,形成从硬件信任根到操作系统的完整信任链. 本文将探讨如何在 QEMU 虚拟环境中实现基于 UEFI Secure Boot 与 TPM 的 GRUB 可信引导过程, 包括原理解析、环境搭建、配置方法及验证测试, 为读者提供一个可复现的技术实践指南, 帮助深入理解可信计算的基础机制.
可信启动原理简介
可信启动(Trusted Boot) 本质是在 安全启动(Secure Boot) 的基础上引入 TPM 作为硬件可信根, 在实现系统启动组件逐级度量的功能上添加了 TPM PCR 安全存储以及远程证明的安全能力.
在启动过程中, 信任链逐级建立: 硬件可信根度量底层固件 BIOS/UEFI 的相关配置与信息, 后者再进一步度量 Grub 等引导加载程序, Grub 再度量 操作系统内核与 initramfs 映像, 当开启了 Linux IMA 安全机制后, 还会进一步将信任链拓展运行时的可执行文件与其他系统文件上. 所有组件的密码学度量值通过 PCR 扩展操作形成不可逆的信任链,为远程证明提供审计基础.
带有 TPM 模块的可信启动过程( 图源 )
这里我们不去掰扯可信、可信计算那一堆稍显复杂且专门的术语与概念, 因为网络上的容易阅读的资料良莠不齐, 而本人又无心思去钻研 TCG 可信计算组 或是 等保 2.0 什么的安全规范.. 因此只从我所看到、所能亲自实现的角度来谈谈, 抛开那些概念, 如何去真正部署与实现一个结合了 TPM 可信芯片的 Linux 系统安全启动流程.
本文所实现的可信启动过程依托于 UEFI 安全启动机制, 因此简单介绍一下安全启动的相关内容.
UEFI 安全启动
UEFI 安全启动是在启动阶段的一项完整性与真实性保护机制, 对于核心的启动组件(如引导加载程序、操作系统内核等)进行数字签名验证, 确保它们在启动过程中未被篡改. 安全启动的实现依赖于 UEFI 固件和数字证书, 通过验证签名来确保只有经过授权的组件才能被启动.
当开启安全启动后, UEFI 固件会检查每个启动组件的数字签名, 如果签名验证失败, 则该组件将不会被加载. 这有助于防止恶意软件在系统启动时加载, 提供了一层额外的安全保护; 而受信任的 EFI 引导加载程序也必须以安全的方式继续启动过程,最终保障操作系统本身是安全的.
UEFI Secure Boot 通过签名证书白名单(允许列表)管理可信组件. UEFI 固件内置平台厂商证书(如 Microsoft UEFI CA), 也可以加载用户自定义的证书, 任何组件需由白名单内证书签发才能加载. 相较于简单的哈希对比, 签名机制支持代码更新和证书吊销(通过 DBX 黑名单).
而当引入了 TPM 芯片后情况会发生什么改变呢?
TPM 可信启动
不同于网络上很多文章指出的:
可信引导会在事前维护一个度量值(哈希)白名单, 启动过程中会度量系统组件并与白名单进行比对, 出现不匹配则拒绝加载.
事实上, 引入 TPM 芯片后也不会去额外维护白名单并进行比对或拦截, 而是在 UEFI 安全启动的基础上, 将启动过程中各个组件的度量值(即哈希值)进行安全存储, 并可以在后续的远程证明过程中, 将这些度量值提供给远程验证方, 以证明当前系统的完整性与可信性.
TPM 通过内部一组特殊的 平台配置寄存器(PCR, Platform Configuration Registers) 来提供安全存储, 在启动过程中, 系统会依次测量各个启动组件(如 BIOS、引导加载程序、内核等), 然后通过哈希算法(如 SHA-1 或 SHA-256)计算得到度量值. 这些度量值不是简单地覆盖 PCR 中的原值, 而是通过 扩展(extend) 操作将新的度量值与PCR中的现有值结合:
$$ PCR_{new} = Hash(PCR_{old} \Vert measurement) $$
这种链式哈希的方式确保了:
-
启动过程的完整记录被保存下来
-
任何组件的变化都会导致最终PCR值的变化
-
PCR值无法被伪造或回滚
TPM 2.0 规范定义了启动阶段PCR使用规范, 表格来自: TCG PC 客户端平台固件配置规范
PCR Index | PCR Usage |
---|---|
0 | SRTM, BIOS, Host Platform Extensions, Embedded Option ROMs and PI Drivers |
1 | Host Platform Configuration |
2 | UEFI driver and application Code |
3 | UEFI driver and application Configuration and Data |
4 | UEFI Boot Manager Code (usually the MBR) and Boot Attempts |
5 | Boot Manager Code Configuration and Data (for use by the Boot Manager Code) and GPT/Partition Table |
6 | Host Platform Manufacturer Specific |
7 | Secure Boot Policy |
8-15 | Defined for use by the Static OS |
16 | Debug |
23 | Application Support |
与之相关的另一个安全能力是 远程证明(Remote Attestation), 其允许一个远程系统(验证者)验证另一个系统(被验证者)的平台完整性和可信状态.
简单来说, 每一个 TPM 内部都有自己的证书与安全密钥, 这些证书通常由 TPM 制造商或可信的认证机构颁发. 验证者发送请求与挑战, 被验证者使用 TPM 生成证明, 对当前平台的 PCR 寄存器(反映系统启动状态)、验证者发送的挑战值、其他平台信息等进行签名后发送给验证者, 验证者即可通过证书和签名验证平台的状态可信与系统启动的完整性.
由此, TPM 模块就拓展了传统 UEFI 安全启动的能力, 不仅通过 PCR 的链式哈希记录了启动过程的完整性, 还可以通过内置的安全原语去向外部的验证者证明自己的平台可信状态.
参考阅读:
-
UEFI 安全启动相关:
-
TPM、可信计算相关:
-
TCG PC Client Specific Platform Firmware Profile Specification | Trusted Computing Group:
-
阿里云的可信实例: 如何使用可信实例_云服务器 ECS(ECS)-阿里云帮助中心
实现过程
目前, 仅部署了带有 TPM 芯片的 QEMU 虚拟机安全启动过程, 基于 UEFI Secure Boot 度量启动过程中的 Grub 以及 Linux 内核; 而有关远程证明相关的内容还尚未集成实现.
实验环境
-
虚拟化工具: QEMU-system-x86_64, 暂时仅关心 x64 架构的模拟情况
-
TPM 芯片: swtpm 模拟 TPM 2.0 设备, 可通过 Socket 或 TCP 端口挂载到 QEMU 虚拟机上.
-
UEFI 固件: OVMF (Open Virtual Machine Firmware), 支持 UEFI Secure Boot.
-
引导加载程序: GRUB-2.12
-
虚拟机内核: Linux-6.12.16, 自编译内核镜像, 版本没有什么特殊想法, 随便掏了一个 kernel.org 上最新的 longterm 版本
-
虚拟机根文件系统: Busybox 与 Buildroot 实现的都过于简单, 很多动态库的支持不好, 由于后续还需要进行其他实验(clang/ebpf相关), 因此直接使用了完整的 Arch Linux 根文件系统
-
宿主机: Arch Linux
基本流程
基本环境搭建
安装相关的软件:
swtpm,
tpm2-tools, efitools, dosfstools(创建 fat32), sbsigntools(安全启动签名); 在 archlinux 官方源都有, 通过 pacman
可以轻松安装.
工具目录大致如下, 其中有一些无关文件, 主要的相关文件有:
-
linux 源码文件夹, 用于自编译内核
-
根文件系统: dream-rootfs.ext4, rootfs.img
- 两个简单脚本 mount.sh 与 umount.sh, 一键将根文件系统文件绑定到 /dev/loopx 循环设备上, 并 mount 到特定目录.
-
Makefile, 配置了 QEMU 的启动脚本
-
secureboot 文件夹: sbsign 相关的密钥管理
-
tpm-state: swtpm 生成的临时文件夹
-
其他: ovmf 副本, 日志文件等.

Linux 内核
Linux 内核主要需要启用若干内核选项: TPM 相关, IMA 相关(用于生成度量日志, 并拓展 PCR[10]), EFIVARS 等:
下面展示的内核选项仅为展示选项所处位置, 实际需开启所有相关的选项.
1 │ Symbol: TCG_TPM [=y]
2 │ Type : tristate
3 │ Defined at drivers/char/tpm/Kconfig:6
4 │ Prompt: TPM Hardware Support
5 │ Depends on: HAS_IOMEM [=y]
6 │ Location:
7 │ -> Device Drivers
8 │ -> Character devices
9 │ -> TPM Hardware Support (TCG_TPM [=y])
10 │ Selects: CRYPTO [=y] && CRYPTO_HASH_INFO [=y]
11 │ Selected by [y]:
12 │ - IMA [=y] && INTEGRITY [=y] && HAS_IOMEM [=y]
13 │ Implies: SECURITYFS [=y]
14
15
16 │ Symbol: INTEGRITY [=y]
17 │ Type : bool
18 │ Defined at security/integrity/Kconfig:3
19 │ Prompt: Integrity subsystem
20 │ Depends on: SECURITY [=y]
21 │ Location:
22 │ -> Security options
23 │ -> Integrity subsystem (INTEGRITY [=y])
24
25
26 │ Symbol: EFIVAR_FS [=y]
27 │ Type : tristate
28 │ Defined at fs/efivarfs/Kconfig:2
29 │ Prompt: EFI Variable filesystem
30 │ Depends on: EFI [=y]
31 │ Location:
32 │ -> File systems
33 │ -> Pseudo filesystems
34 │ (1) -> EFI Variable filesystem (EFIVAR_FS [=y])
需要注意的是, 由于在我的实验环境中, rootfs 与 Linux Kernel 是分离式的, rootfs 尽管使用了 Arch Linux 的文件系统与工具链, 但没有使用对应的内核, 使用的是自编译的内核, 因此无法很轻松地使用内核模块相关的功能.
-
内核模块通常位于
/usr/lib/modules/$(uname -r)
下, 如果是分离式的情况, rootfs 中不存在对应的目录, 也就无法正常使用内核所对应的内核模块了. -
尝试过将宿主机中编译后的内核源码文件夹放进去过, 但好像还是不行; 没有进一步深入研究了..
因此在配置内核选项时, 不要使用以 <M>
内核模块的方式加载对应功能, 而是全部使用 <*>
的方式直接静态编译进内核.
根文件系统
在没有引入 UEFI 系统之前, 直接使用 qemu 的 -kernel
装载内核和 -drive
装载 rootf 即可. 因此此前的旧文件系统使用的是单一 ext4
分区的方式, 这在引入 UEFI 之前能够正常工作.
但是当引入 UEFI 之后, 需要一个额外的 ESP 分区来存放 UEFI 引导加载程序(如GRUB-efi等), 再又其加载内核并启动系统..
因此, 需要构建一个新的双分区的文件系统:
-
dd
创建一个新的磁盘镜像文件 -
fdisk
做分区, 一个 ESP, 一个主系统分区 -
将镜像绑定到
loop
设备:sudo losetup -fP --show disk.img
-
创建文件系统
bash1# 为 ESP 分区创建 FAT32 文件系统 2sudo mkfs.fat -F32 /dev/loop0p1 3 4# 为根分区创建 ext4 文件系统 5sudo mkfs.ext4 /dev/loop0p2
-
挂载分区
bash1sudo mkdir -p /mnt 2sudo mount /dev/loop0p2 /mnt/ 3sudo mkdir -p /mnt/boot/efi 4sudo mount /dev/loop0p1 /mnt/boot/efi
-
安装 Grub, 配置
grub.cfg
bash1# 进入 chroot 环境 2# arch-chroot /mnt 3 4# 在 chroot 环境中安装必要的包 5pacman -S grub efibootmgr 6 7# 安装 GRUB 到 EFI 分区 8grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB 9 10# 生成 GRUB 配置文件 11grub-mkconfig -o /boot/grub/grub.cfg
-
将内核文件 bzImage 复制到
/boot
目录下, 配置 Grub 确保能够正确引导内核. -
卸载分区并分离 loop 设备:
bash1sudo umount /mnt/boot/efi 2sudo umount /mnt 3sudo losetup -d /dev/loop0
至此, 一个基本的双分区的基于 UEFI 启动(还没有到安全启动!) 的文件系统就搭建完成了.
Makefile 启动脚本
配置 Makefile 脚本, 使用 QEMU 组合所有的组件, 启动虚拟机:
-
考虑配置共享文件夹
SHARED_DIR
以及hostfwd
网络端口转发功能(SSH访问), 方便使用 -
由于经常性需要进入 QEMU 虚拟机内去工作, 因此设置了多个启动目标, qemu-direct 不通过 uefi 启动流程, 直接通过 qemu 挂载内核, 当 uefi 安全启动配置失败时, 也可以更方便地进入虚拟机调试.
1# QEMU虚拟机管理Makefile
2# 用于管理带TPM和安全启动特性的QEMU虚拟环境
3
4# 声明伪目标
5.PHONY: all qemu qemu-direct qemu-uefi tpm-setup clean help
6
7# 配置变量
8KERNEL := ./linux/arch/x86_64/boot/bzImage
9ROOTFS := ./rootfs.img
10OVMF_CODE := /usr/share/ovmf/x64/OVMF_CODE.secboot.4m.fd
11OVMF_VARS := /usr/share/ovmf/x64/OVMF_VARS.4m.fd
12TPM_DIR := ./tpm-state
13OVMF_VARS_COPY := ./ovmf_vars_copy.fd
14SHARED_DIR := /home/rqdmap/dream/shared/
15
16# 虚拟机配置
17VM_MEMORY := 8G
18VM_CORES := 4
19SSH_PORT := 10022
20KERNEL_PARAMS := "root=/dev/sda2 rw console=tty0 console=ttyS0 nokaslr quiet loglevel=3"
21
22# 常用QEMU参数(使用模式特定参数扩展)
23QEMU_BASE_OPTS := -M q35,smm=on \
24 -m $(VM_MEMORY) \
25 -smp $(VM_CORES) \
26 -nographic \
27 -enable-kvm \
28 -net nic \
29 -net user,hostfwd=tcp::$(SSH_PORT)-:22 \
30 -virtfs local,path=$(SHARED_DIR),mount_tag=share_tag,security_model=mapped-file,id=share_id
31
32# TPM相关参数
33TPM_OPTS := -chardev socket,id=chrtpm,path=$(TPM_DIR)/swtpm-sock \
34 -tpmdev emulator,id=tpm0,chardev=chrtpm \
35 -device tpm-tis,tpmdev=tpm0
36
37# UEFI相关参数
38UEFI_OPTS := -drive if=pflash,format=raw,unit=0,file=$(OVMF_CODE),readonly=on \
39 -drive if=pflash,format=raw,unit=1,file=$(OVMF_VARS_COPY) \
40 -global driver=cfi.pflash01,property=secure,value=on
41
42# 直接内核启动相关参数
43DIRECT_BOOT_OPTS := -kernel "$(KERNEL)" \
44 -append $(KERNEL_PARAMS)
45
46# 默认目标
47all: tpm-setup qemu
48
49# 帮助信息
50help:
51 @echo "QEMU虚拟环境管理Makefile"
52 @echo ""
53 @echo "可用命令:"
54 @echo " make - 设置TPM并启动QEMU (使用UEFI安全启动,无直接内核启动)"
55 @echo " make qemu - 同上"
56 @echo " make qemu-direct - 使用直接内核启动QEMU (不使用UEFI)"
57 @echo " make qemu-uefi - 使用UEFI和直接内核启动QEMU"
58 @echo " make tpm-setup - 只设置TPM环境"
59 @echo " make clean - 清理TPM状态和OVMF变量副本"
60 @echo " make help - 显示此帮助信息"
61 @echo ""
62 @echo "配置:"
63 @echo " 内存: $(VM_MEMORY)"
64 @echo " CPU核心: $(VM_CORES)"
65 @echo " SSH端口: $(SSH_PORT)"
66
67# 准备TPM目录和OVMF变量副本
68tpm-setup:
69 @echo "设置TPM环境..."
70 -pkill swtpm 2>/dev/null || true
71 mkdir -p $(TPM_DIR)
72 @if [ ! -f $(OVMF_VARS_COPY) ]; then \
73 echo "创建OVMF变量文件副本..."; \
74 cp $(OVMF_VARS) $(OVMF_VARS_COPY); \
75 fi
76 @echo "启动swtpm服务..."
77 swtpm socket --tpmstate dir=$(TPM_DIR) \
78 --ctrl type=unixio,path=$(TPM_DIR)/swtpm-sock \
79 --log level=20 \
80 --tpm2 \
81 --daemon
82
83# 启动QEMU (使用直接内核启动,不使用UEFI)
84qemu-direct: tpm-setup
85 @echo "使用直接内核启动QEMU (无UEFI)..."
86 qemu-system-x86_64 $(QEMU_BASE_OPTS) \
87 $(DIRECT_BOOT_OPTS) \
88 -drive "file=$(ROOTFS),format=raw" \
89 $(TPM_OPTS)
90
91# 启动QEMU (使用UEFI安全启动,不使用直接内核启动)
92qemu: tpm-setup
93 @echo "使用UEFI安全启动QEMU..."
94 qemu-system-x86_64 $(QEMU_BASE_OPTS) \
95 -drive "file=$(ROOTFS),format=raw" \
96 $(TPM_OPTS) \
97 $(UEFI_OPTS)
98
99# 启动QEMU (使用UEFI和直接内核启动)
100qemu-uefi: tpm-setup
101 @echo "使用UEFI和直接内核启动QEMU..."
102 qemu-system-x86_64 $(QEMU_BASE_OPTS) \
103 $(DIRECT_BOOT_OPTS) \
104 -drive "file=$(ROOTFS),format=raw" \
105 $(TPM_OPTS) \
106 $(UEFI_OPTS)
107
108# 清理TPM状态和OVMF变量副本
109clean:
110 @echo "清理环境..."
111 -pkill swtpm 2>/dev/null || true
112 rm -rf $(TPM_DIR)
113 rm -f $(OVMF_VARS_COPY)
114 @echo "清理完成"
准备安全启动
使用 bootctl status
查看当前 UEFI 安全启动的状态, 确认安全启动是否处于 setup 状态.
使用 ls /dev/tpm*
, dmesg
, tpm2_getrandom
等命令检查 TPM 设备是否正确加载.
1$ dmesg | grep -i tpm
2$ ls /dev/tpm*
3$ tpm2_getcap properties-fixed
如果硬件设备看起来都不错, 可以进入下一步正式配置安全启动的签名步骤.
配置安全启动并签名组件
在这一步主要有两种方式, 一种是全流程手动: 生成密钥、在固件中注册密钥、签名 EFI 二进制文件, 另一种是借助 sbctl
工具, 极大简化整体流程.
- 尽管据 Arch Wiki 所说,
sbctl
并不适用于所有硬件, 但至少在本实验环境中工作良好 :)
若使用 swctl
, 事情会变得很简单:
1# 查看目前的安全启动状态
2sbctl status
3
4# 自动生成安全启动密钥
5sbctl create-keys
6
7# 将密钥注册到 UEFI 固件中, -m 参数为额外注册微软密钥
8sbctl enroll-keys -m
9
10# 自动验证启动组件(Grub、vmlinuz等)是否被签名
11sbctl verify
12
13# 签名
14sbctl sign -s /boot/vmlinuz-linux
15
16# 自动对所有启动组件签名
17sbctl sign-all
使用 sbctl create-keys 生成的密钥应离线保存, 并设置 UEFI 变量写保护,避免物理攻击篡改!
而如果手动注册, 则麻烦的多, 不进需要手动去创建不同格式的证书、密钥(key, crt, cer, esl, auth), 还需要使用 Keytools 去将密钥手动注册到 UEFI 中, 并使用 sbsign
工具去一个个签名所有的 EFI 文件..
有一些麻烦, 具体流程不予赘述, 直接参考: Unified Extensible Firmware Interface/Secure Boot - ArchWiki
UEFI 配置正确, 注册密钥并签名完成后, 重启电脑, 理论上即可成功安全引导!
但情况往往不会一帆风顺.
重启后发现, 连 Grub 都无法成功引导, 屏幕上只有少得可怜的报错信息:
1BdsDxe: loading Boot0001 "UEFI QEMU HARDDISK QM00001 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)
2BdsDxe: starting Boot0001 "UEFI QEMU HARDDISK QM00001 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)
3error: prohibited by secure boot policy.
4Entering rescue mode...
5grub rescue> %
经过了百般折腾(重新生成密钥, 重新签名)并与 Claude 激情对线后, 还是没有什么好办法, 少得可怜的报错也让我无从下手.
直到我又 old school 地去谷歌了一下, 才发现这样的问题并不少见, 而罪魁祸首是 Grub 的错误配置.
Grub 配置问题
很容易地可以在 Arch Wiki 的 Grub 页面看到, 其对于安全支持的说明.
其指出在 grub 2.06.r261.g2f4430cc0
版本之后, insmod
已经不再受到支持了, 因为侧载模块文件确实已经违反了安全启动的规则, Grub 会报错:
1error: prohibited by secure boot policy
为此我首先想到的是参考上述 Wiki 指出的, 将 mod 文件嵌入到 Grub EFI 文件中, GRUB_MODULES
提前指定想要的模块, 指令差不多长这样:
1$ grub-install --target=x86_64-efi --efi-directory=esp --modules=${GRUB_MODULES} --sbat /usr/share/grub/sbat.csv --bootloader-id=GRUB
生成新的 GRUB EFI 文件后, 对其重新签名, 居然有了新的有意思的报错:

but nobody cares.. 🤡
我查阅了一些相关的资料, 但好像并不是很有关系.. 但也给我了一些启发, 有个 shim-lock 的东西, 好像需要禁用才能安全启动..
再次陷入一些瓶颈之中, 忽然我又意识到, Arch Wiki 中还提到了一种利用 CA 密钥的支持方式, 这种方式也是禁用了 shim-lock
来实现的, 🤔虽然我没有很理解他的 CA 密钥是个啥意思, 但他也使用到了 TPM 模块, 值得一试.
shim
是微软签名的引导加载程序,用于在 Secure Boot 环境下验证 GRUB。禁用 shim-lock
后,GRUB 直接使用本地密钥验证内核,仅适用于实验环境。生产环境中需保持启用以避免兼容性问题。
使用这种方式重新安装 GRUB:
1$ grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB --modules="tpm" --disable-shim-lock
签名, 签名后重新启动, 立刻就很顺利的启动系统了!
系统启动完成后, 使用 sbctl
以及 tpm2_pcrread
指令简单验证了一下, 发现安全启动确实成功了, 且 pcr[0] 到 pcr[9] 中存在度量值!

结果验证
当启动进入系统后, 希望采用一些明确的指令去进行验证, 确认是使用了安全启动的方式引导进入的系统.
简单与 Claude 对线了一下, 总体感觉没有特别好的策略去验证, 它给我的都是一些查看 TPM 设备情况、mokutil
检查UEFI Secure Boot 状态的方案.. 但这其实可以被上述的 qemu-uefi
Makefile 目标绕过..
目前的验证方案还是通过 TPM 设备相关的指令:
-
tpm2_pcrread
: 直接查看 PCR 寄存的内存, TPM0-9 对应了启动过程中不同的度量对象, 如果 10 个值都有度量内容那么可以认为安全启动的过程中度量了所有的启动组件(BIOS/UEFI, Grub, 内核等)- 使用
qemu-uefi
绕过了 UEFI 启动加载器(即 GRUB), 因此其 pcr8-9 是空的:
- 使用
-
查看 TPM 事件日志; 需要注意, 仅仅在开启了 Linux 内核的 IMA 选项后, 才会生成该度量日志, 具体原因未深究. 事件日志输出部分摘录如下:
bash1$ tpm2_eventlog /sys/kernel/security/tpm0/binary_bios_measurements | bat 2 3───────┬────────────────────────────────────────── 4 │ STDIN 5───────┼────────────────────────────────────────── 6 1 │ --- 7 2 │ version: 1 8 3 │ events: 9 4 │ - EventNum: 0 10 5 │ PCRIndex: 0 11 6 │ EventType: EV_NO_ACTION 12 7 │ Digest: "0000000000000000000000000000000000000000" 13 8 │ EventSize: 45 14 9 │ SpecID: 15 10 │ - Signature: Spec ID Event03 16 11 │ platformClass: 0 17 12 │ specVersionMinor: 0 18 13 │ specVersionMajor: 2 19 14 │ specErrata: 0 20 15 │ uintnSize: 2 21 16 │ numberOfAlgorithms: 4 22 17 │ Algorithms: 23 18 │ - Algorithm[0]: 24 19 │ algorithmId: sha1 25 20 │ digestSize: 20 26 21 │ - Algorithm[1]: 27 22 │ algorithmId: sha256 28 23 │ digestSize: 32 29 24 │ - Algorithm[2]: 30 25 │ algorithmId: sha384 31 26 │ digestSize: 48 32 27 │ - Algorithm[3]: 33 28 │ algorithmId: sha512 34 29 │ digestSize: 64 35 30 │ vendorInfoSize: 0 36 31 │ - EventNum: 1 37 32 │ PCRIndex: 0 38 33 │ EventType: EV_S_CRTM_VERSION 39 34 │ DigestCount: 4 40 35 │ Digests: 41 36 │ - AlgorithmId: sha1 42 37 │ Digest: "1489f923c4dca729178b3e3233458550d8dddf29" 43 38 │ - AlgorithmId: sha256 44 39 │ Digest: "96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7" 45 40 │ - AlgorithmId: sha384 46 41 │ Digest: "1dd6f7b457ad880d840d41c961283bab688e94e4b59359ea45686581e90feccea3c624b1226113f824f315eb60ae0a7c" 47 42 │ - AlgorithmId: sha512 48 43 │ Digest: "5ea71dc6d0b4f57bf39aadd07c208c35f06cd2bac5fde210397f70de11d439c62ec1cdf3183758865fd387fcea0bada2f6c37a4a17851dd1d78fefe6f204ee54" 49 44 │ EventSize: 2 50 45 │ Event: "0000" 51 46 │ - EventNum: 2 52 47 │ PCRIndex: 0 53 48 │ EventType: EV_EFI_PLATFORM_FIRMWARE_BLOB 54 49 │ DigestCount: 4 55 50 │ Digests: 56 51 │ - AlgorithmId: sha1 57 52 │ Digest: "5bca7340dddaba9600419f8ac340c9ca50072d8a" 58 53 │ - AlgorithmId: sha256 59 54 │ Digest: "41e796c599ac59b42cb4f8ea6e6f2d99fa5aa26646c567e09ff8248d8828a800" 60 55 │ - AlgorithmId: sha384 61 56 │ Digest: "16bedc84d3e2e682982a721c223fb32d4f148be7bf27761e1a67e3c255e8038988c25dade2acb150d28af4e81871b57f" 62 57 │ - AlgorithmId: sha512 63 58 │ Digest: "36bd8c5623a7bcac630fe6c30a41f41b1aa2f48bf97463900714a823275d863757cdbc96761e707b6e8795f2956be690c87467d6e1275eabe08ca0ec3185fddd" 64 59 │ EventSize: 16 65 60 │ Event: 66 61 │ BlobBase: 0x820000 67 62 │ BlobLength: 0xe0000 68 69 ...
-
使用未签名的内核/Grub程序去做系统引导, 实测会卡死且无日志输出, 而不是报错提示被拒绝启动, 不确定是 QEMU 的输出原因还是什么, 但确实是无法正常引导进入系统, 由此可以认为 UEFI 安全启动发挥作用了.
- 也可以偷偷修改 内核/GRUB 的若干 bit, 来故意破坏其签名, 但本文没做该实验, 理论上也会被安全启动拦截.
下一步工作
一些实践过程中没有搞懂的问题, 简单整理一下留待后续学习:
-
UEFI 安全引导与 TPM 2.0 PCR 芯片的具体结合原理? TPM 相关的功能支持已经合并进入 UEFI 官方的固件代码中了吗? 在拥有 TPM 芯片时, UEFI 安全启动就能自动将度量结果写入 TPM PCR?
-
shim-lock 与 GRUB 与 SBAT 这一堆引导相关的内容
-
TPM 日志与 IMA 子系统的关系? 开启 IMA 之前不存在 TPM 事件日志, 开启后便开始生成, 并自动拓展到 PCR 10
-
远程证明框架集成, 开源工具参考:
后记
在真正使用 swtpm 作为 vTPM 开展实验之前, 我一直感觉可信相关的概念都是空中楼阁, 且堆砌了这么一堆复杂的术语和概念, 还需要依托硬件设备 TPM/Intel SGX/ARM TrustZone, 个人在没有项目依托的情况下太难开展实验了, 且其中什么所谓的度量这种高深的术语本质也只是一个哈希而已, 远程证明也只是一个最基本的基于证书的认证体系而已, 没有什么技术含量都是概念而已..
但前几天和师兄聊了一下这些东西, 感觉我对这块到底是什么甚至都还不怎么了解, 也没有办法很轻松地去给不懂的人讲明白, 也许是一直以来看不上这块东西且对这块不怎么感兴趣, 所以一直像无头苍蝇一样在乱搞.. 加上前几天偶然间用了一个 Magisk 支持微信指纹支付的模块, 发现它就是用 Arm TEE 来存储个人密码的, 一下子让我感觉到可信、TEE 这些东西离我这么近且这么有作用, 又重新开始想认真看一下了.
本文也只是一个最基本的开始, 虽然从时间上来说已经是过于晚了, 但多少也希望能做一些实质性的内容, 学习一些实质性的知识.
此外, 在做 TPM + 安全引导的过程中, 我有些过于依赖 Claude 给出的指令了.. 以至于放在往常很容易能 Google 出来的内容现在也几乎视而不见了, 这还是给我提了个醒, 如何在 LLM 时代保证高质量的问题搜索与解决能力, 不过度依赖 LLM 便利的生成能力, 保持清醒, 多方求证, 并始终独立思考.