Lazy Diary @ Hatena Blog

PowerShell / Java / miscellaneous things about software development, Tips & Gochas. CC BY-SA 4.0/Apache License 2.0

レインボーテーブルで使う還元関数の作り方

レインボーテーブルでは、チェーンの各要素に対して別々の還元関数を適用する必要があるが、これをどう作るかという話。

まず、単純な還元関数の作り方を説明する。
最初に、定義域中の各記号に対して番号を振っていく。定義域が[0-9A-Z]だったら、0→0, 1→1, ……, 9→9, A→10, B→11, ……, Z→35という具合。
この場合は、定義域中の記号は36種類あることになる。
ここで、36進数というのを考える。各桁が0〜35のどれかになる数ね。

その上で、還元関数は「前のハッシュ関数の出力を36進数として解釈したもの」と定義すればよい。
例えば、レインボーテーブルに記録したいハッシュ関数の出力は16進数で16桁だったとしよう。
ハッシュ関数の出力が「394AC9F876378EA3」だったとすると、これを16進数として解釈して、10進数の数値に直すと「4128334077410905763」になる。
で、この数を36進数として表記するには、この数字を36で割った余りを[0-9A-Z]のどれかに対応させ、さらにその商を36で割って……を繰り返すわけだけれど、最終的には「VD58AL9B9WXS」になる(はず)。これを次にまたハッシュ関数の入力にすれば良いわけね。

では、チェーンの各要素で別々の還元関数を使うにはどうしたらいいの?各要素で違う関数を考えるの?という話になるわけだけれど、これは、還元関数を「ハッシュ関数の出力(上記の例だと「394AC9F876378EA3」)を36進数に直す前に演算○○をする」関数として定義すれば良い。

例えば、加える演算を「論理右シフト」なら、チェーンの最初はシフトしない、次の要素では1ビット右シフトする、次の要素では2ビット右シフトする……という具合。
上記の還元関数はハッシュ値「394AC9F876378EA3」→平文「VD58AL9B9WXS」へと還元をした。
では1ビット右シフトしたら?と考えると、ハッシュ値「394AC9F876378EA3」は2進数だと
11100101001010110010011111100001110110001101111000111010100011
なので、これを1ビット右シフトしたら
11110010100101011001001111110000111011000110111100011101010001
になる。これは16進数だと「3CA564FC3B1BC751」になる。これを36進数で表すと「X78V9XEF8IRK」になるわけで、これは上の還元関数の値とは違うよね?
これで、また別の出力をおこなう還元関数ができたってわけ。
この場合ならハッシュ値の出力は64ビットあるから、これだけで還元関数が64種類作れるね。

もっというと、演算はもっと簡単でよくて、足し算とかでもいい。
チェーンの最初は何も足さない。次は1を足す。次は2を足す。……とかでも良いわけね(これならいくらでも増やせるし)。
ハッシュ関数の入力がちょっとでも変わっていれば良いわけだから、こんな簡単な計算でも問題ないわけ。

実際にレインボーテーブルを作るときにどんな処理をやってるかはわからないけど、例えばこんな方法があるよ、という話でした。