类似于 /dev/urandom 的可配置种子生成器?

5

我正在使用/dev/urandom进行dd,以创建具有随机内容的文件。这个方法效果很好,但是我希望能够通过再次运行相同种子的PRNG来在稍后的时间点上再现文件内容。是否有一种可与字符设备配合使用的可种子PRNG?

我正在使用最新的Linux 3.X内核。


你使用的是哪种编程语言? - Taymon
语言并不是很重要,因为我正在寻找一个设备文件,但我计划从bash运行dd命令!如果有与语言相关的解决方案,我更喜欢C或Python 2.7。 - Jonatan
6个回答

2

Python 3.9 random.randbytes + random.seed

我已经学会了停止与Bash无法完成的事情斗争,只是顺其自然:

randbytes() (
  python -c 'import random;import sys;random.seed(int(sys.argv[1]));sys.stdout.buffer.write(random.randbytes(int(sys.argv[2])))' "$@"
)

使用方法:

randbytes <seed> <nbytes>

e.g.:

randbytes 0 8 | hd

始终使用种子0输出8个相同的伪随机字节:

00000000  cd 07 2c d8 be 6f 9f 62                           |..,..o.b|
00000008

可阅读的多行版本在此处:生成可种子化数据的随机字符串

在我的联想ThinkPad P51上,我可以在0.5秒内将1亿字节转储到ramfs中。然而,如果我尝试转储10亿字节,则会出现以下错误:

Python int too large to convert to C int

所以要记住这一点。

作为比较:

time sudo dd if=/dev/urandom of=ramfs/test bs=4k count=24414

花费了2.5秒,因此速度较慢,这并不奇怪,因为它是一个更随机的源,而Python生成器是确定性的,并且似乎是用C编写的。

在Ubuntu 20.10、Linux内核5.8.0上进行了测试。


2

/dev/urandom旨在尽可能地不可预测。看起来您需要一个更常规的带种子的伪随机数生成器。

这是我刚写的一个C语言示例:

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    char *endptr;
    unsigned long int seed;

    if (argc != 2 || *argv[1] == '\0') {
        fprintf(stderr, "usage: %s seed\n", argv[0]);
        return EXIT_FAILURE;
    }
    errno = 0;
    seed = strtoul(argv[1], &endptr, 0);
    if (errno != 0 || *endptr != '\0' || seed > UINT_MAX) {
        fprintf(stderr, "%s: invalid seed\n", argv[0]);
        return EXIT_FAILURE;
    }
    srandom((unsigned int) seed);

    while (1) {
        int i;
        long int randomnum = random();
        for (i = 0; i < sizeof randomnum; i++) {
            if (putchar((randomnum >> (i * CHAR_BIT)) & UCHAR_MAX) == EOF) {
                return EXIT_SUCCESS;
            }
        }
    }
}

这是一个程序,而不是设备文件,但它的输出格式与您从/dev/urandom获得的格式相同。您可以将其输出通过管道传输到dd并省略if

如果您需要提供一个真正随机的种子来供该程序使用,可以在bash中像这样从/dev/urandom获取:

seed=$(od -vAn -N4 -tu4 </dev/urandom)

将4替换为您的机器上的sizeof(unsigned int)(它可能是4)。


1
从urandom中获取 文档

When a Linux system starts up without much operator interaction, the entropy pool may be in a fairly predictable state. This reduces the actual amount of noise in the entropy pool below the estimate. In order to counteract this effect, it helps to carry entropy pool information across shut-downs and start-ups. To do this, add the following lines to an appropriate script which is run during the Linux system start-up sequence:

    echo "Initializing kernel random number generator..."
    # Initialize kernel random number generator with random seed 
    # from last shut-down (or start-up) to this start-up.  Load and 
    # then save 512 bytes, which is the size of the entropy pool.
    if [ -f /var/random-seed ]; then
            cat /var/random-seed >/dev/urandom
    fi
    dd if=/dev/urandom of=/var/random-seed count=1

5
我认为这并不能得到相同的数字序列,这只是一种保持熵的技巧。 - Taymon
@Taymon,这就是如何做的大概:你可以写入/dev/urandom来设置熵池,这样你就能在需要时恢复随机源的状态。 - Ottavio Campana
1
我的理解是,向/dev/urandom写入数据会增加熵池的熵值;它不会覆盖已经存在的内容。 - Taymon
那么为什么在关闭电源时还要保存它呢?在我链接的页面中,这是我发布的代码下面的部分。 - Ottavio Campana

1

/dev/*random不使用种子,因为它们不是伪随机数生成器。它们从环境中提供随机性(甚至使用硬件作为源,如键盘、中断、网络等)。在/dev/urandom的情况下,仅在池耗尽时才使用种子和RNG。但是当发生这种情况时,它仍然是不可预测的。

因此,请勿从/dev/*random读取随机数字。相反,使用bash工具生成随机数字:

#! /bin/bash
# Seed the RNG RANDOM = 1234
# Print 10 random numbers for i in {1..10} do echo $RANDOM done

上面的脚本每次都会打印相同的数字序列,因为种子是恒定的。

如果要生成字节,即0到255范围内的值,则使用printf而不是echo并将得到的随机数循环到该范围:

printf "\\x$(printf "%x" $(($RANDOM % 256)))"

现在运行脚本并将输出重定向到文件中,它将包含10个“随机”字节。


0

如果您希望使用种子生成随机文件,那么您正在使用错误的工具。/dev/urandom 从环境中获取其随机性(传入和传出网络数据包的时间、磁盘访问等),因此即使您使用相同的种子,也极不可能连续两次获得相同的随机数序列。

您需要一个传统的(也称为仅软件)伪随机数生成器。Mersenne Twister 是一个很好的选择。它应该在您使用的语言中有实现。或者只需使用您的语言自带的任何伪随机数生成器即可。


是的,我可能表达得有些笨拙。我所说的“类似于urandom”的意思是PRNG应该公开一个字符设备。我目前在Python中使用Mersenne Twister,它产生了令人满意的数据,但接口不尽如人意(不是设备文件;)。 - Jonatan
@Jonatan 为什么你想要/需要一个字符设备接口?这对你的程序有什么区别?(只是想了解你的用例。)据我所知,Linux内核本身并没有通过字符设备导出PRNG。因此,任何通过字符设备的PRNG都必须由另一个用户空间程序实现。从字符设备调用它只会增加不必要的内核往返,就我目前所看到的情况而言。 - Christian Hudon

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接