转自 数学345 聊聊生日悖论和生日攻击
我们先聊生日悖论问题,后面再看看什么是生日攻击。
也许你的数学老师问过这样的问题,同学们,我们班上有30个人,你认为至少两个人拥有相同生日的概率会很高吗,比如超过50%?这个问题也可以这样问:至少需要多少人的聚会,才能使得至少两个人拥有相同生日的概率足够高,比如超过50%?
这里为了简单,我们不考虑闰年的情况,不妨认为一年就365天。直觉可能会告诉我们需要大概183个人(即应该为一年所拥有天数的一半)才会较大概率的出现相同的生日。然而你也许已经知道了这个直觉是不正确的,事实上,我们需要的人数远远小于这个数目。
我们首先计算两个人不是同一天生日的概率,即他们的生日没有冲突的概率。
对1个人而言,不冲突的概率是1,这个问题非常简单,因为单个生日不可能与任何其他人的生日冲突。
对2个人而言,不冲突的概率为364/365,因为他/他只会与第一个人的生日出现冲突,即2个人不冲突的概率为
P
(
2
)
=
1
−
1
365
P(2)=1-\frac{1}{365}
P(2)=1−3651
如果第3个人加入这个聚会,他/她可能与前2个人已经在那里的人出现冲突,因此3个人不冲突的概率为:
P
(
3
)
=
(
1
−
1
365
)
⋅
(
1
−
2
365
)
P(3) = (1-\frac{1}{365}) \cdot (1-\frac{2}{365})
P(3)=(1−3651)⋅(1−3652)
所以,t个人之间生日不冲突的概率为:
P
(
t
)
=
(
1
−
1
365
)
⋅
(
1
−
2
365
)
…
(
1
−
t
−
1
365
)
P(t) = (1-\frac{1}{365}) \cdot (1-\frac{2}{365}) \dots (1-\frac{t-1}{365})
P(t)=(1−3651)⋅(1−3652)…(1−365t−1)
当t=366个人时,冲突的概率为1,因为一年只有365天。
现在回归到我们最初的问题:总共需要多少人才能使两个冲突生日的概率达到50%?
从上面的等式可以得到,仅需要23个人就能使出现生日冲突的概率达到0.5,因为至少一个冲突的概率:
P
=
1
−
(
1
−
1
365
)
⋅
(
1
−
2
365
)
…
(
1
−
23
−
1
365
)
=
0.507
P=1-(1-\frac{1}{365}) \cdot (1-\frac{2}{365}) \dots (1-\frac{23-1}{365})=0.507
P=1−(1−3651)⋅(1−3652)…(1−36523−1)=0.507
注意:如果聚会有40个人参加,则出现生日冲突概率大概为90%由于这个思想实验的结果令人匪夷所思,所以它通常也叫生日悖论。
那什么是生日攻击呢?生日悖论和生日攻击有又什么关系呢?我们经常使用电子钱包,在网络上支付、转账、收款,因此必须有种机制来保证这些资金的安全。比如你购买一个东西,下了订单,这个订单从你的手机上发到商家手机上,必须得保证订单没有被修改过。如何保证呢?这里会使用到一种签名的技术,就像是我们的纸质合同上盖了印章一样,可以进行校验是否被篡改了。一种常见的签名技术是使用RSA算法。RSA算法是建立在整数因式分解问题上的:两个大素数相乘在计算上是非常简单的;但是反过来,知道其乘积结果,要想对乘积结果进行因式分解确是非常困难的。这里不对RSA算法展开讨论,但是需要知道在实际上签名的时候,不是直接对原始信息进行RSA签名,而是先对原始信息计算出一串很短的固定的哈希值,然后对该哈希值进行签名。对哈希值进行签名有很多好处,比如由于哈希值非常短,所以比起对原始信息签名来说快很多,签名的结果也相对变短了,重要的是更加安全(这里就不展开阐述了)。这里重点关注的是哈希值。
哈希值是通过哈希函数
z
=
h
(
x
)
z=h(x)
z=h(x)计算的,比如MD5就是一种hash函数,经常用来保护我们输入的密码。比如你在设置密码时候,输入一串密码"123456",那么后台服务器实际上保存的z,
其中
z=h(“123456”)=“e10adc3949ba59abbe56e057f20f883e”,
下一次你登入的时候,输入相同的密码"123456",
那么后台服务器只需要比较此次计算出来的
z
1
=
h
(
"
123456
"
)
z_1=h("123456")
z1=h("123456")和你之前设置密码时保存的z是否相同即可。如果
z
1
=
z
z_1=z
z1=z,那么密码验证通过,如果
z
1
≠
z
z_1 \neq z
z1=z 那么密码验证失败。
为什么不直接保存密码,比较密码是否相等呢?因为如果后台服务器直接保存你设置的密码,那么你的密码就非常容易泄露了:任何人看到了这个明文密码都知道怎么登入你的账号了。保存哈希函数计算的值z,能提高安全性,这是因为哈希函数有以下几种特性:
1.任意的消息大小 h(x)对任何大小的消息x都适用。
2.固定的输出长度
h(x)生成的哈希值z的长度是固定的。比如MD5的哈希值长度为128比特,即16个字节,也就说32个16进制字符(比如前面提到的例子"e10adc3949ba59abbe56e057f20f883e")。
3.有效性 h(x)的计算相对简单。
4.单向性 给定一个输出z, 找到满足h(x)=z的输入x是不可能的。
5.弱抗冲突性
给定
x
1
x_1
x1和
h
(
x
1
)
h(x_1)
h(x1),找到满足
h
(
x
1
)
=
h
(
x
2
)
h(x_1)=h(x_2)
h(x1)=h(x2)的
x
2
x_2
x2在计算上是不可能的。
6.强抗冲突性
找到满足
h
(
x
1
)
=
h
(
x
2
)
h(x_1)=h(x_2)
h(x1)=h(x2)的一对
x
1
≠
x
2
x_1 \neq x_2
x1=x2在计算上也是不可行的。这里
x
1
,
x
2
x_1,x_2
x1,x2都是可以随意构造的消息。
其中4,5特性保证了密码哈希的安全性:因为特性4,使得攻击者即使知道z,也无法知道你的明文密码x;因为特性5,使得攻击者无法找到另外一个不同于你的明文密码 x 2 x_2 x2登入你的账号。
密码的安全性不需要特性6强抗冲突性,已经足够安全,但是特性6可以用来保护上面提到的签名。简单的说如果哈希函数不具备特性6,那么攻击者可以制造出两份签名一样的订单,但是实际上这2份订单的内容是不一样的。
事实上,由于z是固定长度的,也就z的总数是固定的,根据鸽笼原理,冲突始终存在。因此我们只能说在计算上是不可行的,意思是使用现在已知的计算机技术是很难的。
现在的问题是,找到冲突的难度有多大?
比如假设哈希值z总共有
2
80
2^{80}
280种,那么请问攻击者大概要尝试多少次才能找到找到不同的一对消息x,但他们的哈希值都一样。
这个问题其实和前面提到的生日悖论问题一样。我们的直觉可能又会犯错,我们可能猜想需要检查 2 80 2^{80} 280个消息x。然而事实证明,攻击者只需要检查 2 40 2^{40} 240个消息!这个结果非常令人惊讶。这种攻击叫生日攻击,它是密码分析学中经常使用的一个非常强大的工具。下面我们将进行阐明为什么攻击者只需要检查 2 40 2^{40} 240个消息!
在哈希函数中,每个元素对应的可能值不是365个,而是
2
n
2^n
2n个,其中n为h的输出宽度。实际上,n是哈希函数最重要的安全参数。问题是,对于选定的
x
i
x_i
xi和
x
j
x_j
xj而言,
攻击者需要对多少个消息
(
x
1
,
x
2
,
…
,
x
t
)
(x_1,x_2,\dots,x_t)
(x1,x2,…,xt)进行哈希才能使得
h
(
x
i
)
=
h
(
x
j
)
h(x_i)=h(x_j)
h(xi)=h(xj)的概率足够大?
t个哈希值之间不存在冲突的概率为:
P
(
没有冲突
)
=
(
1
−
1
2
n
)
(
1
−
2
2
n
)
⋯
(
1
−
t
−
1
2
n
)
=
∏
i
=
1
t
−
1
(
1
−
i
2
n
)
\begin{aligned} \begin{aligned} P(\text { 没有冲突 }) &=\left(1-\frac{1}{2^{n}}\right)\left(1-\frac{2}{2^{n}}\right) \cdots\left(1-\frac{t-1}{2^{n}}\right) \\ &=\prod_{i=1}^{t-1}\left(1-\frac{i}{2^{n}}\right) \end{aligned} \end{aligned}
P( 没有冲突 )=(1−2n1)(1−2n2)⋯(1−2nt−1)=i=1∏t−1(1−2ni)
从指数函数的 Taylor
级数表示:
e
−
x
=
1
−
x
+
x
2
/
2
!
−
x
3
/
3
!
+
⋯
e^{-x}=1-x+x^{2} / 2 !-x^{3} / 3 !+\cdots
e−x=1−x+x2/2!−x3/3!+⋯知, 以下近似值成立
e
−
x
≈
1
−
x
e^{-x} \approx 1-x
e−x≈1−x 因为
i
/
2
n
<
<
1
i/2^n << 1
i/2n<<1.所以这个概率可以近似为:
P
(
没有冲突
)
≈
∏
i
=
1
t
−
1
e
−
i
2
n
≈
e
−
1
+
2
+
3
+
⋯
+
t
−
1
2
n
\begin{aligned} \begin{aligned} P(\text { 没有冲突 }) & \approx \prod_{i=1}^{t-1} e^{-\frac{i}{2^{n}}} \\ & \approx e^{-\frac{1+2+3+\cdots+t-1}{2^{n}}} \end{aligned} \end{aligned}
P( 没有冲突 )≈i=1∏t−1e−2ni≈e−2n1+2+3+⋯+t−1
而
1
+
2
+
⋯
+
t
−
1
=
t
(
t
−
1
)
/
2
,
1+2+\dots +t-1=t(t-1)/2 ,
1+2+⋯+t−1=t(t−1)/2, 所以概率的近似值可以写作
P
(
没有冲突
)
≈
e
−
t
(
t
−
1
)
2
⋅
2
n
P(\text{没有冲突}) \approx e^{-\frac{t(t-1)}{2\cdot 2^n}}
P(没有冲突)≈e−2⋅2nt(t−1)
回顾一下,我们的目标是确定找到一个冲突所需要的消息个数
(
x
1
,
x
2
,
…
,
x
t
)
(x_1,x_2,\dots,x_t)
(x1,x2,…,xt),因此现在要做的就是求解等式中的t。如果将至少存在一个冲突的概率表示为
λ
=
1
−
P
(
没有冲突
)
\lambda=1-P(\text{没有冲突})
λ=1−P(没有冲突),则
λ
≈
1
−
e
−
t
(
t
−
1
)
2
n
+
1
ln
(
1
−
λ
)
≈
−
t
(
t
−
1
)
2
n
+
1
t
(
t
−
1
)
≈
2
n
+
1
ln
(
1
1
−
λ
)
\begin{aligned} \begin{aligned} \lambda & \approx 1-e^{-\frac{t(t-1)}{2^{n+1}}} \\ \ln (1-\lambda) & \approx-\frac{t(t-1)}{2^{n+1}} \\ t(t-1) & \approx 2^{n+1} \ln \left(\frac{1}{1-\lambda}\right) \end{aligned} \end{aligned}
λln(1−λ)t(t−1)≈1−e−2n+1t(t−1)≈−2n+1t(t−1)≈2n+1ln(1−λ1)
由于实际中
t
>
>
1
t>>1
t>>1,则
t
2
≈
t
(
t
−
1
)
t^2 \approx t(t-1)
t2≈t(t−1) 始终成立,因此,
t
≈
2
n
+
1
ln
(
1
1
−
λ
)
t
≈
2
(
n
+
1
)
/
2
ln
(
1
1
−
λ
)
(
1
)
\begin{aligned} \begin{array}{l} t \approx \sqrt{2^{n+1} \ln \left(\frac{1}{1-\lambda}\right)} \\ t \approx 2^{(n+1) / 2} \sqrt{\ln \left(\frac{1}{1-\lambda}\right)} \quad \quad (1)\end{array} \end{aligned}
t≈2n+1ln(1−λ1)t≈2(n+1)/2ln(1−λ1)(1)
等式(1)非常重要:它将寻找一个冲突所需要哈希的消息t的数目表示为哈希输出长度n和冲突概率 λ \lambda λ的函数。生日攻击最重要的结论就是:找到一个冲突所需要哈希的消息的数目大概等于可能输出值个数的平方根,即大概为 2 n = 2 n / 2 \sqrt{2^n}=2^{n/2} 2n=2n/2。
例如,假设我们想找到输出长度为80位的一个哈希函数(此函数是假设的)的冲突。为了使成功的概率达到50%,我们需要对
t
=
2
81
/
2
ln
(
1
/
(
1
−
0.5
)
)
≈
2
40.2
t=2^{81 / 2} \sqrt{\ln (1 /(1-0.5))} \approx 2^{40.2}
t=281/2ln(1/(1−0.5))≈240.2
个输入值进行哈希。使用当前的计算机完成
2
40
2^{40}
240左右哈希值的计算并检查冲突是可能的!正因为这个原因,所有哈希函数的输出长度必须至少为128位,而大多数现代哈希函数的输出长度会更长。比如
SHA-1输出长度为160位, SHA-256输出长度为256位。
欢迎关注我的微信公众号[数学345]:长按"识别图中二维码";或打开微信扫一扫。