The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Bytes::Random::Secure - Perl extension to generate cryptographically-secure random bytes.

SYNOPSIS

    use Bytes::Random::Secure qw(
        random_bytes random_bytes_base64 random_bytes_hex
    );

    my $bytes = random_bytes(32); # A string of 32 random bytes.
    
    my $bytes_as_base64 = random_bytes_base64(57);

    my $bytes_as_hex = random_bytes_hex(8);

    my $bytes_as_quoted_printable = random_bytes_qp(100);

    my $bytes = random_bytes_from( 'abcde', 10 );

DESCRIPTION

Bytes::Random::Secure provides four functions that can be used anytime you need a string (or MIME Base64 representation, or hex-digits representation, or Quoted Printable representation) of a specific number of random bytes.

This module can be a drop-in replacement for Bytes::Random, with the primary enhancement of using a much higher quality random number generator to create the random data. The random number generator comes from Math::Random::ISAAC, and is suitable for cryptographic purposes. Actually, the harder problem to solve is how to seed the generator. This module uses Crypt::Random::Source to generate the initial seeds for Math::Random::ISAAC. On Windows platforms Crypt::Random::Source needs Crypt::Random::Source::Strong::Win32 to obtain high quality seeds.

In addition to providing random_bytes(), this module also provides three functions not found in Bytes::Random: random_bytes_base64(), random_bytes_hex, and random_bytes_qp.

RATIONALE

There are many uses for cryptographic quality randomness. This module aims to provide a generalized tool that can fit into many applications. You're free to come up with your own use-cases, but there are several obvious ones:

  • Generating per-account random salt to be hashed along with passphrases (and stored alongside them) to prevent rainbow table attacks.

  • Generating a secret that can be hashed along with a cookie's session content to prevent cookie forgeries.

  • Generating raw cryptographic-quality pseudo-random data sets for testing or sampling.

Why this module? This module uses several high quality CPAN tools to first generate strong random seeds, and then to instantiate a high quality random number factory based on the strong seed. The code in this module really just glues together the building blocks. However, it's taken a good deal of research to come up with what I feel is a strong tool-chain that isn't going to fall back to a weaker state on some systems. Hopefully others can benefit from this work.

EXPORTS

By default random_bytes is the only function exported. Optionally random_bytes_base64, random_bytes_hex, and random_bytes_qp may be exported.

FUNCTIONS

random_bytes

    my $random_bytes = random_bytes( 512 );
    

Returns a string containing as many random bytes as requested. Obviously the string isn't useful for display, as it can contain any byte value from 0 through 255.

random_bytes_base64

    my $random_bytes_b64           = random_bytes_base64( $num_bytes );
    my $random_bytes_b64_formatted = random_bytes_base64( $num_bytes, $eol );

Returns a MIME Base64 encoding of a string of $number_of_bytes random bytes. Note, it should be obvious, but is worth mentioning that a base64 encoding of base256 data requires more digits to represent the bytes requested. The actual number of digits required, including padding is 4(n/3). Furthermore, the Base64 standard is to add padding to the end of any string for which length % 57 is a non-zero value.

If an $eol is specified, the character(s) specified will be used as line delimiters after every 76th character. The default is qq{\n}. If you wish to eliminate line-break insertions, specify an empty string: q{}.

random_bytes_hex

    my $random_bytes_as_hex = random_bytes_hex( $num_bytes );

Returns a string of hex digits representing the string of $number_of_bytes random bytes.

Again, it should be obvious, but is worth mentioning that a hex (base16) representation of base256 data requires two digits for every byte requested. So length( random_bytes_hex( 16 ) ) will return 32, as it takes 32 hex digits to represent 16 bytes. Simple stuff, but better to mention it now than forget and set a database field that's too narrow.

random_bytes_qp

    my $random_bytes_qp           = random_bytes_qp( $num_bytes );
    my $random_bytes_qp_formatted = random_bytes_qp( $num_bytes, $eol );

