简介 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 参数指定),私钥被命名为 .key,证书命名为.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配置。
/dts-v1/;/ { description = chrome os kernel image with one or more fdt blobs; #address-cells = ; images { kernel@1 { data = /incbin/(test-kernel.bin); type = kernel_noload; arch = sandbox; os = linux; compression = none; load = ; entry = ; kernel-version = ; signature@1 { algo = sha1,rsa2048; key-name-hint = dev; }; }; fdt@1 { description = snow; data = /incbin/(sandbox-kernel.dtb); type = flat_dt; arch = sandbox; compression = none; fdt-version = ; signature@1 { algo = sha1,rsa2048; key-name-hint = dev; }; }; }; configurations { default = conf@1; conf@1 { kernel = kernel@1; fdt = fdt@1; }; };}; 以下是配置项签名后的its文件。
/dts-v1/;/ { description = chrome os kernel image with one or more fdt blobs; #address-cells = ; images { kernel@1 { data = /incbin/(test-kernel.bin); type = kernel_noload; arch = sandbox; os = linux; compression = lzo; load = ; entry = ; kernel-version = ; hash@1 { algo = sha1; }; }; fdt@1 { description = snow; data = /incbin/(sandbox-kernel.dtb); type = flat_dt; arch = sandbox; compression = none; fdt-version = ; hash@1 { algo = sha1; }; }; }; configurations { default = conf@1; conf@1 { kernel = kernel@1; fdt = fdt@1; signature@1 { algo = sha1,rsa2048; key-name-hint = dev; sign-images = fdt, kernel; }; }; };}; 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。
ubuntu:~/uboot-nextdev$ fdtdump u-boot.dtb | less/dts-v1/;..../ { #address-cells = ; #size-cells = ; compatible = rockchip,rv1126-evb, rockchip,rv1126; model = rockchip rv1126 evaluation board; // signature节点由mkimage工具自动插入生成,节点里保存了rsa-sha算法类型、rsa核心因子参 //数等信息。 signature { key-dev { required = conf; algo = sha256,rsa2048; rsa,np = ; rsa,c = ; rsa,r-squared = ; rsa,modulus = ; rsa,exponent-bn = ; rsa,exponent = ; rsa,n0-inverse = ; rsa,num-bits = ; key-name-hint = dev;};}; 签名方案 上一节内容提到过,在secure boot中一般使用rsa签名方案。
要完成对镜像的签名,就必须使用私钥。而私钥一般是存在服务器上的,在本地pc上只存公钥。要想完成对镜像的签名,就必须把所有镜像上传到服务器重新打包。这种方案上传的文件太多,比较繁琐。下面我们介绍一种常用的签名方案。
在pc上,存放一把公钥和临时私钥,公钥是打包进dtb中的,安全启动时使用。临时私钥是为了生成签名数据。
在本地打包时,使用临时私钥对非安全镜像签名,将签名数据上传到服务器使用真正的私钥进行二次签名。将二次签名的数据和非安全镜像打包在一起,就得到了安全镜像。安全启动时,从dtb中拿出公钥对安全镜像进行校验即可。
这样既可以保证私钥的安全,又避免了上传所有镜像签名的繁琐。
签名镜像+签名配置 在secure boot中,除了对各个独立镜像签名外,还要对fit image中的配置项进行签名。
有些情况下,已经签名的镜像也有可能遭到破坏。例如,也可以使用相同的签名镜像创建一个fit image,但是,其配置已经被改变,从而可以选择不同的镜像去加载(混合式匹配攻击)。也有可能拿旧版本的fit image去替换新的fit image(回滚式攻击)。
下面举个例子。
/ { images { kernel@1 { data = signature@1 { algo = sha1,rsa2048; # kernel image镜像的哈希值,由mkiamge工具自动生成 value = }; }; kernel@2 { data = signature@1 { algo = sha1,rsa2048; value = }; }; fdt@1 { data = ; signature@1 { algo = sha1,rsa2048; vaue = }; }; fdt@2 { data = ; signature@1 { algo = sha1,rsa2048; vaue = }; }; }; configurations { default = conf@1; conf@1 { kernel = kernel@1; fdt = fdt@1; }; conf@1 { kernel = kernel@2; fdt = fdt@2; }; };}; 两个kernel image 都已经被签名了,但是,攻击者可以很容易的将kernel1 和fdt2 作为configuration 3去加载。
configurations { default = conf@1; conf@1 { kernel = kernel@1; fdt = fdt@1; }; conf@1 { kernel = kernel@2; fdt = fdt@2; }; conf@3 { kernel = kernel@1; fdt = fdt@2; }; }; 攻击者可以拿到签名的镜像,并且镜像是正确的。这种组合式攻击会给设备带来很大风险。
因此,为了解决这个问题,除了给镜像签名外,我们可以把配置选项也签名,每个镜像都有自己的签名,在给配置选项签名时,把镜像的hash值也包含进去。具体例子如下:
/ { images { kernel@1 { data = hash@1 { algo = sha1; value = }; }; kernel@2 { data = hash@1 { algo = sha1; value = }; }; fdt@1 { data = ; hash@1 { algo = sha1; value = }; }; fdt@2 { data = ; hash@1 { algo = sha1; value = }; }; }; configurations { default = conf@1; conf@1 { kernel = kernel@1; fdt = fdt@1; signature@1 { algo = sha1,rsa2048; # 对配置项签名,由mkimage工具自动生成 value = ; }; }; conf@2 { kernel = kernel@2; fdt = fdt@2; signature@1 { algo = sha1,rsa2048; value = ; }; }; };}; 如上所示,除了给所有镜像添加了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的启动和对镜像的验签。
$ make o=sandbox sandbox_config$ make o=sandbox$ o=sandbox ./test/vboot/vboot_test.sh/home/hs/ids/u-boot/sandbox/tools/mkimage -d -i dts -o dtb -p 2000build keysdo sha1 testbuild fit with signed imagestest verified boot run: unsigned signatures:: oksign imagestest verified boot run: signed images: okbuild fit with signed configurationtest verified boot run: unsigned config: oksign imagestest verified boot run: signed config: okcheck signed config on the hostsignature check okoktest verified boot run: signed config: oktest verified boot run: signed config with bad hash: okdo sha256 testbuild fit with signed imagestest verified boot run: unsigned signatures:: oksign imagestest verified boot run: signed images: okbuild fit with signed configurationtest verified boot run: unsigned config: oksign imagestest verified boot run: signed config: okcheck signed config on the hostsignature check okoktest verified boot run: signed config: oktest verified boot run: signed config with bad hash: oktest passed 完整校验流程 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的安全。
RFID工位管理系统的简单介绍
Xilinx 面向不断壮大的5G O-RAN虚拟基带单元市场 推出多功能电信加速器卡
使用HEM系列TEM小室对2.4GHZ器件的辐射测量
英飞凌携手北京中清怡和——SLE78超微型NFC安全模块荣获 中金国盛移动金融安全载体认证
如何检测湿度传感器性能?
secure boot的签名和验签方案
先进的数字隔离技术提高了太阳能逆变器的可靠性
单片机驱动蜂鸣器发声原理
芯片产业需要在全球范围内加强合作,共同打造芯片产业链
HOLTEK新推出HT45R5530 TRIAC LED调光照明OTP MCU
开路检测电路中的封闭式电感器
多路输出反激式电源电磁兼容问题研究
网关节点汇聚传感器数据上传物联网云平台
红米5plus最新消息:红米5plus真机上手,屏占比超高,价格也良心
探讨有助于加速太空探索的Qorvo技术
你要知道的波峰焊和回流焊顺序
什么是光电耦合器 有何作用?
基于SPMC65P2404A控制器实现CAN总线智能节点的设计
高速高精度流水线模数转换器的设计
HR9110