4. 固件安全更新(OTA)

OTA更新,说白了就是给设备“打补丁”。但Matter设备不像手机,你没法插USB线刷机。一旦OTA流程有漏洞,整栋楼的设备都可能被攻破。我参与过几个智能家居项目,说实话,OTA这块是最容易出“幺蛾子”的环节。

4.1 OTA更新的安全流程

Matter的OTA流程,核心就三个角色:OTA Provider(提供固件的设备,比如网关)、OTA Requestor(请求更新的设备,比如灯泡)、DCL(分布式合规数据库,用来校验固件来源)。

整个流程我习惯分成四个阶段:

  1. 发现阶段:Requestor通过BDX协议找到Provider,询问“有没有新固件?”
  2. 查询阶段:Provider返回固件版本、大小、摘要等信息。Requestor判断是否需要更新。
  3. 下载阶段:通过BDX块传输协议,分块下载固件。每块都有校验。
  4. 应用阶段:下载完成后,验证签名,写入Flash,重启。

关键点:Matter规定,OTA过程必须使用TLS加密通道。也就是说,固件在传输过程中是加密的。但注意,加密只防窃听,不防篡改——签名验证才是最后一道防线。

我在项目中遇到过一个问题:设备在下载过程中突然断电,重启后固件只写了一半。嗯,这里就要用到“双备份”策略了。

4.2 签名验证机制

签名验证,说白了就是确认“这个固件确实是厂家发的,没人动过手脚”。Matter用的是ECDSA(椭圆曲线数字签名算法),曲线是secp256r1(也就是P-256)。

验证流程是这样的:

  1. 固件包包含:固件数据 + 签名值 + 签名者证书链。
  2. 设备用DCL根证书验证签名者证书链的合法性。
  3. 用签名者公钥验证固件数据的签名。
  4. 全部通过,才允许更新。

你想想看,如果证书链验证失败,说明这个固件来源不可信。我曾经见过一个案例:某厂商把根证书硬编码在代码里,结果根证书过期了,所有设备都无法OTA——这就是典型的“证书生命周期管理”没做好。

我的建议:签名验证一定要在RAM里做,不要在Flash里做。为什么?因为Flash可能已经被损坏了。先把固件下载到RAM,验证通过再写入Flash,这样更安全。

下面是一个简化的签名验证流程:

// 伪代码:固件签名验证
bool verify_ota_firmware(uint8_t* firmware, size_t len, 
                         uint8_t* signature, uint8_t* cert_chain) {
    // 1. 验证证书链
    if (!verify_cert_chain(cert_chain, dcl_root_cert)) {
        return false;  // 证书链不合法
    }
    
    // 2. 提取签名者公钥
    public_key_t* pub_key = extract_pub_key(cert_chain);
    
    // 3. 验证固件签名
    if (!ecdsa_verify(firmware, len, signature, pub_key)) {
        return false;  // 签名不匹配
    }
    
    return true;
}

4.3 回滚保护策略

回滚攻击,是OTA里最阴险的招数。攻击者拿到一个旧版本固件(可能有已知漏洞),强制设备降级到那个版本。你想想看,你辛辛苦苦修了漏洞,结果人家一个OTA又给你降回去了。

Matter怎么防回滚?核心机制是版本号比较。

策略 说明 我踩过的坑
版本号递增 固件版本号必须单调递增,不能回退 版本号用uint32,但有人用字符串比较,结果"9" > "10"
安全版本号 Matter定义的Cluster Revision,存储在不可篡改区域 有人把版本号存在普通Flash里,一擦就没了
熔丝/OTP 硬件级别的防回滚,版本号写入后不可更改 成本高,但最安全。我建议关键设备用这个

警告:千万不要把版本号存在可被用户随意擦写的区域。我曾经见过一个产品,版本号存在配置文件里,用户恢复出厂设置后版本号归零,然后就能刷回旧固件了——这等于没防。

具体实现时,我习惯这样做:

  1. 设备启动时,从安全存储区(比如eFuse或受保护的Flash页)读取当前版本号。
  2. OTA下载完成后,比较新固件的版本号是否大于当前版本号。
  3. 如果小于或等于,直接拒绝更新。
  4. 更新成功后,将新版本号写入安全存储区。

这里有个细节:版本号比较要用无符号整数,别用有符号的。为什么?因为版本号可能很大(比如0xFFFFFFFF),有符号比较会出问题。

4.4 整体流程示意图

下面这张图,是我自己总结的OTA安全更新全流程。你看一眼,基本就明白整个链路了。

Matter OTA安全更新流程 1. 发现阶段 2. 查询阶段 3. 下载阶段 BDX协议握手 版本号比较 TLS加密传输 4. 签名验证 验证证书链 ECDSA签名校验 版本号防回滚 5. 应用更新 注意:所有步骤均在安全环境中执行,失败则回滚到旧版本

4.5 避坑指南

最后,我把自己踩过的坑整理一下,你写代码时注意避开:

  • 签名验证不能跳过:哪怕是在开发阶段,也要走完整验证流程。我见过有人为了调试方便,把验证逻辑注释掉了,结果发布时忘了打开——那批设备全成了“裸奔”状态。
  • 版本号用uint32,别用字符串:字符串比较“9”和“10”会出问题。用整数,简单粗暴。
  • 双备份区要足够大:至少能容纳一个完整固件。我见过有人只留了50%空间,结果新固件比旧固件大,更新失败。
  • OTA失败要有重试机制:网络不稳定是常态。建议重试3次,每次间隔递增(比如1分钟、5分钟、15分钟)。
  • 日志要记录OTA过程:出问题时,日志是唯一的线索。记录版本号、签名结果、失败原因。

小技巧:OTA完成后,建议做一次固件完整性校验。用SHA-256算一遍整个Flash的摘要,和预期值对比。这样能发现Flash写入过程中是否出现了位翻转。

嗯,OTA安全更新这块,说白了就是“传输加密、签名验证、版本防回滚”三件套。每件都做到位,基本就稳了。但别忘了,安全是一个链条,OTA只是其中一环——下一环是什么?你自己琢磨琢磨。