如何在C中使用/ dev / random或urandom?


76

我想使用/dev/random/dev/urandom在C中使用。我该怎么做?我不知道如何用C处理它们,如果有人知道,请告诉我怎么做。谢谢。


查阅这篇非常有启发性的
appas

Answers:


107

通常,最好避免打开文件以获取随机数据,因为该过程中存在多个故障点。

在最近的Linux发行版,该getrandom系统调用可用来获取加密安全随机数,它不能失败,如果 GRND_RANDOM没有指定为标志和读取量最多256个字节。

截至2017年10月,OpenBSD,Darwin和Linux(带有-lbsd)现在都已实现了arc4random加密安全并且不会失败。这使其成为一个非常有吸引力的选择:

char myRandomData[50];
arc4random_buf(myRandomData, sizeof myRandomData); // done!

否则,您可以像使用文件一样使用随机设备。您从他们那里读到并且得到了随机数据。我在这里使用open/ read,但fopen/fread也可以正常使用。

int randomData = open("/dev/urandom", O_RDONLY);
if (randomData < 0)
{
    // something went wrong
}
else
{
    char myRandomData[50];
    ssize_t result = read(randomData, myRandomData, sizeof myRandomData);
    if (result < 0)
    {
        // something went wrong
    }
}

在关闭文件描述符之前,您可能会读取更多随机字节。/ dev / urandom永远不会阻塞,并且总是按照您的请求填充尽可能多的字节,除非系统调用被信号中断。它被认为是加密安全的,应该是您的随机设备。

/ dev / random更挑剔。在大多数平台上,它可以返回的字节数少于您要求的字节数,并且如果没有足够的字节数,它可以阻塞。这使错误处理的故事变得更加复杂:

int randomData = open("/dev/random", O_RDONLY);
if (randomData < 0)
{
    // something went wrong
}
else
{
    char myRandomData[50];
    size_t randomDataLen = 0;
    while (randomDataLen < sizeof myRandomData)
    {
        ssize_t result = read(randomData, myRandomData + randomDataLen, (sizeof myRandomData) - randomDataLen);
        if (result < 0)
        {
            // something went wrong
        }
        randomDataLen += result;
    }
    close(randomData);
}

14
@karim:请不要从/ dev / random中读取所有字节。只是不要。你的程序可能不是唯一需要随机字节系统上的用户。
Zan Lynx

1
@zneak,对不起,我忘了可以在这里编辑帖子。谢谢你让我知道。您的最后修改仅添加了对读取的检查,而不是循环。我将其修改为使用循环,因此代码现在将适当地阻塞。
莫罗格

2
@morrog急切的审阅者拒绝了它,所以我手动进行了更改。抱歉,您对此没有信誉。
zneak

1
@CodesInChaos,我引用Linux联机帮助页:“如果不确定是否应该使用/ dev / random或/ dev / urandom,则可能要使用后者。通常,/ dev / urandom应该使用可用于除长期使用的GPG / SSL / SSH密钥以外的所有内容。”
zneak

2
@zneak强调长寿,因为对于那些人来说,变得多余的偏执并没有伤害。对于正常的加密,可以使用/dev/urandom
CodesInChaos

21

上面还有其他准确的答案。不过,我需要使用FILE*流。这是我做的...

int byte_count = 64;
char data[64];
FILE *fp;
fp = fopen("/dev/urandom", "r");
fread(&data, 1, byte_count, fp);
fclose(fp);

1
可以通过简单地将int指针转换为char指针来直接读取int。fread((char*)(&myInt),sizeof(myInt),1,fp)
阿泽姆·班德·阿里

@ AzeemBande-Ali:为什么不使用fread((int *)(&myInt),sizeof(myInt),1,fp)代替呢?我的意思是强制转换为int *吗?
拉里2014年

4
在两种情况下都不应该在C代码中使用强制类型转换,fread()会使用void *,所以fread(&myInt,...)也是如此。
2015年

为什么需要byte_count?未使用。
CalculatorFeline

@CalculatorFeline此处的byte_count有点令人困惑,op最初可能希望每个索引具有相同的字节长度,但这无法做到……
LinconFive

17

只需打开文件进行读取,然后读取数据即可。在C ++ 11中,您可能希望使用std::random_device它提供对此类设备的跨平台访问。


似乎std::random_device并没有将其纳入2011年标准。它确实出现在N3797草案中
基思·汤普森

2
像看起来那样使之成为C ++ 11到底。std::random_device
legends2k

