Bytes-Random-Secure-Tiny

 view release on metacpan or  search on metacpan

t/27-fips140-1.t  view on Meta::CPAN

## no critic (RCS,VERSION,encapsulation,Module)

# Assure that the crypto-quality of the RNG meets FIPS PUB 140-1:
# Security Requirements for Cryptographic Modules.

# See: http://csrc.nist.gov/publications/fips/fips1401.htm

#     "Statistical random number generator tests. Cryptographic modules
#      that implement a random or pseudorandom number generator shall
#      incorporate the capability to perform statistical tests for
#      randomness. For Levels 1 and 2, the tests are not required. For
#      Level 3, the tests shall be callable upon demand. For level 4,
#      the tests shall be performed at power-up and shall also be
#      callable upon demand. The tests specified below are recommended.
#      However, alternative tests which provide equivalent or superior
#      randomness checking may be substituted."

# These tests were adapted from the algorithms described in the FIPS-140-1
# document (link provided above).  Dana Jacobsen performed the Perl adaptation
# for use with Crypt::Random::TESHA2.  Bytes::Random::Secure incorporates the
# tests without change, except for the randomness source.

# As this set of tests is run at install time, and thereafter upon demand, this
# should comply with the FIPS 140-1 Level 3 specification, with the exception
# that upon failure there will be a test-suite failure, rather than the module
# entering an "error state" as described in the FIPS 140-1 document.
# Test failure at install time would typically force the CPAN installers to
# abort installation, which is the "Perlish" solution.

use 5.006000;

use strict;
use warnings;
use Test::More;
use Bytes::Random::Secure::Tiny qw();
use Time::HiRes qw/gettimeofday/;

$Math::Random::ISAAC::Embedded::EMBEDDED_CSPRNG = 1;
$ENV{'BRST_DEBUG'} = 1;

my $random = Bytes::Random::Secure::Tiny->new(bits=>128);

plan tests => 2 + 2 + 2 + 24 + 2;

my @rbytes;
push @rbytes, $random->bytes(1) for 1..2500;

# FIPS-140 test
{
  is( scalar @rbytes, 2500, "2500 bytes were collected." );
  my $str = join("", map { unpack("B8", $_) } @rbytes);
  is( length($str), 20000, "binary string is length 20000" );

  # Monobit
  my $nzeros = $str =~ tr/0//;
  my $nones = $str =~ tr/1//;
  cmp_ok($nones, '>',  9654, "Monobit: Number of ones is > 9654");
  cmp_ok($nones, '<', 10346, "Monobit: Number of ones is < 10346");

  # Long Run
  ok($str !~ /0{34}/, "Longrun: No string of 34+ zeros");
  ok($str !~ /1{34}/, "Longrun: No string of 34+ ones");

  # Runs
  my @l0;
  my @l1;
  $l0[$_] = 0 for 1 .. 34;
  $l1[$_] = 0 for 1 .. 34;
  {
    my $s = $str;
    while (length($s) > 0) {
      if ($s =~ s/^(0+)//) { $l0[length($1)]++; }
      if ($s =~ s/^(1+)//) { $l1[length($1)]++; }
    }
  }
  # Fold all runs of >= 6 into 6.
  $l0[6] += $l0[$_] for 7 .. 34;
  $l1[6] += $l1[$_] for 7 .. 34;
  # Test thresholds
  cmp_ok($l0[1], '>=', 2267, "Runs: zero length 1 ($l0[1]) >= 2267");
  cmp_ok($l1[1], '>=', 2267, "Runs:  one length 1 ($l1[1]) >= 2267");
  cmp_ok($l0[1], '<=', 2733, "Runs: zero length 1 ($l0[1]) <= 2733");
  cmp_ok($l1[1], '<=', 2733, "Runs:  one length 1 ($l1[1]) <= 2733");
  cmp_ok($l0[2], '>=', 1079, "Runs: zero length 2 ($l0[2]) >= 1079");
  cmp_ok($l1[2], '>=', 1079, "Runs:  one length 2 ($l1[2]) >= 1079");
  cmp_ok($l0[2], '<=', 1421, "Runs: zero length 2 ($l0[2]) <= 1421");
  cmp_ok($l1[2], '<=', 1421, "Runs:  one length 2 ($l1[2]) <= 1421");
  cmp_ok($l0[3], '>=',  502, "Runs: zero length 3 ($l0[3]) >=  502");
  cmp_ok($l1[3], '>=',  502, "Runs:  one length 3 ($l1[3]) >=  502");
  cmp_ok($l0[3], '<=',  748, "Runs: zero length 3 ($l0[3]) <=  748");
  cmp_ok($l1[3], '<=',  748, "Runs:  one length 3 ($l1[3]) <=  748");
  cmp_ok($l0[4], '>=',  223, "Runs: zero length 4 ($l0[4]) >=  223");
  cmp_ok($l1[4], '>=',  223, "Runs:  one length 4 ($l1[4]) >=  223");
  cmp_ok($l0[4], '<=',  402, "Runs: zero length 4 ($l0[4]) <=  402");
  cmp_ok($l1[4], '<=',  402, "Runs:  one length 4 ($l1[4]) <=  402");
  cmp_ok($l0[5], '>=',   90, "Runs: zero length 5 ($l0[5]) >=   90");
  cmp_ok($l1[5], '>=',   90, "Runs:  one length 5 ($l1[5]) >=   90");
  cmp_ok($l0[5], '<=',  223, "Runs: zero length 5 ($l0[5]) <=  223");



( run in 0.510 second using v1.01-cache-2.11-cpan-5511b514fd6 )