Edit online

方案 V2

本节介绍了 SPI Flash 防抄板方案 V2 的详细配置说明,以 D12x 开发板为例。

本方案通过使用 D12x 的 SPIENC 总线加密功能来实现防抄板,结合实际使用的需求,提供对应的软件方案。

Edit online

SPIENC 总线加密

SPIENC 总线加密功能是一个芯片硬件支持的安全功能。芯片使能了 SPIENC 后,内部的 SPIENC 模块对 SPI 总线上传输的数据进行实时的加密或解密,即对写出去的数据进行 AES 加密,读回来的数据进行 AES 解密, 使得保存在 Flash 上的数据总是密文。

SPIENC 进行加解密时,使用芯片 eFuse 中特定密钥区域中的密钥对数据进行加密和解密,该密钥区域可以做到烧录后 CPU 不可读写,在芯片内部也仅有 SPIENC 模块能够访问,因此可以做到硬件安全保密。

启用 SPIENC 需要:
  1. 在芯片中烧录特有的的 AES 密钥,并且将相关密钥区域设置为仅 SPIENC 可访问。

  2. 提供对应的加密固件。

  3. 对 AES 密钥进行妥善管理,防止泄露。

此时芯片和对应的固件就被绑定在一起,提供出去的固件,只能运行在烧录了对应加密密钥的芯片上; 烧录了密钥的芯片,也只能运行使用对应密钥加密后的固件。

Edit online

SPI ENC 功能对应的 eFuse 烧录信息

D12x 使能 SPIENC 功能,需要烧录的 eFuse 信息如下:
  • SECURE 区域中的 SPI_ENC_EN 位。

  • SPI_ENC KEY 区域中的密钥内容。

  • DIS RD/DIS WR 区域中,关于 SPI_ENC KEY 的读写禁止位。

  • SECURE 区域中的 JTAG_LOCK 位 (可选,关闭 JTAG 更安全)。

注:

相关信息可参考芯片用户手册 > 安全 > SID 章节。

1. eFuse 空间划分
用途位数地址禁止位禁写禁读归属备注
DIS320~30--CSTM每位对应 1 个 32bits 空间
  • 15:00 eFuse 读禁止配置
  • 31:16 eFuse 写禁止配置
BROM_SECURE324~71V-CSTMBROM 参数配置区域
CALI648~F2~3V-AIC模拟校准使用
CHIP ID12810~1F4~7V-AIC芯片编号 (子编号用到 Reserved 区域)
SPI_ENC KEY12820~2F8~11VVCSTM安全,连接到 SPI_ENC,对称密钥
Reserved12830~3F12~15--CSTMOEM 可自定义使用
2. SECURE 区域定义
比特位名称描述
31:29--
28SPI_ENC_ENBROM 读取使用,使能 SPI 总线数据加密功能
27:25--
24JTAG_LOCK逻辑组合后连接到 CPU 屏蔽 TDO,关闭 JTAG 调试功能,在安全方案中烧录为 1
Edit online

烧录 eFuse

使用 SPIENC 加密功能,需要将一个 128 位的 AES 密钥烧录到芯片 eFuse 中。制作加密镜像时也需要使用上述值。因此,使用过程中,确保上述值保持不变且已妥善管理,以免泄露。

本节描述了生成 eFuse 烧录程序的详细流程。在示例方案中,提供了设置 AES 密钥的输入 txt 文件和一个用于生成及更新密钥的脚本:

  • SDK/target/d12x/demo68-nor/pack/keys/set_aes_key.txt:设置加密密钥

  • SDK/target/d12x/demo68-nor/pack/keys/gen_spienc_key.bat:Windows 上执行编译时,使用该脚本

  • SDK/target/d12x/demo68-nor/pack/keys/gen_spienc_key.sh:Linux 上执行编译时,使用该脚本

Edit online

生成密钥

根据运行环境执行对应命令,运行生成密钥的脚本:

Linux 环境下:
  1. 确保已经安装 OpenSSL。如未安装,可执行以下命令进行安装:

    sudo apt-get install openssl
  2. 准备初始密钥文件 set_aes_key.txt

    set_aes_key.txt 中有一个初始密钥,需要手动修改其中的 HEX 密钥内容。

  3. 进入开发板根目录:
    cd SDK/target/<soc>/<board>/pack/keys/

    例如:

    cd SDK/target/d12x/demo68-nor/pack/keys/
  4. 使用下列命令运行脚本生成所需的密钥文件和头文件:
    ./gen_spienc_key.sh
    生成的文件如下所示:
    • AES 密钥 spi_aes.key

    • AES 对应的 C 语言头文件 spi_aes_key.h

  5. spi_aes_key.h 文件复制粘贴至下列目录中,供编译烧录 eFuse 的程序时使用:

    lite/bsp/examples_bare/test-efuse/

    spi_aes.key 和其它文件则保留在 SDK/target/<SoC>/<board>/pack/keys/,用于 mk_image.py 生成加密固件时使用,例如 SDK/target/d12x/demo68-nor/pack/keys/

    重要: 生成的密钥务必妥善保管,以免丢失或者泄露。

在 Windows 环境 下:

  1. 准备初始密钥文件 set_aes_key.txtset_nonce.txt

    set_aes_key.txt set_nonce.txt 文件中有一个初始密钥,需要手动修改其中的 HEX 密钥内容。

  2. 进入开发板根目录:
    cd SDK/target/<soc>/<board>/pack/keys/

    例如:

    cd SDK/target/d21x/demo88-nand/pack/keys/
    cd SDK/target/d12x/demo68-nor/pack/keys/
  3. 使用下列命令运行脚本生成所需的密钥文件和头文件:
    ./gen_spienc_key.bat
    生成的文件如下所示:
    • AES 密钥 spi_aes.key

    • AES 对应的 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

    重要: 生成的密钥请妥善保管,以免丢失或者泄露。
  4. spi_aes_key.h 文件复制粘贴至 lite/bsp/examples_bare/test-efuse/ 目录中,供编译烧录 eFuse 的程序时使用。

    spi_aes.key 和其它文件则保留在 SDK/target/<SoC>/<board>/pack/keys/,在使用 mk_image.py 生成加密固件时使用,例如SDK/target/d21x/demo88-nand/pack/keys/SDK/target/d12x/demo68-nor/pack/keys/

    重要: 生成的密钥务必妥善保管,以免丢失或者泄露。
Edit online

编译程序

按照以下步骤配置和编译程序,并生成烧录固件。

  1. 进入 SDK 根目录:
    cd <SDK_ROOT>
  2. 应用一个项目配置,例如:

    scons --apply-def=d12x_demo68-nor_baremetal_bootloader_defconfig
  3. 执行下列命令打开 menuconfig 菜单:
    scons --menuconfig
  4. 分别勾选或者确认已勾选下列参数:
    AIC_USING_SID
    EFUSE_WRITE_SUPPORT
    AIC_SID_BURN_SPIENC_KEY_TEST
    
    AIC_USING_SPIENC
    AICUPG_FIRMWARE_SECURITY

    参数对应的界面配置具体如下:

    Board options  --->
        [*] Using Spienc
        [*]   Enc qspi0
        (0)     set qspi0 tweak
        [*] Using Efuse/SID
            SID Parameter  --->
                [*] support efuse write
                (64) set efuse max word
    
    Bootloader options  --->
        [*] Upgrading  --->
            [*]   Secure transfer firmware and burn
    Drivers options  --->
        Drivers examples  --->
            [*] Enable SID burn spienc key command
  5. 修改代码调用命令烧写 eFuse:

    • 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");  // 加上此句
          ...
      }
    • bsp/examples_bare/test-efuse/efuse_burn_spienc_key_cmd.c:

      如果不需要关闭 JTAG,可以将 burn_jtag_lock_bit() 相关的调用注释掉。

  6. 编译程序 BootLoader:
    scons       # build BootLoader
  7. 编译程序 APP 并且生成烧录固件:
    scons --apply-def=d12x_demo68-nor_rt-thread_helloworld_defconfig
    scons

    编译结果会保存至 SDK/output/<SoC name>_<board name_rt-thread_helloworld/images 目录。例如,SDK/output/d12x_demo68-nor_rt-thread_helloworld/images