Produces a string of $num_bytes random bytes, using MIME Quoted Printable encoding (as produced by MIME::QuotedPrint's encode_qp function. The default configuration uses \n as a line break after every 76 characters, and the "binmode" setting is used to guarantee a lossless round trip. If no line break is wanted, pass an empty string as $eol.

random_string_from

    my $random_bytes = random_string_from( $bag, $length );
    my $random_bytes = random_string_from( 'abc', 50 );

$bag is a string of digits from which random_string_from may choose in building a random string. We call it a 'bag', because it's permissible to have repeated digits in the bag (if not, we could call it a set). Repeated digits get more weight. For example, random_string_from( 'aab', 1 ) would have a 66.67% chance of returning an 'a', and a 33.33% chance of returning a 'b'.

Also, there's nothing preventing the same digit from being chosen more than once. That is, we're not drawing and discarding cards. Each digit in the return value string has an equal chance of landing on any digit in the 'bag'.

Return value is a string of size $length, of characters chosen at random from the 'bag' string.

It is perfectly legal to pass a Unicode string as the "bag", and in that case, the yield will include Unicode characters selected from those passed in via the bag string.

This function is useful for random string generation such as temporary random passwords.

CONFIGURATION

Bytes::Random::Secure's interface keeps it simple. There is generally nothing to configure. This is by design, as it eliminates much of the potential for diminishing the quality of the random byte stream by through misconfiguration. Finding a reliable seed source is the hardest component. If you would prefer to supply your own, skip this module and go directly to Math::Random::ISAAC (or get in touch with me and we can discuss whether your method might be a better choice for this module too). ;)

Crypt::Random::Source provides our strong seed. For better or worse, this module uses Any::Moose, which will default to the lighter-weight Mouse if it is available. If Mouse is not available, but Moose is, Moose will be used. This is a significantly heavier dependency. Unless you are using Moose in your application already, it's probably better to allow Mouse to be used instead. It is my recommendation that if you don't have Mouse installed, you install it right now before you use this module to keep the bloat to a minimum.

If you really have the need to feel useful, you may also install Math::Random::ISAAC::XS. Bytes::Random::Secure's random number generator uses Math::Random::ISAAC. That module implements the ISAAC algorithm in pure Perl. However, if you install Math::Random::ISAAC::XS, you get the same algorithm implemented in C/XS, which will provide better performance. If you need to produce your random bytes more quickly, simply installing Math::Random::ISAAC::XS will result in it automatically being used, and a pretty good performance improvement will coincide.

Win32 Special Dependency

In Win32 environments, Crypt::Random::Source uses a different technique to generate high quality randomness. In a Windows environment, this module has the additional requirement of needing Crypt::Random::Source::Strong::Win32. Unfortunately, the current version of that module has a broken test, and in some cases may fail its test suite. It may be necessary to force the installation of Crypt::Random::Source::Strong::Win32 before Bytes::Random::Secure can be installed.

CAVEATS

It's easy to generate weak pseudo-random bytes. It's also easy to think you're generating strong pseudo-random bytes when really you're not. And it's hard to test for pseudo-random cryptographic acceptable quality.

It's also hard to generate strong (ie, secure) random bytes in a way that works across a wide variety of platforms. A primary goal for this module is to provide cryptographically secure pseudo-random bytes. A secondary goal is to provide a simple user experience (thus reducing the propensity for getting it wrong). A terciary goal (and one that will never be permitted to compromise the primary goal) is to minimize the dependencies required to achieve the primary and secondary goals.

To re-iterate: We want secure random bytes, we want ease of use, and if we can get both while minimizing the dependencies, that would be nice, but is not a requirement.

This module steals some code from Math::Random::Secure. That module is an excellent resource, but implements a broader range of functionality than is needed here. So we just borrowed some code from it, and some of its dependencies.

The primary source of random data in this module comes from the excellent Math::Random::ISAAC. Unfortunately, to be useful and secure, even Math::Random::ISAAC needs a cryptographically sound seed, which we derive from Crypt::Random::Source. Neither of those modules are light on dependencies. The situation becomes even more difficult in a Win32 environment, where Crypt::Random::Source needs the Crypt::Random::Source::Strong::Win32 plug-in, which is even heavier in external dependencies.

The result is that the cost of getting cryptographically strong random bytes on most platforms is a heavy dependency chain, and the cost of getting them in a windows platform is about twice as heavy of a dependency chain as on most other platforms. If you're a Win32 user, and you cannot justify the dependency chain, look elsewhere (and let me know what you find!). On the other hand, if you're looking for a secure random bytes solution that "just works" portably (and are willing to live with the fact that the dependencies are heavier for Windows users), you've come to the right place.

Patches that improve the Win32 situation without compromising the module's primary and secondary goals, and without growing the dependencies for *nix users are certainly welcome.

All users can minimize the number of modules loaded upon startup by making sure that Mouse is available on their system so that Any::Moose can choose that lighter-weight alternative to Moose. Of course if your application already uses Moose, this becomes a non-issue.

A note regarding modulo bias: Care is taken such that there is no modulo bias in the randomness returned either by random_bytes and its siblings, nor by random_string_from. As a matter if fact, this is exactly why the random_string_from function is useful. However, the algorithm to eliminate modulo bias can impact the performance of the random_string_from function. Any time the length of the bag string is significantly less than the nearest greater or equal factor of 2**32, performance suffers. Unfortunately there is no known algorithm that improves upon this situation. Fortunately, for sanely sized strings, it's a minor issue.

AUTHOR

David Oswald <davido [at] cpan (dot) org>

BUGS

Please report any bugs or feature requests to bug-bytes-random-secure at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Bytes-Random-Secure. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Bytes::Random::Secure

You can also look for information at:

ACKNOWLEDGEMENTS

Mojolicious for providing the motivation from its "App secret". Bytes::Random for providing a starting-point for this module. Math::Random::Secure for providing an excellent random number tool.

LICENSE AND COPYRIGHT

Copyright 2012 David Oswald.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.