How to use /dev/random or urandom in C?
Asked Answered
M

5

89

I want to use /dev/random or /dev/urandom in C. How can I do it? I don't know how can I handle them in C, if someone knows please tell me how. Thank you.

Malaya answered 3/4, 2010 at 19:19 Comment(1)
Check out this very informative article on some common caveats of taking this route to (pseudo-)randomness: insanecoding.blogspot.fi/2014/05/…Jerold
A
117

In general, it's a better idea to avoid opening files to get random data, because of how many points of failure there are in the procedure.

On recent Linux distributions, the getrandom system call can be used to get crypto-secure random numbers, and it cannot fail if GRND_RANDOM is not specified as a flag and the read amount is at most 256 bytes.

As of October 2017, OpenBSD, Darwin and Linux (with -lbsd) now all have an implementation of arc4random that is crypto-secure and that cannot fail. That makes it a very attractive option:

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

Otherwise, you can use the random devices as if they were files. You read from them and you get random data. I'm using open/read here, but fopen/fread would work just as well.

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
    }
}

You may read many more random bytes before closing the file descriptor. /dev/urandom never blocks and always fills in as many bytes as you've requested, unless the system call is interrupted by a signal. It is considered cryptographically secure and should be your go-to random device.

/dev/random is more finicky. On most platforms, it can return fewer bytes than you've asked for and it can block if not enough bytes are available. This makes the error handling story more complex:

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);
}
Appointee answered 3/4, 2010 at 19:22 Comment(18)
okay, nice answer. And what if I want to set it to read multiple numbers ? Would i do while(something) { read(..) } or should i open/close it everytime the loop starts over ?Malaya
Hi, can you give me some tips how I can read continuously all the bytes from \dev\random. if it blocks, how can I sense it. But again when more bytes are available I will read them.Yasmin
@Yasmin It doesn't block on Mac OS (which I use), and I'm not too sure about how to deal with block files for these circumstances. You should ask a new question for it.Appointee
@karim: Please never read all the bytes from /dev/random. Just don't. Your program is probably not the only user on the system that needs random bytes.Testator
I need only 256 bytes. then I stop. But sometime I do not get 256 bytes. I have to wait for 1/2 seconds.Yasmin
The code in this answer is currently wrong. You need to check the return value of read to see how many bytes were actually read, and loop until you've gotten enough. As-is this code could leave myRandomInteger filled with old memory rather than random data.Bell
@morrog, I believe my edit will address the issues you've raised. Also don't forget that you can edit anyone's answer if you feel it's inadequate.Appointee
@zneak, sorry I forgot that posts could be edited on here. Thanks for letting me know. Your last modification only added a check on read, instead of a loop. I modified it to use a loop, so the code will now block appropriately.Bell
@Bell Overeager reviewers rejected it, so I did the changes manually. Sorry you don't get credited for it.Appointee
Actually most cryptographers recommend using /dev/urandom. If it has been sufficiently seeded once, it will output unlimited amounts of pseudo random numbers suitable for use in crypto. The only worry is that the initial seeding might not have been sufficient. The "less random" issue only applies is some rare special cases, like early in the boot process on embedded devices or clones virtual machines.Hermon
@CodesInChaos, I'm quoting the Linux manpage: "If you are unsure about whether you should use /dev/random or /dev/urandom, then probably you want to use the latter. As a general rule, /dev/urandom should be used for everything except long-lived GPG/SSL/SSH keys."Appointee
@Appointee Emphasis on long-lived since for those it doesn't hurt to be extra paranoid. For normal crypto use /dev/urandom is fine.Hermon
However you also need to ensure that /dev/random (or /dev/urandom) is actually the random number device and not a sparse file or a symlink to /dev/zero (which may have happened if your machine has been hacked). There's a good article at insanecoding.blogspot.co.uk/2014/05/… that goes into all the pitfalls of using /dev/{u}random blindly.Kowalczyk
Does this need to check the return value from the open(2) call?Steepen
@KyleWpppd, this answer initially checked for no error conditions at all for the sake of simplicity, but someone else decided that it was important enough around read to include it. I'd say that in general, you should always check for error conditions. Additionally, if your application is security-critical, it might be a good idea to check that the file is a character device.Appointee
This answer has been sitting here for 5 years, but I believe the code is wrong. You never even used the file descriptor randomData aside from closing it. You sure it shouldn't read read(randomData ...? Shows the importance of naming.Africanize
@BoppreH, you'll probably be happy to learn that this specific issue has "only" been around for two years (someone else suggested a change that I ended up merging myself without being careful enough). Speaking of suggesting edits, you are very welcome to fix any other issue you find.Appointee
The last paragraph of this answer is making an incorrect assumption, described here: 2uo.de/myths-about-urandom/#structure. In fact, both /dev/random and /dev/urandom sit behind the same CSPRNG. (Your advice to pick /dev/urandom over /dev/random still holds.)Anatolic
D
23

