乱数生成
Wikipedia:Random number generation より:
- 乱数生成器 (RNG) とはパターンの存在しない (つまりランダムな) 数字や記号の羅列を生成するための計算法または物理デバイスのこと。
ランダムなデータの生成はアプリケーションによってはとても重要なことです。(ディスク暗号化などに用いる) 暗号鍵の作成やディスクの完全消去、暗号化されたソフトウェアアクセスポイントなど。
カーネル内蔵の RNG
カーネルに含まれている RNG である /dev/{u}random は暗号鍵の作成などに使われるような高いセキュリティレベルを必要とするランダムデータの生成を行うときに使うことを強く推奨されます。この乱数生成器はデバイスドライバなどの情報源から環境ノイズをエントロピープールに集めます。
man random
コマンドではライブラリ関数の man ページ random(3) が表示されてしまうので注意してください。/dev/random
デバイスに関する情報が必要なときは man 4 random
を実行して表示される random(4) を読んで下さい。
/dev/random
/dev/random
は4096ビット (512バイト) のエントロピープールを使用してランダムデータを生成します。エントロピープールが空になると、プールがまた補充されるまで待機します (これには時間がかかります)。/dev/random
は暗号鍵 (SSL, SSH, dm-crypt の LUKS など) の生成のために設計されており、HDD の中身の消去に使うのは現実的ではありません。システムに十分なエントロピーが収集されるまで止まってしまって、ディスクの消去にものすごく時間がかかるためです。エントロピーが枯渇しているような環境だと (例: リモートサーバー)、永遠に終わらないかもしれません。巨大なディレクトリで検索コマンドを実行するとか、X でマウスを動かすなどをすればエントロピープールは段々と溜まっていきますが、そもそもプールサイズ自体がディスクの消去を行うには不十分な造りになっています。
/proc/sys/kernel/random/entropy_avail
と /proc/sys/kernel/random/poolsize
を比較することでシステムのエントロピープールを目で追うことができます。
Linux カーネル 2.4 ではエントロピーのプールサイズを変更できるように書き換え可能な /proc
エントリが出来ましたが、新しいカーネルでは read_wakeup_threshold
と write_wakeup_threshold
しか書き換えできないようになっています。現在、プールサイズはカーネルの /drivers/char/random.c
の275行目でハードコード (決め打ち) されています:
/* * Configuration information */ #define INPUT_POOL_WORDS 128 #define OUTPUT_POOL_WORDS 32 ...
カーネルのプールサイズは INPUT_POOL_WORDS * OUTPUT_POOL_WORDS
で求めることができ、既に述べたように、4096ビットとなります。
/dev/urandom
/dev/random
と反対に、/dev/urandom
は初期のランダムなシードを使ってストリーム暗号の元とします [1] (カーネル 4.8 以上)。
ストリーム暗号を使用することで、ディスクの徹底消去やブロックデバイス暗号化の準備、LUKS キースロットの消去などの目的に叶うような高品質な疑似乱数データを大量に生成します。
システムの状態や乱数の目的によって使用価値は大きく変わります。例えば、システムが立ち上がったばかりなのか、あるいはエントロピープールを十分に満たすだけの時間を経ているのかは大変重要です。
以下の記事は様々な視点から /dev/urandom
の有用性を説明しています:
- Myths about urandom - 非常に勉強になりますが誤謬も含まれています。
- Safely generate random numbers - エントロピーの収集についてカーネルの仕組みを変更しようとしたときに考えるべきことを説明しています。
Arch Linux の場合、/dev/urandom
には以下のことが当てはまります:
- 可能であればカーネルの
getrandom()
システムコールを使ってください。/dev/urandom
が正しく初期化されていることを確認できます。getrandom(2) を参照。 /dev/urandom
デバイスを使用して長期目的でエントロピーを生成する場合、正しく初期化されることを必ず確認してください。- 起動時に
/dev/urandom
のエントロピーを使用する重要なサービスがある場合は注意してください。最近のシステムでもカーネルのエントロピー初期化は数秒かかることがあり、(仮想化環境など) 場合によってはさらに時間がかかります。カーネルは警告はしますが、いつまでもというわけではありません [2]。
出力例:
$ dmesg | grep -e random: ... [ 0.844818] random: systemd-udevd: uninitialized urandom read (16 bytes read) [ 7.022171] random: fast init done [ 12.916705] random: crng init done
上記の例では、プールを初期化する十分なエントロピーをカーネルが収集する前にデフォルトのブートターゲットに達してしまっています。systemd は起動の初期段階でエントロピーを必要とするため [3]、ブートプロセスでエントロピープールが枯渇してしまう可能性があります。
起動段階で問題があると、/dev/urandom
プールから十分な品質のエントロピーを得られないまま他のサービス (ウェブサーバーの OpenSSL セッションなど) が起動します。そして、OpenSSL の通常の設定では同じシードを使い続け、新しいシードを要求するのはセッションを再起動したときだけです。
代替案
長い暗号鍵の生成などは行わないアプリケーションの場合、パフォーマンスとセキュリティの折り合いをつけて擬似乱数生成器を使うこともあります。Arch Linux のリポジトリには以下のような擬似乱数生成器が存在します:
Yarrow (macOS) や Fortuna (FreeBSD Yarrow の後継)のような暗号的に安全な疑似乱数生成器もあります
OpenSSL issues
OpenSSL supports RDRAND as rdrand
engine (use openssl engine
to view available OpenSSL engines), but OpenSSL and related applications (such as Nginx, Stunnel and CURL) may not use RDRAND by default. OpenSSL does not use the /dev/urandom
directly.
For example, you can pass -engine rdrand
option to the openssl
command line tools, or you can add ssl_engine rdrand;
line to the /etc/nginx/nginx.conf
for nginx.
参照
- RFC4086 - Randomness Requirements for Security (Section 7.1.2 for /dev/random)
- Linux Kernel ML - /dev/random のスループットを高めるパッチの議論 (2013年2月)
- A challenge on /dev/random robustness (2013年6月)
- エントロピーが少ない状態での /dev/random, Yarrow, Fortuna の挙動の解析 (2014年3月)
- Randomness - 様々な RNG について説明している科学記事
- ENT - ランダムシーケンスをテストするシンプルなプログラム (エントロピー, カイ二乗検定, モンテカルロ, 相関など)
- DIY HRNG - 安価な DIY の Arduino HRNG の例
- An Analysis of OpenSSL's Random Number Generator - Paper on RNG reseeding risks in OpenSSL functionality
- Linux Random Number Generator – A New Approach - デバイスのアップデートに関する論文 (2017年3月)