Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

安全启动与签名

这一篇解释一个让很多人第一次成功烧片时困惑的事实:为什么一个签名字段全是零的“假“镜像, 居然能在真硅片上启动? 答案是——开发片的 secure boot 是关掉的。理解这一点, 既能让你明白当前开发流为什么这么顺,也能让你清楚这条流绝不是生产流、以及生产要补什么。 镜像头里签名/哈希字段的精确布局见 应用镜像格式与签名; 本篇讲“为什么“。

secure boot 想解决的问题

安全启动要回答一个问题:flashboot 凭什么相信它即将跳进去的那段 app 是可信的、 没被篡改的? 在一个开了 secure boot 的设备上,flashboot 在跳转前会做两件事:

  1. 真实性验签——用固化在 efuse 里的根密钥,对镜像头里的 ECC(bp256)/ SM2 数字签名做验证。只有用对应私钥签过的镜像才通过。攻击者就算能写 flash, 也伪造不出一个能通过验签的签名(他没有私钥)。
  2. 完整性校验——对 app body 算哈希,和签过名的头里的哈希比对,确保 body 没被改。

关键在第 1 点:真实性靠的是“攻击者拿不到私钥“,这是密码学保证。光有完整性(哈希) 是不够的——因为哈希就在镜像头里,能写 flash 的人改完 body 重算哈希写回头部即可绕过。 没有验签的“安全启动“根本不安全

开发片为什么把它关掉

WS63 用一个 efuse 位 SEC_VERIFY_ENABLE 控制是否启用安全启动。 在开发片上这个 efuse 位是 0,意味着:

flashboot 跳过 ECC/SM2 签名验签,但仍然校验 body 的 SHA-256 哈希,校验通过才跳进 app。

于是引导链对镜像头的签名字段不做检查——但哈希字段照样比对。这就是为什么——

  • 签名区全零的镜像能启动,但 body 哈希必须是真的:打包时签名区填的是全零的占位符 (dummy zero signature),flashboot 既然不验签,全零的签名照样放行;但镜像头里的 body SHA-256 哈希必须算对——secure-off 只跳过 ECC 签名,不跳过 hash,哈希对不上同样拒绝启动。 所以根本不存在某个“假签名“能让任意 body 启动;需要的是 0x300 头 + 真实 body SHA-256。 镜像头的结构当然也要正确(0x300 字节、KeyArea + CodeInfo、body 的 SHA-256 字段位置对——见 启动流程),只是签名的内容无所谓。
  • 整条开发流因此非常顺:WS63 走 route 2——靠 hisi-riscv-rt 的 boot-header feature 在 link 时把 0x300 头烤进 ELF,再用 hisi-fwpkg patch-hash <elf> 补上真实的 body SHA-256 (这一步不可省:secure-off 仍校验 hash,只跳过 ECC 签名),然后直接 probe-rs download <elf> / probe-rs run <elf> → reset,就启动了。没有中间 .img、 也没有 hisi-fwpkg image 这一步。不需要任何私钥、不需要厂商签名工具。 (BS21/BS2X 暂无 link-time 头,仍走 route 1hisi-fwpkg image -o app.img <elf> 后烧 .img。)

这是一个有意的、便利的开发态选择:开发片关掉验签,让 Rust 固件能自由迭代烧录, 不被“每次都得找厂商签名“卡住。

efuse 是一次性的——这件事的份量

要理解为什么“开发片关、量产片开“是条单向门,得知道 efuse 是一次性可编程(OTP)—— 熔丝只能从 0 烧成 1,烧了不可逆。所以:

  • 开发片出厂时 SEC_VERIFY_ENABLE == 0,安全启动关;
  • 一旦在量产环节把这个位烧成 1,这颗片子就永久进入“必须验签才启动“的状态, 回不去了。从那以后,只有用对应私钥签过的镜像才能在它上面跑。

这也是为什么仓库里那个实验性 Rust flashboot 只做完整性校验、明确标注“非真实性验签“ (见 flashboot 深入文档):它对着同一份未签名的头里的哈希 比对——这在关掉 secure boot 的开发片上够用,但绝不是 secure boot,文档里如实写明了 这一点,不假装安全。

真正签名需要什么

如果要把固件跑在一颗开了 secure boot 的量产片上,dummy 全零签名就过不了了,需要:

  • 厂商闭源的 sign_tool——用真正的私钥对镜像生成 ECC-bp256 / SM2 签名,填进 KeyArea。 这个工具是厂商闭源的,不在本仓库、也不可能在本仓库重写(重写一个签名工具没有意义—— 没有对应的、烧进 efuse 的根密钥,签出来的东西在那颗片子上也过不了)。
  • 因此生产推荐复用 fbb_ws63 原厂 flashboot 的完整签名/打包/烧录流程——它有真实验签、 A/B 槽、FOTA、解压、flash 在线加密。本仓库的 Rust 应用在生产里应作为被原厂 flashboot 加载的 app 镜像,按原厂流程签名后烧到 app 分区。

安全含义:别把开发流当生产流

把上面拼起来,要诚实说清楚的边界是:

  • 当前这套 Rust 烧录流是 DEV 流——它工作,是因为开发片关掉了验签。它不提供任何 真实性保证:能写到这片 flash 的人就能换掉你的固件。
  • 在开发/评估阶段这完全没问题——你要的是迭代速度,不是抗篡改。
  • 不要把“全零签名也能启动“误读成“WS63 的安全启动是摆设“。恰恰相反: 量产片烧了 SEC_VERIFY_ENABLE 之后,验签是真的、靠 efuse 根密钥 + 私钥签名, 全零镜像会被拒绝。是开发片主动关掉了它,不是它不存在。

一句话:当前的便利来自一个被有意关掉的安全特性;上生产就得把它打开,并接入厂商签名链。 两者不能混为一谈。镜像头里每个字段(结构版本、签名长度、code_area_lencode_area_hash……)的确切位置见 应用镜像格式与签名