Edit online

防抄板-SPIENC-D12x

6 Dec 2024
Read time: 9 minute(s)

应用场景

本方案针对下列使用场景:

  • 方案商提供主控芯片和开发好的固件给第三方生产商 生产, 方案商对自己的固件进行保护

  • 方案商开发了包含某一功能的固件

  • 生产商不进行开发,而使用方案商提供的固件

  • 方案商为了保护自己的固件,会要求自己的固件只能在方案商授权的主控芯片上运行

  • 他人不能通过拷贝 SPI NOR 上的固件在不经 方案商 授权的主控芯片上运行

方案介绍

本方案通过使用 AIC 主控的 SPIENC 总线加密功能以及安全启动功能来实现防抄板,结合实际使用的需求,提供对应的软件方案。

SPIENC 总线加密

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

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

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

  • 提供对应的加密固件。

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

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

开启防抄板功能

如需开启防抄版功能,执行下列步骤:

  1. 编译一个烧录 eFuse 的 BootLoader,该固件只完成对出货的芯片烧录相关的 eFuse 和密钥,并使能 SPIENC 和安全启动功能

    通过运行特定 eFuse 烧录程序,对芯片进行 eFuse 烧录。
    1. 通过修改 BootLoader 的代码,将烧录 eFuse 的程序集成到 BootLoader 中。

    2. 编译生成烧录 eFuse 专用的固件。

    3. 上电刷机, BootLoader 程序会仅烧录对应的 eFuse 域成功后退出。

    4. 可以用 AiBurn 刷机,也可以用 SD 卡等存储介质刷机。

  2. 编译一个进行了加密的量产固件,该量产固件可以发放给生产商。

  3. 生产商使用方案商提供的主控进行生产,烧录方案商提供的固件。

生成 eFuse 烧录固件

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

本节以 d12x_demo66-nor 开发板为例,描述了生成 eFuse 烧录程序的详细流程。在示例方案中,提供了下列用于生成密钥的脚本:
  • lite/target/d12x/demo68-nor/pack/keys/gen_spienc_key.sh

执行下列步骤,可以生成 eFuse 烧录程序:
  1. 生成密钥
    根据运行环境执行对应命令,运行生成密钥的脚本:
    • Linux 环境下:
      1. 确保已经安装 OpenSSL。如未安装,可执行以下命令进行安装:

        sudo apt-get install openssl
      2. 使用下列命令运行脚本生成所需的密钥文件和头文件:
        cd SDK_ROOT/lite/target/d12x/demo66-nor/pack/keys/
        ./gen_spienc_key.sh
        生成的文件如下所示:
        • AES 密钥 spi_aes.key

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

      3. spi_aes_key.h 文件复制粘贴至 lite/bsp/examples_bare/test-efuse/ 目录中,供编译烧录 eFuse 的程序时使用。

        spi_aes.key 则保留在 lite/target/d12x/demo66-nor/pack/keys/,在 mk_image.py 生成加密固件时使用。

        重要: 生成的密钥请妥善保管,以免丢失或者泄露。
    • Windows 环境 下:

      1. 运行脚本生成一个 AES 密钥 spi_aes.key,并且生成对应的 C 语言头文件 spi_aes_key.h
        cd SDK_ROOT/lite/target/d12x/demo66-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 的程序时使用。

        重要: 生成的密钥请妥善保管,以免丢失或者泄露。
      2. 将下列文件复制到 Windows 的 SDK 目录:
        • SDK/target/d12x/demo66-nor/pack/keys/ 复制到 Window SDK 对应目录中。

        • keys 下的 spi_aes_key.h 文件复制粘贴至 SDK/bsp/examples_bare/test-efuse/spi_aes_key.h 目录中。

  2. 编译程序

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

    1. 应用 BootLoader 的配置:

      cd <SDK_ROOT>
      scons --apply-def d12x_demo66-nor_baremetal_bootloader_defconfig
    2. 打开 BootLoader 的 menuconfig 菜单:
      scons --menuconfig
      
    3. 勾选或确认已勾选下列选项:
      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
      
    4. 修改代码使能 SPIENC:

      • bsp/examples_bare/test-efuse/efuse_burn_spienc_key_cmd.c

        使能文件开头的 D12X_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");  // 加上此句
            ...
        }
    5. 执行下列命令编译程序 BootLoader:
      scons
    6. 编译程序 APP 并且生成烧录固件:
      scons --apply-def=d12x_demo66-nor_rt-thread_helloworld_defconfig
      
      scons
      

      编译结果保存在 SDK/output/d12x_demo66-nor_rt-thread_helloworld/images 目录中。

  3. AiBurn 卡烧录

    使用 AiBurn 烧录 outputd12x_demo68-nor_rt-thread_helloworldimagesd12x_demo68-nor_v1.0.0.img 固件。

  4. SD 卡烧录

    准备一张 SD 卡,确保该卡只有一个分区,并且格式化为 FAT32/ exFAT 文件系统。

    将编译输出目录下的文件复制到 SD 卡的根目录:

    • bootcfg.txt

    • bootloader.aic

    并且将 bootcfg.txt 中的内容修改为:

    boot0=bootloader.aic
    

    将该卡插到板卡中,上电运行,即可完成相关 eFuse 的烧录。

