2、基础知识:固件、模型更新包、签名、哈希、完整性验证等核心概念解析
TinyML 设备的安全更新并非简单的“文件拷贝”,而是一条由多个密码学原语和工程组件构成的信任链。在深入签名方案之前,我们必须先厘清五个核心概念:固件(Firmware)、模型更新包(Model Update Package)、哈希(Hash)、签名(Signature)与完整性验证(Integrity Verification)。它们之间的关系决定了整个更新流程的安全边界。下图展示了这些概念在一条典型更新链路中的位置与交互关系:
上图展示了从构建到部署的完整信任链路。下面我们逐一拆解每个概念的本质、作用与工程实现要点。
2.1 固件与模型更新包:从“二进制”到“可交付单元”
固件(Firmware)是烧录在 MCU 非易失存储器(如 Flash)中的底层软件,负责硬件初始化、调度、外设驱动与运行时环境。在 TinyML 场景中,固件通常包含推理引擎(如 TensorFlow Lite Micro)、设备管理逻辑和引导加载程序(Bootloader)。固件的任何缺陷都可能导致设备变砖,因此其更新必须经过严格验证。
模型更新包(Model Update Package)则是一个更广义的概念——它不仅仅是模型权重文件,而是一个自包含的交付单元,通常包含以下内容:
- 模型文件:量化后的 TFLite 或 ONNX 格式权重,可能经过压缩或加密。
- 元数据:版本号、目标硬件平台、输入/输出张量形状、量化参数、兼容的固件版本范围。
- 校验信息:哈希值、签名值、时间戳或防重放随机数(Nonce)。
- 可选附加资源:标签映射文件、预处理配置、回调脚本等。
将固件与模型分开管理是 TinyML 的常见实践。固件更新频率低(数月一次),而模型更新频率高(每周甚至每天)。因此,模型更新包的设计应轻量化,避免携带完整固件,以减少传输带宽和 Flash 占用。
2.2 哈希(Hash):数据完整性的“指纹”
哈希函数(如 SHA-256)将任意长度的数据映射为固定长度的摘要(32 字节)。其核心特性包括:
- 单向性:从哈希值无法反推原始数据。
- 抗碰撞性:找到两个不同输入产生相同哈希值在计算上不可行。
- 雪崩效应:输入数据微变,哈希值完全改变。
在更新包验证中,哈希用于检测传输错误或恶意篡改。设备端重新计算接收到的包哈希,与发送方提供的哈希比对。若不一致,则说明数据在传输过程中被修改或损坏。
2.3 签名(Signature):数据来源的“身份证明”
数字签名基于非对称加密(如 ECDSA、RSA)。签名过程如下:
- 发送方使用私钥对更新包的哈希值进行签名运算,生成签名值。
- 接收方使用公钥验证签名是否有效。
签名提供了两个关键保证:
- 真实性(Authentication):只有持有私钥的合法发布者才能生成有效签名。
- 不可否认性(Non-repudiation):发布者无法否认曾发布该更新包。
在 TinyML 设备中,公钥通常在出厂时烧录到设备的 OTP(一次性可编程)存储器或安全元件中。私钥则保存在云端签名服务器或离线 HSM(硬件安全模块)中,绝不进入设备。
2.4 完整性验证:从“信任根”到“应用加载”
完整性验证不是单一操作,而是一条信任链。设备启动时,引导加载程序(Bootloader)是第一个信任根。它验证固件镜像的签名,固件再验证模型更新包的签名。每一级验证都建立在前一级可信的基础上。
完整的验证流程通常包含以下步骤:
| 步骤 | 操作 | 输入 | 输出/决策 |
|---|---|---|---|
| 1 | 接收更新包 | 包 + 签名 + 哈希 | 暂存于 RAM 或临时分区 |
| 2 | 计算本地哈希 | 更新包数据 | 本地哈希 H' |
| 3 | 验证签名 | 签名值 + 公钥 + 发送方哈希 H | 签名有效 / 无效 |
| 4 | 比对哈希 | H' 与 H | 一致 / 不一致 |
| 5 | 版本与兼容性检查 | 元数据(版本号、固件版本范围) | 允许更新 / 拒绝 |
| 6 | 写入目标分区 | 验证通过的更新包 | 更新 Flash 并触发重启 |
注意,步骤 3 和 4 的顺序不能颠倒。必须先验证签名(确认哈希 H 来自可信方),再比对哈希(确认数据与 H 一致)。如果先比对哈希,攻击者可以同时替换数据和哈希值。
2.5 案例拆解:一个智能传感器节点的模型更新失败排查
某工业 IoT 项目部署了 500 个基于 STM32L4 的振动传感器节点,通过 BLE 接收模型更新包。某次更新后,部分节点出现推理结果异常,但更新流程显示“成功”。
初始现象:约 12% 的节点在更新后输出错误分类结果,其余节点正常。更新日志显示所有节点均通过了签名验证和哈希比对。
关键输入数据:
- 更新包大小:48,312 字节
- SHA-256 哈希:
a3f2...9c1d(前 4 字节与后 4 字节) - 签名算法:ECDSA P-256
- 固件版本:v2.1.0(支持模型版本 ≥ 3.0)
- 模型版本:v3.2(元数据中声明兼容固件 v2.1.0+)
分析判断过程:
- 签名验证通过,说明包确实来自官方私钥,排除恶意注入。
- 哈希比对通过,说明传输无损坏。
- 异常节点与正常节点的唯一区别是:异常节点的 Flash 剩余空间小于 4 KB,而正常节点均大于 8 KB。
- 进一步检查发现,异常节点在写入模型时,由于 Flash 空间不足,部分模型文件被截断,但截断发生在哈希计算之后——即设备先验证了完整包,再写入时因空间不足导致写入不完整。
实际操作步骤:
- 在固件中增加写入前空间检查:
if (free_flash < package_size + 4KB) reject_update(); - 增加写入后回读校验:写入完成后,重新计算 Flash 中模型数据的哈希,与发送方哈希比对。
- 在元数据中增加
min_free_flash字段,由构建工具自动计算并填充。
结果对比:修复后,连续 3 次更新(共 1500 节点次)均无异常,回读校验捕获了 2 次因 Flash 磨损导致的写入错误。
风险与代价:回读校验增加了约 200ms 的更新时间,但对于安全性要求高的工业场景可接受。空间检查避免了“验证通过但写入失败”的假成功状态。
复盘经验:完整性验证不能止步于“传输层”,必须延伸到“存储层”。哈希比对的是内存中的临时数据,而真正运行的是 Flash 中的持久数据。两者之间可能存在差异(写入截断、位翻转、磨损)。
2.6 常见误区与总结
| 误区 | 后果 | 正确做法 |
|---|---|---|
| 只做哈希校验,不做签名验证 | 攻击者可同时替换包和哈希值 | 签名 + 哈希双重验证 |
| 签名验证通过后跳过哈希比对 | 签名只保护哈希,不保护数据本身 | 验签后必须重新计算本地哈希并比对 |
| 将公钥存储在可写 Flash 中 | 攻击者可替换公钥,伪造任意签名 | 公钥烧录于 OTP 或安全元件 |
| 验证通过后立即写入,无回读校验 | 写入失败或截断导致运行异常 | 写入后回读哈希比对 |
| 忽略版本兼容性检查 | 新模型与旧固件不兼容,推理崩溃 | 元数据中声明兼容范围,设备端强制检查 |