Edit online

AiBurn 烧录

使用 AiBurn 烧录生成的镜像,详情可查看 AiBurn 使用指南。

Edit online

SD 卡烧录

  1. 准备一张 SD 卡,确保该卡只有一个分区,并且格式化为 FAT32/ exFAT 文件系统。
  2. 将编译输出目录下的文件复制到 SD 卡的根目录:
    • bootcfg.txt

    • d211_demo88_nand_page_2k_block_128k_v1.0.0.img
    • bootloader.aic

  3. bootcfg.txt 中的内容修改为:
    boot0=bootloader.aic
  4. 将该卡插到板卡中,上电运行,即可完成相关 eFuse 的烧录。

Edit online

烧录日志

tinySPL [Built on Feb 27 2025 11:21:57]
BROM SPIENC is ENABLED
JTAG LOCK   is ENABLED
SPI ENC KEY:
0x406efde8 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   //烧录后的 key 对 CPU 不可见,所以无法读出
SPI ENC Key is read DISABLED
SPI ENC Key is write DISABLED
Edit online

生成量产固件

按照本节流程编译加密的量产固件。

Edit online

配置 BootLoader

  1. 进入 SDK 根目录:
    cd <SDK_ROOT>
  2. 在 SDK 根目录中执行下列命令:
    scons --apply-def=d12x_demo68-nor_baremetal_bootloader_defconfig
  3. 打开 BootLoader 的 menuconfig 菜单:
    scons --menuconfig
  4. 在配置界面,勾选或确认已勾选下列参数:
    AIC_USING_SPIENC
    AIC_SPIENC_BYPASS_IN_UPGMODE
    参数对应的配置界面如下:
    Board options  --->
        [*] Using Spienc
        [*]   Enc qspi0
        (0)     set qspi0 tweak
        [*] Using Efuse/SID
            SID Parameter  --->
                (64) set efuse max word
    Bootloader options  --->
        [*] Upgrading  --->
            [*]   Secure transfer firmware and burn
    注:

    编译量产固件时,需将编译烧录 eFuse 程序时的代码修改还原。

  5. 在正式发布的固件中,建议将下列参数选项去掉,防止攻击者通过控制台读出 Flash 中的数据,否则可跳过:
    AIC_BOOTLOADER_CMD_MTD
    AIC_MTD_BARE_TEST
    
    参数对应的功能配置界面如下:
    BootLoader options  --->
        Commands  --->
            [ ] mtd read/write
    
    Drivers options  --->
        Drivers examples  --->
            [ ] Enable MTD driver test command
Edit online

配置应用程序

  1. 进入 SDK 根目录:
    cd <SDK_ROOT>
  2. 在 SDK 根目录,执行下列命令:
    scons --apply-def=d12x_demo68-nor_baremetal_bootloader_defconfig
  3. 打开 Application 的 menuconfig 菜单:
    scons --menuconfig
  4. 勾选或确认已勾选下列选项:
    AIC_USING_SPIENC
    对应的配置界面如下:
    Board options  --->
    
        [*] Using Spienc
        [*]   Enc qspi0
        (0)     set qspi0 tweak
  5. 在正式版本的固件中,建议删除 bsp/examples/test-spinor/test_sfud.c bsp/examples/test-spinor/test_fal.c bsp/examples/test-spinand/test_mtd.c 文件中的 sffalmtd_nand 命令命令,防攻击者通过控制台读出 Flash 中的数据,否则可跳过此步。

