1use ExtUtils::MakeMaker;
2
3# Big thanks to David Oswald for configuration ideas.
4test_and_config();
5
6WriteMakefile1(
7    NAME         => 'Crypt::Random::TESHA2',
8    ABSTRACT     => "Random numbers using timer/schedule entropy, aka userspace voodoo entropy",
9    VERSION_FROM => 'lib/Crypt/Random/TESHA2.pm', # finds $VERSION
10    LICENSE      => 'perl',
11    AUTHOR       => 'Dana A Jacobsen <dana@acm.org>',
12
13    BUILD_REQUIRES=>{
14                      'Test::More'       => '0.45',
15                    },
16    PREREQ_PM     => {
17                       'Exporter'        => '5.562',
18                       'Digest::SHA'     => '5.22',
19                       'Time::HiRes'     => '1.9711',
20                       'base'            => 0,
21                       'Carp'            => 0,
22                     },
23
24    META_MERGE    => {
25                      resources  => {
26                       homepage   => 'https://github.com/danaj/Crypt-Random-TESHA2',
27                       repository => 'https://github.com/danaj/Crypt-Random-TESHA2',
28                       },
29                     },
30    MIN_PERL_VERSION => 5.006002,
31);
32
33sub WriteMakefile1 {   # Cribbed from eumm-upgrade by Alexandr Ciornii
34  my %params = @_;
35  my $eumm_version = $ExtUtils::MakeMaker::VERSION;
36  $eumm_version = eval $eumm_version;
37
38  if ($params{BUILD_REQUIRES} and $eumm_version < 6.5503) {
39      #EUMM 6.5502 has problems with BUILD_REQUIRES
40      $params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{BUILD_REQUIRES}} };
41      delete $params{BUILD_REQUIRES};
42  }
43  delete $params{CONFIGURE_REQUIRES} if $eumm_version < 6.52;
44  delete $params{MIN_PERL_VERSION} if $eumm_version < 6.48;
45  delete $params{META_MERGE} if $eumm_version < 6.46;
46  delete $params{META_ADD} if $eumm_version < 6.46;
47  delete $params{LICENSE} if $eumm_version < 6.31;
48  delete $params{AUTHOR} if $] < 5.005;
49  delete $params{ABSTRACT_FROM} if $] < 5.005;
50  delete $params{BINARY_LOCATION} if $] < 5.005;
51
52  WriteMakefile(%params);
53}
54
55sub test_and_config {
56  local $| = 1;
57  print "\n";
58  print "Config: starting initial configuration.\n";
59  # Test that we can create enough entropy.
60  eval {
61    require Time::HiRes;  Time::HiRes->import(qw/gettimeofday usleep/);
62    require Digest::SHA;  Digest::SHA->import(qw/sha256/);
63    1;
64  } or do {
65    print "Config: failed to load prerequisites.\n";
66    return;
67  };
68  print "Config: gathering.";
69
70  my @vars;
71  foreach my $byte (1..200) {
72    print "." unless $byte % 4;
73    my ($start, $t1, $t2) = gettimeofday();
74    my $str = pack("LL", $start, $t1);
75    my %dummy;
76    foreach my $bit (1 .. 8) {
77      usleep(2+3*$bit);
78      (undef, $t2) = gettimeofday();
79      # Note this has nothing to do with the start time or the hash.
80      my $diff = $t2 >= $t1 ? $t2-$t1 : $t2-$t1+1000000;
81      push @vars, $diff - (2+3*$bit);
82      $str .= pack("L", $t1 ^ $t2);
83      $dummy{$str . $_}++ for 1..8;
84      $t1 = $t2;
85    }
86  }
87  print "done\n";
88  my $H = calc_entropy(@vars);
89  my $Hstr = sprintf("%.2f", $H);
90  my $Hstr8 = sprintf("%.2f", 8 * $H);
91  print "Config: Raw usleep 0-order entropy: $Hstr ($Hstr8 bits per byte).\n";
92  # Be conservative and assume half what we measured.
93  my $Hbyte = (8 * $H) / 2;
94  # Also assume no more than 7 from a single round.
95  $Hbyte = 7.0 if $Hbyte > 7.0;
96  # Round up to two digits.
97  $Hbyte = sprintf("%.02f", $Hbyte + 0.005);
98  if ($Hbyte < 1.0) {
99    print "Config:\n";
100    print "Config:  ONLY $Hbyte BITS PER OUTPUT BYTE!\n";
101    print "Config:\n";
102    print "Config: You should use another source.\n";
103    print "Config: Configuring for weak mode.\n";
104    print "\n";
105    $Hbyte = "1.00";
106  }
107  print "Config: choosing $Hbyte bits per output byte\n";
108  print "Config: writing configuration.\n";
109
110  my $config_path = 'lib/Crypt/Random/TESHA2/Config.pm';
111  # One possibility is to use the DATA fh, allowing us to just append.
112  # Instead, use DAOSWALD's method.  It's more work here, but less there.
113  my $fh;
114  open($fh, "<", $config_path) or
115    do { print "Config: Can't open $config_path!\n$!"; return; };
116  my @lines = <$fh>;
117  close $fh;
118  for (@lines) {
119    s/^(my \$_entropy_per_byte =).*/$1 $Hbyte;/;
120  }
121  open($fh, ">", $config_path) or
122    do { print "Config: Can't open $config_path!\n$!"; return; };
123  print $fh @lines or
124    do { print "Config: Can't write to $config_path!\n$!"; return; };
125  close($fh) or
126    do { print "Config: Can't close $config_path!\n$!"; return; };
127  open(my $fh, ">>", $config_path) or
128    do { print "Config: Can't open $config_path!\n$!"; return; };
129
130  print "Config: configuration complete.\n";
131  print "\n";
132}
133
134# calculate the 0-order entropy of an array.  Returns bits per input.
135sub calc_entropy {
136  my @vals = @_;
137  my $total = scalar @vals;
138  # Compute simple entropy H
139  my %freq;
140  $freq{$_}++ for @vals;
141  my $H = 0;
142  foreach my $f (values %freq) {
143    my $p = $f / $total;
144    $H += $p * log($p);
145  }
146  $H = -$H / log(2);
147  return $H;
148}
149