File Coverage

blib/lib/Crypt/DRBG.pm
Criterion Covered Total %
statement 131 132 99.2
branch 52 62 83.8
condition 17 24 70.8
subroutine 16 16 100.0
pod 5 5 100.0
total 221 239 92.4


line stmt bran cond sub pod time code
1             package Crypt::DRBG;
2             $Crypt::DRBG::VERSION = '0.000001';
3 13     13   29495 use 5.006;
  13         43  
4 13     13   64 use strict;
  13         17  
  13         255  
5 13     13   63 use warnings;
  13         29  
  13         285  
6              
7 13     13   10107 use IO::File ();
  13         132380  
  13         20383  
8              
9             =head1 NAME
10              
11             Crypt::DRBG - Base class for fast, cryptographically-secure PRNGs
12              
13             =head1 SYNOPSIS
14              
15             use Crypt::DRBG::HMAC;
16              
17             my $drbg = Crypt::DRBG::HMAC->new(auto => 1);
18             my $data = $drbg->generate(42);
19             ... # do something with your 42 bytes here
20              
21             my $drbg2 = Crypt::DRBG::HMAC->new(seed => "my very secret seed");
22             my @randdigits = $drbg->randitems(20, [0..9]);
23             ... # do something with your 20 random digits here
24              
25             =head1 SUBROUTINES/METHODS
26              
27             =head2 initialize(%params)
28              
29             %params can contain the following:
30              
31             =over 4
32              
33             =item auto
34              
35             If true, use a safe, cryptographically-secure set of defaults.
36             Equivalent to specifying autoseed, autononce, autopersonalize, and
37             fork_safe.
38              
39             =item autoseed
40              
41             If true, derive a seed from /dev/urandom, /dev/arandom, or /dev/random, in that
42             order. Windows support is lacking, but may be added in the future; however,
43             this should function on Cygwin.
44              
45             =item seed
46              
47             If a string, use this value as the seed. If a coderef, call this coderef with a
48             single argument (the number of bytes) to obtain an entropy input. Note that if
49             a string is used, an exception will be thrown if a reseed is required.
50              
51             =item autononce
52              
53             If true, derive a nonce automatically.
54              
55             =item nonce
56              
57             If a string, use this value as the nonce. If a coderef, call this coderef with
58             a single argument (the number of bytes) to obtain a nonce.
59              
60             =item autopersonalize
61              
62             If true, derive a personalization string automatically.
63              
64             =item personalize
65              
66             If a string, use this value as the personalization string. If a coderef, call
67             this coderef to obtain a personalization string.
68              
69             =item fork_safe
70              
71             If true, reseed on fork. If false, the parent and child processes will produce
72             the same sequence of bytes (not recommended).
73              
74             =item cache
75              
76             If enabled, keep a cache of this many bytes and use it to satisfy requests
77             before generating more.
78              
79             =back
80              
81             =cut
82              
83             # Not a method call.
84             sub _rand_bytes {
85 16     16   33 my ($len) = @_;
86              
87 16         54 my $data = '';
88 16         128 my @sources = qw{/dev/urandom /dev/arandom /dev/random};
89 16         81 foreach my $source (@sources) {
90 16 50       201 my $fh = IO::File->new($source, 'r') or next;
91 16         2866 while ($fh->read(my $buf, $len - length($data))) {
92 16         20341 $data .= $buf;
93             }
94 16 50       166 die "Insufficient random data" if length($data) != $len;
95 16         295 return $data;
96             }
97 0         0 die "No random source for autoseed";
98             }
99              
100             sub _get_seed {
101 5468     5468   10810 my ($self, $name, $len, $params, $optional) = @_;
102 5468         9183 my $autoname = "auto$name";
103              
104 5468         6906 my $seed;
105 5468 100 100     27125 if (defined $params->{$name} && !ref $params->{$name}) {
106 5400         9663 $seed = $params->{$name};
107             }
108             else {
109 68         84 my $seedfunc;
110 68 100       211 $seedfunc = $params->{$name} if ref $params->{$name} eq 'CODE';
111 68 100 33     337 $seedfunc = \&_rand_bytes if $params->{$autoname} || $params->{auto};
112 68 100       154 unless ($seedfunc) {
113 34 100       115 die "No seed source" unless $optional;
114 32         103 return '';
115             }
116 34         119 $self->{"${name}func"} = $seedfunc;
117 34         95 $seed = $seedfunc->($len);
118             }
119              
120 5434         11661 return $seed;
121             }
122              
123             sub _get_personalization {
124 2733     2733   4210 my ($self, $params) = @_;
125 2733         4340 my $name = 'personalize';
126 2733         4440 my $autoname = "auto$name";
127              
128 2733         3449 my $seed;
129 2733 100 100     13683 if (defined $params->{$name} && !ref $params->{$name}) {
130 2688         4599 $seed = $params->{$name};
131             }
132             else {
133 45         67 my $seedfunc;
134 45 100       127 $seedfunc = $params->{$name} if ref $params->{$name} eq 'CODE';
135 45 100 66     211 if ($params->{$autoname} || $params->{auto}) {
136             die "Invalid version"
137 9 0 33     44 if defined $params->{version} && $params->{version};
138 9         21 my $version = 0;
139             $seedfunc = sub {
140 9     9   82 my @nums = ($$, $<, $>, time);
141 9         106 my @strings = ($0, $(, $));
142              
143 9         216 return join('',
144             "$version\0",
145             pack("N" x @nums, @nums),
146             pack("Z" x @strings, @strings),
147             );
148 9         88 };
149             }
150             # Personalization strings are recommended, but optional.
151 45 100       195 return '' unless $seedfunc;
152 11         40 $seed = $seedfunc->();
153             }
154              
155 2699         6025 return $seed;
156             }
157              
158             sub _check_reseed {
159 5437     5437   8816 my ($self) = @_;
160              
161 5437         7837 my $reseed = 0;
162 5437         8880 my $pid = $self->{pid};
163 5437 100 100     14864 $reseed = 1 if defined $pid && $pid != $$;
164 5437 50       14511 $reseed = 1 if $self->{reseed_counter} >= $self->{reseed_interval};
165              
166 5437 100       11499 if ($reseed) {
167 6 50       239 die "No seed source" if !$self->{seedfunc};
168 6         337 $self->_reseed($self->{seedfunc}->($self->{seedlen}));
169 6 50       120 $self->{pid} = $$ if $self->{fork_safe};
170             }
171              
172 5437         12207 return 1;
173             }
174              
175             sub initialize {
176 2735     2735 1 9313 my ($self, %params) = @_;
177              
178 2735         8531 my $seed = $self->_get_seed('seed', $self->{seedlen}, \%params);
179 2733         11627 my $nonce = $self->_get_seed('nonce', int(($self->{seedlen} / 2) + 1),
180             \%params, 1);
181 2733         7830 my $personal = $self->_get_personalization(\%params);
182              
183 2733         12570 $self->_seed("$seed$nonce$personal");
184              
185 2733 100       7583 if ($params{cache}) {
186 2         4 $self->{cache} = '';
187 2         5 $self->{cache_size} = $params{cache};
188             }
189              
190 2733         5025 $self->{fork_safe} = $params{fork_safe};
191 2733 100 66     7145 $self->{fork_safe} = 1 if $params{auto} && !defined $params{fork_safe};
192 2733 100       6360 $self->{pid} = $$ if $self->{fork_safe};
193              
194 2733         9050 return 1;
195             }
196              
197             =head2 $drbg->generate($bytes, $additional_data)
198              
199             Generate and return $bytes bytes. There is a limit per algorithm on the number of bytes that can be requested at once, which is at least 2^10.
200              
201             If $additional_data is specified, add this additional data to the DRBG.
202              
203             If the cache flag was specified on instantiation, bytes will be satisfied from
204             the cache first, unless $additional_data was specified.
205              
206             =cut
207              
208             sub generate {
209 5504     5504 1 22068389 my ($self, $len, $seed) = @_;
210              
211             return $self->_generate($len, $seed)
212 5504 100 66     32775 if !defined $self->{cache} || defined $seed;
213              
214 73         102 my $data = '';
215 73         87 my $left = $len;
216 73         132 my $cache = \$self->{cache};
217 73 100       168 $$cache = $self->_generate($self->{cache_size}) if !length($$cache);
218 73         162 while ($left > 0) {
219 77 100       165 my $chunk_size = $left > length($$cache) ? length($$cache) : $left;
220 77         230 $data .= substr($$cache, 0, $chunk_size, '');
221 77         100 $left = $len - length($data);
222 77 100       287 $$cache = $self->_generate($self->{cache_size}) if !length($$cache);
223             }
224              
225 73         212 return $data;
226             }
227              
228             =head2 $drbg->rand([$n], [$num])
229              
230             Like Perl's rand, but cryptographically secure. Uses 32-bit values.
231              
232             Accepts an additional argument, $num, which is the number of values to return.
233             Defaults to 1 (obviously).
234              
235             Note that just as with Perl's rand, there may be a slight bias with this
236             function. Use randitems if that matters to you.
237              
238             Returns an array if $num is specified and a single item if it is not.
239              
240             =cut
241              
242             sub rand {
243 3     3 1 516 my ($self, $n, $num) = @_;
244              
245 3         6 my $single = !defined $num;
246              
247 3 100       12 $n = 1 unless defined $n;
248 3 100       8 $num = 1 unless defined $num;
249              
250 3         8 my $bytes = $self->generate($num * 4);
251 3         17 my @data = map { $_ / 2.0 / (2 ** 31) * $n } unpack("N[$num]", $bytes);
  102         169  
252 3 100       32 return $single ? $data[0] : @data;
253             }
254              
255             =head2 $drbg->randitems($n, $items)
256              
257             Select randomly and uniformly from the arrayref $items $n times.
258              
259             =cut
260              
261             sub randitems {
262 6     6 1 39 my ($self, $n, $items) = @_;
263              
264 6         13 my $len = scalar @$items;
265 6         8 my @results;
266 6         49 my $values = [
267             {bytes => 1, pack => 'C', max => 256},
268             {bytes => 2, pack => 'n', max => 65536},
269             {bytes => 4, pack => 'N', max => 2 ** 31},
270             ];
271 6 0       21 my $params = $values->[$len <= 256 ? 0 : $len <= 65536 ? 1 : 2];
    50          
272              
273             # Getting this computation right is important so as not to bias the
274             # data. $len & $len - 1 is true iff $len is not a power of two.
275 6         11 my $max = $params->{max};
276 6         12 my $mask = $max - 1;
277 6 100       13 if ($len & ($len - 1)) {
278 4         9 $max = $max - ($max % $len);
279             }
280             else {
281 2         4 $mask = $len - 1;
282             }
283              
284 6         19 my $pack = "$params->{pack}\[$n\]";
285 6         19 while (@results < $n) {
286 8         32 my $bytes = $self->generate($params->{bytes} * $n);
287              
288 8         91 my @data = map { $_ & $mask } grep { $_ < $max } unpack($pack, $bytes);
  1136         1836  
  1240         2336  
289 8         82 push @results, map { $items->[$_ % $len] } @data;
  1136         2177  
290             }
291              
292 6         161 return splice(@results, 0, $n);
293             }
294              
295             =head2 $drbg->randbytes($n, $items)
296              
297             Select randomly and uniformly from the characters in arrayref $items $n times.
298             Returns a byte string.
299              
300             This function works just like randitems, but is more efficient if generating a
301             sequence of bytes as a string instead of an array.
302              
303             =cut
304              
305             sub randbytes {
306 3     3 1 28 my ($self, $n, $items) = @_;
307              
308 3         5 my $len = scalar @$items;
309 3         6 my $results = '';
310              
311             # Getting this computation right is important so as not to bias the
312             # data. $len & $len - 1 is true iff $len is not a power of two.
313 3         4 my $max = 256;
314 3     1   11 my $filter = sub { return $_[0]; };
  1         2  
315 3 100       10 if ($len & ($len - 1)) {
316 2         3 $max = $max - ($max % $len);
317 2         9 my $esc = sprintf '\x%02x', $max + 1;
318             $filter = sub {
319 3     3   4 my $s = shift;
320 3         184 eval "\$s =~ tr/$esc-\\xff//d"; ## no critic(ProhibitStringyEval)
321 3         10 return $s;
322 2         7 };
323             }
324              
325 3         13 while (length $results < $n) {
326 4         11 my $bytes = $filter->($self->generate($n));
327 4         14 $results .= join '', map { $items->[$_ % $len] } unpack('C*', $bytes);
  37         81  
328             }
329              
330 3         15 return substr($results, 0, $n);
331             }
332              
333             =head1 AUTHOR
334              
335             brian m. carlson, C<< >>
336              
337             =head1 BUGS
338              
339             Please report any bugs or feature requests to C, or through
340             the web interface at L. I will be notified, and then you'll
341             automatically be notified of progress on your bug as I make changes.
342              
343              
344              
345              
346             =head1 SUPPORT
347              
348             You can find documentation for this module with the perldoc command.
349              
350             perldoc Crypt::DRBG
351              
352              
353             You can also look for information at:
354              
355             =over 4
356              
357             =item * RT: CPAN's request tracker (report bugs here)
358              
359             L
360              
361             =item * AnnoCPAN: Annotated CPAN documentation
362              
363             L
364              
365             =item * CPAN Ratings
366              
367             L
368              
369             =item * Search CPAN
370              
371             L
372              
373             =back
374              
375              
376             =head1 ACKNOWLEDGEMENTS
377              
378              
379             =head1 LICENSE AND COPYRIGHT
380              
381             Copyright 2015 brian m. carlson.
382              
383             This program is distributed under the MIT (X11) License:
384             L
385              
386             Permission is hereby granted, free of charge, to any person
387             obtaining a copy of this software and associated documentation
388             files (the "Software"), to deal in the Software without
389             restriction, including without limitation the rights to use,
390             copy, modify, merge, publish, distribute, sublicense, and/or sell
391             copies of the Software, and to permit persons to whom the
392             Software is furnished to do so, subject to the following
393             conditions:
394              
395             The above copyright notice and this permission notice shall be
396             included in all copies or substantial portions of the Software.
397              
398             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
399             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
400             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
401             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
402             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
403             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
404             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
405             OTHER DEALINGS IN THE SOFTWARE.
406              
407              
408             =cut
409              
410             1; # End of Crypt::DRBG