package Crypt::Random::TESHA2; use strict; use warnings; use Carp qw/croak confess carp/; use Time::HiRes qw/gettimeofday usleep/; use Digest::SHA qw/sha256 sha512/; use Crypt::Random::TESHA2::Config; BEGIN { $Crypt::Random::TESHA2::AUTHORITY = 'cpan:DANAJ'; $Crypt::Random::TESHA2::VERSION = '0.01'; } use base qw( Exporter ); our @EXPORT_OK = qw( random_bytes random_values irand rand is_strong ); our %EXPORT_TAGS = (all => [ @EXPORT_OK ]); our @EXPORT = qw( ); # nothing by default my $_opt_need_strong = 0; my $_opt_weak_ok = 0; my $_entropy_per_raw_byte = Crypt::Random::TESHA2::Config::entropy(); # Protect against possible abuse / misconfiguration $_entropy_per_raw_byte = 1 if $_entropy_per_raw_byte < 1; $_entropy_per_raw_byte = 7 if $_entropy_per_raw_byte > 7; my $_hashalg = \&sha512; my $_pool_size = 512; my $_nehi = 0; # A 64-bit counter my $_nelo = 0; my $_pool; my $_C; # bits of entropy that we think are in the pool # Make sure SHA512 is supported, back off to 256 if not. if (!defined sha512("test")) { $_pool_size = 256; $_hashalg = \&sha256; } sub import { my @options; @options = grep { $_ !~ /^[-:]?weak$/i } @_; $_opt_weak_ok = 1 if @options != @_; @_ = @options; @options = grep { $_ !~ /^[-:]?strong$/i } @_; $_opt_need_strong = 1 if @options != @_; @_ = @options; croak "TESHA2 is not a strong randomness source on this platform" if $_opt_need_strong && !is_strong(); goto &Exporter::import; } # Returns 1 if our installtion-time entropy measurements indicated we could # get enough entropy to make this method work on this platform. sub is_strong { return ($_entropy_per_raw_byte > 1.0) ? 1 : 0; } # Return $n random 32-bit values sub random_values { return map { unpack("L", random_bytes(4)) } 1 .. shift; } # Note, only 32 bits. # TODO: Figure out a portable non-64-bit way to make 52-bit doubles. sub rand { return ($_[0]||1) * (unpack("L", random_bytes(4))/4294967296.0); } sub irand { return unpack("L", random_bytes(4)); } # This uses an entropy pool (see Encyclopedia of Cryptography and Security, # volume 2, "Entropy Sources"). We use SHA-512 to handle a 512-bit pool. # One this this will do is ensure the input from any one byte is nicely # spread out among the 64 bytes of the pool. sub random_bytes { my $n = shift; return '' unless defined $n; if (!defined $_pool) { # Initialize pool with 64 bits of entropy. $_C = 64; # Get some extra bytes at the start. my $nbytes = 4 + int($_C/$_entropy_per_raw_byte) + 1; my $S = join("", map { _get_random_byte() } 1 .. $nbytes); $_pool = $_hashalg->($S); carp "TESHA2 is not a strong randomness source on this platform" if !$_opt_weak_ok && !is_strong(); } my $X = ''; while (length($X) < $n) { my $K = 8 * $n; $K = $_pool_size if $K > $_pool_size; # Add more entropy to pool if needed. while ($_C < $K) { my $nbytes = int( ($K - $_C) / $_entropy_per_raw_byte ) + 1; #warn "want $K bits, pool has $_C bits. Adding $nbytes bytes\n"; my $S = join("", map { _get_random_byte() } 1 .. $nbytes); $_pool = $_hashalg->($_pool . $S); $_C += $_entropy_per_raw_byte * $nbytes; $_C = $_pool_size if $_C > $_pool_size; } # Extract K bits from the pool. $_C -= $K; my $V = $_hashalg->( 'te' . pack("LL", $_nehi, $_nelo) . $_pool ); if ($_nelo < 4294967295) { $_nelo++; } else { $_nehi++; $_nelo = 0; } $X .= substr($V, 0, int($K/8)); } return $X; } # The heart of the system, where we gather entropy from timer jitter # (technically this is scheduler jitter). This is a similar idea to # timer_entropyd, TrueRand, or the old Math::TrulyRandom module. # # *) Cryptographically, there are numerous issues here. This is, at best, # one source to feed to a well designed entropy pool system. # # *) The output of this function passes ENT and TestU01 Rabbit on all systems # I've run it on. timer_entropyd does not, even when von Neumann debiased. # However, even a counter run through SHA256 will pass these tests, which # just indicates the stream data is uncorrelated. # # *) The entropy tests mentioned above are only one part. If a system # returned the same sequence every time, it may pass all the tests but # still be a horrible generator. That's especially not what one wants # from this module. # # *) I discovered Matt Blaze's truerand after I wrote this -- no ideas or # code were used. However, I got the idea from timer_entropyd, which # probably in turn got the idea from truerand. Version 2.1 (1996) of # Matt Blaze's truerand is _far_ more conservative than the old design # in Math::TrulyRandom. First he replaces the old-school byte mixing # with a SHA (very similar to my solution here). Next, he does another # mixing to generate the actual bytes, while the old code would use the # raw results. I use a different method above, but the end result is # somewhat similar -- we take these raw results and stir them further. # # *) My tests using the raw timer data are showing 1.5 - 4 bits per xor. # The truerand 2.1 documentation indicates 0.67 to 1.33 bits per call, # then conservatively halves the number. # # *) Expanding on the above, assume the worst and absolutely no entropy is # gathered. Then each byte will be a sha256 related to the current hires # time, where each byte is mixed in the pool. We get a fine PRNG, just # not seeded well (from a crypto point of view, this is awful). # # *) For each bit, the two microsecond values are xor'd and packed into # 32-bit words. Eight of these are concatenated and a SHA-256 hash is # performed. As long as the sum of entropy gathered from all eight xors # is at least 8, we're good. The sha256 takes care of shuffling bits so # there aren't biases. This generates a much better result than using # boolean differences like timer_entropyd (even if the differences are # sent through von Neumann debiasing). # sub _get_random_byte { my ($start, $t1, $t2) = gettimeofday(); # This basically gives us a counter, so every call is unique. We can # assume it adds no entropy. my $str = pack("LL", $start, $t1); # A hash we will use for a little bit of CPU processing inside the loop. my %dummy; foreach my $bit (1 .. 8) { # Sleep some period of time. Differ the times so we don't hit a # particular beat of the scheduler. usleep(2+3*$bit); (undef, $t2) = gettimeofday(); # xor the two. The entropy is in the variation between what we got # (t2 - t1) and what we expected (2+3*bit + c). $str .= pack("L", $t1 ^ $t2); # A little hash processing to further perturb the times. $dummy{$str . $_}++ for 1..8; $t1 = $t2; } # To truly return a byte: return substr( sha256($str), 0, 1 ); # Return the entire string -- let the pool figure it out. return sha256($str); } 1; __END__ # ABSTRACT: Random numbers using timer/schedule entropy =pod =head1 NAME Crypt::Random::TESHA2 - Random numbers using timer/schedule entropy =head1 VERSION Version 0.01 =head1 WARNING I =head1 SYNOPSIS # Nothing exported by default use Crypt::Random::TESHA2 qw(random_bytes random_values irand rand); # Get 64 random bytes my $seed_string = random_bytes(64); # Get 16 random 32-bit values my @seeds = random_values(16); # Get a 32-bit random integer (value between 0 and 4294967295 inclusive) my $i = irand(); # rand, like system rand, with 32 bits of randomness. my $r1 = rand(); # floating point in range [0,1). my $r2 = rand(1000); # floating point in range [0,1000). # croak if installation determined we couldn't generate enough entropy use Crypt::Random::TESHA2 ':strong'; # No warnings even if we are a weak source use Crypt::Random::TESHA2 ':weak'; # Ask for yourself die "No key for you!" unless Crypt::Random::TESHA2::is_strong(); =head1 DESCRIPTION Generate random numbers using entropy gathered from timer / scheduler jitter. This can be used to generate non-pseudorandom data to seed a PRNG (e.g. C/C, L, etc.) or CSPRNG (e.g. AES-CTR or L). You may use it directly or as part of a random source module that first checks for O/S randomness sources. Only Perl CORE modules are used, making this very portable. However, systems must have a high resolution timer and support C from L. At installation time, measurements are taken of the estimated entropy gathered by the timer differences. If the results indicated we could not get good results, then the module will consider itself "weak". On the first use of any of the functions that return randomness (e.g. random_bytes), the module will carp about not being a strong randomness source. However, two special options, ":strong" and ":weak" may be given to the importer to change this behavior. If ":strong" is used, then the module will croak. If ":weak" is used, then no carp will be generated. The function C can be used at any time for finer control. Note that this should be an unusual case, and neither flag has any effect if the module considers itself strong. =head1 FUNCTIONS =head2 random_bytes($n) Takes an integer and returns a string of that size filled with random data. =head2 random_values($n) Takes an integer and returns an array containing that many random 32-bit integers. The values will be in the range [0,4294967295] (all 32-bit values are possible). =head2 irand Returns a single random 32-bit integer in the range [0,4294967295]. =head2 rand Returns a random float greater than or equal to 0 and less than the value of the argument. If no argument is given or the argument is zero, 1 is used. This has an identical API as system rand, though of course there is no associated srand function. The result has 32 bits of randomness. =head2 is_strong Returns 0 if the installation procedure determined that not enough entropy could be gathered on this system. Returns 1 if it was able. If 0 is returned, then the bytes returned may be no better than a CSPRNG using a convoluted time-based reseed every bit. =head1 METHOD The underlying entropy gathering is done using timing differences between usleep calls. We wrap usleep calls of varying intervals along with some Perl hash processing inside microsecond timer calls. The two values are xored. This is the raw entropy source. Eight of these, along with the current time, are fed to a SHA-256 which can be added to an entropy pool. Measurements of the raw timer entropy (just the timing differences -- no hashes, time, counters, xors, or entropy pool) on systems I have available indicate 1.5 to 4 bits of entropy per usleep. The installation procedure does a measurement of the 0-order entropy gathered from the raw timing process, halves it, limits to the range 1/8 - 7/8, and uses that as the estimated entropy gathered. The actual output random bytes are generated by an entropy pool that uses SHA-512 or SHA-256. This adds data as needed from the above method, then extracts bits as needed to make the output bytes (again using a cryptographic hash and a counter, which means the entropy pool is not exposed). The result will easily pass most stream randomness tests (e.g. FIPS-140, ENT, TestU01 Rabbit), but that is a given based on the last entropy pool stage, so this just shows we provide decorrelated output, not that we make a good seed. =head1 LIMITATIONS Note that pretty much every limitation of this module will apply to L, which many non-cryptographers still think is cryptographically secure (it's recommended in both the perl core documentation as well as L). If you think that module is great for your application, then you should be happy with this one. Probably happier since this is more portable, doesn't hang infinitely, runs much faster, and generates better output on most systems. As mentioned in the L section, this generates userspace entropy -- what most people used until the waning years of the 20th century. We should not have to do this on modern systems that have well designed APIs to get randomness from multiple entropy pools, all managed by production code. In other words, C. Performance is slow (about 10,000 times slower than L), making this something best to be used to seed a PRNG or CSPRNG, rather than using directly. On newer Linux systems and Win32 it runs about 10,000 bits per second. Cygwin runs about 1000 bits per second. Older systems will run slower of course, such as an old HPPA system I use that runs at 40 bits/s. Much of the time is spent sleeping. Gathering entropy with this method depends on high resolution timers. If the timers have low resolution, especially if we had a system with very fast yield turnaround, then we would gather very little entropy. One of the tests tries to determine this, but it isn't perfect. As with all such userspace systems, you should check that it works before using it for anything critical. L section 3.4 discusses a few of the pitfalls of using portable clock-based software, and section 3.6 discusses the desire for multiple entropy sources. Because of the use of SHA-2 hashes along with an entropy pool using a counter, the output stream will pass randomness tests (e.g. FIPS-140, ENT, TestU01 Rabbit) even if there is I underlying entropy. The installation measurements should indicate whether this is happening, but it doesn't measure everything. =head1 AUTHORS Dana Jacobsen Edana@acm.orgE =head1 SEE ALSO =over 4 =item Encyclopedia of Cryptography and Security, volume 2, "Entropy Sources". The entropy pool implemented in this module follows this design. =item HAVEGE (L) Uses multiple methods to gather entropy and feed it to the O/S, which can measure it and add it to a pool. Highly recommended for embedded or network devices that don't have good external interactions, or when running programs that use a lot of entropy (e.g. anything that uses L). =item timer_entropyd (L) Uses a related method (jitter in timing data between usleeps) as this module, but inefficient and only suitable for bulk feeding of an entropy pool. Even after von Neumann debiasing, the output has distinct patterns and at most 0.5 bits of entropy per output bit. HAVEGE is a superior overall solution. However, note a number of other links at the site for other sources as well as links to hardware RNGs. =item L An old module that uses an obsolete version of Matt Blaze's TrueRand. TrueRand version 2.1 fixes a number of issues with the output quality and specifically recommends against using the old method. In addition, the Perl module will not properly run on most current platforms. A pure Perl version is included in the examples directory of this module, but it is still TrueRand version 1 and, like the old module, will not run on Win32. =item L A simple module that gets a good source of O/S non-blocking randomness. =item L A complicated module that has multiple plugins for randomness sources. =back =head1 COPYRIGHT Copyright 2012-2013 by Dana Jacobsen Edana@acm.orgE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The software is provided "AS IS", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software. =cut