File Coverage

blib/lib/Data/Digest.pm
Criterion Covered Total %
statement 58 63 92.0
branch 17 28 60.7
condition 7 15 46.6
subroutine 17 17 100.0
pod 5 5 100.0
total 104 128 81.2


line stmt bran cond sub pod time code
1             package Data::Digest;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Data::Digest - Objects that represent a digest values
8              
9             =head1 SYNOPSIS
10              
11             $digest = Data::Digest->new(
12             'MD5.d41d8cd98f00b204e9800998ecf8427e'
13             );
14            
15             $digest = Data::Digest->new(
16             'SHA-256' => '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU',
17             );
18            
19             $digest->matches( \$data );
20             $digest->matches( $filename );
21              
22             =head1 DESCRIPTION
23              
24             The C class provides utility objects that represents
25             a digest value. It is used primarily as a convenience and to simplify code
26             when dealing with situations where you are provided with a digest, and need
27             to check it against some data.
28              
29             It initially supports 4 different digest types, (MD5, SHA-1,
30             SHA-256 and SHA-512) to provide varying strengths of checking.
31             The default, which is intended for speed and basic non-cryptographic
32             file integrity checking, is MD5.
33              
34             Users hand-crafting guest specifications may want to use a stronger
35             digest.
36              
37             =head1 METHODS
38              
39             =cut
40              
41 2     2   44430 use 5.005;
  2         8  
  2         72  
42 2     2   11 use strict;
  2         4  
  2         56  
43 2     2   19 use Carp ();
  2         3  
  2         29  
44 2     2   1695 use Digest ();
  2         1246  
  2         43  
45 2     2   2447 use IO::File ();
  2         51981  
  2         72  
46 2     2   2322 use Params::Util qw{_STRING _SCALAR0 _INSTANCE};
  2         10738  
  2         200  
47              
48 2     2   19 use vars qw{$VERSION};
  2         3  
  2         96  
