线性同余方法(LCG)是个产生伪随机数的方法。
它是根据递归公式:Sn+1 = (A*Sn + B) mod M 进行实现,
其中A,B,M是产生器设定的常数。我们常说的种子数seed其实就是初始的S0的值。
实现算法:
1 | class prng_lcg: |
基本攻击方式有四种,基本都基于初等数学计算方法,还有基于格理论的攻击方法以后再更新。
1.对于A、B、M以及N0已知的情况
假设我们观察到有一个LCG系统产生了以下三组连续的值,并且知道内部的参数如下:
1 | # 三组连续的值 |
在已知了这些参数之后可以通过递推关系很快推算出未来的数值或者之前的某个数值。
2.增量未知
不清楚增量,但是知道以下信息:
1 | m = 81853448938945944 |
稍稍改写下公式就可以将目标c计算出来
1 | s1 = s0*m + c (mod n) |
3.增量和乘数都未知
虽然不知道增量和乘数但是知道以下数值:
1 | m = # unknown |
解决办法很简单,想想怎么解线性方程组就好了
1 | s_1 = s0*m + c (mod n) |
4.增量、乘数和模数均未知
现在内部状态基本是都不知道了,但是知道初值和随后LCG产生的连续的几个值。
1 | m = # unknown |
用线性方程式无法解决了,因为未知数太多,引入多个随机数值都会给每个方程引入新的未知量:
1 | s1 = s0*m + c (mod n) |
这就相当于六个未知数和三个方程,线性方程组不可能行得通。考虑利用gcd: 如果有几个随机数分别乘以n,那么这几个数的欧几里德算法(gcd)就很可能等于n。
1 | In [944]: n = 123456789 |
某些非0值取模运算是会等于0的:
1 | X = 0 (mod n) |
然后,根据定义,这相当于:
1 | X = k*n |
这种X != 0但是X = 0 (mod n)的情况可以加以利用。我们只需要取几个这样的值进行gcd运算,我们就可以解出n的值。这是在模数未知的情况下十分常用的方法。
在此引入一个序列 – T(n) = S(n+1) - S(n):
1 | t0 = s1 - s0 |
之后就可以得到想要的效果了:
1 | t2*t0 - t1*t1 = (m*m*t0 * t0) - (m*t0 * m*t0) = 0 (mod n) |
可以生成几个这样模是0的随机数算式,进而利用上文讲述的技巧。