SHA-256 哈希算法中的迭代压缩流程详解
题目描述
SHA-256 算法是 SHA-2 家族中的一员,输出 256 比特的杂凑值。其核心处理结构是 Merkle-Damgård 迭代结构,而该结构的核心组件是其压缩函数。我们已经了解过压缩函数的内部轮函数、消息扩展、常量初始化等细节。本题将聚焦于更高一层的处理流程:一个完整的 512 比特消息分组如何通过迭代调用压缩函数,逐步更新 8 个链变量(A, B, C, D, E, F, G, H),并与前一个分组的输出链接,最终生成整个消息的 256 比特摘要。我们将详细分析从初始化哈希值(H0)开始,到处理完最后一个分组(包括填充分组)并产生最终输出的完整迭代过程。
解题过程
SHA-256 处理一个任意长度的输入消息,步骤如下:
步骤 1:消息预处理
此步骤的目的是将任意长度的原始消息,转换为一个长度为 512 比特整数倍的数据,以便分组处理。
- 消息填充:在原始消息末尾添加一个比特‘1’,然后填充若干个比特‘0’,最后附加一个 64 比特的大端序整数,表示原始消息的比特长度。填充规则是保证填充后的总长度是 512 比特的整数倍。假设原始消息长度为 L 比特,则填充的‘0’的个数 k 满足:
L + 1 + k ≡ 448 mod 512。这样,加上最后 64 比特长度,总长度正好是 512 的倍数。 - 消息分块:将填充后的消息分割为 N 个连续的 512 比特分组:
M^(1), M^(2), ..., M^(N)。
步骤 2:初始化哈希值
SHA-256 算法使用 8 个 32 比特的初始化常数,称为初始哈希值 H0。这 8 个常数来自于前 8 个质数(2, 3, 5, 7, 11, 13, 17, 19)的平方根的小数部分的前 32 比特。它们被赋值给 8 个工作变量(链变量):
A0 = 0x6a09e667
B0 = 0xbb67ae85
C0 = 0x3c6ef372
D0 = 0xa54ff53a
E0 = 0x510e527f
F0 = 0x9b05688c
G0 = 0x1f83d9ab
H0 = 0x5be0cd19
这 8 个变量构成了初始状态,记作 H^(0) = A0 || B0 || C0 || D0 || E0 || F0 || G0 || H0。
步骤 3:主循环(对每个消息分组进行处理)
对于第 i 个消息分组 M^(i) (i 从 1 到 N),执行以下操作:
- 消息调度:将 512 比特的
M^(i)分成 16 个 32 比特字M0, M1, ..., M15。然后通过一个线性和非线性混合的递归公式,扩展出另外 48 个 32 比特字W16, W17, ..., W63,总共得到 64 个消息字W0 到 W63(其中 W0 到 W15 就是 M0 到 M15)。这个扩展过程利用了循环右移和异或运算,增加了数据的扩散性。 - 初始化工作变量:将当前链变量(即上一个分组处理完后的结果,对于第一个分组就是 H0)加载到一组临时工作变量 a, b, c, d, e, f, g, h 中:
a = A(i-1) b = B(i-1) c = C(i-1) d = D(i-1) e = E(i-1) f = F(i-1) g = G(i-1) h = H(i-1) - 压缩函数主轮循环:进行 64 轮迭代,t 从 0 到 63。
- 在每一轮 t,会使用到:
- 当前工作变量 a, b, c, d, e, f, g, h。
- 第 t 轮的消息字 Wt。
- 第 t 轮的常数 Kt(一组预定义的 64 个 32 比特常量)。
- 每轮的核心是计算两个中间值:
其中:T1 = h + Σ1(e) + Ch(e, f, g) + Kt + Wt T2 = Σ0(a) + Maj(a, b, c)Ch(e, f, g) = (e AND f) XOR ((NOT e) AND g)是选择函数。Maj(a, b, c) = (a AND b) XOR (a AND c) XOR (b AND c)是多数函数。Σ0(x) = ROTR^2(x) XOR ROTR^13(x) XOR ROTR^22(x)。Σ1(x) = ROTR^6(x) XOR ROTR^11(x) XOR ROTR^25(x)。- ROTR^n(x) 表示将 32 比特字 x 循环右移 n 位。
- 然后更新工作变量(注意更新的顺序非常重要,是从 h 到 b 方向依次传递):
h = g g = f f = e e = d + T1 d = c c = b b = a a = T1 + T2
- 在每一轮 t,会使用到:
- 计算中间哈希值:完成 64 轮循环后,将更新后的工作变量与本次迭代前的链变量(即步骤 3.2 的初始值)进行模 2^32 加法,得到本分组处理完后的新链变量:
这 8 个新值A(i) = a + A(i-1) B(i) = b + B(i-1) C(i) = c + C(i-1) D(i) = d + D(i-1) E(i) = e + E(i-1) F(i) = f + F(i-1) G(i) = g + G(i-1) H(i) = h + H(i-1)A(i) ... H(i)构成了处理完第 i 个分组后的哈希状态H(i),并作为处理下一个分组M^(i+1)的初始链变量。
步骤 4:产生最终摘要
当所有 N 个消息分组都按照步骤 3 处理完毕后,我们得到了最终的链变量 H(N) = A(N) || B(N) || C(N) || D(N) || E(N) || F(N) || G(N) || H(N)。将它们按顺序连接起来,就构成了最终的 256 比特(8 * 32 比特)消息摘要。
总结
SHA-256 的迭代压缩流程清晰地展示了 Merkle-Damgård 结构的运作:
- 初始化:从一个固定的初始值(IV)开始。
- 压缩:将当前状态(链变量)和一个消息分组作为输入,通过一个抗碰撞的压缩函数,输出一个新的、固定长度的状态。
- 链接:将前一个压缩函数的输出作为下一个压缩函数的输入(链变量)。
- 输出:处理完最后一个(包含填充信息的)分组后,最终的链变量就是整个消息的哈希值。
这个过程确保了消息的任意一个比特发生改变,都会以极高的概率导致最终哈希值的完全不同,这是哈希算法具备“雪崩效应”和抗碰撞性的基础。