File Coverage

blib/lib/Data/Entropy/Source.pm
Criterion Covered Total %
statement 114 122 93.4
branch 49 62 79.0
condition 7 9 77.7
subroutine 13 13 100.0
pod 5 5 100.0
total 188 211 89.1


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Data::Entropy::Source - encapsulated source of entropy
4              
5             =head1 SYNOPSIS
6              
7             use Data::Entropy::Source;
8              
9             $source = Data::Entropy::Source->new($handle, "sysread");
10              
11             $c = $source->get_octet;
12             $str = $source->get_bits(17);
13             $i = $source->get_int(12345);
14             $i = $source->get_int(Math::BigInt->new("1000000000000"));
15             $j = $source->get_prob(1, 2);
16              
17             =head1 DESCRIPTION
18              
19             An object of this class encapsulates a source of entropy
20             (randomness). Methods allow entropy to be dispensed in any
21             quantity required, even fractional bits. An entropy source object
22             should not normally be used directly. Rather, it should be used to
23             support higher-level entropy-consuming algorithms, such as those in
24             L.
25              
26             This type of object is constructed as a layer over a raw entropy source
27             which does not supply methods to extract arbitrary amounts of entropy.
28             The raw entropy source is expected to dispense only entire octets at
29             a time. The B devices on some versions of Unix constitute
30             such a source, for example. The raw entropy source is accessed
31             via the C interface. This interface may be supplied by
32             classes other than C itself, as is done for example by
33             C.
34              
35             If two entropy sources of this class are given exactly the same raw
36             entropy data, for example by reading from the same file, and exactly the
37             same sequence of C method calls is made to them, then they will
38             return exactly the same values from those calls. (Calls with numerical
39             arguments that have the same numerical value but are of different
40             types count as the same for this purpose.) This means that a run of an
41             entropy-using algorithm can be made completely deterministic if desired.
42              
43             =cut
44              
45             package Data::Entropy::Source;
46              
47 19     19   2389419 { use 5.006; }
  19         74  
48 19     19   125 use warnings;
  19         43  
  19         1118  
49 19     19   106 use strict;
  19         38  
  19         658  
50              
51 19     19   105 use Carp qw(croak);
  19         49  
  19         9970  
