防抄板-SPIENC-D13x
应用场景
本方案针对下列使用场景:
-
方案商提供主控芯片和开发好的固件给第三方生产商 生产, 方案商对自己的固件进行保护
-
方案商开发了包含某一功能的固件
-
生产商不进行开发,而使用方案商提供的固件
-
方案商为了保护自己的固件,会要求自己的固件只能在方案商授权的主控芯片上运行
-
他人不能通过拷贝 SPI NOR 上的固件在不经
方案商
授权的主控芯片上运行
方案介绍
本方案通过使用 AIC 主控的 SPIENC 总线加密功能以及安全启动功能来实现防抄板,结合实际使用的需求,提供对应的软件方案。
SPIENC 总线加密功能是一个芯片硬件支持的安全功能,芯片使能了 SPIENC 后,内部的 SPIENC 模块对 SPI 总线上传输的数据进行实时的加密或解密,即对写出去的数据进行 AES 加密,读回来的数据进行 AES 解密, 使得保存在 Flash 上的数据总是密文。
SPIENC 进行加解密时,使用芯片 eFuse 中特定密钥区域中的密钥对数据进行加密和解密,该密钥区域可以做到烧录后 CPU 不可读写,在芯片内部也仅有 SPIENC 模块能够访问,因此可以做到硬件安全保密。
-
在芯片中烧录特有的的 AES 密钥,并且将相关密钥区域设置为仅 SPIENC 可访问。
-
提供对应的加密固件。
-
对 AES 密钥进行妥善管理,防止泄露。
此时芯片和对应的固件就被绑定在一起,提供出去的固件,只能运行在烧录了对应加密密钥的芯片上; 烧录了密钥的芯片,也只能运行使用对应密钥加密后的固件。
安全启动功能是通过 RSA 签名和验签的方式,保证芯片只运行经过合法签名的固件,非法固件无法在开启安全启动的芯片上执行。 在防抄板方案中,安全启动可以预防攻击者通过其他手段,运行非法程序读取 Flash 中的固件内容。
开启防抄板功能
如需开启防抄版功能,执行下列步骤:
-
编译一个烧录 eFuse 的 BootLoader,该固件只完成对出货的芯片烧录相关的 eFuse 和密钥,并使能 SPIENC 和安全启动功能
通过运行特定 eFuse 烧录程序,对芯片进行 eFuse 烧录。-
通过修改 BootLoader 的代码,将烧录 eFuse 的程序集成到 BootLoader 中。
-
编译生成烧录 eFuse 专用的固件。
-
上电刷机, BootLoader 程序会仅烧录对应的 eFuse 域成功后退出。
-
可以用 AiBurn 刷机,也可以用 SD 卡等存储介质刷机。
-
-
编译一个进行了加密的量产固件,该量产固件可以发放给生产商。
-
生产商使用方案商提供的主控进行生产,烧录方案商提供的固件。
生成 eFuse 烧录固件
使用 SPIENC 加密功能,需要用到一个 128 位的 AES 密钥,并将其烧录到芯片 eFuse 中。在制作加密镜像时,也需要使用密钥,因此确保密钥保持不变且已妥善管理, 以免泄露。
-
SDK/target/d13x/demo88-nor/pack/keys/set_aes_key.txt:存储密钥
-
SDK/target/d13x/demo88-nor/pack/keys/set_nonce.txt: 存储 NONCE
-
SDK/target/d13x/demo88-nor/pack/keys/gen_spienc_key.bat: Windows上的脚本
-
SDK/target/d13x/demo88-nor/pack/keys/gen_spienc_key.sh: Linux 上的脚本
-
生成密钥根据运行环境执行对应命令,运行生成密钥的脚本:
- 在 Linux 环境下:
-
确保已经安装 OpenSSL。如未安装,可执行以下命令进行安装:
sudo apt-get install openssl
-
准备初始密钥文件 set_aes_key.txt 和 set_nonce.txt。
set_aes_key.txt 和 set_nonce.txt 文件中各有一个初始密钥,需要手动修改其中的
HEX
密钥内容. -
使用下列命令运行脚本生成所需的密钥文件和头文件:
cd SDK_ROOT/lite/target/d13x/demo88-nor/pack/keys/ ./gen_spienc_key.sh
生成的文件如下所示:-
AES 密钥 spi_aes.key
-
对应的 C 语言头文件 spi_aes_key.h
-
spi_nonce.key
-
rotpk.bin
-
rsa_private_key.der
-
rsa_private_key.pem
-
rsa_public_key.der
-
rsa_public_key.pem
-
-
将 spi_aes_key.h 文件复制粘贴至 lite/bsp/examples_bare/test-efuse/ 目录中,供编译烧录 eFuse 的程序时使用。
spi_aes.key 和其他文件则保留在 lite/target/d13x/demo88-nor/pack/keys/,在 mk_image.py 生成加密固件时使用。
重要: 生成的密钥请妥善保管,以免丢失或者泄露。
-
-
在 Windows 环境 下:
-
运行脚本生成一个 AES 密钥 spi_aes.key,并且生成对应的 C 语言头文件 spi_aes_key.h:
cd SDK_ROOT/lite/target/d13x/demo88-nor/pack/keys/ ./gen_spienc_key.sh
- spi_aes.key:
-
在 mk_image.py 生成加密固件时使用。
- spi_aes_key.h:
-
复制到 lite/bsp/examples_bare/test-efuse/spi_aes_key.h
在编译烧录 eFuse 的程序时使用。
重要: 生成的密钥请妥善保管,以免丢失或者泄露。 -
将下列文件复制到 Windows 的 SDK 目录:
-
将 SDK/target/d13x/demo88-nor/pack/keys/ 复制到 Window SDK 对应目录中。
-
将 keys 下的 spi_aes_key.h 文件复制粘贴至 SDK/bsp/examples_bare/test-efuse/spi_aes_key.h 目录中。
-
-
- 在 Linux 环境下:
-
编译程序
按照以下步骤配置和编译 BootLoader,并生成烧录固件。
-
应用 BootLoader 的配置:
cd <SDK_ROOT> scons --apply-def d13x_demo88-nor_baremetal_bootloader_defconfig
-
打开 BootLoader 的 menuconfig 菜单:
scons --menuconfig
-
分别选上或者确认下列选项已经选上:
AIC_USING_SID AIC_SID_BARE_TEST AIC_USING_SPIENC AIC_SPIENC_BYPASS_IN_UPGMODE
Board options ---> [*] Using Spienc [*] Bypass during bootloader burn image [*] Enc qspi0 (0) set qspi0 tweak [*] Using Efuse/SID
Drivers options ---> Drivers examples ---> [*] Enable SID driver test command
-
修改代码使能 SPIENC:
-
bsp/examples_bare/test-efuse/efuse_burn_spienc_key_cmd.c:
使能文件开头的 D13X_BURN_SPIENC_KEY_ENABLE 定义
注:如果不需要关闭 JTAG,可以将 burn_jtag_lock_bit() 相关的调用注释掉。
-
application/baremetal/bootloader/main.c:
在console_set_usrname
之后,添加上一个命令执行代码,console_run_cmd(“efuse_spienc”);
如下所示。int main(void) { console_init(); console_set_usrname("aic"); console_run_cmd("efuse_spienc"); // 加上此句 ... }
-
-
编译程序 BootLoader:
scons
-
编译程序 APP 并且生成烧录固件:
scons --apply-def=d13x_demo88-nor_rt-thread_helloworld_defconfig scons
编译结果保存在 SDK/output/d13x_demo88-nor_rt-thread_helloworld/images 目录中。
-
-
AiBurn 卡烧录
使用 AiBurn 烧录 outputd13x_demo88-nor_rt-thread_helloworldimagesd13x_demo88-nor_v1.0.0.img 固件
-
SD 卡烧录
准备一张 SD 卡,确保该卡只有一个分区,并且格式化为 FAT32/ exFAT 文件系统。
将编译输出目录下的文件复制到 SD 卡的根目录:
-
bootcfg.txt
-
bootloader.aic
并且将 bootcfg.txt 中的内容修改为:
boot0=bootloader.aic
将该卡插到板卡中,上电运行,即可完成相关 eFuse 的烧录。
-
生成量产固件
-
BootLoader 配置
- 进入 SDK
根目录:
cd <SDK_ROOT>
- 在 SDK
根目录中执行下列命令:
scons --apply-def=d13x_demo88-nor_baremetal_bootloader_defconfig
-
打开 BootLoader 的 menuconfig 菜单:
scons --menuconfig
-
在配置界面,勾选或确认已勾选下列参数:
AIC_USING_SPIENC AIC_SPIENC_BYPASS_IN_UPGMODE
配置界面示例如下:Board options ---> [*] Using Spienc [*] Bypass during bootloader burn image [*] Enc qspi0 (0) set qspi0 tweak [*] Using Efuse/SID
注:编译量产固件时,需将编译烧录 eFuse 程序时的代码修改还原。
-
在正式发布的固件中,建议将下列参数选项去掉,防止攻击者通过控制台读出 Flash 中的数据,否则可跳过:
AIC_BOOTLOADER_CMD_MTD AIC_MTD_BARE_TEST
功能配置界面示例如下:BootLoader options ---> Commands ---> [ ] mtd read/write Drivers options ---> Drivers examples ---> [ ] Enable MTD driver test command
- 进入 SDK
根目录:
-
应用程序配置
- 进入 SDK
根目录:
cd <SDK_ROOT>
- 在 SDK
根目录,执行下列命令:
scons --apply-def=d13x_demo88-nor_rt-thread_helloworld_defconfig
-
打开 Application 的 menuconfig 菜单:
scons --menuconfig
-
勾选或确认已勾选下列选项:
AIC_USING_SPIENC
配置界面示例:Board options ---> [*] Using Spienc [*] Enc qspi0 (0) set qspi0 tweak
-
在正式版本的固件中,建议删除 kernel/rt-thread/components/drivers/spi/spi_flash_sfud.c中的 sf 命令,防攻击者通过控制台读出 Flash 中的数据,否则可跳过此步。
宏 RT_USING_FINSH 包住的内容:#if defined(RT_USING_FINSH)...#endif
- 进入 SDK
根目录:
-
固件签名加密
在 SDK/target/d13x/demo88-nor/pack/image_cfg.json 中配置并生成签名加密固件。
-
配置生成签名的组件开启了安全启动后,需要对 BootLoader 进行签名。
-
对于 1.0.5 及以前的 SDK,参考修改
// 签名相关
部分的内容:{ "spi-nor": { // Device, The name should be the same with string in image:info:media:type "size": "16m", // Size of SPI NAND "partitions": { "spl": { "size": "256k" }, "os": { "size": "2m" }, "rodata": { "size": "6m" }, "data": { "size": "7m" } }, }, "image": { ... }, "info": { // Header information about image ... }, "updater": { // Image writer which is downloaded to RAM by USB/UART ... }, "target": { // Image components which will be burn to device's partitions ... }, "temporary": { // Pre-proccess to generate image components from raw data "aicboot": { "bootloader.aic": { "head_ver": "0x00010001", "loader": { "file": "bootloader.bin", "load address": "0x30100000", "entry point": "0x30100100", }, "resource": { "private": "pbp_cfg.bin", "pubkey": "keys/rsa_public_key.der", // 签名相关 "pbp": "d13x.pbp", }, "signature": { // 签名相关 "algo": "rsa,2048", "privkey": "keys/rsa_private_key.der", }, }, }, }, }
-
对于 1.0.6 及以后的 SDK,参考修改
// 签名相关
部分的内容:{ "spi-nor": { // Device, The name should be the same with string in image:info:media:type "size": "16m", // Size of SPI NAND "partitions": { "spl": { "size": "256k" }, "os": { "size": "2m" }, "rodata": { "size": "6m" }, "data": { "size": "7m" } }, }, "image": { ... }, "info": { // Header information about image ... }, "updater": { // Image writer which is downloaded to RAM by USB/UART ... }, "target": { // Image components which will be burn to device's partitions ... }, "pre-process": { // before v1.0.6 is the name "temporary" "aicimage": { // Create aic boot image "usbupg-psram-init.aic": { // No loader, only PreBootProgram to initialize PSRAM "head_ver": "0x00010001", "resource": { "private": "pbp_cfg.bin", "pubkey": "keys/rsa_public_key.der", // 签名相关 "pbp": "d13x.pbp", }, "signature": { "algo": "rsa,2048", "privkey": "keys/rsa_private_key.der", // 签名相关 }, }, "pbp_ext.aic": { "head_ver": "0x00010001", "resource": { "pbp": "d13x.pbp", "pubkey": "keys/rsa_public_key.der", // 签名相关 "private": "pbp_cfg.bin", }, "signature": { // 签名相关 "algo": "rsa,2048", "privkey": "keys/rsa_private_key.der", }, // combine to use with loader.aic "with_ext": "true", }, "loader.aic": { "head_ver": "0x00010001", "loader": { "file": "bootloader.bin", "load address": "0x40300000", "entry point": "0x40300100", // 256 byte aic header }, "resource": { "private": "pbp_cfg.bin", "pubkey": "keys/rsa_public_key.der", // 签名相关 }, "signature": { // 签名相关 "algo": "rsa,2048", "privkey": "keys/rsa_private_key.der", }, }, }, }, }
-
-
对组件进行加密
在 image_cfg.json 的 “temporary” 或 “pre-process” 对象的最后,添加 “spienc” 对象配置。
此处使用的 AES 加密密钥,即为SDK/target/d13x/demo88-nor/pack/keys/ 文件目录中生成的密钥。
在下列示例中,配置了一组需要使用 “spienc” 工具进行加密的组件,其中生成 bootloader.aic.enc 组件的配置参数为:
{ "spi-nor": { // Device, The name should be the same with string in image:info:media:type "size": "16m", // Size of SPI NAND "partitions": { "spl": { "size": "256k" }, "os": { "size": "2m" }, "rodata": { "size": "6m" }, "data": { "size": "7m" } }, }, "image": { ... }, "info": { // Header information about image ... }, "updater": { // Image writer which is downloaded to RAM by USB/UART ... }, "target": { // Image components which will be burn to device's partitions ... }, "pre-process": { // before v1.0.6 is the name "temporary" "spienc": { "bootloader.aic.enc": { "file": "bootloader.aic", // File to be encrypted "address": "0x0", // Flash start address file to be stored "key": "keys/spi_aes.key", // Keys the same in eFuse "nonce": "keys/spi_nonce.key", // Nonce the same in eFuse "tweak": "0", }, "d13x_os.itb.enc": { "file": "d13x_os.itb", // File to be encrypted "address": "0x40000", // Flash start address file to be stored "key": "keys/spi_aes.key", // Keys the same in eFuse "nonce": "keys/spi_nonce.key", // Nonce the same in eFuse "tweak": "0", }, ... }, }, }
-
file: 加密的源文件,此处为前面生成的 bootloader.aic 文件
-
address: 是加密后的文件,存放在 Flash 的开始位置,这里应根据前面的分区表信息计算得到
-
key: 使用的加密密钥
-
nonce: 使用的加密 Nonce 值
-
tweak: 该值不需要配置,保持为 0 即可
对于一个或者多个需要进行加密的组件,都应按照上述方式进行配置。
mk_image.py 工具在读取 image_cfg.json 文件时,逐个处理放在 “spienc” 中的配置,生成对应的加密组件,然后再进行打包。
重要:“spienc” 字段应放在 “temporary”/”pre-process” 的最后,因为 “spienc” 处理时,可能需要依赖前面配置生成的文件,比如”aicboot”。
配置加密时,address 需要填写正确,不然加密结果会不正确。
-
-
配置烧录加密组件
在 image_cfg.json 中配置下列参数,打包加密组件:
-
updater 中打包的程序,应为非加密程序
updater 中配置的参数,都不是 .enc 结尾的组件
-
target 中打包的程序和数据,应为加密后的程序
target 中配置的参数,都是 .enc 结尾的组件
生成加密组件之后,需要打包加密组件,以适配使用 SD 卡烧录加密固件的要求。-
SD 卡启动时,首先运行 updater 中的程序,进入烧录模式。此时由于数据是从 SD 卡加载的,不能为加密程序,否则无法正常执行
-
target 中打包的程序是要烧录到 Flash 的数据,如果不加密,则无法起到保护的作用,因此需要打包加密后的组件
{ "spi-nor": { // Device, The name should be the same with string in image:info:media:type "size": "16m", // Size of SPI NAND "partitions": { "spl": { "size": "256k" }, "os": { "size": "2m" }, "rodata": { "size": "6m" }, "data": { "size": "7m" } }, }, "image": { ... }, "info": { // Header information about image ... }, "updater": { // Image writer which is downloaded to RAM by USB/UART "psram": { "file": "uartupg-psram-init.aic", "attr": ["required", "run"], "ram": "0x30043000" }, "spl": { "file": "bootloader.aic", "attr": ["required", "run"], "ram": "0x40100000" }, }, "target": { // Image components which will be burn to device's partitions "spl": { "file": "bootloader.aic.enc", "attr": ["mtd", "required"], "part": ["spl"] }, "os": { "file": "d13x_os.itb.enc", "attr": ["mtd", "required"], "part": ["os"] }, "rodata": { "file": "rodata.fatfs.enc", "attr": ["mtd", "optional"], "part": ["rodata"] }, "data": { "file": "data.lfs.enc", "attr": ["mtd", "optional"], "part": ["data"] }, }, "pre-process": { // before v1.0.6 is the name "temporary" ... }, }
-
量产
-
AiBurnPro 量产
直接使用 AiBurnPro 量产编译生成的固件 outputd13x_demo88-nor_rt-thread_helloworldimagesd13x_demo88-nor_v1.0.0.img。
-
SD 卡量产方式
-
标准方式
bootcfg.txt + 打包后的镜像文件,如 d13x_demo88-nor_v1.0.0.img
此方式只需要编译生成下列文件后,复制到 SD 卡 FAT32 文件系统的根目录,平台重新上电即可进入烧录:
-
bootcfg.txt
-
d13x_demo88-nor_v1.0.0.img
-
-
Direct Mode
bootcfg.txt + 具体的组件
此方式需要修改 bootcfg.txt,并且将 bootcfg.txt 和使用到的组件复制到 SD 卡 FAT32 文件系统的根目录,平台重新上电即可进入烧录模式。
bootcfg.txt 示例:
boot0=bootloader.aic writetype=spi-nor writeintf=0 write0=bootloader.aic.enc write1=d13x_os.itb.enc,0x40000 write2=rodata.fatfs.enc,0x240000 write3=data.fatfs.enc,0x840000
重要:在修改 bootcfg.txt 文件后,确保使用 UNIX 格式的换行符,非 DOS 格式的换行符,即 ‘n’ 换行,非 ‘rn’ 换行。
-