量产固件

编译加密的量产固件,分为几个步骤:
  1. Boot Loader 配置
    1. 进入 SDK 根目录:
      cd <SDK_ROOT>
    2. 在 SDK 根目录,执行下列命令:
      scons --apply-def=d12x_demo66-nor_baremetal_bootloader_defconfig
      
    3. 打开 Boot Loader 的 menuconfig 菜单:
      scons --menuconfig
      
    4. 勾选或确认已勾选下列选项:

      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 程序时的代码修改还原。

    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
      
  2. 应用程序配置
    1. 进入 SDK 根目录:
      cd <SDK_ROOT>
    2. 在 SDK 根目录,执行下列命令:
      scons --apply-def=d12x_demo66-nor_rt-thread_helloworld_defconfig
       
    3. 打开 Application 的 menuconfig 菜单:
      scons --menuconfig
    4. 勾选或确认已勾选下列选项:
      AIC_USING_SPIENC
      Board options  --->
      
          [*] Using Spienc
          [*]   Enc qspi0
          (0)     set qspi0 tweak
      
    5. 在正式版本的固件中,建议删除 kernel/rt-thread/components/drivers/spi/spi_flash_sfud.c中的 sf 命令,防攻击者通过控制台读出 Flash 中的数据。

      宏 RT_USING_FINSH 包住的内容:
      #if defined(RT_USING_FINSH)...#endif
  3. 固件签名加密

    配置 SDK/target/d12x_demo66-nor/pack/image_cfg.json 文件生成签名加密固件。

  4. 对组件进行加密

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

    此处使用到的 AES 加密密钥,即为 SDK/target/d12x/demo66-nor/pack/keys/ 文件目录中生成的密钥。

    {
        "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
                    "tweak": "0",
                },
                "d12x_os.itb.enc": {
                    "file": "d12x_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
                    "tweak": "0",
                },
                ...
            },
        },
    }

    上述配置中,配置了一组需要使用 “spienc” 工具进行加密的组件,其中 生成 bootloader.aic.enc 组件的配置参数为:

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

    • address: 是加密后的文件,存放在 Flash 的开始位置,这里应根据前面的分区表信息计算得到

    • key: 使用的加密密钥

    • nonce: 使用的加密 Nonce 值

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

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

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

    重要:

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

    配置加密时,address 需要填写正确,不然加密结果会不正确。

  5. 配置烧录加密组件

    image_cfg.json 中配置下列参数,打包加密组件:

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

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

    2. 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": "d12x_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 量产编译生成的固件 outputd12x_demo66-nor_rt-thread_helloworldimagesd12x_demo66-nor_v1.0.0.img

  • SD 卡量产方式
    • 标准方式

      bootcfg.txt + 打包后的镜像文件,如d12x_demo66-nor_v1.0.0.img

      此方式只需要将编译生成的下列文件复制到 SD 卡 FAT32 文件系统的根目录,平台重新上电即可进入烧录。

      • bootcfg.txt

      • d12x_demo66-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=d12x_os.itb.enc,0x40000
      write2=rodata.fatfs.enc,0x240000
      write3=data.fatfs.enc,0x840000
      
      重要:

      bootcfg.txt 在修改后,需要确保换行符为 UNIX 格式的换行符,非 DOS 格式的换行符,即 ‘n’ 换行,非 ‘rn’ 换行。