嵌入式与Linux那些事

电子技术应用专栏作家——嵌入式与Linux那些事。关注嵌入式与Linux的校招社招,本人整理了《嵌入式软件工程师笔试面试指南》PDF,平时发布嵌入式与Linux相关的实用技术文章

secure boot(三)secure boot的签名和验签方案

0
阅读(1456)

简介

FIT 格式支持存储镜像的hash值,并且在加载镜像时会校验hash值。这可以保护镜像免受破坏,但是,它并不能保护镜像不被替换。

而如果对hash值使用私钥签名,在加载镜像时使用公钥验签则可以保护镜像不被替换。因此,公钥必须保存在一个绝对安全的地方。

接下来的内容要求大家了解一些密码学的内容,之前也介绍过一些,可以看这篇文章

secure boot (一)FIT Image

secure boot (二)基本概念和框架

secure boot签名的大致流程:

  • 计算镜像的hash值

  • 利用私钥对hash值签名

  • 签名结果存在FIT Image 中。

secure boot验签的大致流程:

  • 读取FIT Image

  • 获得pubkey

  • 从FIT Image 提取签名

  • 计算镜像的hash

  • 使用公钥验签获得hash值,与计算得到的hash值进行对比

签名是由mkimage工具完成的,验签由uboot完成。

图片

签名算法

原则上讲,任何合适的算法都可以用来签名和验签。在uboot中,目前只支持一类算法:SHA&RSA。

RSA 算法使用提前准备好的公钥就可以完成验签,验签相关的代码量也很少。在验签时,RSA只是在FDT中提取必要的数据进行校验。

当然也可以在uboot中添加合适的算法,如果有其他签名算法(如DSA),可以直接替换rsa.c,并在image-sig.c中添加对应算法即可。

创建RSA key和证书

openssl 创建一副2048的密钥对:

$ openssl genpkey -algorithm RSA -out keys/dev.key  -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537

创建包含pubkey的证书:

$ openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt

查看pubkey的值:

$ openssl rsa -in keys/dev.key -pubout

绑定设备树

在FIT Image的签名节点中需要添加以下 属性,签名节点与哈希节点处于同一级别,被称为signature@1, signature@2等。

  • algo: 算法名称

  • key-name-hint:用来签名的key。密钥对必须存放在单独的文件夹(mkimage 使用-k 参数指定),私钥被命名为 <name>.key,证书命名为<name>.crt

镜像被签名后,以下这些属性都会被自动强制添加:

  • value: 签名后的值(RSA-2048 占256 bytes)

以下这些属性是可选的:

  • timestamp:签名的时间

  • signer-name:签名者的名字(例如mkimage)

  • signer-version:签名的版本(例如"2013.01")

  • comment:签名者或者镜像的额外信息

  • sign-images:签名镜像的列表

  • hashed-nodes:签名者签名的节点列表,一般是包含节点完整路径的字符串。例如:

hashed-nodes = "/", "/configurations/conf@1", "/images/kernel@1",
  "/images/kernel@1/hash@1", "/images/fdt@1",
  "/images/fdt@1/hash@1";

以下是一个待签名镜像的its配置。

以下是配置项签名后的its文件。image.png

以下是配置项签名后的its文件。

image.png

image.png

pubkey的存储

为了校验签名后的镜像,必须把pubkey存放在可信赖的位置。将pubkey存在镜像中是不安全的,很容易被破解。一般我们将其存放在uboot的FDT中(CONFIG_OF_CONTROL)。

pubkey应该作为一个子节点存放在/signature节点中。节点中要加上以下特性:

  • algo:算法名称

  • key-name-hint: 签名使用的key的名称

  • required: 校验某配置所使用的公钥

除此之外,每个算法都有一些必要的特性。RSA算法中,以下特性必须被添加:

  • rsa,num-bits:key的位数

  • rsa,modulus:N,多字节的整数

  • rsa,exponent:E,64位的无符号整数

  • rsa,r-squared:(2^num-bits)^2

  • rsa,n0-inverse:-1 / modulus[0] mod 2^32

下面看一个例子,以下是一个uboot.dtb存放RSA的例子。RSA key被mkimage打包在u-boot.dtb和u-boot-spl.dtb中,然后它们再被打包进u-boot.bin和u-boot-spl.bin。

image.png

签名方案

上一节内容提到过,在secure boot中一般使用RSA签名方案。

要完成对镜像的签名,就必须使用私钥。而私钥一般是存在服务器上的,在本地PC上只存公钥。要想完成对镜像的签名,就必须把所有镜像上传到服务器重新打包。这种方案上传的文件太多,比较繁琐。下面我们介绍一种常用的签名方案。

