MT19937算法及攻击方式
梅森旋转算法(Mersenne twister)是一种周期很长的的伪随机数生成算法,可以快速的产生高质量的伪随机数,python,php以及多种语言中的随机数模块均采用此算法。
MT19937算法原理
梅森旋转算法的最长周期取自一个梅森素数 219937-1,由此命名为MT9937。常见的两种为32位的MT19937-32和64位的MT19937-64
mt19937的随机数生成器结构由一个32位的种子,然后生成一个具有624个32位数的状态数组。这些整数按顺序输出,全部输出后对该状态应用算法以获取下一组 624 个整数。
生成时分三个过程:
利用seed初始化状态
对状态进行旋转
根据状态提取伪随机数
- 32位的MT19937的python代码
1 | def _int32(x): |
攻击
已知624组
这种情况只需要将已知的624组填入状态矩阵,即可进行预测和回溯。
有许多可以进行这类攻击的py库,如randcrack,mt19937predictor等,使用方法大同小异。
1 | from randcrack import RandCrack |
实际应用中,并不是所有的伪随机数都是32位的,也会出现8位、16位、64位、96位。当然也有可能出现其他不规则的情况。因此,上面提到的“连续624组32位”可以被替换成,“连续19968个比特位”。
任意19968个bit
在使用getrandbits()时如果是输出32的倍数时,会将前生成的32比特放在低位,新生成的拼接在高位。
而getrandbits(16) 中,只返回高16比特,其余比特位被丢弃。getrandbits(33) 中,低 32 比特取自第一组,而高1位取自第二组的最高位比特,第二组低31比特被丢弃。
如果给出了不连续的19968比特,这时候我们可以使用已知的部分构建矩阵求解:
对于每一个比特位bi,可以和状态矩阵state用一个线性关系表示出来
如果T是满秩的,求解这个矩阵方程就可以得到s的唯一解,成功复原state,便可以进行溯源和预测。
所以接下来我们只要想办法获得b和T即可。
b的获取
事实上只要我们的已知的任意位置数据大于19968比特即可,但是我们需要知道其对应的位置来确定线性关系以构造方程。
T的获取
虽然已知数据中我们并没有办法获得T,但是其实这个线性关系是可以根据MT19937算法本身得到的。也就是说,如果已知b的对应比特在MT19937产生的随机数输出的位置,则其对应的T也是固定的。
寻找T的过程类似于一个黑盒调用。具体来说,如果我们取s为:(1,0,0,…,0)
按照这个s设置state,然后调用getrandbits(),生成我们已知位置的比特,得到 b’ ,此时得到的 b’ 既是T的第一行。
同理,我们接下来取s:(0,1,0,…,0)
得到T的第二行。全部进行完即可得到完整的T
代码实现:
1 | Dall=list(map(int,open('data3.txt','r').readlines())) |