49             BEGIN {
50 2     2   1825 $VERSION = '1.04';
51             }
52              
53             # For all supported digest types, provide the expected lengths of the digest
54             # in each format in bits.
55             # These will be used to help generate the regular expressions used to
56             # validate the input strings.
57             my %DIGEST = (
58             'MD5' => {
59             bits => 128,
60             digest => 16,
61             hexdigest => 32,
62             b64digest => 22,
63             },
64             'SHA-1' => {
65             bits => 160,
66             digest => 20,
67             hexdigest => 40,
68             b64digest => 27,
69             },
70             'SHA-256' => {
71             bits => 256,
72             digest => 32,
73             hexdigest => 64,
74             b64digest => 43,
75             },
76             'SHA-512' => {
77             bits => 512,
78             digest => 64,
79             hexdigest => 128,
80             b64digest => 86,
81             },
82             );
83              
84              
85              
86              
87              
88             #####################################################################
89             # Constructor and Accessors
90              
91             =pod
92              
93             =head2 new
94              
95             # Two-argument digest constructor
96             $digest = Data::Digest->new(
97             'SHA-256' => '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU',
98             );
99            
100             # One-argument digest constructor
101             $digest = Data::Digest->new(
102             'MD5.d41d8cd98f00b204e9800998ecf8427e'
103             );
104              
105             The C constructor takes one or two strings parameters, and creates
106             a new digest object, that can be stored or used to compared the digest
107             value to existing data, or a file.
108              
109             The basic two-argument form takes the name of a supported digest driver,
110             and the digest value.
111              
112             The digest driver is case sensitive and should be one of C<'MD5'>,
113             C<'SHA-1'>, C<'SHA-256'> or C<'SHA-512'>. (case sensitive)
114              
115             The second param should be a string containing the value of the digest
116             in either binary, hexidecimal or base 64 format.
117              
118             The constructor will auto-detect the encoding type.
119              
120             For example, for a 128-bit MD5 digest, the constructor will allow a
121             16-character binary string, a 32-character hexedecimal string, or
122             a 22-character base 64 string.
123              
124             Returns a C object, or throws an exception on error.
125              
126             =cut
127              
128             sub new {
129 5     5 1 3655 my $class = shift;
130 5         9 my ($driver, $digest, $method) = ();
131 5 100 66     61 if ( @_ == 1 and _STRING($_[0]) ) {
    100 66        
      66        
132 2 100       12 if ( $_[0] =~ /^(\w+(?:-\d+)?)\.(\S+)$/ ) {
133 1         3 $driver = $1;
134 1         2 $digest = $2;
135             } else {
136 1         118 Carp::croak("Unrecognised or unsupported Data::Digest string");
137             }
138             } elsif ( @_ == 2 and _STRING($_[0]) and _STRING($_[1]) ) {
139 2         4 $driver = $_[0];
140 2         4 $digest = $_[1];
141             } else {
142 1         255 Carp::croak("Missing or invalid params provided to Data::Digest constructor");
143             }
144              
145             # Check the digest values
146 3 100       134 my $len = $DIGEST{$driver}
147             or Carp::croak("Invalid or unsupported digest type '$driver'.");
148              
149             # Check the digest content to find the method
150 2 50 33     21 if ( length $digest == $len->{digest} ) {
    50 0        
    0          
151 0         0 $method = 'digest';
152             } elsif ( length $digest == $len->{hexdigest} and $digest !~ /[^0-9a-f]/ ) {
153 2         6 $method = 'hexdigest';
154             } elsif ( length $digest == $len->{b64digest} and $digest !~ /[^\w\+\/]/ ) {
155 0         0 $method = 'b64digest';
156             } else {
157 0         0 Carp::croak("Digest string is not a recognised $driver");
158             }
159              
160             # Create the object
161 2         14 my $self = bless {
162             driver => $driver,
163             digest => $digest,
164             method => $method,
165             }, $class;
166              
167 2         6 return $self;
168             }
169              
170             =pod
171              
172             =head2 driver
173              
174             The C accessor returns the digest driver name, which be one of
175             either C<'MD5'>, C<'SHA-1'>, C<'SHA-256'> or C<'SHA-512'>.
176              
177             =cut
178              
179             sub driver {
180 8     8 1 48 $_[0]->{driver};
181             }
182              
183             =pod
184              
185             =head2 digest
186              
187             The C accessor returns the digest value, in the original format.
188              
189             This could be either binary, hexidecimal or base 64 and without knowing
190             what was originally entered you may not necesarily know which it will be.
191              
192             =cut
193              
194             sub digest {
195 8     8 1 95 $_[0]->{digest};
196             }
197              
198              
199              
200              
201              
202             #####################################################################
203             # Main Methods
204              
205             =pod
206              
207             =head2 as_string
208              
209             The C method returns the stringified form of the digest,
210             which will be equivalent to and suitable for use as the value passed
211             to the single-parameter form of the constructor.
212              
213             print $digest->as_string . "\n";
214             > MD5.d41d8cd98f00b204e9800998ecf8427e
215              
216             Returns a string between around 15 and 90 characters, depending on the
217             type and encoding of the digest value.
218              
219             =cut
220              
221             sub as_string {
222 2     2 1 3558 $_[0]->driver . '.' . $_[0]->digest;
223             }
224              
225             =pod
226              
227             =head2 matches
228              
229             # Check the digest against something
230             $digest->matches( $filename );
231             $digest->matches( $io_handle );
232             $digest->matches( \$string );
233              
234             The C methods checks the digest object against various forms of
235             arbitrary data to determine if they match the digest.
236              
237             It takes a single parameter, consisting of either the name of a file,
238             an L object, or the reference to a C string.
239              
240             Returns true if the digest matches the data, false if not, or throws
241             an exception on error.
242              
243             =cut
244              
245             sub matches {
246 6     6 1 11 my $self = shift;
247 6 100       34 return $self->_matches_file(shift) if _STRING($_[0]);
248 3 50       18 return $self->_matches_scalar(shift) if _SCALAR0($_[0]);
249 0 0       0 return $self->_matches_handle(shift) if _INSTANCE($_[0], 'IO::Handle');
250 0         0 Carp::croak("Did not provide a valid data value to check digest against");
251             }
252              
253             sub _matches_scalar {
254 3     3   5 my ($self, $scalar_ref) = @_;
255              
256             # Generate the digest for the string
257 3         37 my $method = $self->{method};
258 3         10 my $digest = $self->_digest->add($$scalar_ref)->$method();
259              
260 3         119 return ($self->digest eq $digest);
261             }
262              
263             sub _matches_file {
264 3     3   6 my ($self, $file) = @_;
265              
266             # Check the filename
267 3 50       69 -f $file or Carp::croak("File '$file' does not exist");
268 3 50       37 -r $file or Carp::croak("No permissions to read '$file'");
269              
270             # Load and generate the digest for the file
271 3 50       24 my $handle = IO::File->new($file)
272             or Carp::croak("Failed to load '$file': $!");
273 3         260 return $self->_matches_handle($handle);
274             }
275              
276             sub _matches_handle {
277 3     3   5 my ($self, $handle) = @_;
278              
279             # Generate the digest for the handle
280 3         7 my $method = $self->{method};
281 3         8 my $digest = $self->_digest->addfile($handle)->$method();
282              
283 3         205 return ($self->digest eq $digest);
284             }
285              
286             sub _digest {
287 6     6   9 my $self = shift;
288 6 50       16 Digest->new($self->driver)
289             or die("Failed to create Digest object");
290             }
291              
292             1;
293              
294             =pod
295              
296             =head1 SUPPORT
297              
298             Bugs should be reported via the CPAN bug tracker at
299              
300             L
301              
302             For other issues, contact the author.
303              
304             =head1 AUTHOR
305              
306             Adam Kennedy Eadamk@cpan.orgE
307              
308             =head1 SEE ALSO
309              
310             L, L, L
311              
312             =head1 COPYRIGHT
313              
314             Copyright 2006 - 2008 Adam Kennedy.
315              
316             This program is free software; you can redistribute
317             it and/or modify it under the same terms as Perl itself.
318              
319             The full text of the license can be found in the
320             LICENSE file included with this module.
321              
322             =cut