SM3自定义IV计算过程:从哈希算法到长度扩展攻击

admin 2026-06-24 05:24:31 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细解析SM3哈希算法基于Merkle-Damgård结构的自定义IV机制及其在长度扩展攻击中的应用。通过具体攻击场景演示,说明攻击者仅需知晓密钥长度和原始token即可伪造消息认证,并推荐使用HMAC-SM3作为防御方案。文档强调所有基于Merkle-Damgård的哈希函数都存在此特性,切勿使用裸哈希进行消息认证。 综合评分: 85 文章分类: 漏洞分析,WEB安全,安全开发,密码学,应用安全


cover_image

SM3 自定义IV计算过程:从哈希算法到长度扩展攻击

原创

利刃信安 利刃信安

利刃信安

2026年6月23日 18:00 北京

在小说阅读器读本章

去阅读

SM3 自定义IV计算过程:从哈希算法到长度扩展攻击

摘要:SM3 密码杂凑算法(GB/T 32905-2016)采用 Merkle-Damgård 迭代结构,其初始向量 IV 是固定的 8 个 32 比特常量。但如果将 IV 替换为任意 256 比特值会发生什么?


一、SM3 的迭代结构:Merkle-Damgård

SM3 的核心结构是 Merkle-Damgård 迭代压缩。

消息 M → [填充至 512 比特的倍数] → B₁ || B₂ || ... || Bₙ

V₀ = IV (标准常量)
V₁ = CF(V₀, B₁)
V₂ = CF(V₁, B₂)
...
Vₙ = CF(Vₙ₋₁, Bₙ) = SM3(M)

每一步的 CF 压缩函数接受两个输入:

  • • Vᵢ:当前 256 比特链接变量(第一轮用标准 IV)
  • • Bᵢ:当前 512 比特消息分组

输出是下一轮的 Vᵢ₊₁。最后一轮的输出就是 SM3 的杂凑值(token)。

关键洞察:每一轮的 CF 输出完全由当前 V 和当前分组 B 决定。这意味着——如果攻击者能控制 V,他就能控制后续所有压缩结果。


二、标准 IV 与自定义 IV

标准 IV 由 GB/T 32905-2016 §5.3.1 定义,是 8 个固定 32 比特字:

| 寄存器 | 值 | | — | — | | V₀[0] | 0x7380166F | | V₀[1] | 0x4914B2B9 | | V₀[2] | 0x172442D7 | | V₀[3] | 0xDA8A0600 | | V₀[4] | 0xA96F30BC | | V₀[5] | 0x163138AA | | V₀[6] | 0xE38DEE4D | | V₀[7] | 0xB0FB0E4E |

自定义 IV 就是将这 8 个字替换为任意用户指定的值。从数学上看,这完全合法——CF 压缩函数不关心 V 的来源,只关心它的数值。于是:

SM3_custom(M, IV_custom) = 以 IV_custom 为起点的 Merkle-Damgård 迭代结果

这个「合法但非常规」的操作,正是长度扩展攻击的核心武器。


三、长度扩展攻击:自定义 IV 的实战应用

3.1 攻击场景

某服务端使用「裸哈希拼接」做消息认证:

token = SM3(secret || message)

其中 secret 是 16 字节密钥(攻击者不知道),message 是用户可控的明文。攻击者截获到:

| 参数 | 值 | | — | — | | message | user=alice&role=user (20 字节) | | token | 984C2580ECB81F4945A4FCC1BE399115558627DAB8062FD519C43CD3819FEF33 |

攻击者的目标:在不知道密钥的情况下,构造 message' 和 token',使得服务端验证通过。

3.2 数学推导

设原始消息总长 L = 16 + 20 = 36 字节。SM3 对它的填充:

M = secret || message
padding₁ = 0x80 || 0x00... || 0x0000000000000120  (共 28 字节)
B₁ = M || padding₁  =  64 字节  (恰好 1 个 512 比特分组)

于是:

token = CF(IV, B₁)   ← 标准 IV 下的单分组压缩

攻击者想附加 &admin=true(11 字节)。如果他能以 token 为 IV,对扩展块再执行一次 CF 压缩:

forged = CF(token, extension || padding₂)

其中 padding₂ 基于新总长 = 36 + 28 + 11 = 75 字节计算,长度字段为 75 × 8 = 600 = 0x0258

padding₂ = 0x80 || 0x00 × 44 || 0x0000000000000258  (共 53 字节)
extension || padding₂ = 11 + 53 = 64 字节  (恰好 1 个分组)

3.3 为什么等价于合法 token?

根据 Merkle-Damgård 结构:

SM3(secret || message || padding₁ || extension)
  = CF(CF(IV, B₁), extension || padding₂)
  = CF(token, extension || padding₂)
  = forged

攻击者不需要知道密钥! 只需要两个条件:

  • • 知道密钥的长度(16 字节)——用于计算 padding₁ 和 padding₂
  • • 截获的 token ——用作自定义 IV

四、计算示例演示

以下使用 sm3_custom_iv_gui.py 工具对上述场景进行完整计算。

步骤 1:输入参数

密钥长度: 16 字节 (未知, 仅需长度)
原始消息: user=alice&role=user (20 字节)
原始总长度: 16 + 20 = 36 字节
已知 token: 984C2580ECB81F4945A4FCC1BE399115558627DAB8062FD519C43CD3819FEF33
扩展消息: &admin=true (11 字节)