Edit online

固件签名加密

SDK/target/<SoC>/<board>/pack/image_cfg.json 文件中进行配置并生成签名加密固件,例如 SDK/target/d12x/demo68-nor/pack/image_cfg.json

  1. 对组件进行加密

    image_cfg.json 的 “temporary” 或 “pre-process” 对象的最后,添加 “data_crypt” 对象配置。

    此处使用的 AES 加密密钥,即为生成密钥章节 中SDK/target/<soc>/<board>/pack/keys/ 文件目录生成的密钥。

    在下列示例中,配置了一组需要使用 “data_crypt” 工具进行加密的组件,其中生成 bootloader.aic.enc 组件的配置参数为:
    • algo: 加密的算法

    • file: 加密的源文件,此处为前面生成的 bootloader.aic 文件

    • key: 使用的加密密钥

    • tweak: 该值不需要配置,保持为 0 即可

    {
        "spi-nor": {
            ...
        },
        "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"
            "data_crypt": {
                "bootloader.aic.enc": { //加密 bootloader 分区
                    "algo": "spienc-aes-128-ecb",
                    "file": "bootloader.aic", // File to be encrypted
                    "key": "keys/spi_aes.key", // Keys the same in eFuse
                    "tweak": "0",
                },
                "env.bin.enc": { //加密 env 分区,备份分区使用同一个组件
                    "algo": "spienc-aes-128-ecb",
                    "file": "env.bin", // File to be encrypted
                    "key": "keys/spi_aes.key", // Keys the same in eFuse
                    "tweak": "0",
                },
                "d12x_os.itb.enc": { //加密 os 分区
                    "algo": "spienc-aes-128-ecb",
                    "file": "d12x_os.itb", // File to be encrypted
                    "key": "keys/spi_aes.key", // Keys the same in eFuse
                    "tweak": "0",
                },
                "rodata.fatfs.enc": { //加密 rodata 分区
                    "algo": "spienc-aes-128-ecb",
                    "file": rodata.fatfs", // File to be encrypted
                    "key": "keys/spi_aes.key", // Keys the same in eFuse
                    "tweak": "0",
                },
                "data.lfs.enc": { //加密 data 分区
                    "algo": "spienc-aes-128-ecb",
                    "file": "data.lfs.itb", // File to be encrypted
                    "key": "keys/spi_aes.key", // Keys the same in eFuse
                    "tweak": "0",
                },
                ...
            },
        },
    }

    对于一个或者多个需要进行加密的组件,都应按照上述方式进行配置。

    mk_image.py 工具在读取 image_cfg.json 文件时,逐个处理放在 “data_crypt” 中的配置,生成对应的加密组件,然后再进行打包。

    重要:

    “data_crypt” 字段应放在 “temporary”/”pre-process” 的最后,因为处理 “data_crypt” 时,可能需要依赖前面配置生成的文件,比如 ”aicimage”。

  2. 配置烧录加密组件

    image_cfg.json 中配置下列参数,打包加密组件,以适配烧录加密固件的要求:

    1. updater 中打包的程序,应为非加密程序

      updater 中配置的参数,都不是 .enc 结尾的组件

    2. target 中打包的程序和数据,应为加密后的程序

      target 中配置的参数,都是 .enc 结尾的组件

    这是因为:
    • SD 卡启动时,首先运行 updater 中的程序,进入烧录模式。此时由于数据是从 SD 卡加载的,不能为加密程序,否则无法正常执行

    • target 中打包的程序是要烧录到 Flash 的数据,如果不加密,则无法起到保护的作用,因此需要打包加密后的组件

    { "image": {
            ...
        },
        "info": { // Header information about image
            ...
        },
        "updater": { // Image writer which is downloaded to RAM by USB/UART
            "ddr": {
                "file": "usbupg-ddr-init.aic",
                "attr": ["required", "run"],
                "ram": "0x00103000"
            },
            "spl": {
                "file": "bootloader.aic",
                "attr": ["required", "run"],
                "ram": "0x41000000"
            },
        },
        "target": { // Image components which will be burn to device's partitions
            "spl": {
                "file": "bootloader.aic.enc", //使用 enc 结尾的加密组件
                "attr": ["mtd", "required"],
                "part": ["spl"]
            },
            "os": {
                "file": "d21x_os.itb.enc",   //使用 enc 结尾的加密组件
                "attr": ["mtd", "required"],
                "part": ["os"]
            },
            "rodata": {
                "file": "rodata.fatfs.enc", //使用 enc 结尾的加密组件
                "attr": ["mtd", "optional"],
                "part": ["rodata"]
            },
            "data": {
                "file": "data.fatfs.enc", //使用 enc 结尾的加密组件
                "attr": ["mtd", "optional"],
                "part": ["data"]
            },
        },
        "pre-process": { // before v1.0.6 is the name "temporary"
            ...
        },
    }
