「乱数生成」の版間の差分

提供: ArchWiki
ナビゲーションに移動 検索に移動
(カテゴリを修正)
7行目: 7行目:
   
 
== カーネル内蔵の RNG ==
 
== カーネル内蔵の RNG ==
カーネルに含まれている RNG である [[wikipedia:ja:/dev/random|/dev/{u}random]] は暗号鍵の作成などに使われるような高いセキュリティレベルを必要とするランダムデータの生成を行うときに使うことを強く推奨されます。この乱数生成器はデバイスドライバなどの情報源から環境ノイズをエントロピープールに集めます。
 
   
  +
{{Note|Linux RNG は近年大幅に改良されており、4.x および 5.x カーネルシリーズでは多くの変更が加えられています。そのため、それについて存在する情報の多くは古いものです。このページでは、Arch Linux の [[カーネル#公式にサポートされているカーネル|公式にサポートされているカーネル]] を含む最新のカーネルに適用できる現在の実装と動作について説明します。}}
{{ic|man random}} コマンドではライブラリ関数の man ページ {{man|3|random}} が表示されてしまうので注意してください。{{ic|/dev/random}} デバイスに関する情報が必要なときは {{ic|man 4 random}} を実行して表示される {{man|4|random}} を読んで下さい。
 
  +
  +
[https://github.com/torvalds/linux/blob/master/drivers/char/random.c Linux カーネルの組み込み RNG] は、暗号的に安全な擬似乱数データを生成します。これは、ハードウェア RNG、割り込み、CPU ベースのジッターエントロピーなどのさまざまなソースから [[wikipedia:Entropy_(information_ Theory)|entropy]] を収集することによって機能します。単一のエントロピーソースのみに依存することはありません。エントロピーは [[wikipedia:ja:BLAKE_(hash_function)#BLAKE2|BLAKE2s 暗号化ハッシュ関数]] を使用して抽出され、[[wikipedia:ja:Salsa20#ChaCha_variant|ChaCha20]] CRNG (暗号化乱数ジェネレーター) をシードするために使用されます。カーネルが実行されている限り、エントロピーは収集され続け、CRNG は定期的に再シードされます。
  +
  +
Linux RNG は、ユーザー空間がランダムデータを取得するための 3 つのインターフェイスを提供します:
  +
  +
* {{man|2|getrandom}} system call
  +
* {{ic|/dev/random}}
  +
* {{ic|/dev/urandom}}
  +
  +
歴史的に、{{ic|/dev/random}} は {{ic|/dev/urandom}} よりも強力な乱数を提供すると考えられてきた。しかし、{{ic|/dev/random}} と {{ic|/dev/urandom}} の挙動は時間とともに収束し、x86-64 システム上では同等になっています。Arch Linux は x86-64 しかサポートしていないので、Arch Linux では {{ic|/dev/random}} は {{ic|/dev/urandom}} は等価です。(これは全ての x86-64 CPU に RDTSC 命令が存在するということは、[https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/char/random.c?h=v6.1#n1205 CPU ベース jitterentropy アルゴリズム] が常にエントロピーを生成できるということであり、言うまでもなくほとんどの x86-64 CPU は [[wikipedia:RDRAND|RDRAND]] もサポートしているからです)
  +
  +
他のアーキテクチャ (具体的には、高速サイクルカウンターを持たないアーキテクチャ) では、2 つの違いが残っています。それは、カーネルが CRNG が適切に初期化されたと推定するまで、{{ic|/dev/random}} ブロックをブロックすることです。 {{ic|/dev/urandom}} はそうではありません。したがって、一般的な場合、ユーザーとアプリケーションは、長期暗号鍵を生成するときに {{ic|/dev/random}} を使用するか、デフォルトで {{man|2|getrandom}} を使用するという従来のガイダンスに従う必要があります。
  +
  +
{{ic|/dev/random}} は真のランダム データを提供することを目的とするのではなく、むしろ暗号的に安全なランダムデータ (現実世界のすべてのユースケースに十分なデータ) を提供することを目的としているため、それを読み取ってもカーネルのエントロピープールが枯渇することはなくなりました。したがって、{{ic|/proc/sys/kernel/random/entropy_avail}} には常に 256 が含まれている必要があります。これは、ChaCha20 キーのビット単位のサイズです。このファイルにさらに大きな値が含まれることを期待したり、値が "低すぎる" 場合にユーザーがアクションを実行することを期待した過去の文書は無視できます。
   
 
=== /dev/random ===
 
=== /dev/random ===

2024年1月19日 (金) 04:25時点における版

Wikipedia:Random number generation より:

乱数生成器 (RNG) とはパターンの存在しない (つまりランダムな) 数字や記号の羅列を生成するための計算法または物理デバイスのこと。

ランダムなデータの生成はアプリケーションによってはとても重要なことです。(ディスク暗号化などに用いる) 暗号鍵の作成やディスクの完全消去、暗号化されたソフトウェアアクセスポイントなど。

カーネル内蔵の RNG

ノート: Linux RNG は近年大幅に改良されており、4.x および 5.x カーネルシリーズでは多くの変更が加えられています。そのため、それについて存在する情報の多くは古いものです。このページでは、Arch Linux の 公式にサポートされているカーネル を含む最新のカーネルに適用できる現在の実装と動作について説明します。

Linux カーネルの組み込み RNG は、暗号的に安全な擬似乱数データを生成します。これは、ハードウェア RNG、割り込み、CPU ベースのジッターエントロピーなどのさまざまなソースから entropy を収集することによって機能します。単一のエントロピーソースのみに依存することはありません。エントロピーは BLAKE2s 暗号化ハッシュ関数 を使用して抽出され、ChaCha20 CRNG (暗号化乱数ジェネレーター) をシードするために使用されます。カーネルが実行されている限り、エントロピーは収集され続け、CRNG は定期的に再シードされます。

Linux RNG は、ユーザー空間がランダムデータを取得するための 3 つのインターフェイスを提供します:

歴史的に、/dev/random/dev/urandom よりも強力な乱数を提供すると考えられてきた。しかし、/dev/random/dev/urandom の挙動は時間とともに収束し、x86-64 システム上では同等になっています。Arch Linux は x86-64 しかサポートしていないので、Arch Linux では /dev/random/dev/urandom は等価です。(これは全ての x86-64 CPU に RDTSC 命令が存在するということは、CPU ベース jitterentropy アルゴリズム が常にエントロピーを生成できるということであり、言うまでもなくほとんどの x86-64 CPU は RDRAND もサポートしているからです)

他のアーキテクチャ (具体的には、高速サイクルカウンターを持たないアーキテクチャ) では、2 つの違いが残っています。それは、カーネルが CRNG が適切に初期化されたと推定するまで、/dev/random ブロックをブロックすることです。 /dev/urandom はそうではありません。したがって、一般的な場合、ユーザーとアプリケーションは、長期暗号鍵を生成するときに /dev/random を使用するか、デフォルトで getrandom(2) を使用するという従来のガイダンスに従う必要があります。

/dev/random は真のランダム データを提供することを目的とするのではなく、むしろ暗号的に安全なランダムデータ (現実世界のすべてのユースケースに十分なデータ) を提供することを目的としているため、それを読み取ってもカーネルのエントロピープールが枯渇することはなくなりました。したがって、/proc/sys/kernel/random/entropy_avail には常に 256 が含まれている必要があります。これは、ChaCha20 キーのビット単位のサイズです。このファイルにさらに大きな値が含まれることを期待したり、値が "低すぎる" 場合にユーザーがアクションを実行することを期待した過去の文書は無視できます。

/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_thresholdwrite_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/random を使って重要な暗号鍵を生成してはいけません。例えば、共有サーバーの環境などを想定してください。別の環境で鍵を作成してから転送するようにしましょう。暗号学者 D. J. Bernstein はこの問題を Mark Twain の名言 で説明しています。

/dev/urandom

/dev/random と反対に、/dev/urandom は初期のランダムなシードを使ってストリーム暗号の元とします [1] (カーネル 4.8 以上)。

ストリーム暗号を使用することで、ディスクの徹底消去やブロックデバイス暗号化の準備、LUKS キースロットの消去などの目的に叶うような高品質な疑似乱数データを大量に生成します。

警告: 長期間の暗号鍵を生成するのに /dev/urandom は推奨されません。

システムの状態や乱数の目的によって使用価値は大きく変わります。例えば、システムが立ち上がったばかりなのか、あるいはエントロピープールを十分に満たすだけの時間を経ているのかは大変重要です。

以下の記事は様々な視点から /dev/urandom の有用性を説明しています:

  • Myths about urandom - 非常に勉強になりますが誤謬も含まれています。
  • Safely generate random numbers - エントロピーの収集についてカーネルの仕組みを変更しようとしたときに考えるべきことを説明しています。

Arch Linux の場合、/dev/urandom には以下のことが当てはまります:

  1. 可能であればカーネルの getrandom() システムコールを使ってください。/dev/urandom が正しく初期化されていることを確認できます。getrandom(2) を参照。
  2. /dev/urandom デバイスを使用して長期目的でエントロピーを生成する場合、正しく初期化されることを必ず確認してください。
  3. 起動時に /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 の問題

OpenSSLrdrand エンジンとして RDRAND をサポートしています(openssl engine で利用可能な OpenSSL エンジンを表示)が、OpenSSL や関連アプリケーション(NginxStunnelCURL など)はデフォルトで RDRAND を使用しない場合があります。OpenSSL は、/dev/urandom を直接使用しません。

例えば、openssl コマンドラインツールに -engine rdrand オプションを渡したり、nginx の場合は /etc/nginx/nginx.confssl_engine rdrand; 行を追加したりします。

参照