如何安全地生成随机数
随机数 加密
![](/data/attachment/album/201803/25/103321odkv11vbjidznn8d.jpg)


### 使用 urandom


使用 [urandom](http://blog.cr.yp.to/20140205-entropy.html)！使用 [urandom](http://cr.yp.to/talks/2011.09.28/slides.pdf)！使用 [urandom](http://golang.org/src/pkg/crypto/rand/rand_unix.go)！


使用 [urandom](http://security.stackexchange.com/questions/3936/is-a-rand-from-dev-urandom-secure-for-a-login-key)！使用 [urandom](http://stackoverflow.com/a/5639631)！使用 [urandom](https://twitter.com/bramcohen/status/206146075487240194)！


### 但对于密码学密钥呢？


仍然使用 [urandom](https://twitter.com/bramcohen/status/206146075487240194)。


### 为什么不是 SecureRandom、OpenSSL、havaged 或者 c 语言实现呢？


这些是用户空间的 CSPRNG（伪随机数生成器）。你应该用内核的 CSPRNG，因为：


* 内核可以访问原始设备熵。
* 它可以确保不在应用程序之间共享相同的状态。
* 一个好的内核 CSPRNG，像 FreeBSD 中的，也可以保证它播种之前不给你随机数据。


研究过去十年中的随机失败案例，你会看到一连串的用户空间的随机失败案例。[Debian 的 OpenSSH 崩溃](http://research.swtch.com/openssl)？用户空间随机！安卓的比特币钱包[重复 ECDSA 随机 k 值](http://arstechnica.com/security/2013/08/google-confirms-critical-android-crypto-flaw-used-in-5700-bitcoin-heist/)？用户空间随机！可预测洗牌的赌博网站？用户空间随机！


用户空间的生成器几乎总是依赖于内核的生成器。即使它们不这样做，整个系统的安全性也会确保如此。**但用户空间的 CSPRNG 不会增加防御深度；相反，它会产生两个单点故障。**


### 手册页不是说使用 /dev/random 嘛？


这个稍后详述，保留你的意见。你应该忽略掉手册页。不要使用 `/dev/random`。`/dev/random` 和 `/dev/urandom` 之间的区别是 Unix 设计缺陷。手册页不想承认这一点，因此它产生了一个并不存在的安全顾虑。把 `random(4)` 中的密码学上的建议当作传说，继续你的生活吧。


### 但是如果我需要的是真随机值，而非伪随机值呢？


urandom 和 `/dev/random` 提供的是同一类型的随机。与流行的观念相反，`/dev/random` 不提供“真正的随机”。从密码学上来说，你通常不需要“真正的随机”。


urandom 和 `/dev/random` 都基于一个简单的想法。它们的设计与流密码的设计密切相关：一个小秘密被延伸到不可预测值的不确定流中。 这里的秘密是“熵”，而流是“输出”。


只在 Linux 上 `/dev/random` 和 urandom 仍然有意义上的不同。Linux 内核的 CSPRNG 定期进行密钥更新（通过收集更多的熵）。但是 `/dev/random` 也试图跟踪内核池中剩余的熵，并且如果它没有足够的剩余熵时，偶尔也会罢工。这种设计和我所说的一样蠢；这与基于“密钥流”中剩下多少“密钥”的 AES-CTR 设计类似。


如果你使用 `/dev/random` 而非 urandom，那么当 Linux 对自己的 RNG（随机数生成器）如何工作感到困惑时，你的程序将不可预测地（或者如果你是攻击者，非常可预测地）挂起。使用 `/dev/random` 会使你的程序不太稳定，但这不会让你在密码学上更安全。


### 这是个缺陷，对吗？


不是，但存在一个你可能想要了解的 Linux 内核 bug，即使这并不能改变你应该使用哪一个 RNG。


在 Linux 上，如果你的软件在引导时立即运行，或者这个操作系统你刚刚安装好，那么你的代码可能会与 RNG 发生竞争。这很糟糕，因为如果你赢了竞争，那么你可能会在一段时间内从 urandom 获得可预测的输出。这是 Linux 中的一个 bug，如果你正在为 Linux 嵌入式设备构建平台级代码，那你需要了解它。


在 Linux 上，这确实是 urandom（而不是 `/dev/random`）的问题。这也是 [Linux 内核中的错误](https://factorable.net/weakkeys12.extended.pdf)。 但它也容易在用户空间中修复：在引导时，明确地为 urandom 提供种子。长期以来，大多数 Linux 发行版都是这么做的。但**不要**切换到不同的 CSPRNG。


### 在其它操作系统上呢？


FreeBSD 和 OS X 消除了 urandom 和 `/dev/random` 之间的区别；这两个设备的行为是相同的。不幸的是，手册页在解释为什么这样做上干的很糟糕，并延续了 Linux 上 urandom 可怕的神话。


无论你使用 `/dev/random` 还是 urandom，FreeBSD 的内核加密 RNG 都不会停摆。 除非它没有被提供种子，在这种情况下，这两者都会停摆。与 Linux 不同，这种行为是有道理的。Linux 应该采用它。但是，如果你是一名应用程序开发人员，这对你几乎没有什么影响：Linux、FreeBSD、iOS，无论什么：使用 urandom 吧。


### 太长了，懒得看


直接使用 urandom 吧。


### 结语


[ruby-trunk Feature #9569](https://bugs.ruby-lang.org/issues/9569)



> 
> 现在，在尝试检测 `/dev/urandom` 之前，SecureRandom.random\_bytes 会尝试检测要使用的 OpenSSL。 我认为这应该反过来。在这两种情况下，你只需要将随机字节进行解压，所以 SecureRandom 可以跳过中间人（和第二个故障点），如果可用的话可以直接与 `/dev/urandom` 进行交互。
> 
> 
> 


总结：



> 
> `/dev/urandom` 不适合用来直接生成会话密钥和频繁生成其他应用程序级随机数据。
> 
> 
> GNU/Linux 上的 random(4) 手册所述......
> 
> 
> 


感谢 Matthew Green、 Nate Lawson、 Sean Devlin、 Coda Hale 和 Alex Balducci 阅读了本文草稿。公正警告：Matthew 只是大多同意我的观点。




---


via: <https://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/>


作者：[Thomas & Erin Ptacek](https://sockpuppet.org/blog) 译者：[kimii](https://github.com/kimii) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
[How To Safely Generate A Random Number](/blog/2014/02/25/safely-generate-random-numbers/)

## Use urandom

Use [urandom](http://blog.cr.yp.to/20140205-entropy.html). Use [urandom](http://cr.yp.to/talks/2011.09.28/slides.pdf). Use [urandom](http://golang.org/src/pkg/crypto/rand/rand_unix.go). Use [urandom](http://security.stackexchange.com/questions/3936/is-a-rand-from-dev-urandom-secure-for-a-login-key). Use [urandom](http://stackoverflow.com/a/5639631). Use
[urandom](https://twitter.com/bramcohen/status/206146075487240194).

## But what about for crypto keys?

Still [urandom](https://twitter.com/bramcohen/status/206146075487240194).

## Why not {SecureRandom, OpenSSL, havaged, &c}?

These are userspace CSPRNGs. You want to use the kernel’s CSPRNG, because:

The kernel has access to raw device entropy.

It can promise not to share the same state between applications.

A good kernel CSPRNG, like FreeBSD’s, can also promise not to feed you random data before it’s seeded.


Study the last ten years of randomness failures and you’ll read a
litany of userspace randomness failures. [Debian’s OpenSSH debacle](http://research.swtch.com/openssl)?
Userspace random. Android Bitcoin wallets [repeating ECDSA k’s](http://arstechnica.com/security/2013/08/google-confirms-critical-android-crypto-flaw-used-in-5700-bitcoin-heist/)?
Userspace random. Gambling sites with predictable shuffles? Userspace
random.

Userspace OpenSSL also seeds itself from “from uninitialized memory, magical fairy dust and unicorn horns” generators almost always depend on the kernel’s generator
anyways. Even if they don’t, the security of your whole system sure
does. **A userspace CSPRNG doesn’t add defense-in-depth; instead, it
creates two single points of failure.**

## Doesn’t the man page say to use /dev/random?

You But, more on this later. Stay your pitchforks. should ignore the man page. Don’t use /dev/random. The distinction between /dev/random and /dev/urandom is a Unix design wart. The man page doesn’t want to admit that, so it invents a security concern that doesn’t really exist. Consider the cryptographic advice in random(4) an urban legend and get on with your life.

## But what if I need real random values, not psuedorandom values?

Both urandom and /dev/random provide the same kind of randomness. Contrary to popular belief, /dev/random doesn’t provide “true random” data. For cryptography, you don’t usually want “true random”.

Both urandom and /dev/random are based on a simple idea. Their design is closely related to that of a stream cipher: a small secret is stretched into an indefinite stream of unpredictable values. Here the secrets are “entropy”, and the stream is “output”.

Only on Linux are /dev/random and urandom still meaningfully different. The Linux kernel CSPRNG rekeys itself regularly (by collecting more entropy). But /dev/random also tries to keep track of how much entropy remains in its kernel pool, and will occasionally go on strike if it decides not enough remains. This design is as silly as I’ve made it sound; it’s akin to AES-CTR blocking based on how much “key” is left in the “keystream”.

If you use /dev/random instead of urandom, your program will
unpredictably (or, if you’re an attacker, *very predictably*) hang
when Linux gets confused about how its own RNG works. Using
/dev/random will make your programs less stable, but it won’t make
them any more cryptographically safe.

## There’s a catch here, isn’t there?

No, but there’s a Linux kernel bug you might want to know about, even though it doesn’t change which RNG you should use.

On Linux, if your software runs immediately at boot, and/or the OS has just been installed, your code might be in a race with the RNG. That’s bad, because if you win the race, there could be a window of time where you get predictable outputs from urandom. This is a bug in Linux, and you need to know about it if you’re building platform-level code for a Linux embedded device.

This is indeed a problem with urandom (and not /dev/random) on
Linux. It’s also a [bug in the Linux kernel](https://factorable.net/weakkeys12.extended.pdf). But it’s also easily
fixed in userland: at boot, seed urandom explicitly. Most Linux
distributions have done this for a long time. But *don’t* switch to a
different CSPRNG.

## What about on other operating systems?

FreeBSD and OS X do away with the distinction between urandom and /dev/random; the two devices behave identically. Unfortunately, the man page does a poor job of explaining why this is, and perpetuates the myth that Linux urandom is scary.

FreeBSD’s kernel crypto RNG doesn’t block regardless of whether you use /dev/random or urandom. Unless it hasn’t been seeded, in which case both block. This behavior, unlike Linux’s, makes sense. Linux should adopt it. But if you’re an app developer, this makes little difference to you: Linux, FreeBSD, iOS, whatever: use urandom.

## tl;dr

Use urandom.

## Epilog

Right now, SecureRandom.random_bytes tries to detect an OpenSSL to use before it tries to detect /dev/urandom. I think it should be the other way around. In both cases, you just need random bytes to unpack, so SecureRandom could skip the middleman (and second point of failure) and just talk to /dev/urandom directly if it’s available.


*Resolution:*

/dev/urandom is not suitable to be used to generate directly session keys and other application level random data which is generated frequently.

[the] random(4) [man page] on GNU/Linux [says]…


*Thanks to Matthew Green, Nate Lawson, Sean Devlin, Coda Hale, and
Alex Balducci for reading drafts of this. Fair warning: Matthew only
mostly agrees with me.*