Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Better alternatives to random_device in C++?

I have been using random_device rd{} to generate seeds for my Mersenne-Twister pseudo random number generator mt19937 RNG{rd()} as have been suggested here. However, it is written in the documentation (comment in the documentations’ example code), that "the performance of many implementations of random_device degrades sharply once the entropy pool is exhausted. For practical use random_device is generally only used to seed a PRNG such as mt19937". I have tried testing how big this "entropy pool" is, and for 10^6 number of calls, random_device returns me more than 10^2 repeating numbers (see my example code and output below). In other words, if I will try using random_device as a seed to my Mersenne-Twister PRNG, it will generate a solid fraction of repeating seeds.

Question: do people still use random_device in C++ to generate seeds for PRNG or are there already better alternatives?

My code:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

#include <iostream>
#include <random>
#include <chrono>

using namespace std;

int main(){
    
    auto begin = std::chrono::high_resolution_clock::now();
    
    random_device rd{};
    mt19937 RNG{ rd() };

    int total_n_of_calls = 1e6;
    vector<int> seeds;
    
    for(auto call = 0; call < total_n_of_calls; call++){
    int call_rd = rd();
    seeds.push_back(call_rd);
    }
    
    int count_repeats = 0;
    sort(seeds.begin(), seeds.end());
    for(int i = 0; i < seeds.size() - 1; i++) {
        if (seeds[i] == seeds[i + 1]) {
            count_repeats++;
    }
    }
    
    printf("Number of times random_device have been called: %i\n", total_n_of_calls);
    printf("Number of repeats: %i\n", count_repeats);

    auto end = std::chrono::high_resolution_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
    printf("Duration: %.3f seconds.\n", elapsed.count() * 1e-9);

    return 0;
}

The output:

Number of times random_device have been called: 1000000
Number of repeats: 111
Duration: 0.594 seconds.

>Solution :

TL;DR: No, there’s nothing better. You just need to stop abusing it.


The point of random_device is that it asks your platform for bits that are actually random, not just pseudorandom from some deterministic seed.

If the platform / OS thinks the entropy it had was expended, then it cannot offer you this. But honestly, it uses true sources of randomness, from actual randomness hardware in your CPU to timing of disk access, to modify the internal state of a PRNG. That’s all there is to it – to someone external, the bits you get are still unpredictable.

So, the answer is this:

  • you use random_device because you need actually random seeds. There’s no algorithmic shortcut to randomness – the word "algorithm" already says that it’s deterministic. And software, universally, is deterministic, unless it gets random data externally. So, all you can do is ask the operating system, which actually deals with any source of randomness there is in your system. And that’s already exactly what random_device does.
  • So, no, you cannot use something else but actual external entropy, which is exactly what you get most efficiently from random_device (unless you buy an expensive dedicated random generator card and write a driver for it).
  • As the OS uses the random external source to change the internal state of a PRNG, it can produce more random things securely than random events happen – but it needs to keep track of how much bits got taken out of the PRNG, so that it never becomes possible for an attacker to reconstruct prior state with a high probability of being right. Thus, it slows down your consumption of randomness when there’s not enough external randomness to modify the internal state.
  • Thus, 10⁶ calls to generate a seed in a short time sound like you’re doing something wrong; twice as much if these are used to feed a Mersenne twister, an algorithm that is overly complex and slow, but not cryptographically secure. You’re not using this much actual randomness, ever! Don’t reseed, continue to use your seeded PRNG, unless you need cryptographically safety that these seeds are independent.

And that’s exactly the thing: if you’re in a situation where you need to generate 10⁶ independent cryptographically secure keys in less than a few seconds, you’re a bit special. Are you working for someone who does CDNs, where a single operating system would serve millions of new TLS connections per second? If not, reduce your usage of random_device to what it’s actually useful for.

If you want to understand more about the way true randomness ends up in your program, I recommend reading this answer. In short, if you’re actually in need of more random bytes per second than the default random_device offers, try constructing it with "/dev/urandom" as a ctor parameter. It’s still going to be secure, for any assumable definition of what that means in the context in which you’re asking this (which means I assume you’re not writing a cryptographic library for extremely high throughput of key generation).

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading