背景
本文将介绍NXP的AHAB和HAB机制以及差异,详细说明如何一步步使用CST工具对镜像进行签名以及验签的过程。
NXP AHAB介绍(i.MX8使用)
资料来源u-boot/doc/imx/ahab/introduction_ahab.txt:
AHAB认证基于数字签名,其中使用一个或多个private key离线对镜像数据进行签名。然后使用相应的public key在i.MX处理器上验证所得到的签名镜像数据。public key包含在最终二进制文件中,SRK hash在SoC的fuse中(OTP),用于建立信任根(确认SRK是否一致)。
这里面涉及到的private key和public key都是在CST工具中生成,CST在这里可以认为是一个CA。i.MX8 secure boot流程:

1  | 1 - At reset, the SCU ROM and SECO ROM both start execution.  | 
NXP HAB介绍(i.MX7以下的使用)
根据NXP官方文档:Secure Boot on i.MX 50, i.MX 53, i.MX 6 and i.MX 7 Series using HABv4 ,我们可以研究一下HAB是个什么东西。
官方定义
1  | Executing trusted and authentic code on an applications processor starts with securely booting the device.  | 
官方术语
CA(Certificate Authority):持有private key并对public key签名生成数字证书;CAAM(Cryptographic Acceleration and Assurance Module):用于加密,流密码和散列算法的加速器,使用随机数生成器和运行时完整性检查程序;CSF(Command Sequence File):由HAB解释的二进制数据结构,用于指导身份验证操作;CST(Code Signing Tool):在构建主机上运行的应用程序,用于生成CSF和相关的数字签名,产生NXP所需要的SRK等;HAB(High Assurance Boot):启动时在NXP处理器的内部ROM中执行的软件库,通过CSF验证数字签名来验证外部存储器中的软件;OTP(One-Time Programmable):OTP硬件包括屏蔽ROM和电可编程保险丝(eFuse),只能烧写一次,不可改变;PKI(Public Key Infrastructure):公钥证书的层次结构,其中每个证书(根证书除外)都可以使用其上方的公钥进行验证;SRK(Super Root Key):一个RSA密钥对(SRK公钥和秘钥),它构成了启动时认证链的起点。SRK公钥的散列使用OTP硬件嵌入在处理器中。 SRK私钥由CA持有。
HAB身份验证过程
HAB身份验证基于使用RSA算法加密的public key,验证镜像数据,使用一系列private key对镜像数据在安全的环境中进行脱机签名。然后在i.MX处理器上使用相应的public key(SRK)验证所得到的签名镜像数据。
在上图中:
1、在安全的环境中对SW Image内容hash化生成摘要A,然后使用private key对摘要A进行签名(采用RSA算法),得到SW Image和Signature;
2、将该SW Image和Signature烧录到flash中后去启动;
3、在启动的时候会将SW Image内容hash化生成摘要B(如果SW Image没有被修改过,摘要A和摘要B肯定相等),使用Fuse中的public key进行对Signature进行验签得到摘要C,如果摘要B和摘要C相等,表示认证成功,就去启动OS。
注意,这里i.MX处理器中芯片存储的是SRK,而不是hash化后的值,所以该SRK可以直接进行验签。
HAB和AHAB
HAB Library是boot rom code中的一个子模块,AHAB是某些特定的NXP处理器中的安全子系统。他们负责验证作为产品软件一部分的数字签名,并确保当处理器配置为安全设备时,不允许运行未经身份验证的代码。
使用HAB的启动流程
对于HAB,引导加载程序映像包含引导加载程序本身以及:HAB用于验证映像的命令,数字签名数据和public key数字证书数据,统称为命令序列文件(CSF)数据,使用代码签名工具(CST)离线生成CSF数据。

使用AHAB(SECO)的启动流程

使用CST工具进行数字签名
可以在IMX_CST_TOOL 下载CST工具,该工具用于对HAB和AHAB进行代码签名。
- CST输入文件:
要签名的Image镜像 + CSF文件(CSF文件提供了一系列指令给CST,比如说数据镜像从哪里开始签名,使用哪个key去签名) - CST输出文件:
Image镜像 + 数字签名 + CSF文件(该文件只有HAB有)。 

