UEFI secure boot(2)- UEFI variable及签名认证过程

  1. UEFI中Variable的实现

安全启动涉及到的关于启动模式变量以及上述密钥变量的保存,在uefi中,设置开机的启动项,设置开机密码等功能.这部分功能在标准的uefi中都是使用Variable这套机制实现的。而安全启动中的变量的保存也是通过这套机制来实现的。这些变量将分别保存到内存以及flash中。

(1)Variable变量在flash中数据结构

  1. 当安全启动禁用时

即在系统中只保存关于一些启动过程中的相关变量时,格式如下:

在flash中的基地址是0x900000001fc01048

typedef struct {

  EFI_GUID  Signature;

  UINT32  Size;

  UINT8   Format;

  UINT8   State;

  UINT16  Reserved;

  UINT32  Reserved1;

} VARIABLE_STORE_HEADER; //28个字节

后面紧跟着启动过程中的相关变量头数据。结构如下:

typedef struct {

  UINT16      StartId; //0x55aa标识有效

  UINT8       State;

  UINT8       Reserved;

  UINT32      Attributes;

  UINT32      NameSize;

  UINT32      DataSize;

  EFI_GUID    VendorGuid;

} VARIABLE_HEADER; //32个字节

然后是variable名称+数据。

  1. 当安全启动启动后

在flash中的基地址还是0x900000001fc01048,范围为0x60000。

typedef struct {

  EFI_GUID  Signature;

  UINT32  Size;

  UINT8   Format;

  UINT8   State;

  UINT16  Reserved;

  UINT32  Reserved1;

} VARIABLE_STORE_HEADER; //28个字节

后面紧跟着启动过程中的安全启动变量头数据。结构如下:

typedef struct {

  UINT16      StartId;

  UINT8       State;

  UINT8       Reserved;

  UINT32      Attributes;

  UINT64      MonotonicCount;

  EFI_TIME    TimeStamp;

  UINT32      PubKeyIndex;

  UINT32      NameSize;

  UINT32      DataSize;

  EFI_GUID    VendorGuid;

} AUTHENTICATED_VARIABLE_HEADER;

然后是variable名称+数据。

(2)variable系统中的接口

1)VariableServiceGetVariable调用FindVolatileVariable和FindNonVolatileVariable接口来从flash或者memory中获取变量,获取过程就是解析以上数据结构来得到变量以及数据。后续系统通过gRT->GetVariable来调用。

2) VariableServiceSetVariable将系统中变量保存到memory或flash中,memory可直接进行简单的memcpy即可保存,需要保存到flash中的变量则必须要调用Flash的驱动函数去写才能写入。后续系统通过gST->SetVariable()来更新variable。

 

3) VariableServiceSetVariable函数调用UpdateVariable来实现将变量写到flash或内存中。这个接口分为两种情况,在写变量前首先会调用FindVolatileVariable和FindNonVolatileVariable接口来查找变量,如找到,则更新变量。如未找到,则创建一个新的变量。更新flash中变量分为四步,首先创建Variable的头;第二步更新头的中的State位,让这位变成有效;第三步写Variable的数据;最后再次更新head中的State位为VAR_ADDED。

4)Reclaim接口,由于更新变量时是将Variable头中State位改变(比如删除则置为对应状态),在系统中多次重启后,flash可能被写满,这个时候需要Reclaim接口来重新组织flash中数据。Reclaim接口根据Variable头中State位状态来删除或保留vaiable数据。

5)AuthVariableLibProcessVariable接口是当开启安全启动后,更新variable变量调用的接口

1> 在custom模式下,获取密钥数据(分离数据与时间戳),检测密钥文件的格式是否为PK/KEK/db/dbx格式的变量。通过验证则调用UpdateVariable来添加密钥变量。这个模式下可以任意更新PK、KEK、DB以及DBX,不需要经过安全验证这个验证。

2> 在用户模式下,添加KEK需要经过PK的验证,添加DB/DBX需要经过KEK或者PK的验证。这样一级一级验证,防止恶意添加自己的密钥来通过安全启动的验证过程。

6)时间戳及PKI

私钥不会被别人获取或破解,那么如何传递公钥呢?也就是说,依赖方(需要验证数字签名的B)如何才能确定这个公钥A,确实就是真正的公钥,而不是伪造的呢?为了解决这一问题,引入了公钥基础设施这一概念(Public-Key Infrastructures ,简称PKI)。

