乱数はコンピュータ・システムにおいて非常に重要な位置を占めており、乱数なしではおそらく多くのアプリケーションに支障をきたすでしょう。乱数は暗号やセキュリティの分野でも不可欠です。この記事では、乱数の概念と重要性、Linuxシステムで乱数が生成される方法、そして最後にKVM仮想マシンで乱数を生成するためにハードウェア乱数ジェネレータを追加して使用する方法を紹介します。
乱数とは何ですか?
乱数は、カードゲームのカードの配布からSSLセキュリティプロトコルのキーの生成まで、多くのソフトウェアやアプリケーションで必要とされています。乱数には少なくとも2つの条件があります:
- 数字の並びは統計的にランダム
- 既知の塩基配列から後の塩基配列を推定することはできません。
コンピュータが生成する高品質な乱数列の探索は、コンピュータの黎明期から研究者の長年の関心事でした。一般的に、プログラムの動作が予測可能であるため、コンピュータプログラムを使用して真の乱数を生成することは困難であり、設計されたアルゴリズムとユーザー提供のシードを組み合わせてコンピュータが生成した乱数列は、通常「擬似乱数」であり、擬似乱数はしばしば「乱数」として使用されます。乱数」。擬似乱数は一般的なアプリケーションのニーズを満たすことができますが、セキュリティ要件が高い環境やドメインでは明らかな欠点があります:
- 擬似乱数とは、周期的に繰り返される乱数列のことです。
- 同じアルゴリズムで同じシード値を与えれば、まったく同じ乱数列が得られます。
- リバース・エンジニアリングを使えば、シード値からアルゴリズムを推測し、それに続く乱数系列をすべて予測することができます。
真の乱数は、真空中の素粒子の量子的な上昇と下降によって発生するノイズ、ノイズ中の超高輝度発光ダイオードの量子的不確実性、放射性崩壊など、物理世界の物事のランダム性の助けを借りてのみ生成することができます。
乱数が重要な理由
乱数の生成は、暗号鍵、暗号化アルゴリズム、暗号化プロトコルの生成に不可欠な暗号技術の基本的な作業であり、乱数の品質はセキュリティにとって非常に重要です。最近、何者かが乱数の欠点を利用してウェブサイトを攻撃し、管理者へのアクセスに成功したことが報告されました。米国とフランスのセキュリティ研究者も最近、Linuxカーネルの2つのPRNG(/dev/randomと/dev/urandom)のセキュリティを評価し、Linuxの擬似乱数生成器はセキュリティの概念である頑健性を満たしておらず、エントロピーを正しく蓄積していないと結論付けました。乱数がセキュリティシステムにおいて非常に重要な位置を占めていることは明らかです。
Linuxにおける乱数の生成方法
PRNG(疑似乱数生成器)
1994年、米国人ソフトウェアエンジニアのTheodore Y. Ts'oは、パスワード強度を向上させるため、暗号の代わりにSHA-1ハッシュアルゴリズムを使用して、Linuxカーネルに乱数発生器を初めて実装しました。
Linux カーネルは、データのランダム性を表すためにエントロピーを使用します。 エントロピーとは、システムのカオスと無秩序の程度を表す物理量で、システムのエントロピーが大きいほど秩序がない、つまり不確実性が高いことを意味します。カーネルはエントロピー・プールを維持し、デバイス・ドライバやその他のソースからの環境ノイズを収集します。理論的には、エントロピープール内のデータは完全にランダムであり、真の乱数シーケンスを生成するように実装することができます。エントロピー・プール内のデータのランダム性を追跡するために、カーネルはデータがプールに追加されるときにデータのランダム性を推定します。エントロピー推定値はプールに含まれるランダムビットの数を表し、値が大きいほどプール内のデータのランダム性が高いことを示します。 カーネルの乱数生成器PRNGはキャラクタデバイスrandomで、そのコードはdrivers/char/random.cに実装されています。このデバイスは、システム環境のノイズデータを取得してエントロピープールに追加するための一連のインターフェース関数を実装しています。システム環境のノイズデータには、デバイスの2つの割り込みの間隔、入力デバイスの操作間隔、および連続するディスク操作の間隔が含まれます。 対応するインターフェースには
void add_device_randomness(const void *buf, unsigned int size); 
void add_input_randomness(unsigned int type, unsigned int code, 
                unsigned int value); 
