summaryrefslogtreecommitdiff
path: root/lib/Crypt/PRNG.pm
blob: 735d9e1bac65e234d49f21b51b8343ed3500482f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
package Crypt::PRNG;

use strict;
use warnings;
our $VERSION = '0.079_007';

require Exporter; our @ISA = qw(Exporter); ### use Exporter 5.57 'import';
our %EXPORT_TAGS = ( all => [qw(random_bytes random_bytes_hex random_bytes_b64 random_bytes_b64u random_string random_string_from rand irand)] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw();

use Carp;
$Carp::Internal{(__PACKAGE__)}++;
use CryptX;

sub string {
  my ($self, $len) = @_;
  return $self->string_from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", $len);
}

sub string_from {
  my ($self, $chars, $len) = @_;

  $len = 20 unless defined $len;
  return unless $len > 0;
  return unless length($chars) > 0;

  my @ch = split(//, $chars);
  my $max_index = $#ch;
  return if $max_index > 65535;

  my $mask;
  for my $n (1..31) {
    $mask = (1<<$n) - 1;
    last if $mask >= $max_index;
  }

  my $upck = ($max_index > 255) ? "n*" : "C*";
  my $l = $len * 2;

  my $rv = '';
  my @r;
  while (length $rv < $len) {
    @r = unpack($upck, $self->bytes($l)) if scalar @r == 0;
    my $i = (shift @r) & $mask;
    next if $i > $max_index;
    $rv .= $ch[$i];
  }
  return $rv;
}

sub CLONE_SKIP { 1 } # prevent cloning

### FUNCTIONS

{
  ### stolen from Bytes::Random::Secure
  #
  # Instantiate our random number generator(s) inside of a lexical closure,
  # limiting the scope of the RNG object so it can't be tampered with.
  my $RNG_object = undef;
  my $fetch_RNG = sub { # Lazily, instantiate the RNG object, but only once.
    $RNG_object = Crypt::PRNG->new unless defined $RNG_object && ref($RNG_object) ne 'SCALAR';
    return $RNG_object;
  };
  sub rand(;$)                { return $fetch_RNG->()->double(@_) }
  sub irand()                 { return $fetch_RNG->()->int32() }
  sub random_bytes($)         { return $fetch_RNG->()->bytes(@_) }
  sub random_bytes_hex($)     { return $fetch_RNG->()->bytes_hex(@_) }
  sub random_bytes_b64($)     { return $fetch_RNG->()->bytes_b64(@_) }
  sub random_bytes_b64u($)    { return $fetch_RNG->()->bytes_b64u(@_) }
  sub random_string_from($;$) { return $fetch_RNG->()->string_from(@_) }
  sub random_string(;$)       { return $fetch_RNG->()->string(@_) }
}

1;

=pod

=head1 NAME

Crypt::PRNG - Cryptographically secure random number generator

=head1 SYNOPSIS

   ### Functional interface:
   use Crypt::PRNG qw(random_bytes random_bytes_hex random_bytes_b64 random_bytes_b64u
                      random_string random_string_from rand irand);

   $octets = random_bytes(45);
   $hex_string = random_bytes_hex(45);
   $base64_string = random_bytes_b64(45);
   $base64url_string = random_bytes_b64u(45);
   $alphanumeric_string = random_string(30);
   $string = random_string_from('ACGT', 64);
   $floating_point_number_0_to_1 = rand;
   $floating_point_number_0_to_88 = rand(88);
   $unsigned_32bit_int = irand;

   ### OO interface:
   use Crypt::PRNG;

   $prng = Crypt::PRNG->new;
   #or
   $prng = Crypt::PRNG->new("RC4");
   #or
   $prng = Crypt::PRNG->new("RC4", "some data used for seeding PRNG");

   $octets = $prng->bytes(45);
   $hex_string = $prng->bytes_hex(45);
   $base64_string = $prng->bytes_b64(45);
   $base64url_string = $prng->bytes_b64u(45);
   $alphanumeric_string = $prng->string(30);
   $string = $prng->string_from('ACGT', 64);
   $floating_point_number_0_to_1 = $prng->double;
   $floating_point_number_0_to_88 = $prng->double(88);
   $unsigned_32bit_int = $prng->int32;

=head1 DESCRIPTION

Provides an interface to the ChaCha20 based pseudo random number generator (thread-safe and fork-safe).

=head1 FUNCTIONS

=head2 random_bytes

   $octets = random_bytes($length);

Returns C<$length> random octects.

=head2 random_bytes_hex

   $hex_string = random_bytes_hex($length);

Returns C<$length> random octects encoded as hexadecimal string.

=head2 random_bytes_b64

   $base64_string = random_bytes_b64($length);

Returns C<$length> random octects Base64 encoded.

=head2 random_bytes_b64u

   $base64url_string = random_bytes_b64u($length);

Returns C<$length> random octects Base64 URL Safe (RFC 4648 section 5) encoded.

=head2 random_string_from

   $string = random_string_from($range, $length);
   #e.g.
   $string = random_string_from("ABCD", 10);

Returns a random string made of C<$length> chars randomly chosen from C<$range> string.

=head2 random_string

   $alphanumeric_string = random_string($length);
   #or
   $alphanumeric_string = random_string;  # default length = 20

Similar to random_string_from, only C<$range> is fixed to C<'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'>.

=head2 rand

   $n = rand;
   #or
   $n = rand($limit);

Returns a random floating point number from range C<[0,1)> (if called without parameter) or C<[0,$limit)>.

=head2 irand

   $i = irand;

Returns a random unsigned 32bit integer - range C<0 .. 0xFFFFFFFF>.

=head1 METHODS

=head2 new

   $prng = Crypt::PRNG->new;
   #or
   $prng = Crypt::PRNG->new($alg);
   #or
   $prng = Crypt::PRNG->new($alg, $seed);

   # $alg  ... algorithm name 'Frotuna' (DEFAULT), 'RC4', 'Sober128' or 'Yarrow'
   # $seed ... will be used as an initial entropy for seeding PRNG

If C<$seed> is not specified the PRNG is automatically seeded with 32bytes random data taken from C</dev/random> (UNIX) or C<CryptGenRandom> (Win32)

=head2 add_entropy

  $prng->add_entropy($random_data);
  #or
  $prng->add_entropy();

If called without parameter it uses 32bytes random data taken from C</dev/random> (UNIX) or C<CryptGenRandom> (Win32).

B<BEWARE:> you probably do not need this function at all as the module does automatic seeding on initialization as well as reseeding after fork and thread creation.

=head2 bytes

   $octets = $prng->bytes($length);

See L<random_bytes|/random_bytes>

=head2 bytes_hex

   $hex_string = $prng->bytes_hex($length);

See L<random_bytes_hex|/random_bytes_hex>

=head2 bytes_b64

   $base64_string = $prng->bytes_b64($length);

See L<random_bytes_b64|/random_bytes_b64>

=head2 bytes_b64u

   $base64url_string = $prng->bytes_b64u($length);

See L<random_bytes_b64u|/random_bytes_b64u>

=head2 string

   $alphanumeric_string = $prng->string($length);
   #or
   $alphanumeric_string = $prng->string;

See L<random_string|/random_string>

=head2 string_from

   $string = $prng->string_from($range, $length);

See L<random_string_from|/random_string_from>

=head2 double

   $n = $prng->double;
   #or
   $n = $prng->double($limit);

See L<rand|/rand>

=head2 int32

   $i = $prng->int32;

See L<irand|/irand>

=head1 SEE ALSO

L<Crypt::PRNG::Fortuna>, L<Crypt::PRNG::RC4>, L<Crypt::PRNG::Sober128>, L<Crypt::PRNG::Yarrow>

=cut