这是一个很好的问题。
我们必须首先给出一个精确度:许多单向函数,特别是密码学中常用的散列函数,接受来自比输出值空间大得多的空间的输入。例如,为最多 18446744073709551615 位的字符串的输入定义了 SHA-256;有2 18446744073709551616 -1个可能的输入,但由于输出始终是 256 位的序列,因此 SHA-256 只有2 256个可能的输出。必然地,一些不同的输入产生相同的输出。因此,对于给定的 SHA-256 输出,不可能明确地恢复所使用的输入,但可能有可能计算出产生给定输出值的输入。原像阻力是关于:为输出找到匹配输入的难度(不管该输出最初是如何获得的)。
所以我们谈论一个每个人都可以根据任何输入计算的函数(使用一个公开的程序,不涉及秘密值——我们不是在谈论加密)。
学者怎么说
目前尚不清楚单向函数是否真的存在。现在,我们有许多没有人知道如何反转的函数;但这并不意味着它们在数学意义上是不可能反转的。但是请注意,没有证明单向函数不存在,所以希望仍然存在。有些人怀疑单向函数是否存在可能是这些令人讨厌的数学断言之一,既无法证明也无法证伪(哥德尔定理证明了这样的事情一定存在)。但也没有证据证明这一点。
因此,没有证据表明任何给定的散列函数真的能抵抗原像。
有一些功能可以与众所周知的难题相关联。例如,如果n是两个大素数的乘积,那么函数x ⟼ x 2 mod n很难求逆:能够以非素数n为模计算平方根(在一般基础上)等价于能够分解 n,并且已知该问题很难。未证实请注意,要努力;只是数学家在(至少)过去的 2500 年里一直试图有效地分解大整数,尽管已经取得了一些进展,但这些聪明人都没有找到真正的杀手级算法。“RSA 模数”(两个随机选择的相似长度的大素数的乘积)因式分解的世界纪录是一个 768 位整数。
已经提出了一些基于此类“难题”的哈希函数;参见例如MASH-1 和 MASH-2(关于RSA 问题)和ECOH(带有椭圆曲线)。只有少数这样的功能存在,因为:
构建散列函数的更“标准”方式是将密码学家聚集在一起,让他们研究一些提议的设计。在密码分析尝试中存活了几年的功能被认为是“可能是强大的”。SHA-3 竞赛就是这样的努力;获胜者应在今年晚些时候公布。在 51 名候选人(成功通过行政步骤的候选人)中,有 14 人被保留用于“第 2 轮”,这 14 人已被许多密码学家相对仔细地研究过,没有人发现任何真正值得一提的功能。该列表已减少到 5 个,并将“很快”进一步减少到 1 个,但不是出于安全原因(大多数实际数据是关于性能,而不是阻力)。
是什么让 MD5 难以反转
既然我们不知道如何证明一个函数是难求逆的,那么我们最好的办法就是在一个特定的函数上试一试,以便对函数如何实现其表观阻力有一个“直觉”。
我选择MD5,这是众所周知的。是的,MD5 是“损坏的”,但那是为了碰撞,而不是原像。有一种已知的原像攻击,至少在理论上,比通用方式更快(“通用方式”是“运气”,即尝试输入直到找到匹配项,因为 MD5 具有2 128次评估的平均成本) 128 位输出;Sasaki-Aoki 攻击的成本为2 123.4,较低,但仍然太高而无法实际尝试,因此结果仍然是理论上的)。但是 MD5 相对简单,并且已经经受了相当长的一段时间的攻击,所以这是一个有趣的例子。
MD5 包含对数据块的“压缩函数”的多次评估。首先填充输入消息,使其长度变为 512 位的倍数。然后将其拆分为 512 位块。一个 128 位运行状态(保存在称为A、B、C和D的四个 32 位变量中)被初始化为一个常规值,然后使用压缩函数进行处理。压缩函数获取运行状态和一个 512 位消息块,并将它们混合为运行状态的新值。当所有消息块都这样处理完后,运行状态的最终值就是哈希输出。
因此,让我们专注于压缩功能。它是这样工作的:
- 输入:运行状态 ( ABCD )和消息块M。消息块为 512 位;我们将其拆分为 16 个 32 位字M 0、M 1、M 2、... M 15。
- 输出:新的运行状态值。
- 加工:
- 将当前状态保存在一些变量中:A → A',B → B',C → C'和D → D'
- 做 64 轮,如下所示:
- 计算T = B + ((A + f i (B, C, D) + M k + X i ) <<< s i )。这读起来是这样的:我们在B、C和D上计算给定函数f i(一个简单的按位函数,取决于轮数i)。再加上A的值、一个消息词M k和一个常数X i (加法以2 32为模)。将结果向左旋转一些位(移位量也取决于轮次)。最后加上B: 结果是T。
- 轮换状态字:D→A、C→D、B→C、T→B。
- 将保存的状态值添加到当前状态变量:A + A' → A , B + B' → B , C + C' → C , D + D' → D。
重要的一点是有 64 轮,但只有 16 个消息词。这意味着每个消息词进入处理四次。我用粗体写,因为它是中心点;对原像的抵抗来自该特性。在 MD5 规范(RFC 1321)中描述了每一轮使用哪个消息字;该规范还描述了函数f i、循环计数s i和 32 位常数X i。
现在假设您正在尝试“反转” MD5;你从输出开始,慢慢地使用压缩功能。首先,您必须确定第 64 轮的输出。确实,压缩函数的输出是第 64 轮的输出与保存状态(A' B' C' D'值)之和。你两者都没有,所以你必须选择。您希望您能够找到消息词的值,这将允许您为第 1 轮的输入获得一些与您对A'及其兄弟的任意决定相一致的值。
让我们看看当您向后执行压缩函数时情况如何。您有一轮的输出(一轮后的变量A、B、C和D),并且您想重新计算该轮的输入。您已经知道B、C和D的先前值,但是对于A和M k,您有很多选择:每个 32 位值对于A都是可能的,并且每个都有对应的M k。起初,您对此感到高兴;谁会拒绝这样的自由?随便选一个M k,这只需几个操作就产生了相应的 A (试试吧!)。
但是在你以这种方式倒转 16 轮之后(第 49 到 64 轮,因为你是在向后工作),自由就消失了。您已经“选择”了所有消息词的值。当尝试反转第 48 轮时,您希望在该轮之前重新计算A的值;根据 MD5 规范,消息字M 2在第 48 轮中使用,并且您已经选择了M 2的值(当反转第 63 轮时)。所以A只有一种选择。那你说呢。一个选择就足以继续向后走。所以你继续。
现在,您正处于压缩功能的开始阶段。请记住,最初,您对A' B' C' D'的值进行了任意选择:这允许您计算第 64 轮的输出,并开始向后走。现在你已经获得了第 1 轮的输入,它应该与A' B' C' D'相同......并且它不匹配。这很正常:你随意选择了A' B' C' D',而且你还随意选择了消息词M k,所以可以预料大部分时间它不会起作用。因此,您尝试通过追溯更改您对A' B' C' D'的初始选择来修复计算,ķ。但是对任何M k的每次修改都意味着在其他地方进行修改,因为每个M k被使用了四次。因此,您需要进行其他修改以取消其他修改,依此类推...
到那时你开始理解反转 MD5 的问题:每次你触摸一个位,它都会触发整个算法的大量修改,你需要通过触摸其他位来抵消,而且交互太多了. 基本上,您同时处理2 128个球,这对于跟踪所有这些球来说太过分了。
如果每个消息块是 2048 位长,分成 64 个字,并且每个消息字在 MD5 中只使用一次,那么您可以轻松地将其反转。你按上面做:任意选择A' B' C' D',任意选择第 64 到第 5 轮的消息词;对于前四轮,您只需考虑您希望为该轮输入获得的值(与您对A'、B'、C'或D'的任意选择相匹配的值)并计算出相应的消息词。易如反掌。但是 MD5 不是按 2048 位块处理数据,而是按 512 位块处理数据,每个消息字使用四次。
一些额外的曲折
MD5的压缩函数结构实际上是Feistel密码的一种推广。在 Feistel 密码中,数据被分成两半,并且,对于每一轮,我们通过将另一半与从另一半和密钥计算的中间值相加/异或来改变一半;然后我们交换两半。将此方案扩展到四部分拆分,您将获得与 MD5 轮次相同的结构——旋转 90º:MD5 看起来像是使用消息块作为密钥对当前状态的加密(并且额外添加了带有已保存状态的第 64 轮的输出,它使 MD5 与旋转密码不同)。
所以也许我们可以用分组密码构建散列函数?事实上,我们可以:这就是惠而浦的意义所在。建立在旋转块密码之上的散列函数(消息块是密钥);Whirlpool 的分组密码是“W”,它是 Rijndael 的衍生物,也就是众所周知的AES。但是 W 具有更大的块(512 位而不是 128 位)和重新生成的密钥调度。
当你从一个旋转的分组密码中创建一个哈希函数时,对哈希函数的原像攻击在某种程度上等同于对分组密码的密钥重构攻击;所以有一些希望,如果块密码是安全的,那么散列函数也是安全的。再次,有一些刻薄的细节。此外,对于这样的结构,哈希函数上的冲突就像对分组密码的相关密钥攻击一样;相关密钥攻击通常被认为是非致命的,并且经常被忽略(例如,它们不是 AES 竞争评估标准的一部分,而 Rijndael 在这方面被认为有点不稳定,这就是 W 拥有全新密钥的原因日程)。
一些较新的设计建立在不旋转的分组密码之上,因此哈希函数的安全性可以更直接地从分组密码的安全性中推导出来;参见例如 SHA-3 候选Skein,它定义在名为 Threefish 的分组密码上。
相反,可以尝试用散列函数制作分组密码。参见例如SHACAL,它是 SHA-1“直立”。而且,在提示上,SHACAL 有一些相关的关键弱点,这些弱点与 SHA-1 在碰撞方面的已知弱点非常相似(没有计算实际的碰撞,但我们有一个方法应该比通用碰撞查找算法)。
因此,与我在这篇文章的介绍中所说的相反,我们一直在谈论加密。关于散列函数和对称加密之间的联系还有很多有待发现和研究。
TL;DR:此消息没有 TL;DR。完整阅读,否则就离开。