void add_interrupt_randomness(int irq, int irq_flags); 
void add_disk_randomness(struct gendisk *disk); 
カーネルは、他のカーネルモジュールが使用できるように1つのインターフェースを提供します。
void get_random_bytes(void *buf, int nbytes); 
このインターフェースは、指定されたバイト数の乱数を返します。 random ランダムデバイスは、ユーザーランドプロセスが使用する2つのキャラクタデバイス - /dev/randomと/dev/urandom - を提供します:
- /dev/randomは、高品質の乱数を必要とするリクエストに使用されます。 エントロピープールに十分なデータがない場合、dev/randomデバイスを読み込むと、プール内のノイズの総量よりも少ないランダムバイトが返されます。/dev/randomは、高いランダム性を持つ公開鍵やワンタイム暗号文を生成することができます。エントロピープールが空の場合、/dev/randomへの読み込みは、周囲のノイズが十分に収集されるまでブロックされます。この設計により、/dev/randomは真の乱数生成器となり、ランダムデータのエントロピーを最大限に高めることができます。
- /dev/urandomは、擬似乱数データを生成するためにエントロピープールのデータを再利用するノンブロッキング乱数生成器です。つまり、/dev/urandomへの読み取り操作はブロックされませんが、その出力は/dev/randomの出力よりもエントロピーが小さくなる可能性があります。これは、ほとんどのアプリケーションで許容可能なランダム性を持つ、より強度の低いパスワードを生成するための擬似乱数生成器として使用できます。
/dev/randomも書き込み可能で、どのユーザーもエントロピープールにランダムデータを追加できます。エントロピープールのサイズを増やすためにioctlを呼び出すことができるのは管理者だけなので、ランダムでないデータを書き込むことさえ無害です。Linuxカーネルのエントロピーの現在の値とサイズは、例えば/proc/sys/kernel/random/にアクセスすることで取得できます:
# cat /proc/sys/kernel/random/poolsize 
4096 
# cat /proc/sys/kernel/random/entropy_avail 
298 
# cat /proc/sys/kernel/random/uuid 
4f0683ae-6141-41e1-b5b9-57f4bd299219 
しかし、Linuxカーネルの乱数発生器にはいくつかの弱点があります。 組み込みシステム、Live CDシステム、ルーター、ディスクレスワークステーション、および一部のサーバーシステムでは、周囲のエントロピー源がより限られているため、乱数の品質が低下します。NVRAMを搭載したシステムでは、電源切断時に乱数発生器の状態の一部を保存し、次の電源投入時に復元できるようにすることを推奨します。ルーターの場合、ネットワークデータをエントロピーの主なソースとして使用することができます。
でんしせいぎょブレーキシステム
EGDは、/dev/randomデバイスをサポートしていないUnixシステムでも、同様の機能を提供できることがよくあります。これはユーザーランドで動作するデーモンで、暗号化用に高品質のランダムデータを提供します。OpenSSL、GNU Privacy Guard、Apache HTTPサーバーなどの一部の暗号化ソフトウェアは、/dev/randomが利用できない場合にEGDの使用をサポートしています。
EGD、または同様のソフトウェアprngdは、さまざまなソースから擬似ランダム・エントロピーを収集し、このデータを処理してバイアスを除去し、暗号の品質を向上させ、その後、他のプログラムがUnixドメイン・ソケットまたはTCPソケットを介してその出力にアクセスできるようにします。このプログラムは通常、サブプロセスの生成を使用してシステムの状態を照会することでエントロピーを収集します。照会る状態は通常、CPU、I/O、ネットワーク使用量、ログファイルや一時ディレクトリの内容など、揮発性で予測不可能なものです。
EGDは簡単なプロトコルで乱数を必要とするクライアントと通信し、クライアントはEGDソケットに接続してコマンドを送信します:
- command 0: 現在利用可能なエントロピーの照会
- コマンド 1: ノンブロッキング・ランダム・バイト・フェッチ
- コマンド 2: ランダム・バイトのブロック
- command 3:
ハードウェア乱数生成器
現在、信頼性の高い乱数を生成するためのハードウェア乱数ジェネレータは数多くありますが、どれも市販されており、比較的高価です。
IntelのIvy Bridgeファミリーには「Secure Key」と呼ばれる機能があり、プロセッサには乱数を生成するためのハードウェアDRNGが内蔵されています。Linuxカーネルは、RDRANDによって生成された乱数を、アウトオブオーダー操作を使ってエントロピー・プールに混ぜます。 このコードは、drivers/char/random.cのextract_entropy()関数に実装されています。
for (i = 0; i < LONGS(EXTRACT_SIZE); i++) { 
unsigned long v; 
if (!arch_get_random_long(&v)) 
break; 
hash.l[i] ^= v; 
} 
Linuxカーネルのhwrng(ハードウェア乱数発生器)抽象化レイヤーは、RNGデバイスを監視し、エントロピー・プールが少なくなったときにカーネルに乱数データを提供するようにデバイスに要求することができます。Linux Kernel の hwrng(ハードウェア乱数生成器)抽象化レイヤーは、RNG デバイスを監視し、プールが不足しているときにカーネルのエントロピープールにランダムデータを提供するようにデバイスに要求することを選択できます。
KVM仮想マシンでの適用方法
サーバに似た VM 環境では、Host に比べて入力デバイスの操作が少なく、ディスク I/O も比較的少ないため、ゲスト自身の PRNG に依存して生成される乱数の品質は高くなく、VM は通常、乱数データの一部を Host から取得します。KVMのVMには、ハードウェア乱数生成器として動作する半仮想化デバイスvirtio-rngがあります。virtio-rngはLinuxカーネルで2.6.26以降サポートされており、QEMUはバージョン1.3でvirtio-rngのサポートを追加しました。 virtio-rng デバイスは、ホストから乱数のソースを読み取り、ゲストのエントロピープールに入力します。通常、/dev/random が入力ソースとして使用されます。もちろん、データソースは変更できるので、hwrng がホストシステム上に存在する場合は、/dev/hwrng を virtio-rng の入力ソースとして使用できます。 hwrng デバイスをゲストにパススルーすることも可能ですが、仮想マシンのライブマイグレーションで問題があるなど、現実的ではありません。virtio-rng デバイスをゲストに追加するには、入力ソースとして /dev/random を使用する 2 つの方法があります:
使用 libvirt 编辑虚拟机的 XML 
在虚拟机 XML 定义中,在<devices>段落に追加する: 
<rng model='virtio'> 
<backend model='<strong>random</strong>'>/dev/random</backend> 
</rng> 
使用 QEMU command Line 直接添加: 
-object <strong>rng-random</strong>,filename=/dev/random,id=rng0 \ 
-device virtio-rng-pci,rng=rng0 
仮想マシンの起動後、ホスト側で
$ lsof /dev/random 
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME 
qemu-syst 23590 mars   11r   CHR    1,8      0t0 1032 /dev/random 
現在のQEMUプロセスが/dev/randomデバイスを使用していることがわかります。
Guest   
$ cat /sys/devices/virtual/misc/hw_random/rng_available 
virtio 
$ cat /sys/devices/virtual/misc/hw_random/rng_current 
virtio 
$ lsmod | grep virtio_rng 
virtio_rng  12790   0 
.... 
Guestがハードウェア乱数発生器を認識していることがわかります。
$ dd if=/dev/hwrng of=/home/random-data bs=1 
ホスト側の乱数リソースが少ない可能性があるため、bsをあまり大きな値に設定すると、短時間でファイルに書き込むのに十分なデータが得られない可能性があります。一方、ホスト側でマウス・キーボード操作やディスク操作を多く行えば、乱数生成が速くなります。
$ hexdump /home/random-data 
00000000    9501 e702 .... 
00000010    .... .... .... 
入力ソースにはEGDプロトコルを使用します:
使用 libvirt 编辑虚拟机的 XML: 
<rng model='virtio'> 
   <backend model='<strong>egd</strong>' type='tcp'> 
     <source mode='connect' host='.1' service='8000'/> 
   </backend> 
 </rng> 
使用 QEMU command Line 直接添加: 
-chardev socket,host=localhost,port=1024,id=chr0 \ 
-object <strong>rng-egd</strong>,chardev=chr0,id=rng0 \ 
-device virtio-rng- pci,rng=rng0 
概要
乱数はコンピュータシステムにおいて非常に重要な役割を担っています。 本稿では、乱数の概念と重要性について説明し、Linuxにおける乱数生成方法、およびKVM環境において仮想マシンがvirtio-rngを使用して乱数データを取得する方法について紹介します。





