3、哈希算法基础:SHA-256算法原理、哈希值的特性(单向性、抗碰撞性)、为什么BootROM选择SHA-256

聊到BootROM镜像校验,哈希算法是绕不开的核心。说白了,它就是一把“数字指纹锁”。你想想看,一个镜像文件动辄几兆甚至几十兆,BootROM那点可怜的存储空间和算力,怎么可能把整个镜像存下来做比对?这时候就需要哈希算法登场了。

我个人习惯把哈希算法比作“榨汁机”。你扔进去一个苹果(原始数据),出来一杯苹果汁(哈希值)。但你不可能从这杯果汁里,再把那个完整的苹果还原出来。这就是哈希的精髓。

3.1 SHA-256算法原理:它是怎么工作的?

SHA-256,全称是Secure Hash Algorithm 256-bit,属于SHA-2家族。我当年刚接触它时,觉得那一堆移位、异或、逻辑运算头大得很。后来做项目多了,发现你不需要把每一轮迭代的数学公式背下来,但核心流程必须清楚。

它的处理过程大致分三步:

  1. 填充与分块:先把原始数据补位,使其长度对512取模等于448。然后附加一个64位的原始长度信息。这样数据就变成了512位的整数倍。每个512位就是一个“消息块”。
  2. 消息扩展:每个512位的消息块,会被扩展成64个32位的“字”(W0 到 W63)。这一步有点像把原材料切碎,方便后续“烹饪”。
  3. 压缩迭代:这是核心。SHA-256有8个初始哈希值(H0 到 H7),它们是一些固定的常数。然后对这64个字进行64轮循环压缩。每一轮都会用到当前的字、上一轮的哈希值,以及一堆逻辑函数(比如Σ0、Σ1、Maj、Ch)。

嗯,这里要注意,这64轮循环不是简单的加法,里面包含了大量的位运算和条件判断。最终,这8个32位的寄存器值拼接起来,就是那个256位(32字节)的哈希值。

核心公式(简化理解):

Hi = Compress(Hi-1, W0..63)

其中 Hi 是第 i 个消息块处理后的哈希值,Hi-1 是上一个块的输出(第一个块用初始值)。

我曾经在调试一个安全启动失败的问题时,发现就是因为在软件实现中,把消息扩展的循环次数写成了63次,导致最后一位数据没参与运算。结果镜像校验死活过不去。从那以后,我对这些循环边界条件就特别敏感。

3.2 哈希值的两大特性:单向性与抗碰撞性

哈希值为什么能用来做校验?因为它有两个非常硬核的特性。

3.2.1 单向性(One-wayness)

单向性,也叫抗原像性。意思是:给定一个哈希值H(x),你想反推出原始数据x,在计算上是不可行的。

你想想看,SHA-256的输出是256位,也就是2256种可能。这个数字有多大?比宇宙中所有原子的数量加起来还多得多。暴力穷举?算到宇宙毁灭也算不出来。这就是单向性的底气。

在BootROM场景下,单向性保证了:即使攻击者拿到了镜像的哈希值,他也无法反推出一个合法的镜像来绕过校验。他只能老老实实去破解签名算法,或者找别的漏洞。

3.2.2 抗碰撞性(Collision Resistance)

抗碰撞性分两种:强抗碰撞性和弱抗碰撞性。

  • 弱抗碰撞性:给定一个输入x,你找不到另一个输入y(y≠x),使得H(x) = H(y)。
  • 强抗碰撞性:你找不到任意两个不同的输入x和y,使得H(x) = H(y)。

SHA-256目前在这两方面都表现得很强。虽然理论上存在碰撞(因为无限输入映射到有限输出),但找到一对碰撞的难度极高。我记得2017年Google和CWI研究所才宣布找到了SHA-1的第一个实际碰撞案例,而SHA-256的强度比SHA-1高了好几个数量级。

避坑指南:

我曾经见过一个项目,为了省空间,把SHA-256的输出截断成128位来用。结果后来做安全评估时,被指出碰撞概率显著增加。虽然BootROM场景下不一定马上出问题,但作为安全架构师,我建议永远不要截断哈希值。256位就是256位,少一位都不行。

3.3 为什么BootROM选择SHA-256?

这个问题我经常被问到。为什么不选MD5?为什么不选SHA-1?为什么不选更快的算法?

答案其实很现实:安全性与实现成本的平衡。

我们来看一个对比表格:

算法 输出长度 安全性状态 硬件实现成本 BootROM适用性
MD5 128位 已破解(碰撞可构造) 低 ❌ 不安全
SHA-1 160位 已破解(实际碰撞已公开) 中 ❌ 不安全
SHA-256 256位 目前安全 中高 ✅ 推荐
SHA-512 512位 目前安全 高 ⚠️ 资源开销大
SM3 256位 目前安全(国密) 中高 ✅ 国内合规场景

从表格能看出来:

  • MD5和SHA-1已经被淘汰了。用它们做镜像校验,等于给攻击者留后门。我见过一些老旧的IoT设备还在用MD5做固件校验,结果被轻松篡改。
  • SHA-512虽然更安全,但开销太大。BootROM通常只有几十KB的ROM空间,硬件加速器面积也有限。SHA-512需要更多的寄存器和更宽的运算单元,对成本和功耗不友好。
  • SHA-256是“甜点”。它的安全性在可预见的未来(至少10-15年)内是足够的。硬件实现上,它只需要8个32位寄存器和一套逻辑运算单元,面积和功耗都控制得很好。

说白了,BootROM选择SHA-256,是因为它“够用且不贵”。

重要提醒:

SHA-256本身只提供完整性校验,不提供真实性验证。也就是说,它能保证镜像没被改过,但不能保证这个镜像一定是厂家发布的。要解决“谁发布的”这个问题,必须配合非对称签名算法(比如RSA或ECDSA)。这一点在后续章节会详细展开。

下面这张图,是我自己总结的SHA-256在BootROM镜像校验中的位置和流程:

SHA-256在BootROM镜像校验中的角色 原始镜像文件 SHA-256 哈希计算 256位哈希值 (数字指纹) 私钥签名 (RSA/ECDSA) 签名值 (附加到镜像) 签名的镜像 (镜像 + 签名值) 流程说明: 1. 对原始镜像计算SHA-256哈希值,得到256位数字指纹。 2. 用厂家的私钥对这个哈希值进行签名,生成签名值。 3. 将签名值附加到镜像末尾,形成最终的签名镜像。 4. BootROM启动时:先计算镜像的SHA-256,再用公钥验证签名。

从这张图能看出来,SHA-256在整个流程中扮演的是“承上启下”的角色。它把任意大小的镜像,压缩成一个固定长度的摘要,然后签名算法只需要对这个摘要操作就行。这大大降低了签名算法的计算负担。

最后说一句,SHA-256不是银弹。它只是BootROM安全链条上的一环。但这一环,目前来看,是最可靠、最成熟、最经济的选择。我个人在多个芯片平台(ARM Cortex-M、RISC-V、Xtensa)上都用过它做BootROM校验,没出过问题。