机密计算: AMD 解决方案
更多:
- 龙蜥社区 Hygon Arch(需要QQ浏览器或者手机百度浏览器打开)
- Administrative utility for AMD SEV – VirTee/SevCtl Github
- Hygon hag
- AMD SEV基本原理
- 【TEE】【AMD SEV内存加密】 白皮书
- 【TEE】AMD SEV- SNP和Intel TDX的概述
- 水印去除
数据结构
AMD SEV机密计算场景下,Hypervisor的职能稍有改变,可以概括为以下三点:
- 管理主机计算资源管理
- 代理可信组件间通信
- 准备机密计算环境
对于第一点,QEMU/KVM复用已有的逻辑,无需修改,对于二、三点,QEMU/KVM主要基于SEV API实现。具体实现时,KVM需要封装SEV API并对QEMU暴露IOCTL命令字,QEMU按照SEV API定义的流程实现对机密虚机的生命周期管理逻辑。
KVM
分析KVM对QEMU提供的IOCTL命令字涉及的数据结构
虚机管理命令
虚机管理命令字提供与虚机相关操作,因此它打开的是/dev/kvm
字符设备,通过KVM_CREATE_DEVICE
创建虚机关联的fd,再通过KVM_MEMORY_ENCRYPT_OP
命令字下发命令,所有的虚机管理命令作为KVM_MEMORY_ENCRYPT_OP
命令字的参数,通过id
字段的不同来区分,其命令格式如下:
命令格式
linux/arch/x86/include/uapi/asm/kvm.h:
1 |
|
kvm_sev_cmd
结构体中id
字段可选的命令集合。
命令集合
linux/arch/x86/include/uapi/asm/kvm.h:
1 |
|
命令参数
虚机管理命令目的不同,因此每条命令有其对应的参数,以KVM_SEV_LAUNCH_START
为例,其参数格式如下:
1 |
|
平台管理命令字
命令格式
平台管理命令主要是向PSP发送平台管理相关命令,与虚机并无关系,它打开的是/dev/sev
字符设备并直接下发平台管理命令字,其格式如下:
linux/include/uapi/linux/psp-sev.h:
1 |
|
命令集合
sev_issue_cmd
结构体中cmd字段可选的命令集合
linux/include/uapi/linux/psp-sev.h:
1 |
|
命令参数
每个平台命令都有其对应的参数,以PLATFORM_STATUS
为例,在下发PLATFORM_STATUS
时会传入结构体sev_user_data_status
,用于接收PSP的输出
linux/include/uapi/linux/psp-sev.h:
1 |
|
QEMU
SevState
SevState
描述机密虚机在启动或者迁移过程中的整个状态,当虚机状态变为running时,Hypervisor可以切换CPU模式进入Guest态。
1 |
|
SevGuestState
SevGuestState主要描述SEV机密虚机的配置参数和运行时状态。
1 |
|
虚机启动流程
可信认证
基本原理
机密虚机与普通虚机最大的不同是机密虚机所有者即Guest owner假定Hypervisor不可信,因此机密虚机的整个生命周期都不能被Hypervisor监听或篡改,保证机密虚机一定是Guest owner期望的虚机。
在启动流程中,Guest owner首先要做的就是对安全处理器PSP做可信认证,确认安全处理器一定是可信的。Guest owner对PSP的校验就是请求(通过PDH_CERT_EXPORT
)其导出可以证明自己身份的证书,这些证书包括CEK证书、PEK证书、OCA证书以及计算共享密钥用的PDH。Guest owner拿到证书后,做以下处理:
- 对于CEK证书,首先获取签发CEK证书的CA即ASK,使用ASK的公钥解密CEK证书得到CEK公钥的明文,重新计算明文摘要并对比CEK证书的摘要,如果两者相等,说明CEK证书确实是ASK签发的。因为ASK签发CEK的实际动作就是使用ASK的私钥对CEK的公钥进行加密存放到证书中,而ASK的私钥只有ASK可以访问,如果CEK公钥由非ASK的私钥进行加密,那么使用ASK的公钥对CEK公钥解密后,得到的明文摘要一定与证书中的摘要不同。
- 对于PEK证书,获取PEK证书的CA即OCA,使用OCA的公钥解密PEK证书得到PEK公钥明文,重新计算名为摘要并对于PEK证书上的摘要,相等说明PEK证书是OCA签发的。检查OCA是否是自签的证书,如果OCA是自签证书,不做进一步处理,如果OCA是它签证书,找到签发OCA的CA,下载其公钥再对OCA进行验签,整个流程与其它证书验签类似。
- 对于PDH证书,获取签发PDH证书的CA即PEK,使用上述类似流程对其进行验签。
上述所有验签通过后,Guest owner认为PSP就是可信的,继而可以让Hypervisor开始启动虚机,整个流程示意图如下:
具体流程
由于可信认证涉及的角色是Guest owner和PSP,Hypervisor因此不需要介入。PSP的可信认证工具通常由CPU厂商提供,也就是AMD和Hygon(基于AMD架构的国产CPU),我们简单介绍两者的工具:
工具
AMD SevCTL
服务器出厂后,第一次启动时还没有OCA,通过以下命令可以创建一个自签名的OCA:
1
2sevctl generate oca.cert oca.key
sevctl provision oca.cert oca.keyOCA创建完成后,通过以下命令验证当前服务器中的PSP是否可信,该工具会根据CPU ID从AMD官网查询对应的CEK证书并导入到PSP,最后做验证
1
sevctl verify
创建完自签OCA证书并完成CEK证书导入后,可以通过export命令导出PSP的整个证书链供Guest owner验签
1
sevctl export --full /opt/sev/cert_chain.cert
Hygon hag
服务器出厂后第一次上电,需要导入通用的安全证书,首先下载hygon机密计算工具包:
1
2
3
4cd /opt/
git clone https://gitee.com/anolis/hygon-devkit.git
mv hygon-devkit hygon
cp /opt/hygon/bin/hag /usr/bin在线导入通用的安全证书
1
hag general hgsc_import
也可以离线导入通用的安全证书
1
2hag general get_id /* 获取PSP芯片ID */
hag general hgsc_version /* 查询证书版本号 */访问Hygon证书下载官网,输入芯片ID和证书版本号下载证书,名称类似为hygon-hgsc-certchain-v1.0-H905P0005040204.bin,通过以下命令导入到Hygon服务器:
1
hag general hgsc_import -offline -in /path/to/hygon-hgsc-certchain-v1.0-H905P0005040204.bin
最后查询证书导入状态,is HGSC imported为YES
1
hag csv platform_status
完成以上步骤后,导出证书链包含的所有文件:
1 |
|
证书链文件包括:
1 |
|
其中证书链被Guest owner用于校验PSP是否可信,证书链包含三个证书,分别是CEK、OCA 和PEK:
1 |
|
PDH被Guest owner用于计算与PSP之间通信的会话密钥:
1 |
|
接口
的确,证书的导出确实与Hypervisor不相关,但QEMU实际上也提供了虚机运行时导出其证书的接口,以便支持Libvirt等虚机管理工具对机密虚机的能力查询:
1 |
|
静态度量
基本原理
Guest owner完成对PSP的可信认证后,就可以通知Hypervisor启动虚机了,对于要启动的虚机,Guest owner需要确认Hypervisor启动虚机使用的所有引导文件与自己期望的相同。为达此目的,Guest owner需要将启动虚机涉及的所有引导文件和命令行参数作为输入,计算一个度量值,然后在虚机启动后,让PSP将相同的文件和命令行参数也作为输入,重新计算一个度量值,两者对比之后,如果相同,则可以确认Hypervisor在虚机启动过程中并没有对引导文件做手脚,启动的虚机确实是Guest owner期望的虚机。
Guest owner对引导文件和命令行参数进行的度量计算,与PSP可信认证相互独立,因此也可以在最开始启动虚机的时候做,如下面流程图的第一步所示:
AMD SevCTL
sevctl工具提供了measurement命令计算度量值,使用方式如下:
1 |
|
launch-measure-blob指定输出文件的位置
Hygon hag
Hygon的hag工具提供了generate_launch_blob命令计算度量值:
1 |
|
这里我们将OVMF固件作为度量值的输入,计算度量值,示例如下:
1 |
|
Guest owner完成对度量值的计算后,就可以启动虚机了,后续只要获取虚机启动时PSP计算的初始化内存度量值,对比两者是否相等,就可以确认Hypervisor是否启动了一个期望的虚机。这里需要解决的一个问题是,Hypervisor需要怎么做,才能既做到为PSP提供度量值计算的输入,又无法窃取PSP计算的度量值输出,或者无法代替PSP进行度量值计算呢?
我们知道 PSP 将 VMCB 中的 ASID 作为加密进程关联内存的密钥,对内存进行加密。为了让Hypervisor无法代替PSP做度量值计算,SEV的做法是:
- 让Hypervisor将计算度量值所需的材料加载到Guest内存地址空间,这里主要指OVMF、kernel和initrd等启动文件,此时虚机的内存地址空间内容还是明文,Hypervisor虽然可以进行度量值计算,但因为PSP与Guest owner通信是密文,要得到密文必须先获取Guest owner与PSP之前通过PDH协商出来的共享密钥,因此它无法得到度量值加密后的密文数据。
- 通过API ACTIVATE通知PSP将ASID与内存加密的密钥VEK(VM Encryption Key)关联,PSP的具体逻辑是以ASID为索引,在系统的所有密钥槽中找到对应的VEK,将其加载到内存
- 通过API LAUNCH_UPDATE_DATA通知PSP对虚机内存进行加密。此时虚机的内存地址空间变为密文。
- 通过API LAUNCH_MEASURE通知PSP对 ASID关联的内存地址空间内容进行度量值计算,并将加密后的明文返回给Guest owner
具体流程
- 我们知道QEMU命令行支持
BIOS+grub
或OVMF+UEFI+grub
固件启动,但在SEV机密计算场景下,只对OVMF+UEFI+grub
启动方案做了适配。所以在具体启动SEV机密计算虚机,只能以OVMF方式引导虚机; - 另外QEMU命令行既支持以显式指定
kernel+initrd+command line
的方式启动虚机;也支持直接以虚机镜像的方式启动虚机。当用户通过前者启动虚机时,需要指定OVMF、kernel和initrd文件,command line可选,QEMU的传统做法是将OVMF载入虚机内存,将kernel和initrd文件添加到fw_cfg(Firmware Configuration)设备,然后启动虚机,虚机启动过程中,OVMF逻辑会从fw_cfg设备中读取kernel和initrd文件并加载到内存,准备工作完成之后,跳转到kernel,移交控制权,这里可以看出,PSP对虚机初始化内存的度量,只会包含OVMF固件,并没有kernel和initrd文件,因为kernel和initrd是在虚机运行(即Guest态)之后OVMF从设备加载到内存的。在机密计算场景下,由于Hypervisor是不可信的,静态度量并没有包含kernel和initrd的内容,它完全有可能将用户kernel和initrd文件替换为恶意镜像而不被Guest owner发现,机密计算的方案需要考虑如何处理这种情况。对于使用虚机镜像的方式启动虚机的场景,也存在Hypervisor直接将镜像替换为恶意镜像的可能。我们下面分别讨论这两种场景下机密计算给出的解决方案以及具体的实现流程。
内核启动
内核启动指QEMU通过命令行参数显式指定kernel+initrd+cmdline启动虚机,kernel+initrd文件包含在虚机镜像之中
镜像启动
镜像启动指QEMU指定启动盘的方式启动虚机,kernel+initrd包含在虚机镜像之中。OVMF读取磁盘内容,通过grub指定磁盘上的kernel+initrd+cmdline。