1
问题是std::random_device在C ++中,而不是在C中,尽管OP是一个很好的选择并且有好处,但OP询问了如何使用/dev/random/dev/urandomstd::random_device使用std::random_device它,这并不是OP所要求的
Nfagie Yansaneh

8

Zneak是100%正确的。读取一个比启动时需要的随机数稍大的随机数缓冲区也很常见。然后,您可以在内存中填充一个数组,或将它们写到您自己的文件中以备后用。

上面的典型实现:

typedef struct prandom {
     struct prandom *prev;
     int64_t number;
     struct prandom *next;
} prandom_t;

这或多或少像一条刚刚前进的磁带,可以根据需要由另一个线程神奇地补充。这里有很多服务提供了与更强大的发电机,如产生什么,但随机数的大型文件的转储:

  • 放射性衰变
  • 光学行为(光子撞击半透明镜)
  • 大气噪声(不如上述噪声强)
  • 陶醉的猴子农场在键盘上打字和移动鼠标(玩耍)

不要对加密种子使用“预包装”的熵,以防万一。这些集合适合模拟,根本不适合生成密钥等。

不用担心质量,如果您需要大量数字来进行蒙特卡洛模拟,那么以不引起read()阻塞的方式提供它们会更好。

但是,请记住,数字的随机性与生成数字所涉及的复杂性一样具有确定性。/dev/random并且/dev/urandom很方便,但不如使用HRNG(或从HRNG下载大型转储文件)强大。同样值得注意的是,它会/dev/random 通过熵重新填充,因此根据情况它可能会阻塞一段时间。


2
出于加密目的,下载“只有随机数的大文件转储”是可怕的建议。它要求其他人为您的功能提供种子,而这些服务似乎是通过互联网传输未加密的数据。请不要那样做。
dequis 2014年

我澄清了@dequis。我认为使用它们来运行大型仿真没有问题,有点 以为不将其用于keygen / etc是常识,但值得一提的是 这个问题与努力无关,所以我确实没有想到如此具体,但是很有意义。
蒂姆·波斯特

6

zneak的答案很简单,但是实际情况要复杂得多。例如,您首先需要考虑/ dev / {u} random是否真的是随机数设备。如果您的计算机受到感染,并且设备被替换为/ dev / zero或稀疏文件的符号链接,则可能会发生这种情况。如果发生这种情况,则随机流现在是完全可预测的。

最简单的方法(至少在Linux和FreeBSD上)是在设备上执行ioctl调用,仅当设备是随机生成器时才会成功:

int data;
int result = ioctl(fd, RNDGETENTCNT, &data); 
// Upon success data now contains amount of entropy available in bits

如果这是在第一次读取随机设备之前执行的,那么可以肯定地说您已经拥有了随机设备。因此,@ zneak的答案可以更好地扩展为:

int randomData = open("/dev/random", O_RDONLY);
int entropy;
int result = ioctl(randomData, RNDGETENTCNT, &entropy);

if (!result) {
   // Error - /dev/random isn't actually a random device
   return;
}

if (entropy < sizeof(int) * 8) {
    // Error - there's not enough bits of entropy in the random device to fill the buffer
    return;
}

int myRandomInteger;
size_t randomDataLen = 0;
while (randomDataLen < sizeof myRandomInteger)
{
    ssize_t result = read(randomData, ((char*)&myRandomInteger) + randomDataLen, (sizeof myRandomInteger) - randomDataLen);
    if (result < 0)
    {
        // error, unable to read /dev/random 
    }
    randomDataLen += result;
}
close(randomData);

Insane Coding博客介绍了此问题以及不久前的其他陷阱;我强烈建议您阅读整篇文章。我必须归功于他们从中寻求解决方案的地方。

编辑添加(2014-07-25)...碰巧
,我昨晚读到,作为LibReSSL努力的一部分,Linux似乎正在获取GetRandom()系统调用。截至撰写本文时,尚无何时在内核通用发行版中提供该功能。但是,这将是获取加密安全的随机数据的首选接口,因为它消除了通过文件访问提供的所有陷阱。另请参见LibReSSL可能的实现


2
具有足够能力将/ dev / random或/ dev / urandom替换为其他东西的攻击者通常也具有足够的能力来加载内核模块,从而破坏您确定是否为随机设备的所有尝试。
zneak 2015年

手册页getrandom()是在内核3.17中引入的。因此,截至2018年1月17日,现货Ubuntu 16.04尚没有此版本。uname -a在终端中运行以检查您的内核版本。
–erapert
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.