在更高的层次上,引入了一个具有极高权威的第三方C,这个第三方在公钥上附加数字签名,以证明该公钥来自于私钥的创建者A创建的公钥。可以将这类数字签名理解为“荣誉证书”,“证书”的颁发者被称为证书授权机构(Certificate Authority,简称CA)。需要数字签名的B会使用ca给的公钥解密证书,获取A的公钥,保证公钥的正确性。

通常这些特定的CA是由更高级CA进行签名认证过的。通过这样的职能划分,不但大大减小了分发顶级CA(通常称为信任锚或根CA)的数量,安全性也不减反增。顶级CA对所需的证书进行自签名,也就是说他们签署了用来自证的证书(他们自己证明自己是自己)。从技术方面来说这是不需要的 ,之所以这么做是因为依赖方(依赖于证书的这一方)需要知道这个特定的根CA确实拥有所需的公钥,因此为了方便起见(证书的一致使用),常常使用自签名证书。

由CA颁发的证书是可以被其撤销的。撤销实际上是由CA作出的声明,表明用来产生对应公钥的某个实体(称之为A)的私钥因产生了安全问题而不再受信任:这可能是由于A丢失了他的私钥或是因为它被怀疑已经遭受了入侵或是其他的一些原因。一旦证书被撤销,它就不能再用于验证由相应的私钥做出的签名。正因如此,在数字签名上加盖时间戳就显得很必要:依赖方可以通过时间戳的信息来确定签名是否创建在证书被撤销之前。(由签名时间戳和申明撤销的时间比较,确认签名的有效性)

 

  1. 签名过程

1、生成证书

需要三个证书: PK、KEK、DB

1)生成一些自签名的X.509证书:

umask 0077

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=my PK name/" -keyout PK.key -out PK.crt -days 3650 -nodes -sha256

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=my KEK name/" -keyout KEK.key -out KEK.crt -days 3650 -nodes -sha256

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=my db name/" -keyout db.key -out db.crt -days 3650 -nodes -sha256

2)转换格式

cert-to-efi-sig-list /tmp/CA/PK.crt /tmp/CA/PK.esl

cert-to-efi-sig-list /tmp/CA/KEK.crt /tmp/CA/KEK.esl

cert-to-efi-sig-list /tmp/CA/boot0.crt /tmp/CA/db.esl

  1. 建立信任链

PK将自签名,KEK将由PK签名,引导证书将由KEK签名

sign-efi-sig-list -k PK.key -c PK.crt PK /tmp/CA/PK.esl /tmp/PK.auth

sign-efi-sig-list -k PK.key -c PK.crt KEK /tmp/CA/KEK.esl /tmp/CA/KEK.auth

guid=$(uuidgen)

sign-efi-sig-list -k KEK.key -c KEK.crt $guid /tmp/CA/db.esl /tmp/CA/db.auth

./cert-to-efi-sig-list dbx.esl

2、签名二进制文件

安装了自定义UEFI签名密钥后,需要对自己的EFI二进制文件进行签名。Linux内核本身可以从3.3版开始构建为可引导的EFI二进制文件。可以签署和引导任何有效的EFI二进制文件。

一种选择是直接对内核映像签名。如果的发行版使用的是二进制内核,那么需要在重新启动系统之前对每个新的内核更新进行签名。如果使用自编译内核,则需要在构建每个内核之后对其进行签名。

Linux sbsigntools包可以从大多数Linux发行版的存储库中获得,是对UEFI二进制文件进行签名时的首选工具。UEFI安全启动二进制文件应该使用X509格式签名。命令是sbsign,它被调用如下:

sbsign --key DB.key --cert DB.crt unsigned.efi --output signed.efi

由于在MIPS下这个工具还没有支持,可以使用osslsigncode工具,它也生成签名。此工具不是专门用于安全引导,但它生成的签名符合所需的规范。使用osslsigntool对二进制文件进行签名的方式与sbsign类似:

osslsigncode -certs DB.crt -key DB.key -h sha256 -in unsigned.efi -out signed.efi

 

3、验证过程

在DxeImageVerificationHandler函数中,将预加载的efi文件进行安全验证。

1、首先获取image类型,目前实验显示UEFI内部efi imgae类型为IMAGE_FROM_FV,这类image在安全引导过程属于不验证文件,即直接加载。而其他类型的image文件则需要通过验证才可以加载。比如BOOTMIPS.EFI文件。然后从flash或者内存中获取SecureBoot变量,如获取到,判断SecureBoot是否为使能状态。禁止状态直接返回,如使能,验证DOS头,获取文件信息,判断是否为有效的PeImage文件。

