CLOVER🍀

That was when it all began.

Linuxで、ランダム(/dev/random、/dev/urandom)に関する情報を見る

これは、なにをしたくて書いたもの?

Linuxでの乱数生成では、/dev/randomもしくは/dev/urandomという疑似デバイスファイル(キャラクタデバイスファイル)が
使用されます。

ここで、エントロピープールがどうの、という話をよく見るわけですが、このあたりのドキュメントって見たことがないな、と
思いまして。

ちょっと調べてみようかな、と。

環境

今回の確認環境は、こちらです。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.2 LTS
Release:    20.04
Codename:   focal


$ uname -srvmpio
Linux 5.4.0-73-generic #82-Ubuntu SMP Wed Apr 14 17:39:42 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Ubuntu Linux 20.04 LTSで、カーネルは5.4です。

/dev/randomと/dev/urandom

そもそも、/dev/randomと/dev/urandomについて。

こちらについては、manを見るのがよいでしょう。

random(4): kernel random number source devices - Linux man page

urandom(4): kernel random number source devices - Linux man page

randomとurandomでページがあるのですが、内容が同じですね。

Man page of RANDOM

Man page of RANDOM

Ubuntu Linuxのドキュメントの日本語訳がしっかりしているので、こちらを見るとよいかもです。

(Linux 1.3.30 から提供されている) /dev/random 、 /dev/urandom キャラクタースペシャルファイルは カーネル乱数ジェネレーターへのインターフェースを提供する。

なので、catで読み出せます。コンソールが埋まりますけど。

$ cat /dev/random
$ cat /dev/urandom

エントロピープールは、入力デバイスなどの環境ノイズから生成されるようです。

乱数ジェネレーターはデバイスドライバやその他の源からの環境ノイズを エントロピープールへ集める。 また、ジェネレーターはエントロピープール内のノイズのビット数の推定値を保持する。このエントロピープールから乱数が生成される。

このため、Linuxの起動直後のユーザーがあまり操作していない状態でのエントロピープールは、予測可能になりやすい状態に
あると言えるようです。

よく言われる、/dev/randomと/dev/urandomの違い。

読み込みが行われると、 /dev/random デバイスはエントロピープールのノイズビットの数の推定値のうち、 ランダムバイトのみを返す。 /dev/random はワンタイムパッド (one-time pad) や鍵の生成のような 非常に高い品質を持った無作為性が必要になる用途に向いているだろう。 エントロピープールが空の時は、/dev/random からの読み出しは、 更なる環境ノイズが得られるまで、ブロックされる。

/dev/urandom デバイスから読み出しでは、 エントロピーがより高くなるのを待つためのブロックは行われない。 十分なエントロピーがない場合、 要求されたバイトを作成するのに疑似乱数生成器が使用される。 その結果、 この場合の返り値はこのドライバで使われているアルゴリズムに基づく暗号攻撃に対して、 論理的には弱くなることになる。 この攻撃をどのように行うかという事については、現在研究論文などの 形で入手できる資料はない、しかし、そのような攻撃は論理的に存在可能である。 もし、この事が心配なら、(/dev/urandom ではなく) /dev/random を利用すればいい。

乱数生成には、エントロピープールが必要になります。/dev/randomは乱数を返しますがエントロピープールが空になると
ブロックし、/dev/urandomはエントロピープールが空になると疑似乱数生成を行うもののブロックしない、という性質に
なります、と。

どちらを使うか、というのは一般には/dev/urandomで十分で、鍵などを作成する場合は/dev/random、という使い分けに
なります。

/dev/random と /dev/urandom のどちらを使うべきか迷った場合、たいていは /dev/urandom の方を使いたいと思っているはずだろう。 一般に、長期に渡って使われる GPG/SSL/SSH のキー以外の全てのものに /dev/urandom を使用すべきである。

Ubuntu Manpage: random, urandom - カーネル乱数ソースデバイス

Ubuntu Manpage: random, urandom - カーネル乱数ソースデバイス

/procやsysctlから見るランダムに関する情報

/dev/randomおよび/dev/urandomのmanページを見ていると、/procディレクトリのファイルの説明が出てきます。

ちなみに、カーネルのドキュメントには5.9から載ったみたいです。

Documentation for /proc/sys/kernel/ / random

/proc/sys/kernel/ディレクトリには、ランダムに関する情報を確認できるファイルがあります。全部で7つですね。

  • boot_id … UUIDが取得できるが、最初に取得した値から変化しない
  • entropy_avail … 現在利用可能なエントロピー数(ビット単位)
  • poolsize … エントロピープールのサイズ(ビット単位)
  • urandom_min_reseed_secs … (廃止)
  • uuid … 読み込む度にUUIDが生成される
  • write_wakeup_threshold … エントロピー数がこの値を下回ると、/dev/randomへの書き込みのためのプロセスが起動する(ビット単位)
  • read_wakeup_threshold … /dev/randomを呼び出して休止しているプロセスを起こすために必要な、エントロピー数(ビット単位)

