File Coverage

blib/lib/gmd5.pm
Criterion Covered Total %
statement 192 192 100.0
branch 16 20 80.0
condition n/a
subroutine 38 38 100.0
pod 7 7 100.0
total 253 257 98.4


line stmt bran cond sub pod time code
1             package gmd5;
2              
3 1     1   125489 use strict;
  1         2  
  1         45  
4 1     1   6 use warnings;
  1         2  
  1         75  
5 1     1   7 use feature 'state';
  1         2  
  1         181  
6 1     1   8 use Exporter qw(import);
  1         3  
  1         3873  
7              
8             our $VERSION = '2.4.1'; # Incremented to reflect fixes
9              
10             our @EXPORT_OK = qw(md5 md5_hex);
11              
12             sub new {
13 10     10 1 26137 my ($class) = @_;
14 10         44 state @h_init = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
15 10         97 my $self = bless {
16             h => [@h_init],
17             buffer => '',
18             length => 0,
19             pos => 0,
20             w => [(0) x 16],
21             final_digest => undef,
22             }, $class;
23 10         28 return $self;
24             }
25              
26             sub reset {
27 12     12 1 1769 my ($self) = @_;
28 12         18 state @h_init = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
29 12         49 $self->{h} = [@h_init];
30 12         37 $self->{buffer} = '';
31 12         17 $self->{length} = 0;
32 12         14 $self->{pos} = 0;
33 12         18 $self->{final_digest} = undef;
34 12         20 return $self;
35             }
36              
37             sub add {
38 24     24 1 69 my ($self, $data) = @_;
39 24         56 _validate_input($data);
40 23 100       44 return $self unless length($data);
41 21         46 $self->{final_digest} = undef;
42 21         57 _append_data($self, $data);
43 21         45 _process_buffer($self) while _has_enough_data($self);
44 21         45 return $self;
45             }
46              
47             sub digest {
48 23     23 1 38 my ($self) = @_;
49 23 100       73 if (defined $self->{final_digest}) {
50 1         22 return $self->{final_digest};
51             }
52 22         50 my $digest = _compute_digest($self);
53 22         48 $self->{final_digest} = $digest;
54 22         36 state @h_init = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
55 22         52 $self->{h} = [@h_init];
56 22         68 return $digest;
57             }
58              
59             sub hexdigest {
60 22     22 1 679 my ($self) = @_;
61 22         44 my $digest = $self->digest;
62 22         47 return _format_hex($digest);
63             }
64              
65             sub md5 {
66 2     2 1 6629 my ($data) = @_;
67 2         8 _validate_input($data);
68 1         4 my $md5 = gmd5->new;
69 1         6 $md5->add($data);
70 1         3 return $md5->digest;
71             }
72              
73             sub md5_hex {
74 6     6 1 275807 my ($data) = @_;
75 6         23 _validate_input($data);
76 5         53 my $md5 = gmd5->new;
77 5         23 $md5->add($data);
78 5         16 return $md5->hexdigest;
79             }
80              
81             sub _validate_input {
82 32     32   56 my ($data) = @_;
83 32 100       118 die "Input must be defined" unless defined $data;
84             }
85              
86             sub _append_data {
87 21     21   35 my ($self, $data) = @_;
88 21         48 $self->{buffer} .= $data;
89 21         37 $self->{length} += length($data);
90             }
91              
92             sub _has_enough_data {
93 30     30   58 my ($self) = @_;
94 30         47 my $buffer_length = length($self->{buffer});
95 30         43 my $position = $self->{pos};
96 30         88 return $buffer_length - $position >= 64;
97             }
98              
99             sub _process_buffer {
100 31     31   44 my ($self) = @_;
101 31         48 my $buf_ref = \$self->{buffer};
102 31         47 my $pos = $self->{pos};
103 31         42 my $len = length($$buf_ref);
104 31         42 my $block_num = 0;
105              
106 31         54 while (_can_process_block($pos, $len)) {
107 37         98 my $w = [(0) x 16];
108 37         91 my $block = _extract_block($buf_ref, $pos);
109 37         91 _unpack_words($w, $block, $block_num);
110 37         97 _process_block($self, $self->{h}, $w);
111 37         100 $pos = _increment_position($pos);
112 37         107 $block_num++;
113             }
114              
115 31         70 _update_buffer($self, $buf_ref, $pos);
116             }
117              
118             sub _can_process_block {
119 68     68   97 my ($pos, $len) = @_;
120 68         180 return $pos + 64 <= $len;
121             }
122              
123             sub _extract_block {
124 37     37   53 my ($buf_ref, $pos) = @_;
125 37         94 return substr($$buf_ref, $pos, 64);
126             }
127              
128             sub _unpack_words {
129 37     37   59 my ($w, $block, $block_num) = @_;
130 37         176 @$w = unpack("V16", $block);
131             }
132              
133             sub _increment_position {
134 37     37   60 my ($pos) = @_;
135 37         58 return $pos + 64;
136             }
137              
138             sub _update_buffer {
139 31     31   56 my ($self, $buf_ref, $pos) = @_;
140 31 50       89 $$buf_ref = $pos ? substr($$buf_ref, $pos) : '';
141 31         82 $self->{pos} = 0;
142             }
143              
144             sub _compute_digest {
145 22     22   34 my ($self) = @_;
146 22         44 my $len = _compute_bit_length($self);
147 22         63 my $original_buffer = $self->{buffer};
148 22         57 _append_padding($self);
149 22         39 my $pad_len = length($self->{buffer}) - length($original_buffer) - 1;
150 22         54 _append_length($self, $len);
151 22         45 _validate_buffer_length($self);
152 22         48 _process_buffer($self);
153 22         41 my $digest = _construct_digest($self);
154 22         68 $self->{final_digest} = $digest;
155 22         61 $self->{buffer} = $original_buffer;
156 22         51 return $digest;
157             }
158              
159             sub _compute_bit_length {
160 22     22   36 my ($self) = @_;
161 22         40 my $len = $self->{length} * 8;
162 22 50       50 die "Invalid length" if $len < 0;
163 22         35 return $len;
164             }
165              
166             sub _append_padding {
167 22     22   38 my ($self) = @_;
168 22         45 _append_one_bit($self);
169 22         47 _append_zero_padding($self);
170             }
171              
172             sub _append_one_bit {
173 22     22   40 my ($self) = @_;
174 22         45 $self->{buffer} .= "\x80";
175             }
176              
177             sub _append_zero_padding {
178 22     22   32 my ($self) = @_;
179 22         75 my $current_len = length($self->{buffer});
180 22         46 my $pad_len = (56 - $current_len % 64) % 64;
181 22         69 $self->{buffer} .= "\x00" x $pad_len;
182             }
183              
184             sub _append_length {
185 22     22   38 my ($self, $len) = @_;
186 22         77 my $length_bytes = _pack_length($len);
187 22         55 $self->{buffer} .= $length_bytes;
188             }
189              
190             sub _pack_length {
191 22     22   38 my ($len) = @_;
192 22         131 return pack("V2", $len & 0xffffffff, $len >> 32);
193             }
194              
195             sub _validate_buffer_length {
196 22     22   37 my ($self) = @_;
197 22 50       63 die "Invalid buffer length" unless length($self->{buffer}) % 64 == 0;
198             }
199              
200             sub _construct_digest {
201 22     22   52 my ($self) = @_;
202 22         38 my $h = $self->{h};
203 22         116 return pack("V4", @$h);
204             }
205              
206             sub _format_hex {
207 22     22   35 my ($digest) = @_;
208 22         222 return unpack("H*", $digest);
209             }
210              
211             sub _rotate_left {
212 2368     2368   3225 my ($value, $shift) = @_;
213 2368         2864 my $left_shift = ($value << $shift) & 0xffffffff;
214 2368         2862 my $right_shift = ($value >> (32 - $shift)) & 0xffffffff;
215 2368         11270 return ($left_shift | $right_shift) & 0xffffffff;
216             }
217              
218             sub _process_block {
219 37     37   57 my ($self, $h, $w) = @_;
220 37         53 state @t = map { $_ & 0xffffffff } (
  64         142  
221             0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
222             0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
223             0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
224             0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
225             0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
226             0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
227             0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
228             0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
229             );
230 37         51 state @s = (
231             7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
232             5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
233             4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
234             6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
235             );
236 37         75 state @g = (
237             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
238             1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,
239             5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,
240             0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9
241             );
242              
243 37 50       80 my $start_time = time() if $ENV{GMD5_PROFILE};
244 37         87 my @initial_h = @$h;
245 37         68 my ($a, $b, $c, $d) = @initial_h;
246 37         79 for my $i (0 .. 63) {
247 2368         3939 ($a, $b, $c, $d) = _compute_round($self, $i, $a, $b, $c, $d, \@t, \@s, $w, \@g);
248             }
249 37         81 _update_hash_values($h, $a, $b, $c, $d, \@initial_h);
250             }
251              
252             sub _compute_round {
253 2368     2368   3871 my ($self, $i, $a, $b, $c, $d, $t, $s, $w, $g) = @_;
254 2368         2678 $a &= 0xffffffff;
255 2368         2526 $b &= 0xffffffff;
256 2368         2877 $c &= 0xffffffff;
257 2368         2543 $d &= 0xffffffff;
258 2368         2648 my $f;
259 2368 100       3891 if ($i < 16) {
    100          
    100          
260 592         830 $f = _compute_f($b, $c, $d);
261             } elsif ($i < 32) {
262 592         982 $f = _compute_g($b, $c, $d);
263             } elsif ($i < 48) {
264 592         845 $f = _compute_h($b, $c, $d);
265             } else {
266 592         873 $f = _compute_i($b, $c, $d);
267             }
268 2368         2899 $f &= 0xffffffff;
269 2368         3252 my $word = $w->[$g->[$i]] & 0xffffffff;
270 2368         2946 my $table_value = $t->[$i] & 0xffffffff;
271 2368         3047 my $sum = ($a + $f) & 0xffffffff;
272 2368         2958 $sum = ($sum + $word) & 0xffffffff;
273 2368         2761 $sum = ($sum + $table_value) & 0xffffffff;
274 2368         3317 my $temp = _rotate_left($sum, $s->[$i]) & 0xffffffff;
275 2368         2979 $temp = ($b + $temp) & 0xffffffff;
276 2368         5461 return ($d, $temp, $b, $c);
277             }
278              
279             sub _compute_f {
280 592     592   883 my ($b, $c, $d) = @_;
281 592         693 $b &= 0xffffffff;
282 592         737 $c &= 0xffffffff;
283 592         657 $d &= 0xffffffff;
284 592         4108 my $f = (($b & $c) | (~$b & $d)) & 0xffffffff;
285 592         759 return $f;
286             }
287              
288             sub _compute_g {
289 592     592   862 my ($b, $c, $d) = @_;
290 592         678 $b &= 0xffffffff;
291 592         669 $c &= 0xffffffff;
292 592         662 $d &= 0xffffffff;
293 592         664 my $bd = $b & $d;
294 592         725 my $not_d = (~$d) & 0xffffffff;
295 592         708 my $c_not_d = $c & $not_d;
296 592         756 my $f = ($bd | $c_not_d) & 0xffffffff;
297 592         10890 return $f;
298             }
299              
300             sub _compute_h {
301 592     592   851 my ($b, $c, $d) = @_;
302 592         626 $b &= 0xffffffff;
303 592         634 $c &= 0xffffffff;
304 592         639 $d &= 0xffffffff;
305 592         691 my $bc = $b ^ $c;
306 592         712 my $f = ($bc ^ $d) & 0xffffffff;
307 592         781 return $f;
308             }
309              
310             sub _compute_i {
311 592     592   833 my ($b, $c, $d) = @_;
312 592         637 $b &= 0xffffffff;
313 592         649 $c &= 0xffffffff;
314 592         689 $d &= 0xffffffff;
315 592         714 my $not_d = (~$d) & 0xffffffff;
316 592         741 my $b_not_d = $b | $not_d;
317 592         672 my $f = ($c ^ $b_not_d) & 0xffffffff;
318 592         805 return $f;
319             }
320              
321             sub _update_hash_values {
322 37     37   73 my ($h, $a, $b, $c, $d, $initial_h) = @_;
323 37         63 $h->[0] = ($initial_h->[0] + $a) & 0xffffffff;
324 37         56 $h->[1] = ($initial_h->[1] + $b) & 0xffffffff;
325 37         49 $h->[2] = ($initial_h->[2] + $c) & 0xffffffff;
326 37         79 $h->[3] = ($initial_h->[3] + $d) & 0xffffffff;
327             }
328              
329             1;