在密码学中,加密并非唯一重要的工具。哈希函数作为另一种基础密码学原语,在数据完整性、数字签名和加密货币等领域发挥着关键作用。尽管你可能在其他领域接触过哈希函数,但密码学中的哈希函数具备一些特殊性质,使其能够满足更高的安全需求。本文将深入探讨哈希函数的定义、核心属性、常见攻击方式以及如何扩展其输入域。
加密货币中的核心问题
传统货币体系是中心化的,由中央机构制定货币政策、确认所有权并管理整个系统。而加密货币则通过密码学工具构建去中心化系统,比特币便是其中最著名的例子。在这种去中心化环境中,几个关键问题需要解决:
双重支付问题
假设A向B支付一枚加密货币(由唯一标识符ID代表的比特串)。如果没有中央机构验证,A可能使用同一ID向C再次支付。解决方案是建立公共账本记录所有交易。当A向B支付时,“A将硬币ID转移给B”的消息会被添加到账本中。一旦账本确认B获得所有权,B就会向A发货。如果A试图重复使用同一硬币,C通过查看账本会发现硬币已不属于A,从而拒绝交易。
账本共识问题
在去中心化系统中,谁负责维护账本并添加新交易?用户如何就交易顺序达成共识?当出现分叉(即不同方声称的下一个交易冲突)时,系统需要一种机制确保用户对有效账本保持共识。
比特币的解决方案是引入工作量证明机制:用户通过消耗计算资源(CPU周期)来获得账本更新权。具体而言,系统要求用户找到特定输入x,使得哈希函数H(ID∥x)的输出前T位为0。找到解需要大约O(2^T)次计算,而验证只需一次哈希计算。成功添加交易的用户(矿工)会获得新铸造的硬币作为奖励。
交易标识符冲突
为确保交易顺序不可篡改,新交易t_{n+1}会包含之前所有交易t_1,…,t_n的哈希值H(t_1,…,t_n)作为标识符。由于哈希函数将大集合映射到较小集合,输出不可能唯一。但只要找到碰撞(两个不同输入产生相同输出)在计算上困难,该系统就是安全的。
哈希函数的定义与属性
哈希函数的核心是将任意长度比特串映射到固定长度输出(例如160或256位)。与加密不同,哈希函数不需要密钥,且必须是公开和确定性的,以便多方验证结果。
形式化定义
哈希函数是高效可计算的函数:
[ H:{0,1}^* \rightarrow {0,1}^\ell ]
其中{0,1}^*表示所有长度比特串的集合,\ell为输出长度。计算哈希函数的过程称为哈希,输出称为哈希值。
安全属性
由于输入空间大于输出空间,碰撞必然存在。但密码学哈希函数需满足以下安全性质:
- 碰撞抵抗性:难以找到两个不同输入\mathbf{b} ≠ \mathbf{b}',使得H(\mathbf{b}) = H(\mathbf{b}')
- 第二原像抵抗性:给定输入\mathbf{b},难以找到另一输入\mathbf{b}' ≠ \mathbf{b}使得H(\mathbf{b}) = H(\mathbf{b}')
- 原像抵抗性:给定哈希值h,难以找到输入\mathbf{b}使得H(\mathbf{b}) = h
这些属性之间存在内在联系:碰撞抵抗性蕴含第二原像抵抗性。而对于实际使用的哈希函数(执行有意义压缩),第二原像抵抗性通常也意味着原像抵抗性。
生日攻击:碰撞搜索的优化
对哈希函数H: {0,1}^* → {0,1}^\ell的暴力攻击最多需要2^ℓ + 1次尝试(因为只有2^ℓ种输出)。这似乎表明ℓ位输出提供ℓ比特安全性。但生日悖论表明实际情况并非如此。
生日悖论原理
考虑40名学生的班级,任何两人共享生日的概率是多少?直觉上可能认为概率很低(一年365天)。但计算显示,所有人生日都不同的概率仅为:
[ \frac{364}{365} \times \frac{363}{365} \times \cdots \times \frac{326}{365} \approx 0.108768 ]
因此,至少两人共享生日的概率高达约89.1%。这种反直觉现象源于碰撞概率随样本数平方根增长。
对哈希函数的影响
对于输出长度为ℓ的哈希函数,找到碰撞的概率在约√(2^ℓ) = 2^{ℓ/2}次尝试后就会变得显著。这意味着:
- 哈希函数实际提供ℓ/2比特安全性
- 若需要ℓ比特安全性,应选择输出长度为2ℓ的哈希函数
例如,SHA-256提供128比特安全性,足以抵抗生日攻击。
Merkle-Damgård变换:扩展输入域
实际哈希函数常先构建固定长度输入函数(如H: {0,1}^{2ℓ} → {0,1}^ℓ),再通过Merkle-Damgård变换扩展至任意长度输入。SHA家族哈希函数便采用此方法。
变换过程
- 将输入\mathbf{x}(长度L ≤ 2^ℓ)分块为\mathbf{x}_1, …, \mathbf{x}_n,每块长ℓ位
- 添加额外块\mathbf{x}_{n+1},包含L的二进制编码(因L ≤ 2^ℓ -1,可用ℓ位表示)
- 递归计算:
[ \mathbf{z}_i = H(\mathbf{z}_{i-1} | \mathbf{x}_i) \quad \text{对于} i=1,\dots,n+1 ]
其中|表示连接,\mathbf{z}_0为初始化向量(通常全0,无需保密) - 最终哈希值\mathbf{H}(\mathbf{x}) = \mathbf{z}_{n+1}
安全性保证
关键性质:若底层函数H是碰撞抵抗的,则通过Merkle-Damgård构造的\mathbf{H}也是碰撞抵抗的。这确保了扩展后哈希函数的安全性。
常见问题
哈希函数与加密函数有何区别?
加密函数需要密钥且可逆(解密),而哈希函数无需密钥且不可逆。哈希函数用于完整性验证和承诺,加密则用于保密性。
为什么哈希函数碰撞不可避免?
因为输入空间(任意长度)远大于输出空间(固定长度),根据鸽巢原理,碰撞必然存在。密码学要求的是计算上难以找到碰撞。
生日攻击对哈希函数选择有何实际影响?
为抵抗生日攻击,哈希函数输出长度应足够长。目前推荐至少256位(如SHA-256),以提供128比特安全性。较短哈希函数(如MD5、SHA-1)已不推荐使用。
Merkle-Damgård构造是否有缺点?
该构造可能容易受到长度扩展攻击:已知H(x)和x长度时,可计算H(x∥y)而不知x。现代哈希函数如SHA-3采用不同结构避免此问题。
哈希函数在区块链中具体如何应用?
在比特币中,哈希函数用于:
- 创建交易标识符
- 工作量证明(寻找特定模式哈希)
- 连接区块形成链式结构
每个区块包含前区块哈希,确保历史交易不可篡改。
如何选择适合的哈希函数?
应根据安全需求选择:
- 通用应用:SHA-256或SHA-3
- 性能敏感场景:Blake2
- 已破解算法:避免MD5、SHA-1
始终关注最新密码学标准和建议。