52              
53             our $VERSION = "0.008";
54              
55             =head1 CONSTRUCTOR
56              
57             =over
58              
59             =item Data::Entropy::Source->new(RAW_SOURCE, READ_STYLE)
60              
61             Constructs and returns an entropy source object based on the given raw
62             source. RAW_SOURCE must be an I/O handle referring to a source of entropy
63             that can be read one octet at a time. Specifically, it must support
64             either the C or C method described in L.
65             READ_STYLE must be a string, either "getc" or "sysread", indicating which
66             method should be used to read from the raw source. No methods other
67             than the one specified will ever be called on the raw source handle,
68             so a full implementation of C is not required.
69              
70             The C method should be used with B and its ilk,
71             because buffering would be very wasteful of entropy and might consequently
72             block other processes that require entropy. C should be preferred
73             when reading entropy from a regular file, and it is the more convenient
74             interface to implement when a non-I/O object is being used for the handle.
75              
76             =cut
77              
78             sub new {
79 20     20 1 3302354 my($class, $rawsrc, $readstyle) = @_;
80 20 50       103 croak "no raw entropy source given" unless defined $rawsrc;
81 20 50       165 croak "read style `$readstyle' not recognised"
82             unless $readstyle =~ /\A(?:getc|sysread)\z/;
83 20         271 return bless({
84             rawsrc => $rawsrc,
85             readstyle => $readstyle,
86             limit => 1,
87             num => 0,
88             }, $class);
89             }
90              
91             =back
92              
93             =head1 METHODS
94              
95             =over
96              
97             =item $source->get_octet
98              
99             Returns an octet of entropy, as a string of length one. This provides
100             direct access to the raw entropy source.
101              
102             =cut
103              
104             sub get_octet {
105 72860     72860 1 1944624 my($self) = @_;
106 72860 50       159034 if($self->{readstyle} eq "getc") {
    0          
107 72860         182155 my $errno = $!;
108 72860         120600 $! = 0;
109 72860         192907 my $octet = $self->{rawsrc}->getc;
110 72860 100       211368 unless(defined $octet) {
111 4         39 my $errmsg = $!;
112 4 50       19 unless($errmsg) {
113 0         0 $errmsg = "EOF";
114 0         0 $! = $errno;
115             }
116 4         991 croak "entropy source failed: $errmsg";
117             }
118 72856         135634 $! = $errno;
119 72856         227584 return $octet;
120             } elsif($self->{readstyle} eq "sysread") {
121 0         0 my $octet;
122 0         0 my $n = $self->{rawsrc}->sysread($octet, 1);
123 0 0       0 croak "entropy source failed: ".(defined($n) ? $! : "EOF")
    0          
124             unless $n;
125 0         0 return $octet;
126             }
127             }
128              
129             # ->_get_small_int may be used only with a native integer argument, up to 256.
130              
131             sub _get_small_int {
132 35004     35004   68669 my($self, $limit) = @_;
133 19     19   8298 use integer;
  19         256  
  19         187  
134 35004         61795 my $reqlimit = $limit << 15;
135 35004         49629 while(1) {
136 35004         88156 while($self->{limit} < $reqlimit) {
137 15389         41132 $self->{num} = ($self->{num} << 8) +
138             ord($self->get_octet);
139 15388         49160 $self->{limit} <<= 8;
140             }
141 35003         65883 my $rep = $self->{limit} / $limit;
142 35003         55458 my $uselimit = $rep * $limit;
143 35003 50       74322 if($self->{num} < $uselimit) {
144 35003         59879 my $num = $self->{num} / $rep;
145 35003         58732 $self->{num} %= $rep;
146 35003         55576 $self->{limit} = $rep;
147 35003         79391 return $num;
148             }
149 0         0 $self->{num} -= $uselimit;
150 0         0 $self->{limit} -= $uselimit;
151             }
152             }
153              
154             # ->_put_small_int is used to return the unused portion of some entropy that
155             # was extracted using ->_get_small_int.
156              
157             sub _put_small_int {
158 2945     2945   5858 my($self, $limit, $num) = @_;
159 2945         5108 $self->{limit} *= $limit;
160 2945         6169 $self->{num} = $self->{num} * $limit + $num;
161             }
162              
163             =item $source->get_bits(NBITS)
164              
165             Returns NBITS bits of entropy, as a string of octets. If NBITS is
166             not a multiple of eight then the last octet in the string has its most
167             significant bits set to zero.
168              
169             =cut
170              
171             sub get_bits {
172 222     222 1 127358 my($self, $nbits) = @_;
173 222         407 my $nbytes = $nbits >> 3;
174 222         359 $nbits &= 7;
175 222         407 my $str = "";
176 222         699 $str .= $self->get_octet while $nbytes--;
177 221 100       669 $str .= chr($self->_get_small_int(1 << $nbits)) if $nbits;
178 221         1212 return $str;
179             }
180              
181             =item $source->get_int(LIMIT)
182              
183             LIMIT must be a positive integer. Returns a uniformly-distributed
184             random number between zero inclusive and LIMIT exclusive. LIMIT may be
185             either a native integer, a C object, or an integer-valued
186             C object; the returned number is of the same type.
187              
188             This method dispenses a non-integer number of bits of entropy.
189             For example, if LIMIT is 10 then the result contains approximately 3.32
190             bits of entropy. The minimum non-zero amount of entropy that can be
191             obtained is 1 bit, with LIMIT = 2.
192              
193             =cut
194              
195             sub _break_int {
196 33017     33017   615536 my($num) = @_;
197 33017         53391 my $type = ref($num);
198 33017 100       84052 $num = $num->as_number if $type eq "Math::BigRat";
199 33017         233193 my @limbs;
200 33017         75145 while($num != 0) {
201 92468         2060625 my $l = $num & 255;
202 92468 100       1661153 $l = $l->numify if $type ne "";
203 92468         324169 push @limbs, $l;
204 92468         198873 $num >>= 8;
205             }
206 33017         763390 return \@limbs;
207             }
208              
209             sub get_int {
210 27317     27317 1 3075418 my($self, $limit) = @_;
211 27317         58696 my $type = ref($limit);
212 27317         63070 my $max = _break_int($limit - 1);
213 27317         51887 my $len = @$max;
214 27317         42657 my @num_limbs;
215 27317 100       63410 if($len) {
216 27121         40228 TRY_AGAIN:
217             my $i = $len;
218 27121         50438 my $ml = $max->[--$i];
219 27121         80735 my $nl = $self->_get_small_int($ml + 1);
220 27120         63796 @num_limbs = ($nl);
221 27120   100     111010 while($i && $nl == $ml) {
222 4230         8068 $ml = $max->[--$i];
223 4230         8864 $nl = $self->_get_small_int(256);
224 4230 100       11057 if($nl > $ml) {
225 65         274 $self->_put_small_int(255-$ml, $nl-$ml-1);
226 65         497 goto TRY_AGAIN;
227             }
228 4165         15052 push @num_limbs, $nl;
229             }
230 27055         78836 push @num_limbs, ord($self->get_octet) while $i--;
231             }
232 27315 100       70186 my $num = $type eq "" ? 0 : Math::BigInt->new(0);
233 27315         175747 for(my $i = $len; $i--; ) {
234 84581         1552412 my $l = $num_limbs[$len-1-$i];
235 84581 100       167694 $l = Math::BigInt->new($l) if $type ne "";
236 84581         512972 $num += $l << ($i << 3);
237             }
238 27315 100       453282 $num = Math::BigRat->new($num) if $type eq "Math::BigRat";
239 27315         266887 return $num;
240             }
241              
242             =item $source->get_prob(PROB0, PROB1)
243              
244             PROB0 and PROB1 must be non-negative integers, not both zero.
245             They may each be either a native integer, a C object,
246             or an integer-valued C objects; types may be mixed.
247             Returns either 0 or 1, with relative probabilities PROB0 and PROB1.
248             That is, the probability of returning 0 is PROB0/(PROB0+PROB1), and the
249             probability of returning 1 is PROB1/(PROB0+PROB1).
250              
251             This method dispenses a fraction of a bit of entropy. The maximum
252             amount of entropy that can be obtained is 1 bit, with PROB0 = PROB1.
253             The more different the probabilities are the less entropy is obtained.
254             For example, if PROB0 = 1 and PROB1 = 2 then the result contains
255             approximately 0.918 bits of entropy.
256              
257             =cut
258              
259             sub get_prob {
260 3034     3034 1 2014377 my($self, $prob0, $prob1) = @_;
261 3034 50 33     15774 croak "probabilities must be non-negative"
262             unless $prob0 >= 0 && $prob1 >= 0;
263 3034 100       16447 if($prob0 == 0) {
    100          
264 49 50       106 croak "can't have nothing possible" if $prob1 == 0;
265 49         233 return 1;
266             } elsif($prob1 == 0) {
267 135         366 return 0;
268             }
269 2850         7298 my $max0 = _break_int($prob0 - 1);
270 2850         8004 my $maxt = _break_int($prob0 + $prob1 - 1);
271 2850         5200 my $len = @$maxt;
272 2850 100       6909 push @$max0, (0) x ($len - @$max0) unless @$max0 == $len;
273 2880         4295 TRY_AGAIN:
274             my $maybe0 = 1;
275 2880         4223 my $maybebad = 1;
276 2880         4938 my($mtl, $m0l, $nl);
277 2880         5341 for(my $i = $len - 1; ; $i--) {
278 3460 100       12301 $nl = $self->_get_small_int(
279             $i == $len-1 ? $maxt->[-1] + 1 : 256);
280 3460 100       8310 $m0l = $maybe0 ? $max0->[$i] : -1;
281 3460 100       6689 $mtl = $maybebad ? $maxt->[$i] : 256;
282 3460 100       6257 my $lastlimb = $i ? 0 : 1;
283 3460 100 100     14925 if($nl < $m0l + $lastlimb) {
    100          
    100          
284 1628         4554 $self->_put_small_int($m0l + $lastlimb, $nl);
285 1628         7638 return 0;
286             } elsif($nl > $m0l && $nl < $mtl + $lastlimb) {
287 1222         3997 $self->_put_small_int($mtl + $lastlimb - $m0l - 1,
288             $nl - $m0l - 1);
289 1222         6081 return 1;
290             } elsif($nl > $mtl) {
291 30         122 $self->_put_small_int(255 - $mtl, $nl - $mtl - 1);
292 30         540 goto TRY_AGAIN;
293             }
294 580 100       1224 $maybe0 = 0 if $nl > $m0l;
295 580 100       1545 $maybebad = 0 if $nl < $mtl;
296             }
297             }
298              
299             =back
300              
301             =head1 SEE ALSO
302              
303             L,
304             L,
305             L,
306             L,
307             L,
308             L
309              
310             =head1 AUTHOR
311              
312             Andrew Main (Zefram)
313              
314             Maintained by Robert Rothenberg
315              
316             =head1 COPYRIGHT
317              
318             Copyright (C) 2006, 2007, 2009, 2011, 2025
319             Andrew Main (Zefram)
320              
321             =head1 LICENSE
322              
323             This module is free software; you can redistribute it and/or modify it
324             under the same terms as Perl itself.
325              
326             =cut
327              
328             1;