Cryptographically secure randomness in Perl

By Abhijit Menon-Sen <ams@toroid.org>

2011-02-05

I can usually sidestep the need for cryptographically secure randomness, but I decided to investigate the available options to generate CSRF protection tokens.

On Linux, /dev/random is a good, cryptographically secure random number generator, but the entropy available without special hardware support is severely limited. Reads from /dev/random block for however long it takes (even minutes) to gather enough entropy to satisfy the request. Entropy is in high demand throughout the system, so this is not a resource that can be indiscriminately drawn upon.

One standard solution to this problem is to use the output of a randomly keyed block cipher running in counter (CTR) mode. This provides only as much entropy as the random key, but if /dev/random is used to generate the key, the result is suitable for many purposes. Andrew Main's Data::Entropy module implements this strategy in Perl.

Setting up Data::Entropy is a bit complicated. One must create a RawSource::Local to read from /dev/random, wrap that in a Source, read a 256-bit key from it and use that to set up Crypt::Rijndael, feed that to a RawSource::CryptCounter, and wrap the result in another Source, set it as the default entropy_source using with_entropy_source, and finally use D::E::Algorithms to fetch random numbers in whatever format is desired.

use Crypt::Rijndael;
use Data::Entropy qw(with_entropy_source entropy_source);
use Data::Entropy::Algorithms qw(rand_bits rand_int ...);
use Data::Entropy::RawSource::CryptCounter;
use Data::Entropy::RawSource::Local;
use Data::Entropy::Source;

my $really_secure = 1;
with_entropy_source(
    Data::Entropy::Source->new(
        Data::Entropy::RawSource::Local->new("/dev/urandom"),
        "sysread"
    ), sub {
        $main::prng = Data::Entropy::Source->new(
            Data::Entropy::RawSource::CryptCounter->new(
                Crypt::Rijndael->new(entropy_source->get_bits(256))
            ), "sysread"
        );
    }
);

with_entropy_source $main::prng, sub {
    $bits = rand_bits(256);
    ...
};

Given that the above code is something of a best practice, Andrew plans to make the module easier to use in this configuration in future.