为了安全的额外设计
我们要让一般的固件更新变成安全的固件更新。体现在:
a. 固件是保密的
b. 固件是完整的
c. 固件是可靠的
固件的保密,在传输过程中是通过加解密技术保证的;在设备更新过程中的保密,则是通过STM32 安全启动所提供的安全机制来保证。
而固件的完整性,传输过程中的完整性则通过加密技术中的哈希函数 SHA256 或者 AESGCM 来保证。当然 ,单独谈完整性,在有攻击的情况下 ,是没有意义的 。哈希函数一般都是结合加密技术来保证完整性。在安全启动里,我们需要验证该固件是否可靠。在那时 ,我们需要重新计算该哈希值或者认证码,我们要进行比较,只有完全相等,才认为该固件没有被破坏。
固件的来源可靠性,则通过加密技术中的认证函数,或者是数字签名技术 ECDSA 或者是认证码 AES-GCM。
总结一下 ,如果我们需要让固件在更新过程是保密、完整、可靠的目标,我们要在流程上增加额外的环节,例如增加加密与解密的阶段。增加的环节会影响到传输过程中的设计选择,同时数据结构设计时需要增加额外的字段。
安全固件更新的额外流程
① 固件的加密与解密
我们要在发布之前对固件采用 AES 进行加密。使用的加密密钥应该和预埋在安全启动部分的密钥一样。对称密钥要进行保密。我们需要让安全启动保证对称密钥的保密、完整、可靠三个属性的保护。
相应的,在收到固件之后,需要对固件进行解密。STM32 SBSFU 的双镜像解密操作时是发生在启动时,而不是固件下载时。这种设计是为了双镜像的数据交换 。Flash 运行的代码不能是正在改写的的代码,而启动时两个镜像都没有在使用 。
② 固件的认证与验证
我们还要对固件进行签名或者认证。如果采用非对称密钥技术,则需要将公钥预埋在安全启动部分。由安全启动对该公钥进行完整可靠性的保证。相应的,在通过通讯接口接收到需要更新的固件,安全启动要校验签名或者使用对称密钥的认证码进行比对。
③ 固件版本的检查
针对可能的版本攻击,我们需要在安全启动的安全数据区,要保存软件的正确版本,我们要确保版本总是向前增长,而不是向后减小。
固件的传输选择
我们使用STM32 来构造安全固件更新,那么我们希望在数据链路上的传输的固件是加密的。
我们前面提到我们有两种固件更新的流程,一种是近距离通过UART 或者STLink 更新,一种是通过云端来更新固件。对于保密性,我们强调的是端到端的安全。除非我们非常信任第三方平台,否则,固件的加密应该是整体加密。也就是说,即使有了中间结点 IoT 平台,固件更新可以从起点就开始加密,同时不需要在中间结点解密。
考虑到安全,那么CRC32 是可以被加密级别函数所取代,所以 CRC32 校验和可以被加密级的哈希函数以及认证码取代。
当然,这种情况下,差分更新会受到影响。如果需要差分更新,则需要仔细评估整个更新链条的安全性。
对于压缩,因为 从 服务器端 传送 至设备端,可以认为风险低 。
固件的存储
考虑到一种整体安全的情况下,这也是我们STM32 客户所提到的一种案例,假设服务器被攻破了,那么所有的固件更新操作都可能是错误,推荐用户加入冗余。例如服务器会让 MCU 擦除掉 MCU 的固件。这个时候 MCU 作为一个被动执行者,不可能不执行。如果MCU 只保存一份正确的固件,那么下次重新启动时候,可能 MCU 就无法运行了。如果我们已经考虑到冗余,我们需要在MCU 里总是保存一份正确的固件代码。使用 IWDG 来判断启动时是否有正确的固件执行,如果没有,则总是从保存的正确的默认固件里启动。
安全固件更新的数据结构
在传输的数据结构中,增加了随机数字段,这是加解密算法所需要的字段;增加了哈希字段,这就是为了检查固件的完整性;增加了签名字段,这是为了检查的固件的可靠性。
在启动数据区中,增加了固件的版本,用户的密钥信息。