getrandom()
の詳細を説明する回答を書きました 初期エントロピーを待っているブロック。
しかし、彼は「/dev/urandom がエントロピーが低いためにセキュリティの問題を示唆する唯一の瞬間は、自動化された新しい OS インストールの最初の瞬間である」と言って、urandom を少し誇張していると思います。
あなたの心配には十分な根拠があります。まさにそのこととその意味について未解決の質問があります。問題は、永続的なランダム シードが入力プールから出力プール (ブロッキング プールと CRNG) に移動するのにかなりの時間がかかることです。この問題は、/dev/urandom
を意味します 起動後の数分間、潜在的に予測可能な値を出力します。あなたが言うように、解決策は、ブロッキング /dev/random
のいずれかを使用することです 、または getrandom()
を使用する ブロックするように設定します。
実際、初期の起動時にカーネルのログに次のような行が表示されることは珍しくありません:
random: sn: uninitialized urandom read (4 bytes read, 7 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 15 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 16 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 16 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 20 bits of entropy available)
これらはすべて、十分なエントロピーが収集される前であっても、ノンブロッキング プールにアクセスした場合のインスタンスです。問題は、エントロピーの量が小さすぎて、この時点で暗号的に十分に安全ではないことです. 2 つの可能な 4 バイト値が存在するはずですが、7 ビットのエントロピーしか利用できないため、異なる可能性は 2 つまたは 128 しかありません。
<ブロック引用>Halderman はまた、エントロピー プールはブートごとにいっぱいになると言っているようで、Pornin が彼の回答で言っているように、最初の OS インストールではそうではありません。私のアプリケーションにとってはそれほど重要ではありませんが、疑問に思っているのはどれですか?
それは実際にはセマンティクスの問題です。実際のエントロピー プール (ランダムな値を含む、カーネルに保持されているメモリのページ) は、永続的なエントロピー シードと環境ノイズによって、ブートごとに満たされます。ただし、エントロピー シード それ自体は、インストール時に作成されるファイルであり、システムがシャットダウンするたびに新しいランダム値で更新されます。 Pornin はランダム シードをエントロピー プールの一部 (一般的なエントロピー配布および収集システムの一部のように) と見なしているのに対し、Halderman はそれを別個のものと見なしている (エントロピー プールは技術的にはメモリ、それ以上のものはありません)。実のところ、エントロピー シードは起動ごとにエントロピー プールに供給されますが、実際にプールに影響を与えるには数分かかる場合があります。
ランダム性の 3 つのソースの概要:
<オール>
/dev/random
- ブロッキング キャラクター デバイスは、読み取られるたびに「エントロピー カウント」を減らします (エントロピーが実際には枯渇していないにもかかわらず)。ただし、起動時に十分なエントロピーが収集されるまでブロックされるため、早い段階で安全に使用できます。
/dev/urandom
- ノンブロッキング キャラクター デバイスは、誰かが読み取るたびにランダム データを出力します。十分なエントロピーが収集されると、ランダム データと見分けがつかない事実上無制限のストリームが出力されます。残念ながら、互換性の理由から、十分な 1 回限りのエントロピーが収集される前のブートの早い段階でも読み取り可能です。
getrandom()
- エントロピー プールが必要最小限のエントロピーで適切に初期化されている限り、ランダム データを出力するシステムコール。デフォルトでは、ノンブロッキング プールから読み取ります。 GRND_NONBLOCK
が与えられた場合 十分なエントロピーがない場合、エラーを返します。 GRND_RANDOM
が与えられた場合 フラグ、/dev/random
と同じように動作します 、エントロピーが利用可能になるまで単にブロックします。
3 番目のオプションである getrandom()
を使用することをお勧めします。 システムコール。これにより、プロセスは暗号的に安全なランダム データを高速で読み取ることができ、十分なエントロピーが収集されていない場合にのみ、ブートの早い段階でブロックされます。 Python の os.urandom()
の場合 あなたが言うように、関数はこのsyscallのラッパーとして機能するので、使用しても問題ありません。そうすべきかどうかについて実際に多くの議論があったようで、最終的に十分なエントロピーが利用可能になるまでブロックすることになりました.
もう少し先のことを考えてみましょう:上記のように新鮮でナイーブな環境のベスト プラクティスは何ですか?
これは一般的な状況であり、対処する方法がいくつかあります:
-
/dev/random
を使用するなどして、初期起動時にブロックするようにしてください またはgetrandom()
. -
可能であれば、永続的なランダム シードを保持します (つまり、ブートごとにストレージに書き込むことができる場合)。
-
最も重要なのは、ハードウェア RNG を使用することです .これは最も効果的な手段です。
ハードウェア乱数ジェネレーターを使用することは非常に重要です。 Linux カーネルは、サポートされている HWRNG インターフェイス (存在する場合) を使用してエントロピー プールを初期化し、ブート エントロピー ホールを完全に排除します。多くの組み込みデバイスには、独自の乱数発生器があります。
これは、カーネルが環境ノイズからエントロピーを安全に生成するために必要な高解像度タイマーを備えていない可能性があるため、多くの組み込みデバイスにとって特に重要です。たとえば、MIPS プロセッサの一部のバージョンには、サイクル カウンターがありません。
<ブロック引用>urandom を使用して (ユーザーランドだと思いますか?) CSPRNG をシードする方法と理由を教えてください。これは getrandom にどのように勝っていますか?
ノンブロッキング ランダムネス デバイスは、高性能を目的として設計されていません。最近まで、現在のようにストリーム暗号ではなく SHA-1 をランダム性に使用していたため、デバイスは非常に遅くなりました。ランダム性のためにカーネル インターフェイスを使用すると、カーネルへの呼び出しごとにコストのかかるコンテキスト スイッチが必要になるため、ローカルのユーザー空間 CSPRNG よりも効率が低下する可能性があります。カーネルは、そこから大量に引き出したいアプリケーションを考慮して設計されていますが、ソース コード内のコメントを見ると、カーネルがこれを正しいことと見なしていないことが明らかです:
/*
* Hack to deal with crazy userspace progams when they are all trying
* to access /dev/urandom in parallel. The programs are almost
* certainly doing something terribly wrong, but we'll work around
* their brain damage.
*/
OpenSSL などの一般的な暗号化ライブラリは、ランダム データの生成をサポートしています。それらは一度シードすることも、時々再シードすることもでき、並列化からより多くの利益を得ることができます。さらに、特定のオペレーティング システムやオペレーティング システムのバージョンの動作に依存しない、移植可能なコードを作成することも可能になります。
大量のランダム性が必要ない場合は、カーネルのインターフェースを使用してもまったく問題ありません。生涯にわたって多くのランダム性を必要とする暗号化アプリケーションを開発している場合は、OpenSSL のようなライブラリを使用して処理することをお勧めします。
システムには次の 3 つの状態があります。
<オール>CPRNG を安全に初期化するのに十分なエントロピーを収集している:
2a.収集した以上のエントロピーを放出しました。
2b.収集されたよりも少ないエントロピーを与えました。
歴史的に、人々は (2a) と (2b) の区別が重要であると考えていました。これにより、2 つの問題が発生しました。まず、それは間違っています。適切に設計された CPRNG にとって、この区別は無意味です。そして第二に、(2a)-vs-(2b) の区別を強調することで、人々は (1) と (2) の区別を見落としてしまいました。これは実際には非常に重要なことです。人々は、(1) を (2a) の特殊なケースに折りたたんだだけです。
本当に必要なのは、状態 (1) でブロックされ、状態 (2a) または (2b) ではブロックされないものです。
残念ながら、昔は (1) と (2a) が混同されていたため、これは選択肢ではありませんでした。 /dev/random
という 2 つのオプションしかありませんでした 、ケース (1) および (2a) でブロックされ、 /dev/urandom
、ブロックされたことはありません。しかし、状態 (1) はほとんど発生しません。適切に構成されたシステムではまったく発生しません。以下を参照してください。その場合は /dev/urandom
ほとんどすべてのシステムで、ほぼ常に優れています。 「常に urandom を使用する」というブログ投稿はすべてそこから生まれました。(2a) と (2b) の状態を無意味かつ有害に区別するのをやめるよう人々を説得しようとしていたのです。
しかし、ええ、これらのどちらもあなたが実際に望んでいるものではありません.したがって、新しい getrandom
デフォルトでは状態 (1) でブロックされ、状態 (2a) または (2b) ではブロックされません。したがって、最新の Linux では、オーソドックスを次のように更新する必要があります:always use getrandom
デフォルト設定で .
余分なシワ:
-
getrandom
/dev/random
のように動作するデフォルト以外のモードもサポートします 、GRND_RANDOM
経由でリクエストできます 国旗。私の知る限り、このフラグは実際には役に立たない.使用しないでください。 -
getrandom
/dev/urandom
よりもさらに多くのボーナス特典があります :ファイルシステムのレイアウトに関係なく機能し、ファイル記述子を開く必要はありません。どちらも、使用される環境について最小限の仮定を行いたい汎用ライブラリにとって問題があります。これは、暗号化セキュリティには影響しません。 、しかし操作的にはいいです。 -
適切に構成されたシステムは、初期の起動時でも常にエントロピーを利用できます (つまり、状態 (1) になることは決してありません)。これを管理するには多くの方法があります:前回のブートからのエントロピーを保存して、次のブートで使用します。ハードウェア RNG をインストールします。 Docker コンテナーはホストのカーネルを使用するため、そのエントロピー プールにアクセスできます。高品質の仮想化セットアップには、ゲスト システムがハイパーバイザー インターフェイスを介してホスト システムからエントロピーをフェッチできるようにする方法があります (たとえば、「virtio rng」を検索します)。もちろん、すべてのシステムが適切に構成されているわけではありません。システムの構成が不十分な場合は、代わりに適切に構成できるかどうかを確認する必要があります。原則としてこれは簡単に安くできるはずですが、実際には人々はセキュリティを優先しないため、クラウド プロバイダーを切り替える、別の組み込みプラットフォームに切り替えるなどの作業が必要になる場合があります。そして残念なことに、これはあなた (またはあなたの上司) が喜んで支払うよりも高価であることに気付くかもしれません。もしそうなら、私の同情.
-
@forest が指摘しているように、多くの CPRNG 値が必要な場合は、十分に注意すれば、
getrandom
を使用しながら、ユーザー空間で独自の CPRNG を実行することでこれを高速化できます。 (再)シード用。ただし、これは非常に「専門家のみ」のことであり、独自の暗号プリミティブを実装している状況と同じです。getrandom
を使用して測定してわかった場合にのみ実行してください。 あなたのニーズに対して直接は遅すぎるおよび あなたは重要な暗号化の専門知識を持っています。セキュリティが完全に破られるような方法で CPRNG 実装を台無しにするのは非常に簡単ですが、出力は依然としてランダムに「見える」ので、気付かないでしょう。