图片

在PC上,存放一把公钥和临时私钥,公钥是打包进dtb中的,安全启动时使用。临时私钥是为了生成签名数据。

在本地打包时,使用临时私钥对非安全镜像签名,将签名数据上传到服务器使用真正的私钥进行二次签名。将二次签名的数据和非安全镜像打包在一起,就得到了安全镜像。安全启动时,从dtb中拿出公钥对安全镜像进行校验即可。

这样既可以保证私钥的安全,又避免了上传所有镜像签名的繁琐。

签名镜像+签名配置

在secure boot中,除了对各个独立镜像签名外,还要对FIT Image中的配置项进行签名。

有些情况下,已经签名的镜像也有可能遭到破坏。例如,也可以使用相同的签名镜像创建一个FIT image,但是,其配置已经被改变,从而可以选择不同的镜像去加载(混合式匹配攻击)。也有可能拿旧版本的FIT Image去替换新的FIT image(回滚式攻击)。

下面举个例子。

image.png

两个kernel image 都已经被签名了,但是,攻击者可以很容易的将kernel1 和fdt2 作为configuration 3去加载。

image.png

攻击者可以拿到签名的镜像,并且镜像是正确的。这种组合式攻击会给设备带来很大风险。

因此,为了解决这个问题,除了给镜像签名外,我们可以把配置选项也签名,每个镜像都有自己的签名,在给配置选项签名时,把镜像的hash值也包含进去。具体例子如下:

image.png

如上所示,除了给所有镜像添加了hash值,还为每个配置添加了签名。mkimage将会对configurations/conf@1签名(/images/kernel@1, /images/kernel@1/hash@1,/images/fdt@1, /images/fdt@1/hash@1) 。签名会被写入  /configurations/conf@1/signature@1/value

验签

FIT image 在加载时会验签。如果'required' 指定了验签的公钥,则会使用这把公钥校验该配置对应的所有镜像。

为了支持FIT格式,以下配置项必须被选上。

CONFIG_FIT_SIGNATURE :使能FIT image的签名和验签

CONFIG_RSA :使能RSA签名算法

默认情况下,使能FIT Image的签名和验签后,CONFIG_IMAGE_FORMAT_LEGACY会被禁用。即FIT uboot image的只能引导FIT kernel Image。

如果需要引导legacy kernel image,需要手动添加CONFIG_IMAGE_FORMAT_LEGACY 定义。

测试

为了校验签名和验签是否正确,可以使用测试脚本test/vboot/vboot_test.sh。下面以sandbox为例子来说明bootm的启动和对镜像的验签。

image.png

完整校验流程

OTP校验loader

那么,这种镜像校验方式有个很重要的问题,公钥存在哪里才是安全的呢?

一般SOC中会有一个叫OTP或EFUSE的区域,这部分区域比较特殊,只可以写入一次,写入后就再也不可以修改了。把公钥存储在OTP中,就可以很好地保证其不能被修改。

OTP的存储空间很小,一般只有几KB,因此并不适合直接存放RSA公钥。一般都是将RSA公钥的hash val 存放在OTP中。像sha256的hash值仅为256 bits,而RSA 公钥本身一般存放在镜像中。

在使用公钥之前,只需要使用OTP中的公钥hash值验证镜像附带公钥的完整性,即可确定公钥是否合法。

图片

RSA公钥需要一般使用芯片厂家的工具写入loader。安全启动时,bootrom首先从loader固件头中获取RSA公钥并校验合法性;然后再使用该公钥校验SPL的固件签名。

spl校验uboot

SPL把RSA公钥保存在u-boot-spl.dtb中,u-boot-spl.dtb会被打包进u-boot-spl.bin文件(最后打包进loader);安全启动时SPL从自己的dtb文件中拿出RSA公钥对uboot.img进行安全校验。

uboot校验kernel

U-Boot把RSA公钥保存在u-boot.dtb中,u-boot.dtb会被打包进u-boot.bin文件(最后打包为uboot.img);安全启动时U-Boot从自己的dtb文件中拿RSA公钥对boot.img进行校验。

总结

从bootrom到kernel为止的安全启动,统一使用一把RSA公钥完成安全校验,并且当前这级的RSA Key已经作为自身固件的一部分,由前一级loader完成了安全校验,从而保证了Key的安全。

原文链接:https://mp.weixin.qq.com/s/eUcSPFi4OzZdAlvM-086RQ

微信图片_20220708145705.jpg

电子技术应用专栏作家 嵌入式与Linux那些事