Edit online

UART 烧录

直接使用编译生成的 img 文件进行量产烧录即可。

Edit online

SD 卡量产

  • 标准方式

    编译生成下列文件后,将其复制到 SD 卡 FAT32 文件系统的根目录中,等待平台重新上电即可进入烧录:

    • bootcfg.txt

    • 打包后的 img 文件,例如 d12x_demo68-nor_v1.0.0.img

  • Direct Mode

    修改 bootcfg.txt,并且将 bootcfg.txt 和使用到的组件复制到 SD 卡 FAT32 文件系统的根目录,等待平台重新上电即可进入烧录模式。

    bootcfg.txt 示例:

    boot0=bootloader.aic
    writetype=spi-nand
    writeintf=0
    write0=bootloader.aic.enc
    write1=d21x_os.itb.enc,0x40000
    write2=rodata.fatfs.enc,0x240000
    write3=data.fatfs.enc,0x840000
重要:

在修改 bootcfg.txt 文件后,确保使用 UNIX 格式的换行符,而非 DOS 格式的换行符,即 ‘\n’ 换行,而非 ‘\r\n’ 换行。

Edit online

eFuse 与固件合并烧录

将 eFuse 和量产固件分开可以放松对固件的管控,但需要进行两次烧录,同时 eFuse 的固件和量产固件必须要匹配才能生产。为了降低使用难度,可以把 eFuse 和量产固件的烧录合并到同一个固件中进行,但因为该固件包含了要烧录的 Key,需要对该固件进行保护。

实现方法是通过在 eFuse 烧录完成后继续运行,烧录固件,需要配置 BootLoader 宏 AIC_SID_CONTINUE_BOOT_BURN_AFTER

  1. 进入 SDK 根目录:
    cd <SDK_ROOT>
  2. 在 SDK 根目录中执行下列命令:
    scons --apply-def=d12x_demo68-nor_baremetal_bootloader_defconfig
  3. 打开 BootLoader 的 menuconfig 菜单:
    scons --menuconfig
  4. 在配置界面,勾选或确认已勾选下列参数:
    AIC_USING_SID
    EFUSE_WRITE_SUPPORT
    AIC_SID_BURN_SPIENC_KEY_TEST
    AIC_SID_CONTINUE_BOOT_BURN_AFTER
    
    AIC_USING_SPIENC
    AICUPG_FIRMWARE_SECURITY
    参数对应的配置界面如下:
    Board options  --->
        [*] Using Spienc
        [*]   Enc qspi0
        (0)     set qspi0 tweak
        [*] Using Efuse/SID
            SID Parameter  --->
                [*] support efuse write
                (64) set efuse max word
    Bootloader options  --->
        [*] Upgrading  --->
            [*]   Secure transfer firmware and burn
    Drivers options  --->
        Drivers examples  --->
           [*] Enable SID burn spienc key command
           [*]   Enable SID continue to boot after burning