/dev/random 总是返回相同的序列

6

我打算使用/dev/random输出作为openssl密钥生成的种子,然后我编写了这个小程序只是为了检查我即将要做的事情:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#define LEN 128

void uc2hex(char* hex, unsigned char* uc, unsigned short uc_len)
{
    FILE* bp=fmemopen(hex,2*uc_len+1,"w");
    unsigned short i;
    for(i=0;i<uc_len;i++)
    {
        fprintf(bp,"%02x",uc[i]);
        //printf("%02x\n",uc[i]);
        //fprintf(bp,"%d-",i);
    }
    fprintf(bp,"%c",'\0');
    fclose(bp);
}

int main()
{
    unsigned char buf[LEN];
    char str[2*LEN+1];
    int fd=open("/dev/random",O_RDONLY);
    read(fd,buf,LEN);
    uc2hex(str,buf,LEN);
    printf("%s\n",str);
    close(fd);
    return 0;
}

我运行了这个程序一两次,似乎一切正常,但是接着我再次连续运行了四次,输出如下:

[walter@eM350 ~]$ ./random 
0ee08c942ddf901af1278ba8f335b5df8db7cf18e5de2a67ac200f320a7a20e84866f533667a7e66a4572b3bf83d458e6f71f325783f2e3f921868328051f8f296800352cabeaf00000000000000000001000000000000005d08400000000000c080300e00000000000000000000000010084000000000000006400000000000
[walter@eM350 ~]$ ./random 
1f69a0b931c16f796bbb1345b3f58f17f74e3df600000000bb03400000000000ffffffff00000000880e648aff7f0000a88103b4d67f000000305cb4d67f000030415fb4d67f0000000000000000000001000000000000005d08400000000000c080300e00000000000000000000000010084000000000000006400000000000
[walter@eM350 ~]$ ./random 
4e8a1715238644a840eb66d9ff7f00002e4e3df600000000bb03400000000000ffffffff00000000a8ec66d9ff7f0000a871a37ad97f00000020fc7ad97f00003031ff7ad97f0000000000000000000001000000000000005d08400000000000c080300e00000000000000000000000010084000000000000006400000000000
[walter@eM350 ~]$ ./random 
598c57563e8951e6f0173f0cff7f00002e4e3df600000000bb03400000000000ffffffff0000000058193f0cff7f0000a8e1cbda257f0000009024db257f000030a127db257f0000000000000000000001000000000000005d08400000000000c080300e00000000000000000000000010084000000000000006400000000000

在我看来,这些似乎不是128字节的随机字符串,因为它们大多数都是相同的。因此,除了NSA篡改Linux内核随机数生成器的可能性之外,我只能猜测这与我的计算机中可用熵有关,当我请求太多连续字节时熵就会耗尽。我的问题是: 1)这个猜测正确吗? 2)假设1)是正确的,如何知道是否有足够的熵生成真正的随机字节序列?


4
请使用 /dev/urandom。真正的随机数很稀缺,不应该被这样浪费掉。 - Kerrek SB
定义“浪费”是什么意思?如果我从/dev/urandom读取数据,熵计数也会减少。不同之处在于,当计数达到0时,urandom并不会停止。 - glglgl
1
@glglgl:区别在于urandom使用真正的随机性来种子伪随机性。如果有更多可用的随机性,确实可以得到更好的随机性,但是在可用随机性的情况下,伪随机性至少与其一样好。 - Kerrek SB
@KerrekSB 我知道这一点,但我认为使用 urandom 也会消耗熵(从而干扰 random)。但似乎它们使用两个完全不同的熵池。 - glglgl
2个回答

13

来自read命令的手册页面:

当读取成功时,read()、readv()和pread()函数将返回实际读取并放置在缓冲区中的字节数。如果描述符引用具有在文件结尾之前剩余那么多字节数的普通文件,则系统保证读取所请求的字节数,但除此以外不做保证。

最重要的是:检查read的返回值,并查看您实际读取了多少字节 - 可能没有足够的熵来生成您请求的字节数。

int len = read(fd, buf, LEN);
printf("read() returned %d bytes: ", len);
if (len > 0)
{
    uc2hex(str, buf, len);
    printf("%s\n", str);
}

测试:

$ ./a.out 
read() returned 16 bytes: c3d5f6a8ee11ddc16f00a0dea4ef237a
$ ./a.out
read() returned 8 bytes: 24e23c57852a36bb
$ ./a.out 
read() returned 16 bytes: 4ead04d1eedb54ee99ab1b25a41e735b
$

1
当然...我被OP没有直接阻止调用所困惑。我猜在调用之间发生了一些自然的随机性。好的建议,绝对+1。 - Kerrek SB

2

正如其他人建议的那样,您需要检查返回值以获取读取的字节数。

如果/dev/random没有足够的字节可用,则会返回较少的字节数。

但是,在接下来的调用中,您仍然使用了预期的长度:

uc2hex(str,buf,LEN);
printf("%s\n",str);

所以,你正在转换和打印未初始化的内存。如果在调用之间没有对该内存进行写入,则不会更改其值,因此我不会感到惊讶随后的调用显示相同的值。

编辑:更好的方法是:

int nBytes=read(fd,buf,LEN);
uc2hex(str,buf,nBytes);
printf("%s\n",str);

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