【发布时间】:2011-01-21 21:58:33
【问题描述】:
我理解,根据鸽笼原理,如果物品数量大于容器数量,那么至少一个容器将多于一件物品。它是哪个容器有关系吗?这如何适用于 MD5、SHA1、SHA2 哈希?
【问题讨论】:
我理解,根据鸽笼原理,如果物品数量大于容器数量,那么至少一个容器将多于一件物品。它是哪个容器有关系吗?这如何适用于 MD5、SHA1、SHA2 哈希?
【问题讨论】:
不,它是哪个容器都没有关系,事实上这对于加密哈希并不重要; 更更重要的是birthday paradox,它表示在发现冲突之前,您只需要平均对sqrt(numberNeededByPigeonHolePrincipal) 值进行哈希处理。
因此,哈希需要足够大,以使搜索空间的平方根太大而无法进行暴力破解。 SHA1 的搜索空间平方根是 280,截至 2012 年 3 月,还没有发现具有相同 SHA1-hash 的两个值(尽管我预测这会发生在接下来的一两年……);与 SHA2 相同,这是一个哈希家族,它们都有更大的搜索空间。 MD5 一直是broken for a while。
【讨论】:
如果要散列的项目多于槽位,则会发生散列冲突。但是,如果您的哈希算法很差,那么即使项目/插槽比率非常小,您也会看到冲突。一个好的散列算法(包括你将在野外看到的大多数算法)会尝试将生成的散列尽可能均匀地分布在整个输出空间,从而最大限度地减少冲突。
请注意,哈希冲突并不是世界末日。例如,在哈希表中使用时,它只是意味着在一个槽中存储了多个项目,并且表代码将不得不遍历多一点才能找到或添加目标项目,稍微增加查找时间。
您会看到人们将 MD5 称为“损坏的”散列算法,而实际上,它只是用作加密散列的糟糕算法。它会比你自己建造的要好。
【讨论】:
散列函数的要点是将项目随机分配到容器中。对于任何好的散列函数,它不/不应该“重要”哪个容器是哪个容器,因为它们必须是不可区分的。
这不适用于试图比随机分布做得更好的“完美哈希”实现——与你提到的算法不同。
正如 Michael 所提到的,在物品数量与插槽一样多之前很久就会发生冲突。如果你想处理birthday paradox,你必须有优雅的冲突处理(或完美的哈希)。
【讨论】:
我认为您将哈希函数用于哪个应用程序是一个重要的区别。例如,散列容器中的频繁冲突会降低性能。密码学中的频繁冲突将产生更具破坏性的后果(请参阅:cryptographic hash function on Wikipedia)。
即使使用“体面”的散列算法,碰撞也相对容易发生。例如,在 Java 中,
String s = new String(new char[size]);
总是散列为 0。也就是说,所有仅包含 \0 的字符串在 Java 中散列为 0。
至于“它是哪个容器有关系吗?”,这又取决于应用程序。您可以设计散列函数,将“相似”对象散列到附近的值。例如,当您要搜索相似对象时,这很有用。只需将它们全部散列,看看它们落在哪里。在这种情况下,碰撞或接近碰撞是可取的,因为它将相似的对象分组。
在其他应用程序中,您甚至希望对象中最细微的变化都会导致完全不同的哈希值。例如,在密码学中就是这种情况,您希望尽可能确定某些内容没有被修改。在这种情况下,要找到哈希到相同值的不同对象要困难得多。
【讨论】:
根据您的应用程序,像 MDA、SHA1/2 等加密哈希可能不是理想的选择,正是因为它们看起来像是完全随机的,因此会给您带来生日悖论所预测的冲突。传统上,使用基于余数运算的简单散列的一个原因是密钥应该是序列号或类似的,因此余数运算将比随机预期的冲突更少。例如。如果键是整数 1..1000 如果散列函数是键 mod 1009,那么在大小为 1009 的容器中可能根本没有冲突。人们有时会通过仔细选择容器大小和散列函数来手动调整系统实现平分。
当然,如果您不得不担心人们恶意选择会导致您遇到困难的密钥,或者上游系统会向您发送非常有偏见的密钥(因为例如它有自己的哈希表并决定处理所有哈希到 X立刻)。您可能希望使用基于密钥加密哈希函数的哈希来防御这种情况。
【讨论】: