【问题标题】:Equivalent of /dev/urandom on Windows?相当于 Windows 上的 /dev/urandom?
【发布时间】:2014-02-26 17:46:28
【问题描述】:

我的应用程序想要获得一个随机数,如果可用,最好带有熵,但不需要加密质量,并且想要确保在系统熵池耗尽时调用不会阻塞(例如在服务器上在农场)。

我知道 CryptGenRandom,但未指定其在逆熵条件下的阻塞行为。

在 Unix 上,/dev/urandom 支持此用例。 Windows 上是否有等效的功能?我宁愿避免使用非系统 RNG 来获得非阻塞语义。

【问题讨论】:

  • 呃,你从哪里读到CryptGenRandom 可能会阻止?不过,对于非加密应用程序,您可以使用“常规”PRNG,也许就像“哑”LCG 一样简单。
  • CryptoGenRandom 说它依靠硬件状态变化来填充它的熵池,并且它的文档没有说明如果池是空的它的行为。所以它可能会阻塞。我需要一种不会阻塞的解决方案,而不是未指定阻塞行为的解决方案。我确实可以使用外部 RNG,但我询问 Windows 中是否已经有等效的功能。问题已更新。
  • 您是在问是否有一个函数永远不会耗尽熵,或者您是在问是否有一个函数在没有熵的情况下失败而不是阻塞?
  • 我要求一个返回随机数且不阻塞的调用。如果没有熵,我没有要求呼叫失败。目前提出的答案是针对我所提出的问题以外的问题。

标签: windows random


【解决方案1】:

对于玩具应用程序,您可以使用标准库函数rand(),但在 Windows 上的实现质量非常差。对于加密安全的随机数,您可以使用rand_s() 标准库函数。

更好的选择是在您的程序中包含一个合适的伪随机数生成器。 Mersenne Twister 是 IMO 一个不错的选择,特别是因为有很多可用的实现(包括在 C++11 标准库和 Boost 中)。

【讨论】:

  • @HansPassant 在 MSVC 标准库中。它也在 Windows NT crtdll 中(它不是 MSVC 的一部分,但实际上 Windows 的一部分)。
  • 没有必要争论这个。 crtdll Windows的一部分,如果你愿意,你可以很容易地在其中调用函数;你甚至可以生成自己的导入库(这并不难)。 MSVC 运行时也可以说是 Windows 的一部分,因为它总是被安装并且其他(非 Microsoft)编译器使用它。
  • @alastair:Windows 不是 Microsoft Visual C/C++ 运行时交付渠道 - blogs.msdn.com/b/oldnewthing/archive/2014/04/11/10516280.aspx
  • @MikeDimmick 我不否认这一点,或者那篇文章中的 cmets。然而,事实是 99% 的 Windows 软件都使用 Microsoft 的运行时,无论是 Windows 本身附带的软件(微软显然讨厌人们使用),还是 Visual C++ 附带的软件。
  • @MikeDimmick英特尔、IBM 或 Borland 运行时,也许我不会将 Visual C 运行时视为 Windows 的事实上的一部分。但事实并非如此。
【解决方案2】:

如果我需要随机数的非阻塞行为,我通常会预先生成 n 数字并将它们存储在内存变量中:即如果我知道我每秒需要 30 个随机数,则需要 3 秒来计算它们(包括块),然后我将在加载主代码时预先生成 300,将它们存储在数组或向量中并在需要时使用它们;在使用它们时,我每次使用一个单独的线程时都会在一个单独的线程上生成另一个,用新生成的随机数替换使用的随机数并移动到列表中的下一个,这样当我达到限制时(在这种情况下300) 我知道什么时候我可以简单地从我的数组/向量/列表的开头重新开始,并且所有随机数都是新的并且将是非阻塞的(因为它们是预先生成的)。

这意味着你可以使用任何你喜欢的随机数生成器,而不必担心阻塞行为,但是它会花费更多的内存,但是对于我需要随机数的那种编码来说可以忽略不计。

希望这会有所帮助,因为我无法将这一切都放入评论中:)

【讨论】:

  • 如果 RNG 依赖于耗尽的熵池,那么任何额外的延迟隐藏都不会阻止它阻塞。系统已经在幕后收集了熵,那么您的方法有什么收获?
  • 它们将被阻塞,但在一个单独的线程上,该线程仅用于填充随机数的列表/向量/数组,因此由于它与主线程分开处理它们,我可以在知道我的主线程可以立即/非阻塞地使用随机数,而阻塞是在单独的线程上执行的。唯一一次丢失边缘的情况是随机数的使用速度比生成速度快,此时会遇到与原始问题完全相同的问题。计划将确保存储足够的数据:)
  • 如果有可用的熵,你会增加开销而没有任何收益,因为普通的 RNG 调用不会阻塞。如果没有可用的熵,那么您将一无所获。当您是其他人将使用的代码的作者时,您不能计划在远程服务器上创建熵 - 您需要一个非阻塞实现,这正是我所问的。
【解决方案3】:

您可以等待一个充满熵的好种子,然后按照 GMasucci 的建议预先生成一长串随机数。

除非您的系统已经受到威胁,否则似乎一个好的种子足以生成一系列不相关的数字,如@9​​87654321@ 中讨论的那样

从讨论中我了解到,只有在您的系统状态(您的熵源已知且攻击者知道其当前状态)受到威胁时才需要连续提供(“真实”/“新鲜”)随机数一点。在为您的块密码提供更多随机性之后,其输出的可预测性将变得更低。

种子的来源?两件或多件不太可能受到威胁的受信任软件。我试图模糊使用时间函数作为种子的函数的可预测性:本地 rand_function() + 一些变量延迟 + mysql 的 rand()。 从那里,一些好的库生成的伪随机数列表。

【讨论】:

  • 谢谢,但我正在寻求非阻塞语义。实现/使用另一个 RNG 确实有助于避免在耗尽池后出现后续块,但这是 /dev/urandom 已经做的事情的一个子集,并且不能解决原始问题 - 第一次调用系统 RNG可能会阻塞,我什至无法查询它是否会阻塞以知道我是否可以获得可能存在的熵。
  • 现代 Linux 系统似乎可以在关闭之前将一些随机数保存在文件中。这样,当系统重新启动时,它已经有一个种子池准备好被喂给 RNG。系统阻塞的唯一方法应该是不正常关机或系统第一次初始化。
  • 启动并不是唯一一次池可以为空。池可能会因使用而耗尽,例如农场上的服务器(就像我在原始问题中所说的那样)。这种情况下的熵供应可能仅来自网络数据包,并且在任何情况下,您都可以设计计算负载来耗尽它。应该有一种方法来指定您是否想要阻塞语义,但到目前为止还没有人确定一个。
  • 但是,只要没有人破坏您实现的 RNG 密码块,您为什么需要持续提供熵?据我了解,在一个好的 RNG 中,下一个随机数与最后一个随机数无关,因此种子只需要一次(因此被称为“种子”)。就提供的链接而言,它解释了 /dev/urandom 和 /dev/random 的工作原理(您正在寻找什么)。是的,池可以用尽——任何池,因为没有无限内存之类的东西,但您不需要等待链接(以及链接的链接)中解释的更多熵。
  • 我从来没有要求提供恒定的熵。我已经要求在 Windows 上提供非阻塞随机数,如果有熵的话。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-03
  • 2017-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多