変更できるのは、write_wakeup_thresholdとread_wakeup_thresholdくらいで、あとは読み取り専用みたいです。

現在の値を確認してみましょう。

まずは、/procファイルシステムでentropy_availを見てみましょう。

$ cat /proc/sys/kernel/random/entropy_avail
1712

少し時間を置いて見ると、値が増えます。poolsizeが増えた場合は、利用可能なエントロピーが増えたということを意味しますね。

$ cat /proc/sys/kernel/random/entropy_avail
1720

uuidについては、参照する度に値が変わります。

$ cat /proc/sys/kernel/random/uuid
be0a15c4-c32f-48c8-94af-6a0009aa4fed

$ cat /proc/sys/kernel/random/uuid
4337ab9e-137d-4b7c-8b1a-0bcedd2aa58d

sysctlでも確認できます。これなら、一気に見れますね。

$ sudo sysctl -a | fgrep kernel.random.
kernel.random.boot_id = b13979d5-7e52-45ab-a9e4-14d475cab5b3
kernel.random.entropy_avail = 1757
kernel.random.poolsize = 4096
kernel.random.read_wakeup_threshold = 64
kernel.random.urandom_min_reseed_secs = 60
kernel.random.uuid = 6fbdb4d3-29a3-4842-ac21-ff76fc5f32ec
kernel.random.write_wakeup_threshold = 1024

sysctlで見てもkernel.random.uuidは呼び出す度に値が変わりますし、kernel.random.poolsizeは呼び出した時点で
値が増えていたりします。

$ sudo sysctl -a | fgrep kernel.random.
kernel.random.boot_id = b13979d5-7e52-45ab-a9e4-14d475cab5b3
kernel.random.entropy_avail = 1766
kernel.random.poolsize = 4096
kernel.random.read_wakeup_threshold = 64
kernel.random.urandom_min_reseed_secs = 60
kernel.random.uuid = bd6fdfb2-5c0f-4a02-a675-919585c341de
kernel.random.write_wakeup_threshold = 1024

エントロピープールを消費してみる

ここで、エントロピープールを消費してみましょう。

以下のコマンドを実行している間に

$ cat /dev/random | hexdump
$ cat /dev/urandom | hexdump

別のターミナルでkernel.random.*を確認してみます。

$ watch 'sudo sysctl -a | fgrep kernel.random.'

まずは、/dev/randomから。

$ cat /dev/random | hexdump

実行するとひたすら乱数が表示され続けるので載せませんが、headで切るとこんな感じですね。

$ cat /dev/random | hexdump | head -n 10
0000000 9dd4 8cf2 c40e 9bd5 df2c 8fed fb03 1bdc
0000010 bf56 db83 4e9f e88b 5230 6175 ff41 3824
0000020 ab8e 7df8 1f17 b32b eb1f c56e bfdd 5f90
0000030 55f5 87d5 c953 e0da d60a de10 2aa2 9ea4
0000040 e7fe 7da5 8429 10f9 d39a 8f13 3e4d 4c92
0000050 accf 7e91 9022 5323 82e4 5b0b 965f 270a
0000060 406d 3447 b790 d2fc 2413 c9f6 583f 6d95
0000070 0453 01c9 9e40 5a8e e837 ea29 0077 acaa
0000080 2525 ec19 0ccb 6ee0 10c3 f8a8 3ef2 838d
0000090 30a1 26e0 c5dd fa57 0960 c153 47a4 4790

エントロピープールの状態がこうだったとして

$ sudo sysctl -a | fgrep kernel.random.
kernel.random.boot_id = d6f78aae-e986-47ad-ac06-1c5b5dba07ff
kernel.random.entropy_avail = 2334
kernel.random.poolsize = 4096
kernel.random.read_wakeup_threshold = 64
kernel.random.urandom_min_reseed_secs = 60
kernel.random.uuid = 0ea5219b-30fc-4400-be99-f58a292f98ef
kernel.random.write_wakeup_threshold = 1024

以下のコマンドを実行すると

$ cat /dev/random | hexdump

kernel.random.entropy_availの値が増減するのが確認できると思います。

が、わかりやすくブロックしません…。
※kernel.random.read_wakeup_thresholdを1024とかにしてもうまくいきませんでした…

こちらについては、kernel.random.entropy_availの値がほぼ下がりません…。

$ cat /dev/urandom | hexdump

/dev/randomの読み出しがブロックするところまで確認したかったのですが…情報はある程度見ることは
できましたし、今回はここまでにしましょうか。