java が生成する乱数を予測する
java が生成する17兆通りある乱数を予測してみる
1. 乱数生成をするスクリプトを書く
2. java の乱数生成を調べる
「予測できるらしい」しかしらないのでちゃんと調べる
Random random = new Random();
random.nextInt();
この nextInt() はソースコード内で next() を呼び出していて,next() のソースコードは以下のようになる
protected synchronized int next(int bits)
{
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
return (int) (seed >>> (48 - bits));
}
このとき乱数は seed で返されるが,それ以外の値は全て既知. なので,ひとつ seed が分かればそれ以降の seed も知ることができる. 取り出す seed は必要な上位 bit 分を最後の演算で呼び出す. 例えば nextInt(32) で呼び出したとき,返ってくるのは 32 bit の乱数で,残りの 16 bit が未知ということになる. これくらいなら簡単に予測できる
3. 4 bit だけで予測する
予測すべき範囲が 2^44 bit になるので,さっきより甚大に増える. そこで,もらえる乱数を増やして考える. さっきの変数をそれぞれ a=0x5DEECE66DL, b=0xBL, m = ((1L « 48) - 1) とおく.
$$x_{i+1} = a x_i + b \mod m$$
以下では mod m を省略する. 係数で b が邪魔なので,与えられた複数の乱数 x を組み合わせると,例えば以下のようになる.
$$x_3 - x_2 = a(x_2 - x_1)$$
このとき,与えられた乱数を seed, それ以外の未知乱数(システム内部にある)を unknown_seed とすると, 以下のようになる.
$$x_2 - x_1 = (seed*2^{44} + unknown\_seed)$$
m 倍すれば mod 演算の答えは 0 になる.
$$m(x_2 - x_1) = 0$$ $$(x_3-x_2) - a(x_2-x_1) = 0$$ $$(x_4-x_3) - a^2(x_2-x_1) = 0$$ $$(x_5-x_4) - a^3(x_2-x_1) = 0$$
で,これをさっきの式と展開すると,
$$m(x_2-x_1)=m(seed2^{44}+unknown\_seed)=s~m$$
$$m~unknown\_seed = s~m - m~seed2^{44}$$
となる.(s は係数で,x_2-x_1 以外は m が消えないのでいったん消さずにこのまま) このとき,右辺が無視できるほど小さいとすると(ε),
$$s~m - m~seed~2^{44} = ε$$ $$s~m = ε - m~seed~2^{44}$$ $$s = ε/m - m~seed~2^{44}/m$$
となるが,ε/m は無視できるほど小さいので,右辺は既知の数だけになる. すると s が求まる. 先の右辺を小さくするために工夫をする必要がある.
s が求まれば, $$x_2 = ax_1 + b$$ と合わせて $x_1, x_2$ が分かる. $x_1 = (x_2-x_1-c)/(a-1) \mod M$ となるが,M と $(x_2-x_1-c)/(a-1)$ が互いに素でないので,逆数($a-1$) が求まらない. そこで,$x_1(a-1) = (x_2-x_1-c) \mod 2$ みたいにすれば総当たりで得ることができる.
4. LLL
LLL を知らないのでいくつか例を交えつつ見てみる.
格子は,一次独立な n 本のベクトル $v_1, v_2, …, v_n \in \mathbb{R}^m(1 \leq i \leq n)$ のすべての整数係数の線形結合で表される集合全体をいう. 単純な座標は(1,0), (0,1) からなる格子といえる. このときのベクトルの集合 $ \{v_1, v_2, …, v_n\}$ を格子の基底という. $n$ を格子の次数という.
- SVP : 格子に含まれるベクトルのうち,もっとも短い($\neq 0$)ものを探す問題
- CVP : ベクトル空間に含まれるベクトル v が与えられたとき,これにもっとも近い格子 L 上の 点を見つける問題
- LLL : 同じ格子を張れるような別の短い・直交した基底を求める問題.
Markle-Hellman Knapsack 暗号
$n$ ビットの平文を暗号化するために,超増加列 $w$ とランダムな $r, q$ を用意する. 超増加列 $w$ は,$w_0 \leq w_1, w_0 + w_1 \leq w_2, …$ となるような順列のことをいう. また,$\beta = r~w \mod q$ とする. $a$ を平文とする. このとき, $$ c = \sum a_i~\beta_i$$ で表される暗号文があり,$c, \beta$ から $a$ を求めることが難しい,というのが今回の問題. このとき,$a_0\beta_0 + a_1\beta_1 + … + -c = 0$ と式変形する. このとき,未知の $a$ を $x$ とおき,LLL を使って,格子を保つ小さな $x$ を求めることができる.
\begin{array}{cc} (x_0 & x_1 & … & 1) * (1 & 0 & … &\beta_0)(0 & 1 &…& \beta_1)… \end{array}
とすれば,計算結果は $(x_0~x_1~…~0)$ となるはず. LLL は $(x_0 ~ x_1 ~ … ~ 1)$ をなるべく小さくしたいので,LLL に投げれば求まる.