如何非常快速地生成加密强的伪随机数据?

信息安全 密码学 openssl 随机的
2021-08-21 18:57:14

在不安装不常见的软件包的情况下生成快速的加密强伪随机数据流的最佳方法是什么?从 /dev/urandom 绘图太慢了。

我从阅读 'wipe' 手册页中得到了一个想法:

将使用新的 RC6 算法作为 PRNG;RC6 以 128 位种子为密钥,然后对一个空块进行重复加密以获得伪随机流。

只用一个密钥加密一个空块可能不是最好的主意,尤其是在需要大量伪随机数据时,所以我很快在 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 字节)。重新播种间隔是随机的,以防止任何人猜测新密钥的使用位置。我故意选择 CBC 作为块模式而不是 CTR,即使它更快,因为我读过 CTR 仅适用于恒定流,而且我非常有限的密码知识告诉我,一直重启的重复计数器不是好主意什么时候“流”只有几 MB 长,虽然我真的不确定。

我担心的一个问题是离开 openssl 的流是否完全加密。如果输入不能被块大小整除,openssl 是否会将额外数据留空(在结果流中留下零散的“间隙”)?填充与此有关吗

所以我的问题是,这是创建快速随机数据的好方法吗?有没有我错过/忽略的东西等等,比如使用错误的块模式?

2个回答

编辑:明显是盲目的,所以我错过了。OpenSSL 已经为其自己的内部 PRNG 提供了一个接口,该接口以机器的/dev/urandom. 只需这样做:

openssl rand 10000000

产生一千万个伪随机字节。在我的笔记本电脑上,这似乎比 快 3 倍/dev/urandom,即 11 MByte/s 左右。如果这对您来说还不够快,请继续阅读。


原始回复:

这实际上取决于所涉及的硬件和上下文。既然您在谈论 bash 脚本/dev/urandom和 OpenSSL,我想:

  • 你使用 Linux。
  • 您想生成一个充满伪随机字节的大文件。
  • 您需要一个解决方案,它是现有“正常”包的脚本程序集,而不是编程解决方案。

事实上,如果你能写自己的C代码,你最好的选择将是CTR模式与为AES加密AES-NI指令(最近的PC上提供这些指令),或专用的流密码(的例如,一个这些)。在 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 64 个16 字节块(即 2.5 亿 TB)的块。有可能你无论如何都达不到它。从这个意义上说,“重新播种”并不是真正需要的。
  • 当您“重新播种”时(如果您决定维护此功能,这是不必要的),请使用/dev/urandom,而不是/dev/random. /dev/urandom对密码学来说很好(操作系统启动脚本确保内部 PRNG 被正确播种),尽管实施时存在误入歧途的疑虑/dev/random另一方面,/dev/random可能会阻塞,因此非常慢(没有什么比阻塞进程更慢的了)。
  • 当心字符串。bash使用字符串;/dev/random/dev/urandom返回bytes,包括值为 0 的字节。这可能会导致人为截断密钥,并降低安全性。这不好。相反,您应该将字节编码为十六进制,-K-iv使用openssl.
  • OpenSSL 可以添加一个以 8 个固定字节开头的标头(因此完全非随机)。-K如果您使用and ,它不会这样做-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 个伪随机字节的序列,这些字节适合用于加密目的。随意修改bscount参数,但请记住,这dd将分配一个大小为 的 RAM 缓冲区bs,因此您通常希望保持bs较小,但不要太小以避免过多往返内核(8192 到 65536 通常是好的值)。

我在这里使用 MD5 只是为了方便字节到十六进制的转换(MD5 的已知缺点在这种情况下对安全性没有任何影响)。

该脚本应该适用于任何体面的 Linux 安装。它只使用默认情况下应该存在的包。在我不是很强大的笔记本电脑上,这个脚本以每秒 73 MBytes 的速率生成伪随机字节,这还不错,可能足以满足您的目的(如果这还不够,那么您可能会遇到其他问题 I/ O 瓶颈)。

如果您发现自己经常耗尽随机数据源,我建议您购买硬件随机数生成器,例如http://www.entropykey.co.uk/

您通常受限于 CPU 上的随机数生成器硬件可以生成的随机数据量。诸如此类的外部设备可以以更快的速度生成良好的随机数据。