我的问题是:是否每个哈希值 H 都有值 V?
例如,给定 md5 哈希值 f2c057ed1807c5e4227737e32cdb8669(完全随机),我们能找到它的来源吗?
这实际上是两个非常不同的问题:每个输出是否有输入,以及我们是否可以为每个输出找到输入。
对于你的第一个问题:我们不知道。对于给定的散列函数,可能输入的数量远远大于可能输出的数量,因此如果该函数不是满射的(即如果输出没有任何匹配的输入),我们会感到非常惊讶。例如,对于 MD5,有2128个可能的输出和 2 个18446744073709551616 -1 个可能的输入,因此我们预计每个输出平均有大约 2 个18446744073709551488 个相应的输入。有一个没有相应输入的输出是相当难以置信的。
但是,我们不知道如何证明它。在很大程度上,我们预计对于任何具体的散列函数都很难证明该属性(这种完全性证明本身并不是该函数的弱点,但是,随意地,散列函数的安全性依赖于关于这种分析的结构的难处理性)。
对于你的第二个问题:我们强烈希望它是不可行的。这就是所谓的原像电阻:对于给定的输出y ,要找到满足h ( x ) = y的x在计算上应该是不可行的。即使在数学上证明了这样的输入一定存在(它没有被证明,但它被强烈怀疑,如上所述),实际找到它仍然应该是非常昂贵的。
如果散列函数是“理想的”,那么最好的攻击就是运气:您尝试可能的输入,直到找到匹配项。如果输出的大小为 n位,则“luck”的平均工作量应为 2 n;n足够大(并且n = 128足够大),这是现有计算机无法做到的。如果“运气”仍然是最广为人知的攻击,则散列函数可以抵抗原像。
众所周知,MD5对原像的抵抗力并不理想,因为已经发现了一次努力 2 123.4的攻击——比 2 128快大约 10 倍,但在不可行领域还有很长的路要走,所以这种攻击只是理论上的。
不过,我想指出两点:
如果我给你一个未知x的h ( x ),但你对我使用的x有一些想法(例如,你知道x是人类用户可以选择和记住的“密码”),那么你可以尝试x符合该想法的值。寻找h ( x ) 的原像的实际平均工作量是 2 n和M /2 中的较小者,其中M是可能输入x的空间大小。对于“原始”原像攻击,所有位序列都是可能的输入,因此M很大,成本为 2 n. 但是,如果在特定的上下文中,已知x是从相对较小的空间中取出的,那么找到“正确的” x可以大大加快。
如上所述,原像不应该是唯一的:对于给定的y ,应该存在大量值x ,这些值散列到y。因此,对于给定的输出,您找不到“那个”对应的输入,而是“一个”对应的输入,它可能是也可能不是“正确的”输入。
第三个问题?事实上,尝试制作一个完整的输入到输出的地图是不同的。
正如@Xander 指出的那样,可能的 MD5 输出数量(2 128)太大而无法存储在地球上的任何地方;它超过了所有已构建的硬盘的累积大小。但是,如果您可以解决该存储问题,则可以考虑制作完整地图的成本。
如果您对所有 128 位输出单独使用“运气”攻击,您可能预计总成本为 2 256(2 128乘以 2 128 成本)。但是,您可以通过同时处理所有输出来做得更好,即尝试随机(或顺序)输入并在获得输出时简单地填充您的大表。通过努力 2 128,您应该获得大约 63% 的完整地图,而“独立运气”方法需要多于 2 255的努力才能获得相同的结果。
编辑:正如@Owen 和@kasperd 所指出的,关于输入数量的论点实际上并不足以引发满射性;内部功能结构很重要。MD5 和 SHA-1 是Merkle-Damgård函数,这意味着它们的构建方式如下:
有一个内部伪随机排列P:对于给定大小的输入块b(在 MD5 和 SHA-1 的情况下为 512 位),P b是正好n位的序列空间的排列(对于n = 128 MD5,对于 SHA-1, n = 160)。
压缩函数定义为:
f ( b , x ) = P b ( x ) + x
也就是说,对于块b,我们将对应于b的排列应用于第二个输入x,然后我们将x “添加”到该排列的输出。(在 MD5 和 SHA-1 的情况下,加法是在 32 位字的基础上完成的,但这里的细节并不重要。)
为了处理完整的输入消息m,首先用额外的位填充消息,以便总大小成为块大小的倍数,并且还对原始消息长度进行编码。然后将填充的输入分成连续的块b 0、b 1等。寄存器r被初始化为大小为n位的常规值(“IV”,在 MD5 和 SHA-1 标准中指定)。然后将块一个一个地处理:注入块b i,我们计算f ( b i , r ),输出是r的新值. 当所有块都被处理后,r包含完整的哈希函数输出。
压缩函数中的加法步骤将伪随机排列P b变为伪随机函数。一个相关的结果是,对于给定的值 b,f ( b , x ) 不太可能是满射的。事实上,对于所有 2 n 个输入x,我们期望值f ( b , x )仅覆盖所有可能的 2 n个n位序列的大约 63% 。
这种处理产生了有趣的结果。首先,考虑所有大小正好为 1 GB 的输入(“传统”千兆字节正好是 1073741824 字节):有 2 8589934592 个这样的序列,即比 2 128多得多。但是,当对所有这些消息应用 MD5 时,它们都将被恰好填充一个额外的块(8589934592 = 16777216×512,因此将附加一个大小为 512 的额外块),此外,最后一个块将是相同的对于所有 1 GB 输入(它对输入长度进行编码,但在其他方面是确定性的,没有随机性并且不依赖于输入位的值)。让我们称b z为最后一个块。1 GB 输入消息的MD5因此,首先对前 16777216 个块进行大量处理,产生一个 128 位的值x,并且哈希输出 MD5( m ) 等于f ( b z , x )。
因此,所有 1 GB 的消息最终都会缩减为一个相同的压缩函数f ( b z , x )。因此,我们预计哈希输出仅覆盖所有 128 位序列的约 63%。这个例子证明了关于散列函数输入数量的参数是不完整的(尽管它给出了正确的想法)。
相反,如果我们考虑所有长度正好为 300 位的消息,那么它们都将被散列为f ( b , IV),具有 2 300 个不同的块b。因此,我们有 2 300 个伪随机排列P b,全部应用于相同的 128 位输入(IV),产生 2 300 个128 位结果,这些结果都应该均匀分布在 128 位值的空间中. 将 IV 添加到所有这些都不会改变这种一致性。在这种情况下,计数论点起作用,因此主观性变得很可能。
编辑2:关于“63%”。当您在大小为N的空间中均匀地生成一个随机值时,命中给定值x的概率为 1/ N;因此,未达到给定值x的概率为 ( N -1)/ N。
现在尝试N次:您随机、均匀且独立地生成N 个值(特别是,您可能会生成多次相同的值)。对于给定的x,不属于这N个值的概率是错过N次的概率,即:
P = (( N -1)/ N ) N。
这可以近似如下:
P = e N ln (1-1/ N ) = e N (-1/ N + o(1)) = e -1+o(1)
因此,对于较大的N值,错过任何给定值的概率接近 1/ e。因此,具有N个随机值的大小为N的空间的预期覆盖率将接近 1-(1/ e )。这大约是 63.21%。