There are other accurate answers above. I needed to use a FILE* stream, though. Here's what I did...

int byte_count = 64;
char data[64];
FILE *fp;
fp = fopen("/dev/urandom", "r");
fread(&data, 1, byte_count, fp);
fclose(fp);
Dumps answered 16/8, 2012 at 15:0 Comment(5)
An int can be read directly by simply casting the int pointer to a char pointer. fread((char*)(&myInt),sizeof(myInt),1,fp)Beryllium
@AzeemBande-Ali:Why don't you use fread((int*)(&myInt),sizeof(myInt),1,fp) instead ? I mean a cast to int* ?Poirier
In neither case should a cast be used in C code, fread() takes a void *, so just do fread(&myInt, ... );Connecticut
Why do you need byte_count? It's unused.Oreste
@Oreste The byte_count here is a bit confusing, op maybe initially wanted each index to have same byte length, but this could not do ...Meli
U
16

Just open the file for reading and then read data. In C++11 you may wish to use std::random_device which provides cross-platform access to such devices.

Underpinnings answered 3/4, 2010 at 19:22 Comment(3)
It appears that std::random_device didn't make it into the 2011 standard. It does appear in the N3797 draft.Flue
Looks like std::random_device did make it into C++11 in the end.Margueritamarguerite
Problem is that std::random_device is in C++ and not in C, and the OP asked how to use /dev/random or /dev/urandom not how to use std::random_device although it is a good choice to use std::random_device and it has benefits, it is just not what the OP asked forAmmunition
L
8

Zneak is 100% correct. Its also very common to read a buffer of random numbers that is slightly larger than what you'll need on startup. You can then populate an array in memory, or write them to your own file for later re-use.

A typical implementation of the above:

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

This becomes more or less like a tape that just advances which can be magically replenished by another thread as needed. There are a lot of services that provide large file dumps of nothing but random numbers that are generated with much stronger generators such as:

  • Radioactive decay
  • Optical behavior (photons hitting a semi transparent mirror)
  • Atmospheric noise (not as strong as the above)
  • Farms of intoxicated monkeys typing on keyboards and moving mice (kidding)

Don't use 'pre-packaged' entropy for cryptographic seeds, in case that doesn't go without saying. Those sets are fine for simulations, not fine at all for generating keys and such.

Not being concerned with quality, if you need a lot of numbers for something like a monte carlo simulation, it's much better to have them available in a way that will not cause read() to block.

However, remember, the randomness of a number is as deterministic as the complexity involved in generating it. /dev/random and /dev/urandom are convenient, but not as strong as using a HRNG (or downloading a large dump from a HRNG). Also worth noting that /dev/random refills via entropy, so it can block for quite a while depending on circumstances.

Lowis answered 4/4, 2010 at 4:21 Comment(2)
Downloading "large file dumps of nothing but random numbers" is terrible advice for cryptograhic purposes. It's asking someone else to provide the seed to your functions, and those services seem to transfer that data unencrypted over the internet. Please don't do that.Arty
@Arty I clarified. I see no problem with using them to run large simulations, kinda thought it would be common sense to not use them for keygen / etc, but it's worth being oddly specific to the point. The question was endeavor-agnostic, so it really didn't occur to me to be so specific, but good point.Lowis
K
7

zneak's answer covers it simply, however the reality is more complicated than that. For example, you need to consider whether /dev/{u}random really is the random number device in the first place. Such a scenario may occur if your machine has been compromised and the devices replaced with symlinks to /dev/zero or a sparse file. If this happens, the random stream is now completely predictable.

The simplest way (at least on Linux and FreeBSD) is to perform an ioctl call on the device that will only succeed if the device is a random generator:

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

If this is performed before the first read of the random device, then there's a fair bet that you've got the random device. So @zneak's answer can better be extended to be:

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);

The Insane Coding blog covered this, and other pitfalls not so long ago; I strongly recommend reading the entire article. I have to give credit to their where this solution was pulled from.

Edited to add (2014-07-25)...
Co-incidentally, I read last night that as part of the LibReSSL effort, Linux appears to be getting a GetRandom() syscall. As at time of writing, there's no word of when it will be available in a kernel general release. However this would be the preferred interface to get cryptographically secure random data as it removes all pitfalls that access via files provides. See also the LibReSSL possible implementation.

Kowalczyk answered 23/7, 2014 at 12:22 Comment(2)
An attacker with enough power to replace /dev/random or /dev/urandom with something else typically also has enough power to load a kernel module to screw up every attempt that you make at determining if it's a random device or not.Appointee
The man page says getrandom() was introduced in kernel 3.17. So stock Ubuntu 16.04 doesn't have it as of 2018-01-17. Run uname -a in a terminal to check your kernel version.Jock

© 2022 - 2024 — McMap. All rights reserved.