Python 如何计算查找冲突所需的哈希数?

Python 如何计算查找冲突所需的哈希数?,python,hash,cryptography,Python,Hash,Cryptography,我正在开发一个程序,该程序使用十六进制字符将图像URL散列为10个字符的字符串,例如64fd54ad29 它是用Python编写的,散列计算如下: def hash_short(self, url): return hashlib.sha1(url).hexdigest()[:10] 我担心与如此短的哈希冲突。我预计大约100万次哈希之后会发生冲突,但当我运行蛮力时,我需要1000万次哈希 计算 十六进制数字有16个可能的值,或2^4。对于10个字符,我有2^40个可能性,或40位熵

我正在开发一个程序,该程序使用十六进制字符将图像URL散列为10个字符的字符串,例如64fd54ad29

它是用Python编写的,散列计算如下:

def hash_short(self, url):
     return hashlib.sha1(url).hexdigest()[:10]
我担心与如此短的哈希冲突。我预计大约100万次哈希之后会发生冲突,但当我运行蛮力时,我需要1000万次哈希

计算 十六进制数字有16个可能的值,或2^4。对于10个字符,我有2^40个可能性,或40位熵

为了获得1的概率,我们需要查看2^40+1 URL(根据鸽子洞原理),但我们预计冲突会更快

n位哈希的生日攻击(即暴力攻击)将在2^(n/2)次尝试后发现冲突。因此,我们将在大约2^20个URL后看到冲突,即1048576

暴力强迫 我编写了一个简单的Python脚本,它迭代了一长串URL,并将每个哈希值与我以前看到的进行了比较。我花了10800000个URL找到我的第一个冲突:
“http://c69025.r25.cf3.rackcdn.com/_image1/_Model/34897.jpg“
”http://media.editd.com/assets/matrix/full/72f9a997b67c65c66f4adc769ee0a127d1db25eb.jpg“
将两个哈希都添加到
“ba2be44bd1”

总结
不是我的数学不正确就是我很不走运。是哪一个?我有多倒霉?

我想你的碰撞检测代码错了:

import hashlib
import random
import string

def hash_short(url):
     return hashlib.sha1(url).hexdigest()[:10]

hashes = dict()
while True:
    if len(hashes) % 10000 == 0:
        print len(hashes)
    newurl = ''.join(random.choice(string.lowercase) for _ in xrange(30))
    newhash = hash_short(newurl)
    if newhash in hashes and newurl != hashes[newhash]:
        print 'found a collision!'
        print newhash
        print newurl
        print hashes[newhash]
        print len(hashes)
        break
    hashes[newhash] = newurl
输出(运行一次):


很明显,我所谓的URL不是,但这应该与一个好的散列函数没有区别(SHA1适合于此)。如果您发现一个数据集在SHA1的前5个字节上的冲突率非常低,那么做得很好!请使用最后5个字节重试:-)

你有多倒霉?当你有1000万个散列时,你的
2**40
空间已经占满了大约100k的一部分。因此,没有碰撞的概率大致为(手指悬空),
(99999.0/100000)**1000万,即
3.7e-44
。所以,如果我的数学是正确的[编辑:事实并非如此,请参阅评论]你在天文学上被判有罪,毫无疑问是不幸的

作为一个保守的概率上限,在已经有100万个哈希之后,你进行了900万次试验。无碰撞的概率严格小于
(999999.0/1000000)**9000000
,仅为0.0001。您可以通过进一步拆分来生成更小的边界:您进行了100万次试验,占用了900万个哈希。或者你可以精确地计算概率(CodesInChaos做到了:
1e-20


因此,贝叶斯统计就是这样,我估计代码中出现错误的概率比所有这些数字都高,即使是非常大的保守界:-)

您尝试过多少次?您的代码不正确。它应该只需要那么长的时间,概率为10^-20@alKid我只搜索了一次冲突,但我使用的是程序中的实际URL。@code我已经添加了代码。这是非常简单的东西,我没有看到任何明显的错误。@CodesInChaos:你使用了和我一样的估算技术(即你在抱怨我的计算器)还是更好的估算技术(即你在抱怨我的手指在空中)?我使用了两种技术。根据经验:
exp(-0.5*10^2)
作为一种正确的技术
n=2**40;p=1;对于(inti=0;;i++){p*=(n-i)/n;}
@CodesInChaos:那就足够了。不过,正如你所说,我的估计“只”差10^-20;-)“用最后5个字节再试一次”啊哈!我尝试了不同的5字节片段,发现140万和190万个URL之间的冲突取决于片段。我不得不得出结论,给我的数据集一直在丢弃具有相同散列的URL。@Wilfredhaughes:啊,是的,我没有想到创建你的数据集的人可能知道你在计划什么,并采取行动破坏你的一天;-)
import hashlib
import random
import string

def hash_short(url):
     return hashlib.sha1(url).hexdigest()[:10]

hashes = dict()
while True:
    if len(hashes) % 10000 == 0:
        print len(hashes)
    newurl = ''.join(random.choice(string.lowercase) for _ in xrange(30))
    newhash = hash_short(newurl)
    if newhash in hashes and newurl != hashes[newhash]:
        print 'found a collision!'
        print newhash
        print newurl
        print hashes[newhash]
        print len(hashes)
        break
    hashes[newhash] = newurl
...
770000
780000
found a collision!
216be03ec7
txnbkwrfkpkmiexloxrifdsnjumkex
xlnmlhobtsswjvmqnjupaybkspptpo
780758