web-dev-qa-db-ja.com

暗号学的に強力な疑似ランダムデータを非常に迅速に生成する方法

一般的ではないパッケージをインストールせずに、暗号学的に強力な疑似ランダムデータの高速ストリームを生成する最良の方法は何ですか?/dev/urandomからの描画は遅すぎます。

私は「ワイプ」マンページを読むことからアイデアがありました:

新鮮なRC6アルゴリズムをPRNGとして使用します。 RC6は128ビットシードでキー化され、次に、ヌルブロックが繰り返し暗号化されて、擬似ランダムストリームが取得されます。

単一のキーのみでnullブロックを暗号化することは、特に大量の疑似ランダムデータが必要な場合は、おそらく最善のアイデアではないため、bashでこれをすばやく作成しました。

while dd if=/dev/zero bs=$(shuf -n 1 -i 1024-67108864) count=1 2>/dev/null | openssl aes-256-cbc -nosalt -k "$(head -c 32 < /dev/random)"; do :; done > [whateverneedsrandomness]

これは、ランダムな32バイトのシードでゼロのストリームを暗号化します。ストリームは平均32 MBごとに再シードされます(範囲は1 KBから64 MBの間で、1バイトの解像度です)。再シード間隔はランダムです。これは、新しいキーがどこで使用されているか誰にも推測されないようにするためです。 CTRは一定のストリームにのみ有効であると読んだため、CTRの代わりにCBCを意図的にブロックモードとして選択しました。非常に限られた暗号の知識により、常に再起動する繰り返しカウンターは、 「ストリーム」の長さが数MBになるのは良い考えですが、確かにはわかりません。

Opensslから出るストリームが完全に暗号化されているかどうかが心配です。入力がブロックサイズで割り切れない場合、opensslは余分なデータを空白のままにしますか(結果のストリーム全体に散在する0の「ギャップ」を残します)? パディング はそれと何か関係がありますか?

だから私の質問は、これは高速ランダムデータを作成する良い方法ですか?また、間違ったブロックモードを使用するなど、見落とし/見落としなどはありますか?

7
kkarl88

編集:自明性は盲目でしたので、見逃しました。 OpenSSLはすでに、マシンの/dev/urandomでシードされた独自の内部PRNGへのインターフェイスを提供していますが、独自の暗号で拡張しています。これを行うだけです:

openssl Rand 10000000

1000万の疑似ランダムバイトを生成します。私のラップトップでは、これは/dev/urandomの約3倍、つまり11 MByte/sほど速いようです。これが十分に速くない場合は、読み進めてください。


元の応答:

それは本当に関連するハードウェアとコンテキストに依存します。 bashスクリプト/dev/urandomとOpenSSLについて話しているので、次のように考えています。

  • Linuxを使用している。
  • 疑似ランダムバイトでいっぱいの大きなファイルを作成したいとします。
  • プログラムによるソリューションではなく、既存の「通常の」パッケージのスクリプト化されたアセンブリであるソリューションが必要です。

実際、独自のCコードを記述できる場合、最良のオプションは、CTRモードで AES-NI 命令を使用するAES暗号化(これらの命令を提供する最近のPCで)、または専用のストリーム暗号です。 (例 these )。 2.4 GHz Core2 CPUでは、Sosemanukから700 MByte/sの疑似ランダムバイトを取得できました。今後は、スクリプトに限定されると思います。

OpenSSL暗号化の使用は悪い考えではありませんが、詳細は次のとおりです。

  • 各チャンクの長さをランダム化することは、役に立たない複雑さです。
  • AES-256に行く必要はありません。 AES-128は既に問題ありません。 AES-256はAES-128よりも40%遅く、セキュリティの実質的な改善はありません。
  • ゼロの長いシーケンスをCBCで暗号化するとき、実際にはブロック暗号をOFBモードで実行しています(ブロックを暗号化し、何度も何度も暗号化します)。 2を下回っている限り、これは妥当ですn/2制限、つまりAES(128ビットブロック)では、2より長いチャンクを使用しないことを意味します6416バイトのブロック、つまり2億5000万テラバイト。とにかくあなたはそれに到達しないでしょう。その意味で、「再シード」は実際には必要ありません。
  • 「再シード」する場合(この機能を維持する必要がある場合は不要)、/dev/urandomではなく/dev/randomを使用してください。 /dev/urandom実装の誤解された偏執狂的なクォームにもかかわらず、/dev/randomは暗号化に適しています(OSブートスクリプトは内部PRNGが適切にシードされることを保証します)。一方、/dev/randomはブロックする可能性があるため、本当に遅くなる(ブロックされたプロセスよりも遅くなることはない)。
  • 文字列に注意してください。 bashは文字列を使用します。 /dev/randomおよび/dev/urandomは、値0のバイトを含むbytesを返します。これにより、人工的にキーが切り捨てられ、セキュリティが低下する場合があります。これは悪いです。代わりに、バイトを16進数でエンコードし、-Kおよび-ivコマンドライン引数をopensslでエンコードする必要があります。
  • OpenSSLは、8固定バイトで始まるヘッダーを追加することがあります(したがって、完全にランダムではありません)。 -K-ivを使用している場合は、そうしません。
  • OpenSSLもいくつかのパディングを使用するので、最後に余分な16バイトのブロックを取得します(これは問題ではありません。パディングはクリアテキストにあり、暗号化によって「ランダム化」されます)。

これにより、次のスクリプトが作成されます。

#!/bin/sh
key=$(dd if=/dev/urandom bs=16 count=1 2>/dev/null | md5sum | cut -d' ' -f1)
iv=$(dd if=/dev/urandom bs=16 count=1 2>/dev/null | md5sum | cut -d' ' -f1)
dd if=/dev/zero bs=65536 count=8192 2>/dev/null | openssl aes-128-cbc -K $key -iv $iv

呼び出されると、このスクリプトは、暗号化の目的に適した、正確に536870928疑似ランダムバイトのシーケンスを出力します。 bsパラメータとcountパラメータは自由に変更してください。ただし、ddはRAMサイズbsのバッファを割り当てるため、通常はbsを小さくしすぎないようにしてください。カーネル(8192から65536は通常、適切な値です)。

ここでは、バイトから16進数への変換の便宜のためにのみMD5を使用しています(MD5の既知の欠点は、この場合のセキュリティにはまったく影響しません)。

このスクリプトは、適切なLinuxインストールで機能します。デフォルトで存在するはずのパッケージのみを使用します。私のそれほど強力ではないラップトップでは、このスクリプトは73 Mバイト/秒の速度で疑似ランダムバイトを生成します。これは悪くはなく、おそらく目的に十分です(それが十分でない場合、おそらくI /に他の問題が発生します。 Oボトルネック)。

10
Tom Leek

ランダムデータソースを頻繁に使い果たしてしまう場合は、 http://www.entropykey.co.uk/ などのハードウェア乱数ジェネレータに投資することをお勧めします。

通常、CPUの乱数ジェネレーターハードウェアによって生成できるランダムデータの量に制限されます。これらのような外部デバイスは、はるかに高速で適切なランダムデータを生成できます。

1
deed02392