CST目录结构
ca\:包含OpenSSL配置文件。 使用OpenSSL命令行工具生成签名密钥和证书时,将使用这些配置文件;crts\:包含用于签名的public key证书,最初此目录为空;linux32/和linux64/:包含CST工具执行的文件。bin/cst用于CST执行对代码签名;bin/srktool为HAB4和AHAB生成SRK表和e-fuse文件;keys/:包含用于签名的private key,最初时候此目录只包含生成PKI tree的脚本,格式如下:
包含的脚本如下:habx_pki_tree.sh/.bat脚本,其中x为3或者4,该脚本有Linux和Windows两种,都是用于生成支持HABx所需要的一系列密钥和证书;ahab_pki_tree.sh/.bat脚本,该脚本有Linux和Windows两种,都是用于生成支持AHAB所需要的一系列密钥和证书;
一步步porting security boot
生成PKI tree
主要生成private key和public key 数字证书,生成的PKI tree包含4个SRK。
在csf文件中会指定使用哪个SRK,签名后会往container中相应字段的值写入,这样的话在读取到container head之后就可以根据这个值来判断使用fuse的哪个SRK。
那为什么要用4个呢?通常情况下,可以让不同的批次指定不同的SRK。或者是同一个SRK永久了,可以指定换另外一个SRK。废弃掉的SRK可以在csf文件中使用Revocations来指定废弃哪个。csf文件用例如下:
1  | [Header]  | 
我对SRK可以理解为private key和public key certificates,前者用于对镜像进行签名或者是对public key进行加密生成数字证书。使用以下命令生成PKI tree。
1  | # tar -zxvf cst-3.1.0.tgz  | 
生成的PKI tree结构如下,不带SGK。运行结果如下。
1  | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  | 
生成SRK table和SRK hash
- 输入文件是在上一个步骤中创建的
public key certificates; - 输出文件是
SRK table和SRK hash。 
通过CA的private key对public key certificates进行解密就可以拿到可以对镜像认证的public key。
在AHAB架构中:
SRK table就是一系列的public key证书,SRK table会打包到已签名的镜像中;SRK hash是SRK tablehash化生成的数字摘要,会被烧写到i.MX芯片的SOC SRK_HASH[511:0]OTP中;
在认证过程期间的目标设备上,AHAB代码(SECO)会将镜像中的SRK table hash化然后与SoC SRK_HASH中的SRK hash比较,如果相等,则说明用于验签的public key是正确的,AHAB代码可以用该public key进行后续的镜像认证。对于SRK table中未使用的4个密钥中的任何一个,相应的密钥位将设置为0。后续的镜像认证,就是完全按照HAB的过程来了。
为什么生成SRK table后还要生成SRK hash呢?主要原因是:
- 由于
OTP空间小,将public keyhash化之后,可以减小存储空间; - 为了防止
public key和private key被替换。假设有个图谋不轨的人,使用CST生成private key对自己的镜像签名,然后再将public key加入到镜像中,镜像中的public key被hash化后肯定不与fuse中的SRK hash不一致,因此无法验签通过。 
1  | # cd ../crts/  | 
各个参数含义如下:
1  | -a, --ahab_ver:  | 
用户可以使用如下命令验证生成的SRK table和SRK hash的sha256sum是否一致。
1  | # od -t x4 --endian=big SRK_1_2_3_4_fuse.bin  | 
对镜像签名
将镜像和csf文件拷贝到cst目录下,按照csf要求对镜像进行签名。其中使用mkimage生成flash.bin的时候会打印如下log,csf_boot_image.txt文件中的offset字段必须根据这个log的内容做相应的修改。csf文件用于指定从哪块区域的image去验证,使用哪个key去验证。
1  | CST: CONTAINER 0 offset: 0x400  | 
对镜像签名命令如下,生成flash.signed.bin的签名文件。
1  | # cd ../../  | 
烧写SRK fuse到soc中
执行如下命令,获取到烧写fuse的值:
1  | # od -t x4 SRK_1_2_3_4_fuse.bin  | 
因此,烧写fuse的值为(只针对i.MX8QXP),可以在uboot命令行中使用fuse命令进行烧写。
1  | fuse prog 0 730 0x475e1dca  | 
烧写fuse的原理如下: https://imxdev.gitlab.io/tutorial/Burning_eFuses_on_i.MX8_and_i.MX8x_families/
验证签名的boot镜像是否认证成功
将第3步生成的签名镜像flash.signed.bin烧写到板子上,从板子启动。
在最新的imx_v2018.03_4.14.98_2.0.0_ga分支上,无需通过scu的串口来查看。直接在uboot命令行中使用ahab_status就可以确定是否验证成功。
配置SCFW支持debug monitor
配置SCFW如下几个功能:
- 如果使用板子上使用
AF28(SCU.UART0.RX)和AH30(SCU.UART0.TX)做为SCU的串口,那么必须在platform/board/mx8qx_mek/board.c文件中打开ALT_DEBUG_SCU_UART宏,否则就是使用到M4 UART; - 编译
scfw_tcm.bin的时候使用make qx B=mek V=1 R=B0 M=1 D=1 DL=2命令,M=1表示打开debug monitor功能,也就是可以通过串口与SCFW交互,D=1表示打开debug功能,DL=2表示debug level。 
详细的可以参考[System Controller Firmware Porting Guide.pdf]和[sc_fw_port.pdf]下面的debug monitor的功能:
OS镜像签名
将kernel编译出来的Image和.dtb文件拷贝到imx-mkimage环境中,这个可以从 imx-mkimage 下载。假定目前处于imx-mkimage环境下。
1、拷贝所需要的镜像和dtb
1  | # cp -rf ~/work2/xxx/kernel-imx/arch/arm64/boot/Image ./iMX8QX/  | 
2、修改soc.mk为:
1  | flash_linux flash_b0_linux: $(MKIMG) Image xxxxx.dtb  | 
3、编译生成OS镜像的flash_os.bin 镜像拷贝到cst目录下。
1  | # make SOC=iMX8QX flash_linux  | 
4、拷贝OS镜像签名所需的csf文件csf_linux_img.txt
5、修改csf_linux_img.txt文件中的offset字段。
6、在cst目录下对flash_os.bin进行签名:
1  | # ./release/linux64/bin/cst -i csf_linux_img.txt -o os_cntr_signed.bin  | 
SPL(Secondary Program Loader)的特殊处理
SPL被用于在初始化ARM ATF和u-boot之前初始化引导程序。u-boot中支持AHAB功能,这个功能对于完全验证flash.bin至关重要。
在SPL目标上,只有SCFW,SPL,M4镜像在SCU ROM阶段验证。为了验证ATF和U-boot,必须在SPL阶段调用sc_misc_seco_authenticate()进行验证。
包含SPL的启动镜像
包含SPL的镜像包含3个container,如下:
- 第1个container只包含SECO FW,NXP签名,在SCU ROM阶段使用SECO ROM验证;
 - 第2个container包含SCFW,SPL,M4镜像,这些由OEM签名,在SCU ROM阶段使用SECO FW验证;
 - 第3个container包含u-boot和ATF,SPL负责load这个container,和使用接口通过SCU发送命令给SECO FW对container进行验证;

 
