4. Secure DFU核心概念

Secure DFU 与早期 Legacy DFU 最大的区别,是把“升级包”拆成了两个在语义上完全不同的对象:Init Packet 和 Firmware Image。前者描述“这次升级是什么、能不能升”,后者才是真正要写进 Flash 的固件数据。理解这两个对象以及它们携带的签名、CRC、版本信息,是排查 DFU 失败、设计自定义升级流程的基础。

Init Packet:升级的“元数据信封”

Init Packet 是一个独立的小数据包,在固件数据真正传输之前先发送给 Bootloader。它不包含可执行代码,而是携带本次升级的元信息,典型字段包括:

  • 固件类型:SoftDevice、Bootloader、Application 或它们的组合。
  • 固件大小:Firmware Image 的字节数,用于后续接收校验。
  • 固件版本:如 0x00000002,用于版本比较。
  • CRC32:对 Firmware Image 整体计算的校验值。
  • 签名:对上述字段的 ECDSA(P-256)签名,Secure DFU 的核心安全凭证。

Bootloader 收到 Init Packet 后,会先用公钥验证签名,再检查版本、CRC 等条件。只有 Init Packet 校验通过,Bootloader 才会进入接收 Firmware Image 的状态。因此 Init Packet 相当于“先验票、后入场”。

Firmware Image:真正写入 Flash 的数据

Firmware Image 是编译产物经过打包后的二进制数据,通常由 nrfutil pkg generate 生成,内部包含固件本体以及必要的元数据。它被分片传输,每片写入 Flash 的升级区。传输完成后,Bootloader 会重新计算整包 CRC32,并与 Init Packet 中声明的 CRC 比对,一致才认为数据完整。

签名、CRC 与版本校验的分工

这三者常被混为一谈,但职责完全不同:

机制作用失败后果
ECDSA 签名证明升级包来源可信、未被篡改拒绝升级,返回签名错误
CRC32检测传输或存储过程中的数据损坏拒绝激活,返回 CRC 错误
版本校验防止降级或重复升级拒绝升级,返回版本错误

签名解决“是不是我信任的人给的”,CRC 解决“数据有没有坏”,版本解决“该不该升”。三者缺一不可。

升级状态机

Secure DFU 在 Bootloader 内部维护一个状态机,典型状态迁移如下:

IDLE
  │ 收到 Init Packet
  ▼
VALIDATING_INIT
  │ 签名/版本/CRC 校验通过
  ▼
RECEIVING_IMAGE
  │ 分片接收完成
  ▼
VALIDATING_IMAGE
  │ 整包 CRC 通过
  ▼
ACTIVATING
  │ 写入目标区域并复位
  ▼
DONE / 回到 IDLE

任一校验失败,状态机会回到 IDLE 并返回对应错误码,例如 NRF_DFU_ERR_EXT_API、NRF_DFU_ERR_CRC、NRF_DFU_ERR_SIGNATURE。理解状态机有助于定位问题发生在“验票”还是“入场”阶段。

Secure DFU 升级状态机与校验链 Init Packet • 固件类型 / 大小 • 版本号 • CRC32 • ECDSA 签名 Firmware Image • 固件二进制数据 • 分片传输 • 写入升级区 • 整包 CRC 校验 IDLE VALIDATING_INIT RECEIVING_IMAGE VALIDATING_IMAGE ACTIVATING → DONE 发送 分片传输 签名/版本失败 CRC 失败 校验失败返回 NRF_DFU_ERR_SIGNATURE NRF_DFU_ERR_CRC NRF_DFU_ERR_EXT_API → 回到 IDLE 校验通过条件 签名验证 OK 版本 ≥ 当前版本 CRC32 一致 → 进入下一状态

实际打包与校验示例

以 nRF5 SDK 为例,生成一个带签名的升级包通常使用如下命令(参数为示例值):

nrfutil pkg generate \
  --hw-version 52 \
  --sd-req 0x00B6 \
  --application-version 2 \
  --application app.hex \
  --key-file private.pem \
  app_dfu_package.zip

其中 --application-version 2 会写入 Init Packet 的版本字段,--key-file 用于生成 ECDSA 签名。Bootloader 端使用对应的公钥(烧录在 Bootloader 中)验证签名。若版本号小于等于当前固件版本,Bootloader 会拒绝升级并返回版本错误。

提示:CRC32 是对 Firmware Image 整体计算的,不是对 Init Packet 计算。签名则覆盖 Init Packet 中的关键字段。两者校验对象不同,排查时不要混淆。
注意:私钥 private.pem 一旦丢失,已烧录对应公钥的设备将无法再接受新固件升级。生产环境应妥善备份密钥,并避免在多个产品线复用同一密钥。

常见错误与定位思路

  • 签名错误:检查私钥与设备公钥是否匹配,确认 --key-file 正确。
  • CRC 错误:检查传输链路是否丢包,确认 Firmware Image 未被截断或修改。
  • 版本错误:确认 --application-version 大于设备当前版本。
  • 状态机卡在 VALIDATING_INIT:多为 Init Packet 字段不完整或签名格式错误。

掌握 Init Packet、Firmware Image、签名、CRC、版本校验与状态机之间的关系,就能在 DFU 失败时快速判断问题出在“验票”还是“入场”,而不是盲目重试。