Pure Linux OTP-Key Security Boot Flow SOP
1. SECURITY BOOT介绍¶
1.1. 概述¶
Security Boot需要做的工作总体上包括签章(或加密+签章)和验签(或验签+解密)两大部分,验签即签章的验证,其中加密和解密为可选,用户可根据自身需求取舍。
一般情况下加密是为了防止信息被泄露,而验签是为了防止信息被篡改。加密(详见加密Security Boot流程章节)和签章(详见签章介绍部分)的工作将使用脚本完成,而验签和解密的工作将由固件中Software完成。Security Boot的验签和解密总体流程如下。
图1-1 验签和解密总体流程
1.1.1. 秘钥介绍¶
从秘钥使用的角度看,签章和验签分别使用了RSA Private Key
和RSA Public Key
,加密和解密使用同一把AES Key
。
-
验签流程中所用的
RSA Public Key
有两把。第一把为
OTP Key
,该把Key会在系统Power On后,Hardware会自动从OTP中载入,用来做签章验证的动作,这边Software是无法介入的。而OTP Key的内容,可以通过Linux Tool或U-Boot的Command Line烧录至OTP的指定地址中。第二把为
CUST Key
,该Key会嵌在IPL和IPL_CUST的bin文件中,通过Software在整个boot flow中用来做签章验证的动作。 -
验签流程中所用的
AES Key
为OTP Key
,该把Key使用之前需要烧录到OTP存储单元中,在使用的时候,由Hardware载入到crypto engine进行使用,可以在整个boot flow中用来做AES解密的动作。OTP AES Key
的内容由用户自定义,所以由用户维护。
1.1.2. 流程介绍¶
-
Authenticate IPL by OTP Key-RSA
该流程为ROM code从OTP中读取RSA Public Key后,对IPL进行签章的验证,如图1-1的步骤1。由于OTP的RSA Public Key可以进行Write Lock动作(后面章节会详细提及),阻止再次被写,所以OTP的RSA Public Key可以做到不被替换,从而保证了IPL不会被篡改。
-
Authenticate IPL_CUST by IPL's CUST Key-RSA
该流程为IPL读取事先嵌在IPL的CUST RSA Public Key后,对IPL_CUST进行签章的验证,如图1-1的步骤2。由于IPL不会被篡改,所以保证了嵌在IPL的CUST RSA Public Key不会被篡改,从而保证了IPL_CUST也不会被篡改。
-
Authenticate U-Boot/kernel by IPL_CUST's CUST Key-RSA
该流程为IPL_CUST读取事先嵌在IPL_CUST的CUST RSA Public Key后,对U-Boot进行签章的验证,如图1-1的步骤3。同样U-Boot也读取这把Key对下一阶段的Linux Kernel进行签章验证,如图1-1的步骤5。
-
Decrypt U-Boot/kernel by OTP Key–AES
如果开启加密机制,U-Boot及Linux Kernel会通过AES128(ECB)来做加密动作,在Boot流程中,IPL_CUST使用OTP中事先烧录好的AES Key对U-Boot执行解密动作,同样U-Boot也对Linux Kernel执行解密动作,如图1-1的步骤4、6。
1.2. OTP Key¶
1.2.1. OTP RSA Key¶
OTP Key
为ROM code
用来验证IPL的签章所使用的RSA Public Key
,由N-key
及E-key组成,需通过Linux Tool或在Uboot下预先烧录到OTP中。在开机时,则会由Hardware自动载入。
OTP Key
相应的地址空间只能烧写一次,此外,OTP Key
带有Read Lock
和Write Lock
功能,来防止OTP Key
被非法读取和篡改(烧写OTP RSA Key
及其Lock相关内容后面章节会提及)。
1.2.2. OTP AES Key¶
OTP AES Key
长度为128bit,若使用"签章+加密"则会用到OTP AES Key
,需要在使用之前就将Key烧录于OTP存储单元中(烧写OTP AES Key
相关内容后面章节会提及)。
1.3. CUST Key¶
CUST Key
可由RSA Public Key
(RSA-2048,长度为2048 bit)及AES Key
(ECB-128,长度为128 bit)所组成,在签章过程中,IPL的bin文件和IPL_CUST的bin文件都会嵌入RSA Public Key
,bin文件组成结构变化情况分别如下图。
1.3.1. IPL结构¶
图1-2 IPL结构
1.3.2. IPL_CUST结构¶
图1-3 IPL_CUST结构
1.4. Boot Flow结构¶
Boot Flow请参考下图,该图为从ROM到Linux Kernel的Boot Flow结构,其中Signature为签章数据部分,每个Flow的Signature均会嵌入到相应bin文件的最后,每个Flow均包含Get Key和Authenticate的动作直到Linux Kernel,部分Flow支持解密(是否开启解密取决于需求)。Get Key有三种,OTP RSA Key和OTP AES Key均由Hardware获取,CUST RSA Key由Software获取。需要注意的是IPL_CUST是Insert CUST RSA Key后才进行签章的,所以其Signature嵌入在CUST RSA Key后面。
图1-4 Boot Flow结构
1.5. OTP开启Security Boot¶
当烧录完OTP Key后,真正启动Security Boot必须要烧录OTP_Secure_Boot相应的Register栏位(OTP烧录方法后面章节会详细提及),将该栏位烧录为0xFF,则会启动OTP的Security Boot(注:开启后无法关闭,每次启动都将会强制走Security Boot flow)。
1.6. 普通Security Boot流程¶
如下所示,Generate Signature是在local端执行,这部分目前方案是使用Python脚本完成(后面章节会详细提及),先对Image file进行SHA-256的计算,生成Digest,再通过RSA-2048做加密动作,最后生成256Bytes的signature,再将signature嵌入至Image后端。
Verify Signature是在boot flow中执行,各阶段的boot code会对下一段的Imag进行SHA-256计算,生成Digest,并取出signature来做RSA-2048的解密生成Digest',然后对比Digest和Digest',若对比一致,则Verify success,否则Verify fail。
图1-5 普通Security Boot流程
1.7. 加密Security Boot流程¶
如下所示,Generate Signature是在local端执行,这部分目前方案是使用Python脚本完成(后面章节会详细提及),一开始会先将Image file通过AES-128(ECB)加密成Cipher file,再对Cipher file进行SHA-256计算,生成Digest,再通过RSA-2048做加密动作,最后生成256Bytes的signature,再将signature嵌入至Image后端。
Verify Signature是在boot flow中,各阶段的boot code会对下一段的Imag进行SHA-256计算,生成Digest,并取出Signature来做RSA-2048的解密后,生成Digest',最后对比Digest和Digest'。如对比成功,则对Cipher file进行AES-128(ECB) 解密的动作,最后解密为能开机的image。
图1-6 加密Security Boot流程
2. OTP Key读写操作说明¶
2.1. 生成 RSA Key¶
通过openssl来生成RSA Key。相关的命令操作,请参考如下:
-
Generate Private Key
-
openssl genrsa -out private.pem 2048
-
Generate Public Key
-
openssl rsa -in private.pem -out public.pem -outform PEM -pubout
2.2. 使用U-BOOT烧录OTP¶
2.2.1. 生成U-Boot Command Scripts¶
通过Tool (key_proc.py
) 可产生U-Boot command scripts
来方便通过U-Boot来将RSA-Public Key烧录至OTP指定位置上。
-
./key_proc.py --exportkey --rsa=public.pem
-
exportkey: 将public.pem 转换成rsaKey.bin的binary file
-
rsa: 输入openssl所生成的public key (public.pem)
-
生成文件——执行该command后会生成的文件如下
rsaKey.bin——RSA Public N-Key binary file
rsaKey.txt——Otp Command List for write RSA Public N-Key rsaKey.txt会列出烧录Key的Uboot command list,可以直接使用该command list来进行烧录。
rsaKey.bin |
---|
rsaKey.txt |
2.2.2. 启动Secure Boot¶
通过烧录OTP中的Secure Boot的栏位来启动Secure boot功能,一旦启动Secue Boot后,在ROM阶段就会开始进行IPL的签章验证。启动方式可通过烧录OTP_SECURE_BOOT (0x2)。
这边需确认是否有正确开启Secure Boot,在未开启前,其Boot行为如下:
-
[普通安全boot流程] Boot flow不会对签章进行验证,所以是可以完成整个Boot流程。
-
[加密安全boot流程] Boot flow不会对签章进行验证,也不会对Image进行解密,所以会无法正常开机。
2.3. OTP Command Format¶
如下图表,列出在UBOOT下执行OTP烧录的两个COMMAND,分别能对OTP进行读写的动作。
WRITE COMMAND |
---|
READ COMMAND |
2.4. OTP Command Support List¶
2.4.1. OTP_RSA_N (0x0)¶
该Command被使用来存取OTP中的RSA Public N-Key
,总共包含256-Bytes的OTP位置。在设置完该Command后,须于下次启动才会生效。
WRITE COMMAND: otp -w 0x0 offset writedata |
---|
READ COMMAND: otpctrl -r 0x0 |
2.4.2. OTP_RSA_E (0x1)¶
该Command被使用来存取OTP中的RSA Public E-Key,总共包含4-Bytes的OTP位置。在设置完该Command后,须于下次启动才会生效。
WRITE COMMAND: otpctrl -w 0x1 0x0 0x01000100 |
---|
READ COMMAND: otpctrl -r 0x1 |
2.4.3. OTP_SECURE_BOOT (0x2)¶
该Command被使用来启动secure boot,设置为0xFF表示启动secure boot。在设置完该Command后,须于下次启动才会生效。在设置完该Command后,须于下次启动才会生效。
WRITE COMMAND: otpctrl -w 0x2 0x0 0xFF |
---|
READ COMMAND: otpctrl -r 0x2 |
2.4.4. OTP_RSA_KEY_LOCK_AND_BLOCK (0x3)¶
该Command被使用来对OTP内的RSA Public Key
栏位进行LOCK跟BLOCK的动作。LOCK表示无法更改RSA Public Key
的内容值,而BLOCK则表示SW无法读出位于OTP内的Public Key
。在设置完该Command后,须于下次启动才会生效。
LOCK |
---|
WRITE COMMAND: otpctrl -w 0x3 0x0 0x04 |
READ COMMAND: otpctrl -r 0x3 |
BLOCK |
WRITE COMMAND: otpctrl -w 0x3 0x0 0x10 |
READ COMMAND: otpctrl -r 0x3 |
2.4.5. OTP_AES_KEY (0x4)¶
该Command被使用来存取OTP中的AES Key,总共包含16-Bytes的OTP位置。在设置完该Command后,须于下次启动才会生效。
WRITE COMMAND: otpctrl -w 0x0 offset writedata | |
---|---|
READ COMMAND: otpctrl -r 0x4 | |
Example | |
制作AES KEY: echo '000102030405060708090A0B0C0D0E0F' | xxd -r -ps > aesKey.bin |
otpctrl -w 0x4 0x0 0x03020100 | |
otpctrl -w 0x4 0x4 0x07060504 | |
otpctrl -w 0x4 0x8 0x0B0A0908 | |
otpctrl -w 0x4 0xC 0x0F0E0D0C |
2.4.6. OTP_AES_KEY_LOCK_AND_BLOCK (0x6)¶
该Command被使用来对OTP内的AES Key栏位进行LOCK跟BLOCK的动作。LOCK表示无法更改AES Key的内容值,而BLOCK则表示SW无法读出位于OTP内的AES Key。在设置完该Command后,须于下次启动才会生效。
LOCK |
---|
WRITE COMMAND: otpctrl -w 0x6 0x0 0x01 |
READ COMMAND: otpctrl -r 0x6 |
BLOCK |
WRITE COMMAND: otpctrl -w 0x6 0x0 0x04 |
READ COMMAND: otpctrl -r 0x6 |
2.5. ERROR CODE¶
FF01: 无效的COMMAND |
---|
FF02: 无效的OTP地址 |
FF03: 无效的OTP执行许可证 |
3. 安全镜像生成¶
3.1. 概述¶
此功能依据用户需求可分成是否进行明文内容的加密,如下两种制作流程,此流程能一路支持到Linux Kernel,如下章节会逐步介绍如何通过tool生成Images。如下是注意事项:
-
without AES
-
IPL.bin需先Insert CUST Key (Public Key),然后签章必须是由OTP KEY所生成
-
IPL_CUST.bin需先Insert CUST Key (Public Key),然后在通过CUST Key (Private Key) 生成签章,这边Insert的Key为用来验证IPL_CUST、U-Boot和Linux Kernel的签章
-
with AES
-
IPL.bin需先Insert CUST Key (Public Key),然后签章必须是由OTP KEY所生成
-
IPL_CUST.bin需先Insert CUST Key (Public Key),然后再通过CUST Key (Private Key) 生成签章,但不需要进行AES加密。这边Insert的CUST Key (Public Key)为用来验证U-Boot和Linux Kernel的签章。
-
U-Boot和Linux Kernel都需要先进行AES加密,然后再通过CUST Key (Private Key) 生成签章。
图3-1 签章流程
3.2. 制作RSA Key¶
这边的制作方式同样适用于OTP Key及CUST Key中的RSA Key,可依据客户需求使用同一把Key或制作出两把不同的RSA Key。出于安全性考虑,建议使用两把不同的RSA key。
-
生成RSA私钥
openssl genrsa -out private.pem 2048
-
生成RSA公钥
openssl rsa -in private.pem -out public.pem -outform PEM -pubout
3.3. 制作AES-128 Key¶
这边通过xxd tool来生成AES-128 key binary file
,请指定32bytes的key,通过如下command来生成。
echo '000102030405060708090A0B0C0D0E0F' | xxd -r -ps > aesKey.bin
3.4. IPL.bin 签章¶
IPL需要先Insert Key后再做签章,步骤如下:
-
Insert Key,通过
key_proc.py
执行不包含AES加密:
./key_proc.py --insert --rsa=./public.pem -f ./IPL.bin
执行后会生成
IPL.cipher.bin
包含AES加密:
./key_proc.py --insert --rsa=./public.pem -f ./IPL.AES.bin
执行后会生成
IPL.AES.cipher.bin
-
制作签章,通过
key_proc.py
执行(注:必须用OTP的RSA Private Key
签章)不包含AES加密:
./key_proc.py --sign --rsa=./private-otp.pem -f ./IPL.cipher.bin
执行后会生成
IPL.cipher.bin.sig
包含AES加密:
./key_proc.py --sign --rsa=./private-otp.pem -f ./IPL.AES.cipher.bin
执行后会生成IPL.AES.cipher.bin.sig
3.5. IPL_CUST.bin 签章¶
IPL_CUST需要先Insert Key后再做签章,步骤如下:
-
Insert Key,通过
key_proc.py
执行不包含AES加密:
./key_proc.py --insert --rsa=./public.pem -f ./IPL_CUST.bin
执行后会生成
IPL_CUST.cipher.bin
包含AES加密:
./key_proc.py --insert --rsa=./public.pem -f ./IPL_CUST.AES.bin
执行后会生成
IPL_CUST.AES.cipher.bin
-
制作签章,通过
key_proc.py
执行不包含AES加密:
./key_proc.py --sign --rsa=./private.pem -f ./IPL_CUST.cipher.bin
执行后会生成
IPL_CUST.cipher.bin.sig
包含AES加密:
./key_proc.py --sign --rsa=./private.pem -f ./IPL_CUST.AES.cipher.bin
执行后会生成
IPL_CUST.AES.cipher.bin.sig
3.6. U-Boot 签章¶
如下设定针对SPI NOR的U-Boot版本,其档名为u-boot.xz.img.bin
,如为SPI-NAND版本,请改u-boot_spinand.xz.img.bin
,关于U-Boot的制作可区分为是否进行AES的加密。
-
制作签章
不包含AES加密:
不需做Image的加密动作,执行后会产生
u-boot.xz.img.bin.sig
./key_proc.py --sign --rsa=./private.pem -f u-boot.xz.img.bin
包含AES加密:
需要先对Image做AES加密,执行后会产生
u-boot.xz.img.aes.bin
,之后再对加密后的Image进行签章,最后产生u-boot.xz.img.aes.bin.sig
。./key_proc.py --encrypt --aes=./aesKey.bin -f ./u-boot.xz.img.bin ./key_proc.py --sign --rsa=./private.pem -f ./u-boot.xz.img.aes.bin
3.7. Linux Kernel 签章¶
-
制作签章
不包含AES加密: 不需做Image的加密动作,执行后会生成kernel.sig
./key_proc.py --sign --rsa=./private.pem -f kernel
包含AES加密:
需要先对Image做AES加密,执行后会生成kernel.aes,之后再对加密后的Image进行签章,最后生成
kernel.aes.sig
。./key_proc.py --encrypt --aes=./aesKey.bin -f ./kernel ./key_proc.py --sign --rsa=./private.pem -f ./kernel.aes
3.8. Linux Rootfs 签章¶
-
制作签章
不包含AES加密:
不需做Image的加密动作,执行后会生成rootfs.sqfs.sig
./key_proc.py --sign --rsa=./private.pem -f rootfs.sqfs
包含AES加密:
非ramdisk属性的rootfs无法支持加解密机制
3.9. 签章验证¶
-
将完成签章的Images刻录至flash中,刻录后如能正常进入U-Boot,表示从IPL至U-Boot的签章是没有问题的。
-
在Uboot阶段需要设定环境变量来对Linux Kernel进行验证,请参考如下command进行设定。
不包含AES加密:
-
SPI-NOR
setenv bootcmd ' sf probe 0;sf read 0x22000000 ${sf_kernel_start} ${sf_kernel_size}; mxp r.info IPL_CUST; sf read 0x23C00000 ${sf_part_start} ${sf_part_size}; dcache off; sigauth 0x22000000 0x23C00000; dcache on; bootm 0x22000000; '
-
SPI-NAND
setenv bootcmd ' nand read.e 0x23C00000 IPL_CUST0 0x20000;nand read.e 0x22000000 KERNEL 0x500000; dcache off; sigauth 0x22000000 0x23C00000; dcache on; bootm 0x22000000; '
包含AES加密:
-
SPI-NOR
setenv bootcmd ' sf probe 0;sf read 0x22000000 ${sf_kernel_start} ${sf_kernel_size}; mxp r.info IPL_CUST; sf read 0x23C00000 ${sf_part_start} ${sf_part_size}; dcache off; sigauth 0x22000000 0x23C00000 --aes; dcache on; bootm 0x22000000; '
-
SPI-NAND
setenv bootcmd ' nand read.e 0x23C00000 IPL_CUST0 0x20000;nand read.e 0x22000000 KERNEL 0x500000; dcache off; sigauth 0x22000000 0x23C00000 --aes; dcache on; bootm 0x22000000; '
-
-
若customer有验签rootfs的需求,在Uboot阶段需要设定环境变量来对Linux Rootfs进行验证,请参考如下command进行设定(针对以下command,若load image改为实际size而非分区size,则可减少一定耗时):
不包含AES加密:
-
SPI-NOR
setenv rootfs_size [size]
如实际烧录的rootfs size为0x1af100,则setenv rootfs_size 1af100; saveenv;
sf probe 0; mxp r.info rootfs; sf read 0x22000000 ${sf_part_start} ${sf_part_size}; mxp r.info IPL_CUST; sf read 0x23C00000 ${sf_part_start} ${sf_part_size}; dcache off; sigauth 0x22000000 0x23C00000; dcache on;
结合kernel的验签,bootcmd为:
-
Kernel验签+ rootfs验签:
setenv bootcmd ' dcache off; sf probe 0; mxp r.info rootfs; sf read 0x22000000 ${sf_part_start} ${sf_part_size}; mxp r.info IPL_CUST; sf read 0x23C00000 ${sf_part_start} ${sf_part_size}; sigauth 0x22000000 0x23C00000; sf read 0x22000000 ${sf_kernel_start} ${sf_kernel_size}; sigauth 0x22000000 0x23C00000; dcache on; bootm 0x22000000; '
-
Kernel(验签+解密)+ rootfs验签:
setenv bootcmd ' dcache off; sf probe 0; mxp r.info rootfs; sf read 0x22000000 ${sf_part_start} ${sf_part_size}; mxp r.info IPL_CUST; sf read 0x23C00000 ${sf_part_start} ${sf_part_size}; sigauth 0x22000000 0x23C00000; sf read 0x22000000 ${sf_kernel_start} ${sf_kernel_size}; sigauth 0x22000000 0x23C00000 --aes; dcache on; bootm 0x22000000; '
-
-
SPI-NAND
setenv rootfs_size [size]
如实际烧录的rootfs size为0x1af100,则setenv rootfs_size 1af100; saveenv;
nand read.e 0x23C00000 IPL_CUST0 0x20000;nand read.e 0x22000000 rootfs 0x600000; dcache off; sigauth 0x22000000 0x23C00000; dcache on;
结合kernel的验签,bootcmd为:
-
Kernel验签+ rootfs验签:
setenv bootcmd ' dcache off; nand read.e 0x23C00000 IPL_CUST0 0x20000; nand read.e 0x22000000 rootfs 0x600000; sigauth 0x22000000 0x23C00000; nand read.e 0x22000000 KERNEL 0x500000; sigauth 0x22000000 0x23C00000; dcache on; bootm 0x22000000; '
-
Kernel(验签+解密)+ rootfs验签:
setenv bootcmd ' dcache off; nand read.e 0x23C00000 IPL_CUST0 0x20000; nand read.e 0x22000000 rootfs 0x600000; sigauth 0x22000000 0x23C00000; nand read.e 0x22000000 KERNEL 0x500000; sigauth 0x22000000 0x23C00000 --aes; dcache on; bootm 0x22000000; '
-
包含AES加密:
非ramdisk属性的rootfs无法支持加解密机制
-
4. 注意事项¶
4.1. IPL¶
针对Secure Boot的功能,特定的IPL及IPL_CUST必须被使用。
4.1.1. 普通安全boot流程¶
-
IPL:
IPL.bin
-
IPL_CUST:
IPL_CUST.bin
4.1.2. 加密安全boot流程¶
-
IPL:
IPL.AES.bin
-
IPL_CUST:
IPL_CUST.AES.bin
4.2. 自编tool签章¶
针对U-Boot、Linux Kernel来生成签章文件时,仅使用其的Data部分来进行SHA计算,并通过RSA来产生256Bytes的签章,所以如客户为使用自行编制的tool来产生签章,须注意应排除U-Boot和Linux Kernel前64Bytes的Header,仅以Data部分来进行计算。