2、检测如果efi文件没有签名信息,则计算image的hash值,查看hash值如果在DB数据库中且不在DBX中,则通过验证,否则验证失败。

3、检测如果efi文件包含签名信息,则遍历db数据库中签名证书,检测证书类型(如果为hash值,则跳过)。检测efi文件的签名信息不被dbx禁止且可以通过db签名验证。则验证通过,否则,继续检测efi文件(可执行image以及签名信息)的hash值不在dbx且在db中存在。则也可以通过验证。如以上检验全不满足,说明签名信息不符合。验证失败。

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
随着国家十四五新战略规划的推出,众多国内企业都参与到国产芯片替代浪潮中来,可以预测未来越多的国产芯片会被设计、生产和使用在我们日常所使用的电子产品中,国产芯片拥有巨大的市场前景。 目前国产芯片采用的体系架构主要有X86、ARM、MIPS、RISC V、PowerPC、Alpha等。我们知道电子产品正常工作必须要有操作系统和各种应用软件,没有操作系统和应用软件的芯片就是一堆废铁,而大多数人并不知道的是没有系统固件来加载操作系统的电脑亦是一堆废铁, UEFI就是由UEFI行业协会提出和维护一种行业标准的系统固件,它支持目前市面上的大多数芯片体系结构和操作系统,随着标准的不断演进相信越来越多的体系结构的芯片和操作系统会被支持。 笔者从事BIOS开发已有十余年的时间,见证了Legacy BIOS辉煌与隐退,也有幸了参与了新世纪初系统固件从Legacy BIOS往UEFI BIOS的迁移的全过程。科技行业风起云涌新技术新架构日新月异,每每回望不禁感慨我辈可谓是“眼见着他起高楼,眼见着他宴宾客”的那一波BIOS人。曾经系统固件江湖还是Legacy BIOS的天下,BIOS人使用汇编语言编码、通过中断来与操作系统沟通。自UEFI框架被广泛使以来开我们的发环境从纯汇编变成了99%的C语言加1%的汇编语言的模式,开发效率大大的加强了。 虽然UEFI框架大大加快了开发效率,但是由于系统固件开发属于比较偏门和专业的领域,学习和入门门槛比较高,现有的BIOS工程师又分布在大大小小的各个公司内部缺乏有效沟通和交流,同时BIOS源码又属于敏感和机密数据受到各种NDA限制,市面上对UEFI框架介绍的资料少之又少,因此笔者从2000左右开始就陆续以Cstyle_0x007为ID在https://blog.csdn.net/CStyle_0x007发布一系列博文,现已有数十篇原创文章。刚开始的想法是把博文当作工作笔记方便自己随时查阅,后来慢慢发展成了与业内外感兴趣的朋友的沟通交流的平台。 随手写的博文难免有错误与纰漏为了避免误导大众,准备把博文重新整理在纠正谬误同时也会补充一些新的内容,尽量做到所写的每句话都是无误的,也欢迎有兴趣的朋友踊跃提出意见和建议。组建了微信公众号,目的在于方便有兴趣的朋友一起交流,名字初步定为“固件C字营”,其中“固件”泛指一切固化的软件,这里主要指UEFI BIOS系统固件,“C”泛指“China“,我们可以把这里当作大家沟通交流的营地,我们会不定时发布一些行业资讯、工作、学习心得,感兴趣扫描下面二维码就可以加入,也可以发邮件到CstyleFi[email protected]投稿分享你的想法。 本文取名《UEFI内核的导读》这里的UEFI专指“UEFI BIOS”,全文专注于对UEFI内核的梳理与分享,同时兼顾对X86系统固件生态中常用的工程技术的介绍,主要包含以下内容:UEFI启动流程以及各个阶段主要完成的任务及参考的实现方式导读UEFI及PI规范中的常见Protocol的实现与使用技巧UEFI固件生态中常见外设、总线、行业标准的协议内容及使用方法 雄关漫道真如铁,而今迈步从头越,系统固件雄起之路道阻且长,相信我们的BIOS人一定可以为国产芯片的起飞助力、为系统固件团队的壮大贡献自己的一份微薄之力,为每一个不畏艰难、不惧寂寞坚守在工作岗位的BIOS人加油,好样的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值