SHA-256哈希算法中的字扩展(Message Schedule)计算详解
你已经看过很多关于SHA-256不同部分的讲解,包括其轮常数、压缩函数、填充规则等。今天,我们将深入其字扩展(亦常被称为消息调度)计算过程。这是SHA-256压缩函数中,将输入的一个512位消息分组转换为64个32位字(W_t)的关键预处理步骤,其设计直接影响算法的扩散性和安全性。
第一步:理解字扩展的目的与背景
在SHA-256中,输入消息被填充后分割成连续的512位块。每个块是压缩函数的输入。压缩函数需要64轮运算,每轮需要一个32位的“消息字” W_t(其中 t = 0 到 63)作为输入。
- 核心问题:一个消息块只有512位,即16个32位字(记作 M_0, M_1, ..., M_15)。但我们却需要64个字。
- 解决方案:通过一个确定性的、具有良好密码学性质的递归函数,将这初始的16个字“扩展”成后续的48个字。这个扩展过程就是字扩展计算。
- 密码学目的:
- 提供轮输入:为64轮运算的每一轮提供唯一的输入字W_t。
- 引入非线性与时序依赖性:扩展算法使用了位运算和小量常数,使得W_t不仅依赖于当前块的部分原始消息字,还依赖于之前已生成的扩展字。这增强了算法的扩散性,即使输入消息只有微小的改变,也会通过扩展过程影响到后续许多轮的W_t。
- 破坏局部性:防止攻击者仅通过操纵某个特定的输入字就能轻易控制多轮的轮输入。
第二步:字扩展计算的分段规则
字扩展的64个字 W_0 到 W_63 的计算是分段进行的:
-
前16个字 (t = 0 到 15):直接取自当前处理的512位消息块。
- 将消息块视为16个连续的32位大端序字。
W_t = M_t, 对于 t = 0, 1, ..., 15。- 这提供了算法的原始输入。
-
后48个字 (t = 16 到 63):通过一个递推公式计算得出。这是字扩展的核心。
第三步:深入后48个字的递推公式
对于 t = 16 到 63,字 W_t 的计算公式为:
W_t = σ₁(W_{t-2}) + W_{t-7} + σ₀(W_{t-15}) + W_{t-16}
让我们拆解这个公式的每一个部分:
-
σ₀(x) 函数 (小Sigma 0):
- 定义:
σ₀(x) = ROTR⁷(x) XOR ROTR¹⁸(x) XOR SHR³(x) ROTRⁿ(x):将32位字x循环右移n位。SHRⁿ(x):将32位字x逻辑右移(高位补0)n位。- 作用:对输入字
x进行非线性位混合。循环右移和逻辑右移的组合能有效地打乱位的顺序,并将高位的信息扩散到低位。
- 定义:
-
σ₁(x) 函数 (小Sigma 1):
- 定义:
σ₁(x) = ROTR¹⁷(x) XOR ROTR¹⁹(x) XOR SHR¹⁰(x) - 作用:与 σ₀ 类似,但使用不同的位移常数,提供另一种位混合模式。
- 定义:
-
公式解读:
W_t = σ₁(W_{t-2}) + W_{t-7} + σ₀(W_{t-15}) + W_{t-16}- 每个新的字
W_t由四个前驱字组合而成:W_{t-2}和W_{t-15}:分别经过 σ₁ 和 σ₀ 函数的非线性变换。W_{t-7}和W_{t-16}:直接相加。
- 下标的选取(-2, -7, -15, -16)确保了在计算过程中依赖了相对“久远”的、来自原始16个字不同位置的信息。这极大增强了扩展的复杂性和扩散性。
- 所有的加法
+都是 模 2³² 加法(即结果超出2³²的部分被丢弃,这与计算机中32位无符号整数溢出的行为一致)。
第四步:一个简化的逐步计算示例
假设我们有一个消息块,已经得到了前16个字 W_0 到 W_15(具体值我们用符号表示以关注过程)。
目标:计算 W_16。
过程:
-
确定依赖项:
W_{14}(因为 16-2 = 14)W_{9}(因为 16-7 = 9)W_{1}(因为 16-15 = 1)W_{0}(因为 16-16 = 0)
-
计算中间值:
A = σ₁(W_{14})- 计算
ROTR¹⁷(W_{14}) - 计算
ROTR¹⁹(W_{14}) - 计算
SHR¹⁰(W_{14}) - 将上述三个结果进行按位异或得到
A。
- 计算
B = σ₀(W_{1})- 计算
ROTR⁷(W_{1}) - 计算
ROTR¹⁸(W_{1}) - 计算
SHR³(W_{1}) - 将上述三个结果进行按位异或得到
B。
- 计算
-
模 2³² 加法:
W_16 = A + W_{9} + B + W_{0}- 注意:这里的
+是模加法。在实际计算中,就是简单的无符号整数相加并允许自然溢出。
后续计算:
- 计算
W_17时,依赖W_{15},W_{10},W_{2},W_{1}。 - 如此反复,直到算出
W_63。 - 整个过程中,σ₀ 和 σ₁ 函数像两个小型“搅拌器”,不断将旧字的位模式打乱并注入到新字中。
第五步:安全性与设计考量
SHA-256的字扩展设计比其前身SHA-1更复杂、更安全。
- 抗碰撞攻击:复杂的非线性扩展使得寻找能产生相同扩展字序列的两个不同消息块(前16个字)变得极其困难。这是抵御碰撞攻击的关键。
- 抗长度扩展攻击:长度扩展攻击不直接依赖于字扩展的弱点,但SHA-256通过其独特的填充和最终处理方式(与MD5、SHA-1不同)有效防御了此类攻击。字扩展确保了一个消息块内部的充分混淆。
- 与轮函数的协同:扩展得到的64个字 W_t,会按顺序在64轮压缩函数中,与轮常数 K_t 一同注入。扩展过程的复杂性与轮函数本身的复杂性(Ch, Maj, Σ0, Σ1)叠加,构成了强大的单向压缩。
总结:SHA-256的字扩展不是一个简单的复制或线性变换,而是一个精心设计的、具有反馈和非线性特性的递推过程。它将512位输入“拉伸”并“调味”成2048位(64字)的内部序列,为接下来的64轮压缩提供了充满变化的驱动力,是算法整体混淆和扩散机制的重要组成部分。