(私は暗号学者ではありませんが、何年にもわたって多くのことを吸収してきました。クライアントの乱数生成を何年も前に精査するのを手伝わなければなりませんでした。その結果、以下で説明する Crypt::Random バグが見つかりました。)
これらすべての ifdef を適切にインデントすると、シード コードはより意味のあるものになります。これは 5.16.0 のコードです。
U32
Perl_seed(pTHX)
{
dVAR;
/*
* This is really just a quick hack which grabs various garbage
* values. It really should be a real hash algorithm which
* spreads the effect of every input bit onto every output bit,
* if someone who knows about such things would bother to write it.
* Might be a good idea to add that function to CORE as well.
* No numbers below come from careful analysis or anything here,
* except they are primes and SEED_C1 > 1E6 to get a full-width
* value from (tv_sec * SEED_C1 + tv_usec). The multipliers should
* probably be bigger too.
*/
#if RANDBITS > 16
# define SEED_C1 1000003
# define SEED_C4 73819
#else
# define SEED_C1 25747
# define SEED_C4 20639
#endif
#define SEED_C2 3
#define SEED_C3 269
#define SEED_C5 26107
#ifndef PERL_NO_DEV_RANDOM
int fd;
#endif
U32 u;
#ifdef VMS
# include <starlet.h>
/* when[] = (low 32 bits, high 32 bits) of time since epoch
* in 100-ns units, typically incremented ever 10 ms. */
unsigned int when[2];
#else
# ifdef HAS_GETTIMEOFDAY
struct timeval when;
# else
Time_t when;
# endif
#endif
/* This test is an escape hatch, this symbol isn't set by Configure. */
#ifndef PERL_NO_DEV_RANDOM
# ifndef PERL_RANDOM_DEVICE
/* /dev/random isn't used by default because reads from it will block
* if there isn't enough entropy available. You can compile with
* PERL_RANDOM_DEVICE to it if you'd prefer Perl to block until there
* is enough real entropy to fill the seed. */
# define PERL_RANDOM_DEVICE "/dev/urandom"
# endif
fd = PerlLIO_open(PERL_RANDOM_DEVICE, 0);
if (fd != -1) {
if (PerlLIO_read(fd, (void*)&u, sizeof u) != sizeof u)
u = 0;
PerlLIO_close(fd);
if (u)
return u;
}
#endif
#ifdef VMS
_ckvmssts(sys$gettim(when));
u = (U32)SEED_C1 * when[0] + (U32)SEED_C2 * when[1];
#else
# ifdef HAS_GETTIMEOFDAY
PerlProc_gettimeofday(&when,NULL);
u = (U32)SEED_C1 * when.tv_sec + (U32)SEED_C2 * when.tv_usec;
# else
(void)time(&when);
u = (U32)SEED_C1 * when;
# endif
#endif
u += SEED_C3 * (U32)PerlProc_getpid();
u += SEED_C4 * (U32)PTR2UV(PL_stack_sp);
#ifndef PLAN9 /* XXX Plan9 assembler chokes on this; fix needed */
u += SEED_C5 * (U32)PTR2UV(&when);
#endif
return u;
}
エントロピーを取得するいくつかの異なる方法がすべて一緒にインターリーブされているため、コードは非常に紛らわしいです。基本的に 2 つのパスがあります。システムのランダム デバイスと、インタプリタと環境の状態からの収集です。
これは最も簡単で、おそらく最も強力な方法です。OSにブロックしないランダムデバイスがある場合、つまり. /dev/urandom
そこから 32 ビットを読み取ります。終わり! #ifndef PERL_NO_DEV_RANDOM
(素敵な二重否定) そのビットを制御します。これは、ほぼすべての Unix システムで行われます。この時点で、Perl のランダム シードの分析は、特定の OS の実装に切り替わります/dev/urandom
。
- クロック、pid、およびスタック ポインターから何かを導出します。
システムにランダムなデバイスがない場合 (基本的には Windows)、Perl はフォールバックして、システム値を予測するのが難しいと思われるいくつかのシステム値を混ぜ合わせてシードを導出します。
- マイクロ秒単位または秒単位の時間は、
gettimeofday()
存在するかどうかによって異なります。
- プロセス ID
PerlProc_getpid()
、.
- 現在のスタック ポインターのメモリ位置
PTR2UV(PL_stack_sp)
。
その情報で何をすべきか、そしてこれが最初の大きなコメントが何であるかは、実際のハッシュアルゴリズムを使用してそれらを一緒にマッシュアップすることです. 代わりに、さまざまな定数 (SEED_C1
などSEED_C2
) を掛けて加算します。これは間違いなく欠陥です。
その情報はすべて、理論上は予測可能です。システム情報を予測する際の最新技術が何であるかはわかりませんが、時間 + pid + スタック ポインターはエントロピーを取得するためのかなり一般的な方法であり、この件に関する論文は必ずあります。
Perl のすべての方法に共通する追加の欠陥があります。これは、64 ビット マシンでも 32 ビットのみを使用して行われます。から 64 ビットをプルするのでは/dev/urandom
なく、32 ビットだけをプルします。64 ビットの情報がある場合でも、32 ビットのプロセス ID、スタック ポインター、または時間情報のみを調べます。
コードを読んだ後、私は 3 つの懸念があります。
マルチGPUシステムがそれをブルートフォースする可能性があります。
- (Unix) あなたの はいかがですか
/dev/urandom
。
/dev/urandom
あまりにも速く引きすぎると、エントロピーが不足する可能性があります。ブロックする代わりに、より弱いエントロピーを生成します。これは Perl の制御範囲外ですが、システム全体の弱点です。さらに、一部のプログラムは、必要以上にエントロピーを引き出す可能性があり/dev/urandom
ます。何年も前に Crypt::Random でまさにそれを行っていたバグを発見しました。
- (Windows) その弱いハッシュ アルゴリズム。
32 ビットの問題に次いで、これがおそらく最も弱いリンクです。
シードが提供されると、それはどの乱数関数に渡されますか? rand 関数が貧弱であると、シードの推測が容易になります。Perl はいくつかを探しますが、通常はdrand48
. で何を使用しているかを確認できますuse Config; print $Config{randfunc}'
。それがどれほどうまく機能するかはわかりませんが、OS X drand48 の man ページrandom(3)
にはより強力であると書かれており、Linux の man ページにはdrand48 is obsoleteと書かれています。
この機能はそれ以来変更されていません...ああ、90年代後半です。これは util.c に移動されましたが、深刻な変更は加えられていません。 git blame 132efe8bfb7cd0fb1beb15aaf284e33bf44eb1fa^ pp.c
実際の履歴を表示します。 を探しますS_seed
。愛情が必要なのかもしれません。他のほとんどの言語には、より高度な乱数ジェネレーターがあります。