C++ - Extracting random numbers from /dev/urandom

2019-08-14 02:58发布

I need many cryptographically secure numbers, so I was thinking about extracting randomness from /dev/urandom and then "converting" it into (say) unsigned long long int. I guess it should be very efficient and it seems it is cryptographically secure, but I will investigate this aspect more in the future.
Now the question is: how can I do so?

I found this code:

char * block;
short size = 1;
ifstream urandom("/dev/urandom", ios::in|ios::binary);
urandom.read(block,size);
urandom.close();

Does it make sense? And how do I convert what I get to the type I desire?

EDIT - Using random interface of C++11

Following a suggestion from the comments, I tried using a uniform distribution over the integers and a random_device initialized as /dev/urandom. Here is the code:

std::uniform_int_distribution<unsigned int> dist(0, modulus-1);
std::random_device urandom("/dev/urandom");
for(unsigned int i = start ; i < end ; ++i)
{
    vector[i] = dist(urandom);
}

The problem is that this code is approximately 1000 times slower than before (I was using a xorshift128+ generator): 5 milliseconds vs. almost 5 seconds. Is this normal? Honestly, I thought that streaming bytes in from /dev/urandom and converting them to unsigned int would have been way faster... Am I missing something?

标签: c++ random
1条回答
爷的心禁止访问
2楼-- · 2019-08-14 03:29

So first of your example is incorrect and would cause undefined behaviour.

The char* block isn't pointing to any allocated data, so the ifstream::read would actually write into unallocated memory.

Apart from this the type of size is signed short while it should be size_t.

In order to read a unsigned long long int you could use `ifstream' like this:

#include <iostream>
#include <fstream>

int main()
{
    using namespace std;
    unsigned long long int random_value = 0; //Declare value to store data into
    size_t size = sizeof(random_value); //Declare size of data
    ifstream urandom("/dev/urandom", ios::in|ios::binary); //Open stream
    if(urandom) //Check if stream is open
    {
        urandom.read(reinterpret_cast<char*>(&random_value), size); //Read from urandom
        if(urandom) //Check if stream is ok, read succeeded
        {
            std::cout << "Read random value: " << random_value << std::endl;
        }
        else //Read failed
        {
            std::cerr << "Failed to read from /dev/urandom" << std::endl;
        }
        urandom.close(); //close stream
    }
    else //Open failed
    {
        std::cerr << "Failed to open /dev/urandom" << std::endl;
    }
    return 0;
}

The interesting part would be where this actually reads with urandom.read(reinterpret_cast<char*>(&random_value), size);

The size should be clear. By using sizeof we get the actual size in bytes of the data we want to store the random value into. This is useful as this value might be different on different architectures (e.g. 32-bit and 64-bit).
Be careful if the data type you pass here is a pointer. sizeof will only return the size of the pointer and not the size of the data it is pointing to.

random_value is of type unsigned long long int. So &random_value is the type of the appropriate pointer unsigned long long int*. But we want to read bytes (char) and therefore need to change/cast the value from unsigned long long int* to char* (reinterpret_cast<char*>(&random_value)).

查看更多
登录 后发表回答