File Coverage

blib/lib/Crypt/PasswdMD5.pm
Criterion Covered Total %
statement 69 69 100.0
branch 15 16 93.7
condition 4 6 66.6
subroutine 8 8 100.0
pod 3 4 75.0
total 99 103 96.1


line stmt bran cond sub pod time code
1             package Crypt::PasswdMD5;
2              
3 2     2   1413 use strict;
  2         5  
  2         67  
4 2     2   10 use warnings;
  2         3  
  2         52  
5              
6 2     2   9 use Digest::MD5;
  2         13  
  2         91  
7              
8 2     2   9 use Exporter 'import';
  2         3  
  2         2191  
9              
10             our @EXPORT = qw/unix_md5_crypt apache_md5_crypt/;
11             our @EXPORT_OK = (@EXPORT, 'random_md5_salt');
12             our $VERSION ='1.40';
13              
14             # ------------------------------------------------
15              
16             my($itoa64) = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
17             our($Magic) = q/$1$/; # Magic strings. Need 'our' because of local just below.
18             my($max_salt_length) = 8;
19              
20             # ------------------------------------------------
21              
22             sub apache_md5_crypt
23             {
24             # Change the Magic string to match the one used by Apache.
25              
26 2     2 1 27 local $Magic = q/$apr1$/;
27              
28 2         7 return unix_md5_crypt(@_);
29              
30             } # End of apache_md5_crypt.
31              
32             # ------------------------------------------------
33              
34             sub random_md5_salt
35             {
36 12   66 12 1 3431 my($len) = shift || $max_salt_length;
37 12         20 my($salt) = '';
38              
39             # Sanity check.
40              
41 12 100 66     82 $len = $max_salt_length unless ( ($len >= 1) and ($len <= $max_salt_length) );
42 12         206 $salt .= substr($itoa64,int(rand(64)),1) for (1..$len);
43              
44 12         29 return $salt;
45              
46             } # End of random_md5_salt.
47              
48             # ------------------------------------------------
49              
50             sub to64
51             {
52 48     48 0 70 my($v, $n) = @_;
53 48         1658 my($ret) = '';
54              
55 48         101 while (--$n >= 0)
56             {
57 176         217 $ret .= substr($itoa64, $v & 0x3f, 1);
58              
59 176         305 $v >>= 6;
60             }
61              
62 48         107 return $ret;
63              
64             } # End of to64.
65              
66             # ------------------------------------------------
67              
68             sub unix_md5_crypt
69             {
70 8     8 1 468 my($pw, $salt) = @_;
71              
72 8         23 my($passwd);
73              
74 8 100       21 if (defined $salt)
75             {
76 6         87 $salt =~ s/^\Q$Magic//; # Take care of the magic string if present.
77 6         33 $salt =~ s/^(.*)\$.*$/$1/; # Salt can have up to 8 chars...
78 6         15 $salt = substr($salt, 0, 8);
79             }
80             else
81             {
82 2         27 $salt = random_md5_salt(); # In case no salt was proffered.
83             }
84              
85 8         49 my($ctx) = Digest::MD5 -> new; # Here we start the calculation.
86              
87 8         25 $ctx -> add($pw); # Original password...
88 8         20 $ctx -> add($Magic); # ...our magic string...
89 8         19 $ctx -> add($salt); # ...the salt...
90              
91 8         30 my($final) = Digest::MD5 -> new;
92              
93 8         19 $final -> add($pw);
94 8         20 $final -> add($salt);
95 8         20 $final -> add($pw);
96              
97 8         31 $final = $final -> digest;
98              
99 8         48 for (my $pl = length($pw); $pl > 0; $pl -= 16)
100             {
101 6 50       41 $ctx -> add(substr($final, 0, $pl > 16 ? 16 : $pl) );
102             }
103              
104             # Now the 'weird' xform.
105              
106 8         23 for (my $i = length($pw); $i; $i >>= 1)
107             {
108 20 100       35 if ($i & 1)
109             {
110 12         48 $ctx -> add(pack('C', 0) );
111             }
112              
113             # This comes from the original version, where a
114             # memset() is done to $final before this loop.
115              
116             else
117             {
118 8         35 $ctx -> add(substr($pw, 0, 1) );
119             }
120             }
121              
122 8         26 $final = $ctx -> digest;
123              
124             # The following is supposed to make things run slower.
125             # In perl, perhaps it'll be *really* slow!
126              
127 8         23 for (my $i = 0; $i < 1000; $i++)
128             {
129 8000         27677 my($ctx1) = Digest::MD5 -> new;
130              
131 8000 100       17129 if ($i & 1)
132             {
133 4000         8849 $ctx1 -> add($pw);
134             }
135             else
136             {
137 4000         11019 $ctx1 -> add(substr($final, 0, 16) );
138             }
139              
140 8000 100       15938 if ($i % 3)
141             {
142 5328         19776 $ctx1 -> add($salt);
143             }
144              
145 8000 100       13761 if ($i % 7)
146             {
147 6856         13371 $ctx1 -> add($pw);
148             }
149              
150 8000 100       12214 if ($i & 1)
151             {
152 4000         11710 $ctx1 -> add(substr($final, 0, 16) );
153             }
154             else
155             {
156 4000         8375 $ctx1 -> add($pw);
157             }
158              
159 8000         43633 $final = $ctx1 -> digest;
160             }
161              
162             # Final xform
163              
164 8         18 $passwd = '';
165 8         131 $passwd .= to64(int(unpack('C', (substr($final, 0, 1) ) ) << 16)
166             | int(unpack('C', (substr($final, 6, 1) ) ) << 8)
167             | int(unpack('C', (substr($final, 12, 1) ) ) ), 4);
168 8         45 $passwd .= to64(int(unpack('C', (substr($final, 1, 1) ) ) << 16)
169             | int(unpack('C', (substr($final, 7, 1) ) ) << 8)
170             | int(unpack('C', (substr($final, 13, 1) ) ) ), 4);
171 8         41 $passwd .= to64(int(unpack('C', (substr($final, 2, 1) ) ) << 16)
172             | int(unpack('C', (substr($final, 8, 1) ) ) << 8)
173             | int(unpack('C', (substr($final, 14, 1) ) ) ), 4);
174 8         37 $passwd .= to64(int(unpack('C', (substr($final, 3, 1) ) ) << 16)
175             | int(unpack('C', (substr($final, 9, 1) ) ) << 8)
176             | int(unpack('C', (substr($final, 15, 1) ) ) ), 4);
177 8         44 $passwd .= to64(int(unpack('C', (substr($final, 4, 1) ) ) << 16)
178             | int(unpack('C', (substr($final, 10, 1) ) ) << 8)
179             | int(unpack('C', (substr($final, 5, 1) ) ) ), 4);
180 8         33 $passwd .= to64(int(unpack('C', substr($final, 11, 1) ) ), 2);
181              
182 8         112 return $Magic . $salt . q/$/ . $passwd;
183              
184             } # End of unix_md5_crypt.
185              
186             # ------------------------------------------------
187              
188             1;
189              
190             =pod
191              
192             =encoding utf-8
193              
194             =head1 NAME
195              
196             Crypt::PasswdMD5 - Provide interoperable MD5-based crypt() functions
197              
198             =head1 SYNOPSIS
199              
200             use strict;
201             use warnings;
202              
203             use Crypt::PasswdMD5;
204              
205             my($password) = 'seekrit';
206             my($salt) = 'pepperoni';
207             my($unix_crypted) = unix_md5_crypt($password, $salt);
208             my($apache_crypted) = apache_md5_crypt($password, $salt);
209              
210             Or:
211              
212             use strict;
213             use warnings;
214              
215             use Crypt::PasswdMD5 'random_md5_salt';
216              
217             my($length) = 7;
218             my($salt_1) = random_md5_salt($length);
219             my($salt_2) = random_md5_salt(); # Default to $length == 8.
220              
221              
222             =head1 DESCRIPTION
223              
224             C provides a function compatible with Apache's C<.htpasswd> files.
225             This was contributed by Bryan Hart .
226             This function is exported by default.
227              
228             The C provides a crypt()-compatible interface to the rather new MD5-based crypt() function
229             found in modern operating systems. It's based on the implementation found on FreeBSD 2.2.[56]-RELEASE.
230             This function is also exported by default.
231              
232             For both functions, if a salt value is not supplied, a random salt will be
233             generated, using the function random_md5_salt().
234             This function is not exported by default.
235              
236             =head1 LICENSE AND WARRANTY
237              
238             This code and all accompanying software comes with NO WARRANTY. You
239             use it at your own risk.
240              
241             This code and all accompanying software can be used freely under the
242             same terms as Perl itself.
243              
244             =head1 METHODS
245              
246             =head2 apache_md5_crypt($password, $salt)
247              
248             This sets a magic variable, and then passes all the calling parameters to L.
249              
250             Returns an encrypted version of the given password.
251              
252             Basically, it's a very poor choice for anything other than password authentication.
253              
254             =head2 random_md5_salt([$length])
255              
256             Here, [] indicate an optional parameter.
257              
258             Returns a random salt of the given length.
259              
260             The maximum length is 8.
261              
262             If C<$length> is omitted, it defaults to 8.
263              
264             =head2 unix_md5_crypt($password, $salt)
265              
266             Returns an encrypted version of the given password.
267              
268             Basically, it's a very poor choice for anything other than password authentication.
269              
270             =head1 SUPPORT
271              
272             Bugs should be reported via the CPAN bug tracker at
273              
274             L
275              
276             =head1 AUTHOR
277              
278             Luis E. Muñoz .
279              
280             Maintenance by Ron Savage as of V 1.40.
281              
282             =cut