Perl 的 srand() 默认种子(发布版本 5.004)的弱点是什么?



我可以找到大量关于在Perl版本5.004之前使用time()的问题的文档,但没有下文。

对于家庭作业,我们被要求基于默认Perlsrand()在默认种子设定中仍然存在缺陷的假设,对程序的结果进行逆向工程。perl5.004版本的变更日志指出,srand()默认种子现在基于"难以预测的系统相关值的大量混合">

是这样吗?如果是,这些价值观是什么?它们是否有任何固有的弱点?

如果正确地缩进所有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;
}

这个代码非常令人困惑,因为它实际上是几种不同的熵交织在一起的方法。基本上有两条路径:系统随机设备和从解释器和环境的状态收集。

  • 系统随机设备

这是最简单的方法,可能也是最强的方法。如果您的操作系统有一个不阻塞的随机设备,即/dev/urandom从中读取32位。完成!#ifndef PERL_NO_DEV_RANDOM(漂亮的双负)控制该位。这在几乎所有Unix系统上都可以实现。在这一点上,分析Perl的随机种子切换到您特定操作系统的/dev/urandom的实现。

  • 从时钟、pid和堆栈指针派生一些东西

如果你的系统没有随机设备,基本上是Windows,Perl就只能通过混合一些可能难以预测的系统值来派生种子。

  • 以微秒或秒为单位的时间取决于gettimeofday()是否存在
  • 进程idPerlProc_getpid()
  • 当前堆栈指针PTR2UV(PL_stack_sp)的内存位置

应该对这些信息做些什么,这就是开头的大评论,就是使用真正的哈希算法将它们混合在一起。相反,它将它们乘以各种常数(SEED_C1SEED_C2等)并相加。这肯定是有缺陷的。

从理论上讲,所有这些信息都是可以预测的。我不知道预测系统信息的最新技术是什么,但时间+pid+堆栈指针是一种相当常见的获取熵的方法,肯定会有关于这个主题的论文。

Perl的所有方法都有一个共同的额外缺陷,即使在64位机器上,它也只使用32位。它不会从/dev/urandom中提取64位,只提取32位。即使有64位的信息,它也只会查看32位的进程id、堆栈指针或时间信息。

看完代码后,我有三个顾虑。

  • 它只使用了32位的随机性

多GPU系统可能会强行执行。

  • (Unix)你的/dev/urandom有多好

/dev/urandom如果你从中提取太多太快,它可能会耗尽熵。它不会阻塞,而是会产生较弱的熵。这超出了Perl的控制范围,但却是整个系统的弱点。此外,一些程序可能会提取比耗尽/dev/urandom所需的更多的熵。几年前,我们在地穴中发现了一个错误:Random,它就是这么做的。

  • (Windows)那个弱哈希算法

除了32位问题之外,这可能是最薄弱的环节。

  • 它使用的是什么随机函数

一旦提供了种子,它将把它传递给什么随机数函数?一个糟糕的rand函数使猜测种子变得更容易。Perl查找几个,通常以drand48结束。您可以看到它与use Config; print $Config{randfunc}'一起使用的内容。我不知道这有多好,但OS X drand48手册页说random(3)更强大,Linux手册页说drand48已经过时了。

该功能自。。。哦,亲爱的90年代末。它被移到了util.c,但还没有被认真对待。git blame 132efe8bfb7cd0fb1beb15aaf284e33bf44eb1fa^ pp.c显示真实历史,查找S_seed。它可能需要一些爱。大多数其他语言都有更高级的随机数生成器。

最新更新