File Coverage

blib/lib/Authen/Passphrase/DESCrypt.pm
Criterion Covered Total %
statement 108 110 98.1
branch 66 92 71.7
condition 14 33 42.4
subroutine 23 23 100.0
pod 14 14 100.0
total 225 272 82.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Authen::Passphrase::DESCrypt - passphrases using the DES-based Unix
4             crypt()
5              
6             =head1 SYNOPSIS
7              
8             use Authen::Passphrase::DESCrypt;
9              
10             $ppr = Authen::Passphrase::DESCrypt->new(
11             salt_base64 => "my",
12             hash_base64 => "TYK.j.88/9s");
13              
14             $ppr = Authen::Passphrase::DESCrypt->new(
15             salt_random => 12,
16             passphrase => "passphrase");
17              
18             $ppr = Authen::Passphrase::DESCrypt
19             ->from_crypt('myTYK.j.88/9s');
20              
21             $ppr = Authen::Passphrase::DESCrypt->new(
22             fold => 1,
23             initial => "xyzzy!!!",
24             nrounds => 500,
25             salt_base64 => "quux",
26             hash_base64 => "QCKcHlgVsRY");
27              
28             $fold = $ppr->fold;
29             $initial = $ppr->initial;
30             $initial_base64 = $ppr->initial_base64;
31             $nrounds = $ppr->nrounds;
32             $nrounds_base64 = $ppr->nrounds_base64_4;
33             $salt = $ppr->salt;
34             $salt_base64 = $ppr->salt_base64_2;
35             $salt_base64 = $ppr->salt_base64_4;
36             $hash = $ppr->hash;
37             $hash_base64 = $ppr->hash_base64;
38              
39             if($ppr->match($passphrase)) { ...
40              
41             $passwd = $ppr->as_crypt;
42             $userPassword = $ppr->as_rfc2307;
43              
44             =head1 DESCRIPTION
45              
46             An object of this class encapsulates a passphrase hashed using some
47             form of the DES-based Unix crypt() hash function. This is a subclass
48             of L, and this document assumes that the reader is
49             familiar with the documentation for that class.
50              
51             The crypt() function in a modern Unix actually supports several different
52             passphrase schemes. That is not what this class is about. This class
53             is concerned only with one family of schemes, variants of the DES-based
54             scheme that crypt() originally implemented, which confusingly is usually
55             referred to merely as "crypt()". To handle the whole range of passphrase
56             schemes supported by the modern crypt(), see the
57             L constructor and the
58             L method in L.
59              
60             I this password scheme is weak by modern standards, and in
61             any case does not support a large password space. Cracking crypt()ed
62             passwords has been a routine activity since the early 1990s. This scheme
63             is supported for compatibility reasons only, and should not be used
64             except when compatibility is required. Do not use this in the design of
65             any new system or for new passwords in any system that supports better
66             passphrase schemes.
67              
68             =head2 The traditional DES-based Unix crypt() password scheme
69              
70             The traditional Unix crypt() password scheme is based on the DES block
71             encryption algorithm. Using the password as a 56-bit key, it passes a
72             64-bit data block, initialised to zero, through the encryption function
73             25 times, and the hash is the 64-bit output of this process. A 12-bit
74             salt is used to tweak the encryption algorithm.
75              
76             The 56-bit key is extracted from the password in a very poor way.
77             Only the first eight bytes of the password are used, and any remainder
78             is ignored. This makes it impossible to use a passphrase, rather than
79             a password, hence the terminology in this section. Of the eight bytes
80             used, the top bit is also ignored; this function hails from the days of
81             pure ASCII.
82              
83             A password hash of this scheme is conventionally represented in ASCII as
84             a 13-character string using a base 64 encoding. The base 64 digits are
85             "B<.>", "B", "B<0>" to "B<9>", "B" to "B", "B" to "B"
86             (in ASCII order). The first two characters give the 12-bit salt.
87             The remaining eleven characters give the 64-bit hash. Because the base
88             64 encoding can represent 66 bits in eleven digits, more than the 64
89             required, the last character of the string can only take sixteen of the
90             base 64 digit values.
91              
92             =head2 Variant DES-based Unix crypt() passphrase schemes
93              
94             To make password cracking more difficult, historically some Unix sites
95             modified the crypt() function to be incompatible with the standard one.
96             This was easily achieved by initialising the data block to something
97             other than the standard all-bits-zero. Another variation used was to
98             increase the number of encryption rounds, which makes cracking take
99             longer in addition to being non-standard. Password hashes on such a
100             system looked normal but were not interoperable with standard crypt()
101             implementations. To interpret them properly it is necessary to know
102             the modified parameters.
103              
104             BSDi standardised an extended DES-based scheme. The salt is extended to
105             24 bits, and the number of encryption rounds is variable. Passphrases
106             longer than 8 characters are handled by an additional step that folds
107             (hashes) them down to 8 characters, rather than just throwing away
108             the characters after the eighth. Passphrase hashes in this scheme
109             are conventionally represented in ASCII as a "B<_>" followed by 19
110             characters of base 64. The first four base 64 digits give the number
111             of encryption rounds, the next four give the salt, and the remaining
112             eleven give the hash.
113              
114             =cut
115              
116             package Authen::Passphrase::DESCrypt;
117              
118 7     7   374041 { use 5.006; }
  7         20  
119 7     7   45 use warnings;
  7         35  
  7         329  
120 7     7   45 use strict;
  7         30  
  7         251  
121              
122 7     7   1686 use Authen::Passphrase 0.003;
  7         115  
  7         225  
123 7     7   37 use Carp qw(croak);
  7         12  
  7         487  
124 7         598 use Crypt::UnixCrypt_XS 0.08 qw(
125             fold_password crypt_rounds
126             base64_to_block block_to_base64
127             base64_to_int24 int24_to_base64
128             base64_to_int12 int12_to_base64
129 7     7   3048 );
  7         4189  
130 7     7   2752 use Crypt::SysRandom 'random_bytes';
  7         19453  
  7         524  
131              
132             our $VERSION = "0.009";
133              
134 7     7   2194 use parent "Authen::Passphrase";
  7         1415  
  7         39  
135              
136             =head1 CONSTRUCTORS
137              
138             =over
139              
140             =item Authen::Passphrase::DESCrypt->new(ATTR => VALUE, ...)
141              
142             Generates a new passphrase recogniser object using the generalised
143             DES-based crypt() algorithm. The following attributes may be given:
144              
145             =over
146              
147             =item B
148              
149             Truth value indicating whether the BSDi passphrase folding scheme should be
150             used for long passphrases. Default false, for compatibility with the
151             original DES-based scheme.
152              
153             =item B
154              
155             The initial data block to encrypt, as a string of exactly eight bytes.
156             Default all bits zero, for compatibility with the original DES-based
157             scheme.
158              
159             =item B
160              
161             The initial data block to encrypt, as a string of eleven base 64 digits.
162              
163             =item B
164              
165             The number of encryption rounds to use, as a Perl integer. Default 25,
166             for compatibility with the original DES-based scheme.
167              
168             =item B
169              
170             The number of encryption rounds to use, as a string of four base 64
171             digits.
172              
173             =item B
174              
175             The salt, as an integer in the range [0, 16777216).
176              
177             =item B
178              
179             The salt, as a string of two or four base 64 digits.
180              
181             =item B
182              
183             Causes salt to be generated randomly. The value given for this attribute
184             must be either 12 or 24, giving the number of bits of salt to generate.
185              
186             =item B
187              
188             The hash (output of encryption), as a string of exactly eight bytes.
189              
190             =item B
191              
192             The hash, as a string of eleven base 64 digits.
193              
194             =item B
195              
196             A passphrase that will be accepted.
197              
198             =back
199              
200             The salt must be given, and either the hash or the passphrase. The other
201             parameters default to those used in the original DES-based crypt().
202              
203             =cut
204              
205             sub new {
206 67     67 1 361273 my $class = shift;
207 67         120 my $self = bless({}, $class);
208 67         98 my $passphrase;
209 67         134 while(@_) {
210 185         255 my $attr = shift;
211 185         231 my $value = shift;
212 185 100       694 if($attr eq "fold") {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
213             croak "foldness specified redundantly"
214 13 50       39 if exists $self->{fold};
215 13         39 $self->{fold} = !!$value;
216             } elsif($attr eq "initial") {
217             croak "initial block specified redundantly"
218 1 50       4 if exists $self->{initial};
219 1 50       9 $value =~ m#\A[\x00-\xff]{8}\z#
220             or croak "not a valid raw block";
221 1         5 $self->{initial} = "$value";
222             } elsif($attr eq "initial_base64") {
223             croak "initial block specified redundantly"
224 6 50       15 if exists $self->{initial};
225 6 50       20 $value =~ m#\A[./0-9A-Za-z]{10}[.26AEIMQUYcgkosw]\z#
226             or croak "\"$value\" is not a valid ".
227             "encoded block";
228 6         29 $self->{initial} = base64_to_block($value);
229             } elsif($attr eq "nrounds") {
230             croak "number of rounds specified redundantly"
231 19 50       54 if exists $self->{nrounds};
232 19 50 33     108 croak "\"$value\" is not a valid number of rounds"
233             unless $value == int($value) && $value >= 0;
234 19         46 $self->{nrounds} = 0+$value;
235             } elsif($attr eq "nrounds_base64") {
236             croak "number of rounds specified redundantly"
237 12 50       22 if exists $self->{nrounds};
238 12 50       33 croak "\"$value\" is not a valid number of rounds"
239             unless $value =~ m#\A[./0-9A-Za-z]{4}\z#;
240 12         39 $self->{nrounds} = base64_to_int24($value);
241             } elsif($attr eq "salt") {
242             croak "salt specified redundantly"
243 47 50       87 if exists $self->{salt};
244 47 50 33     177 croak "\"$value\" is not a valid salt"
      33        
245             unless $value == int($value) &&
246             $value >= 0 && $value < 16777216;
247 47         114 $self->{salt} = 0+$value;
248             } elsif($attr eq "salt_base64") {
249             croak "salt specified redundantly"
250 19 50       51 if exists $self->{salt};
251 19 50       93 $value =~ m#\A(?:[./0-9A-Za-z]{2}|[./0-9A-Za-z]{4})\z#
252             or croak "\"$value\" is not a valid salt";
253 19 100       88 $self->{salt} = length($value) == 2 ?
254             base64_to_int12($value) :
255             base64_to_int24($value);
256             } elsif($attr eq "salt_random") {
257             croak "salt specified redundantly"
258 1 50       4 if exists $self->{salt};
259 1 50 33     3 croak "\"$value\" is not a valid salt size"
260             unless $value == 12 || $value == 24;
261 1         13 $self->{salt} = unpack("I", random_bytes(4)) % (1 << $value);
262             } elsif($attr eq "hash") {
263             croak "hash specified redundantly"
264 33 50 33     89 if exists($self->{hash}) ||
265             defined($passphrase);
266 33 50       95 $value =~ m#\A[\x00-\xff]{8}\z#
267             or croak "not a valid raw hash";
268 33         83 $self->{hash} = "$value";
269             } elsif($attr eq "hash_base64") {
270             croak "hash specified redundantly"
271 18 50 33     85 if exists($self->{hash}) ||
272             defined($passphrase);
273 18 50       59 $value =~ m#\A[./0-9A-Za-z]{10}[.26AEIMQUYcgkosw]\z#
274             or croak "\"$value\" is not a valid ".
275             "encoded hash";
276 18         75 $self->{hash} = base64_to_block($value);
277             } elsif($attr eq "passphrase") {
278             croak "passphrase specified redundantly"
279 16 50 33     46 if exists($self->{hash}) ||
280             defined($passphrase);
281 16         31 $passphrase = $value;
282             } else {
283 0         0 croak "unrecognised attribute `$attr'";
284             }
285             }
286 67 100       144 $self->{fold} = !!0 unless exists $self->{fold};
287 67 100       147 $self->{initial} = "\0\0\0\0\0\0\0\0" unless exists $self->{initial};
288 67 100       128 $self->{nrounds} = 25 unless exists $self->{nrounds};
289 67 50       112 croak "salt not specified" unless exists $self->{salt};
290 67 100       123 $self->{hash} = $self->_hash_of($passphrase) if defined $passphrase;
291 67 50       118 croak "hash not specified" unless exists $self->{hash};
292 67         194 return $self;
293             }
294              
295             =item Authen::Passphrase::DESCrypt->from_crypt(PASSWD)
296              
297             Generates a new passphrase recogniser object using the DES-based crypt()
298             algorithm, from a crypt string. Two forms of crypt string are supported.
299              
300             The first form of crypt string must consist of 13 base 64 digits.
301             The first two give the salt, and the next eleven give the hash.
302             Long passphrases are not folded, the initial block is all bits zero,
303             and 25 encryption rounds are performed.
304              
305             The second form of crypt string must consist of an "B<_>" followed
306             by 19 base 64 digits. The first four give the number of encryption
307             rounds, the next four give the salt, and the next eleven give the hash.
308             Long passphrases are folded, and the initial block is all bits zero.
309              
310             =cut
311              
312             sub from_crypt {
313 2     2 1 305022 my($class, $passwd) = @_;
314 2 100       12 if($passwd =~ /\A[^\$].{12}\z/s) {
    50          
315 1 50       5 $passwd =~ m#\A([./0-9A-Za-z]{2})([./0-9A-Za-z]{11})\z#
316             or croak "malformed DES crypt data";
317 1         4 my($salt, $hash) = ($1, $2);
318 1         5 return $class->new(salt_base64 => $salt, hash_base64 => $hash);
319             } elsif($passwd =~ /\A_.{19}\z/s) {
320 1 50       5 $passwd =~ m#\A_([./0-9A-Za-z]{4})([./0-9A-Za-z]{4})
321             ([./0-9A-Za-z]{11})\z#x
322             or croak "malformed _ data";
323 1         5 my($nr, $salt, $hash) = ($1, $2, $3);
324 1         6 return $class->new(fold => 1, nrounds_base64 => $nr,
325             salt_base64 => $salt, hash_base64 => $hash);
326             }
327 0         0 return $class->SUPER::from_crypt($passwd);
328             }
329              
330             =item Authen::Passphrase::DESCrypt->from_rfc2307(USERPASSWORD)
331              
332             Generates a new passphrase recogniser object using the DES-based
333             crypt() algorithm, from an RFC 2307 string. The string must consist of
334             "B<{CRYPT}>" (case insensitive) followed by an acceptable crypt string.
335              
336             =back
337              
338             =head1 METHODS
339              
340             =over
341              
342             =item $ppr->fold
343              
344             Returns a truth value indicating whether passphrase folding is used.
345              
346             =cut
347              
348             sub fold {
349 27     27 1 7846 my($self) = @_;
350 27         103 return $self->{fold};
351             }
352              
353             =item $ppr->initial
354              
355             Returns the initial block, as a string of eight bytes.
356              
357             =cut
358              
359             sub initial {
360 8     8 1 20 my($self) = @_;
361 8         34 return $self->{initial};
362             }
363              
364             =item $ppr->initial_base64
365              
366             Returns the initial block, as a string of eleven base 64 digits.
367              
368             =cut
369              
370             sub initial_base64 {
371 23     23 1 47 my($self) = @_;
372 23         143 return block_to_base64($self->{initial});
373             }
374              
375             =item $ppr->nrounds
376              
377             Returns the number of encryption rounds, as a Perl integer.
378              
379             =cut
380              
381             sub nrounds {
382 15     15 1 54 my($self) = @_;
383 15         71 return $self->{nrounds};
384             }
385              
386             =item $ppr->nrounds_base64_4
387              
388             Returns the number of encryption rounds, as a string of four base
389             64 digits.
390              
391             =cut
392              
393             sub nrounds_base64_4 {
394 26     26 1 48 my($self) = @_;
395 26         125 return int24_to_base64($self->{nrounds});
396             }
397              
398             =item $ppr->salt
399              
400             Returns the salt, as a Perl integer.
401              
402             =cut
403              
404             sub salt {
405 14     14 1 35 my($self) = @_;
406 14         61 return $self->{salt};
407             }
408              
409             =item $ppr->salt_base64_2
410              
411             Returns the salt, as a string of two base 64 digits. Cs if it
412             doesn't fit into two digits.
413              
414             =cut
415              
416             sub salt_base64_2 {
417 45     45 1 90 my($self) = @_;
418 45         92 my $salt = $self->{salt};
419 45 100       592 croak "salt $salt doesn't fit into two digits" if $salt >= 4096;
420 43         219 return int12_to_base64($salt);
421             }
422              
423             =item $ppr->salt_base64_4
424              
425             Returns the salt, as a string of four base 64 digits.
426              
427             =cut
428              
429             sub salt_base64_4 {
430 27     27 1 1293 my($self) = @_;
431 27         117 return int24_to_base64($self->{salt});
432             }
433              
434             =item $ppr->hash
435              
436             Returns the hash value, as a string of eight bytes.
437              
438             =cut
439              
440             sub hash {
441 13     13 1 26 my($self) = @_;
442 13         50 return $self->{hash};
443             }
444              
445             =item $ppr->hash_base64
446              
447             Returns the hash value, as a string of eleven base 64 digits.
448              
449             =cut
450              
451             sub hash_base64 {
452 118     118 1 212 my($self) = @_;
453 118         630 return block_to_base64($self->{hash});
454             }
455              
456             =item $ppr->match(PASSPHRASE)
457              
458             =item $ppr->as_crypt
459              
460             =item $ppr->as_rfc2307
461              
462             These methods are part of the standard L interface.
463              
464             =cut
465              
466              
467              
468             sub _hash_of {
469 170     170   308 my($self, $passphrase) = @_;
470 170 100       633 $passphrase = fold_password($passphrase) if $self->{fold};
471             return crypt_rounds($passphrase, $self->{nrounds}, $self->{salt},
472 170         2026115 $self->{initial});
473             }
474              
475             sub match {
476 154     154 1 28868 my($self, $passphrase) = @_;
477 154         408 return $self->_hash_of($passphrase) eq $self->{hash};
478             }
479              
480             sub as_crypt {
481 36     36 1 75 my($self) = @_;
482 36 100 66     281 if(!$self->{fold} && $self->{initial} eq "\0\0\0\0\0\0\0\0" &&
    100 66        
      33        
      66        
483             $self->{nrounds} == 25 && $self->{salt} < 4096) {
484 16         58 return $self->salt_base64_2.$self->hash_base64;
485             } elsif($self->{fold} && $self->{initial} eq "\0\0\0\0\0\0\0\0") {
486 10         19 return "_".$self->nrounds_base64_4.$self->salt_base64_4.
487             $self->hash_base64;
488             } else {
489 10         1168 croak "passphrase can't be expressed as a crypt string";
490             }
491             }
492              
493             =back
494              
495             =head1 SEE ALSO
496              
497             L,
498             L
499              
500             =head1 AUTHOR
501              
502             Andrew Main (Zefram)
503              
504             =head1 COPYRIGHT
505              
506             Copyright (C) 2006, 2007, 2009, 2010, 2012
507             Andrew Main (Zefram)
508              
509             =head1 LICENSE
510              
511             This module is free software; you can redistribute it and/or modify it
512             under the same terms as Perl itself.
513              
514             =cut
515              
516             1;