对包含SPL的镜像签名

(1)、编译生成第3个container镜像:
1  | make SOC=iMX8QX u-boot-atf-container.img  | 
编译的log如下:
1  | AP file_offset = 0x91800 size = 0x4d800  | 
(2)、对第3个container签名:
a. 在cst中创建csf_uboot_atf.txt文件,内容如下:
1  | [Header]  | 
b. 对镜像进行签名
1  | ./release/linux64/bin/cst -i csf_uboot_atf.txt -o signed-u-boot-atf-container.img  | 
将生成的signed-u-boot-atf-container.img拷贝到imx-mkimage目录中重新打包,并重命名为u-boot-atf-container.img。
c、编译生成flash.bin镜像
1  | make SOC=iMX8QX flash_spl_container  | 
编译的log如下:
1  | CST: CONTAINER 0 offset: 0x400  | 
d、对flash.bin镜像签名
将生成的flash.bin镜像拷贝到cst目录中签名。
1  | ./release/linux64/bin/cst -i csf_boot_image.txt -o signed-flash.bin  | 
带SPL和不带SPL签名的差异
- 带SPL的镜像总共有3个container,不带SPL只有2个container;
 - 带SPL的镜像需要将第3个container(u-boot + ATF)先签名,然后将签名后的镜像再跟第2个container(SCFW,SPL,M4镜像)打包再一起再进行签名最终生成
flash.bin镜像。 
参考资料
i.MX High Assurance Boot (HAB) / Secure Boot
mx8_mx8x_secure_boot.txt
mx8_mx8x_spl_secure_boot.txt
introduction_ahab.txt:
CAAM测试程序
i.MX HAB介绍
High Assurance Boot (HAB) for dummies