步骤 2:计算 padding₁

SM3 对 36 字节消息的填充:

36 × 8 = 288 比特
追加 0x80 → 37 字节
补零至 56 (mod 64): 37 + 19 = 56 字节
长度字段: 288 = 0x0120 (大端序, 8 字节)

padding₁ = 80000000000000000000000000000000000000000000000000000120

验证:36 + 28 = 64 ≡ 0 (mod 64) ✓

步骤 3:构造扩展块

新总长度: 36 + 28 + 11 = 75 字节
75 × 8 = 600 比特 = 0x0258

对 75 字节消息的填充:
追加 0x80 → 76,补零至 56: (56 - 76%64) % 64 = 44
padding₂ = 0x80 + 0x00×44 + 0x0000000000000258  (共 53 字节)

扩展块 = extension(11B) || padding₂(53B) = 64 字节 = 1 个分组

步骤 4:以 token 为 IV 执行 CF 压缩

将 token 拆分为 8 个 32 比特字作为新 IV:

V₀ = [984C2580, ECB81F49, 45A4FCC1, BE399115,
      558627DA, B8062FD5, 19C43CD3, 819FEF33]

对 64 字节扩展块执行标准 CF 压缩(消息扩展 → 64 轮迭代 → 异或)。输出:

V₁ = [EF5BD588, 7E59CB85, A6E870EE, 0D4A2EA5,
      E73150A9, B1A5108C, 5E58B970, A6BC411F]

步骤 5:攻击结果

伪造 token  = EF5BD5887E59CB85A6E870EE0D4A2EA5E73150A9B1A5108C5E58B970A6BC411F

伪造消息 = known_msg || padding₁ || extension
         = 757365723D616C69636526726F6C653D75736572  ← user=alice&role=user
           80000000000000000000000000000000000000000000000000000120  ← padding₁
           261646D696E3D74727565  ← &admin=true

攻击者发送:
  message' = 757365723D...00000120261646D696E3D74727565
  token'   = EF5BD5887E59CB85A6E870EE0D4A2EA5E73150A9B1A5108C5E58B970A6BC411F

服务端计算 SM3(secret || message') → 结果等于 token' → 验证通过! ✓

五、攻击的条件与局限

攻击者需要

| 条件 | 说明 | | — | — | | 密钥长度 | 用于计算 padding₁ 的位置和 padding₂ 的长度字段 | | 原始消息内容 | 截获的明文,用于构造完整的 padding₁ | | 合法 token | 截获的 SM3(secret || message) 值 |

攻击者不需要

  • • 密钥内容 —— 整个攻击不涉及密钥
  • • 离线字典攻击 —— 不需要暴力破解

局限性

  • • 只能追加数据到消息末尾,不能修改前面的内容
  • • 追加的数据前面会夹杂 padding₁ 的不可打印字节
  • • 如果服务器对消息格式做严格校验(如 URL 解码),可能失败

六、防御方案

| 方案 | 原理 | 推荐度 | | — | — | — | | HMAC-SM3 | HMAC(K, M) = SM3(opad ⊕ K || SM3(ipad ⊕ K || M)) ,两次杂凑打破可扩展性 | ★★★★★ | | SM3(K || M || K) | 密钥同时放在首尾,攻击者无法控制尾部 | ★★★ | | SM3(SM3(K || M)) | 外层 SM3 只接收固定长度输入,无法利用内部状态 | ★★★★ | | SM3 带盐值 | 每次通信使用不同随机值 | ★★★ |

最佳实践:使用 HMAC-SM3(GM/T 0046 标准),而非裸哈希拼接。


七、概念类比

想象一台搅拌机(CF 压缩函数):

  • • 标准模式:你放入 8 种固定原料(标准 IV)+ 64 份食材 → 搅拌 → 得到 8 种混合糊
  • • 自定义 IV 模式:你把上一步的混合糊直接倒入搅拌机,再加 64 份新食材 → 再次搅拌

攻击者的核心操作就是截获别人搅拌好的糊(token),然后把它当做初始原料倒入自己的搅拌机,再加上自己选的食材(extension + padding₂),就能假装整个料理都是同一个人做的


八、参考标准

| 标准 | 内容 | | — | — | | GB/T 32905-2016 | SM3 密码杂凑算法 | | GB/T 32918.2-2016 | SM2 椭圆曲线公钥密码算法(数字签名) | | GM/T 0046 | HMAC-SM3 消息认证码 | | NIST SP 800-185 | SHA-3 派生函数(含 KMAC,HMAC 的现代替代) | | RFC 2104 | HMAC: Keyed-Hashing for Message Authentication |


结语:SM3 的 Merkle-Damgård 结构赋予了「自定义 IV」完全合法的数学地位。长度扩展攻击不是 SM3 的漏洞,而是所有基于 Merkle-Damgård 结构的哈希函数(MD5、SHA-1、SHA-256、SM3)的固有特性。理解了自定义 IV 的计算过程,就理解了为什么 「绝不要用裸哈希做消息认证」


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:利刃信安 利刃信安 利刃信安《SM3 自定义IV计算过程:从哈希算法到长度扩展攻击》

评论:0   参与:  0