File Coverage

blib/lib/Authen/Passphrase/DESCrypt.pm
Criterion Covered Total %
statement 109 111 98.2
branch 66 92 71.7
condition 14 33 42.4
subroutine 23 23 100.0
pod 14 14 100.0
total 226 273 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 6     6   120361 { use 5.006; }
  6         26  
  6         274  
119 6     6   36 use warnings;
  6         20  
  6         257  
120 6     6   31 use strict;
  6         10  
  6         281  
121              
122 6     6   2590 use Authen::Passphrase 0.003;
  6         224  
  6         200  
123 6     6   42 use Carp qw(croak);
  6         8  
  6         94600  
124 6         796 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 6     6   1885520 );
  6         92016  
130 6     6   8987 use Data::Entropy::Algorithms 0.000 qw(rand_int);
  6         141271  
  6         697  
131              
132             our $VERSION = "0.008";
133              
134 6     6   63 use parent "Authen::Passphrase";
  6         12  
  6         31  
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             The source of randomness may be controlled by the facility described
186             in L.
187              
188             =item B
189              
190             The hash (output of encryption), as a string of exactly eight bytes.
191              
192             =item B
193              
194             The hash, as a string of eleven base 64 digits.
195              
196             =item B
197              
198             A passphrase that will be accepted.
199              
200             =back
201              
202             The salt must be given, and either the hash or the passphrase. The other
203             parameters default to those used in the original DES-based crypt().
204              
205             =cut
206              
207             sub new {
208 67     67 1 1931 my $class = shift;
209 67         236 my $self = bless({}, $class);
210 67         100 my $passphrase;
211 67         250 while(@_) {
212 185         7532 my $attr = shift;
213 185         274 my $value = shift;
214 185 100       1246 if($attr eq "fold") {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
215 13 50       57 croak "foldness specified redundantly"
216             if exists $self->{fold};
217 13         117 $self->{fold} = !!$value;
218             } elsif($attr eq "initial") {
219 1 50       10 croak "initial block specified redundantly"
220             if exists $self->{initial};
221 1 50       6 $value =~ m#\A[\x00-\xff]{8}\z#
222             or croak "not a valid raw block";
223 1         5 $self->{initial} = "$value";
224             } elsif($attr eq "initial_base64") {
225 6 50       25 croak "initial block specified redundantly"
226             if exists $self->{initial};
227 6 50       34 $value =~ m#\A[./0-9A-Za-z]{10}[.26AEIMQUYcgkosw]\z#
228             or croak "\"$value\" is not a valid ".
229             "encoded block";
230 6         59 $self->{initial} = base64_to_block($value);
231             } elsif($attr eq "nrounds") {
232 19 50       57 croak "number of rounds specified redundantly"
233             if exists $self->{nrounds};
234 19 50 33     111 croak "\"$value\" is not a valid number of rounds"
235             unless $value == int($value) && $value >= 0;
236 19         78 $self->{nrounds} = 0+$value;
237             } elsif($attr eq "nrounds_base64") {
238 12 50       45 croak "number of rounds specified redundantly"
239             if exists $self->{nrounds};
240 12 50       89 croak "\"$value\" is not a valid number of rounds"
241             unless $value =~ m#\A[./0-9A-Za-z]{4}\z#;
242 12         79 $self->{nrounds} = base64_to_int24($value);
243             } elsif($attr eq "salt") {
244 47 50       126 croak "salt specified redundantly"
245             if exists $self->{salt};
246 47 50 33     332 croak "\"$value\" is not a valid salt"
      33        
247             unless $value == int($value) &&
248             $value >= 0 && $value < 16777216;
249 47         170 $self->{salt} = 0+$value;
250             } elsif($attr eq "salt_base64") {
251 19 50       69 croak "salt specified redundantly"
252             if exists $self->{salt};
253 19 50       100 $value =~ m#\A(?:[./0-9A-Za-z]{2}|[./0-9A-Za-z]{4})\z#
254             or croak "\"$value\" is not a valid salt";
255 19 100       142 $self->{salt} = length($value) == 2 ?
256             base64_to_int12($value) :
257             base64_to_int24($value);
258             } elsif($attr eq "salt_random") {
259 1 50       5 croak "salt specified redundantly"
260             if exists $self->{salt};
261 1 50 33     5 croak "\"$value\" is not a valid salt size"
262             unless $value == 12 || $value == 24;
263 1         6 $self->{salt} = rand_int(1 << $value);
264             } elsif($attr eq "hash") {
265 33 50 33     169 croak "hash specified redundantly"
266             if exists($self->{hash}) ||
267             defined($passphrase);
268 33 50       4258 $value =~ m#\A[\x00-\xff]{8}\z#
269             or croak "not a valid raw hash";
270 33         161 $self->{hash} = "$value";
271             } elsif($attr eq "hash_base64") {
272 18 50 33     125 croak "hash specified redundantly"
273             if exists($self->{hash}) ||
274             defined($passphrase);
275 18 50       74 $value =~ m#\A[./0-9A-Za-z]{10}[.26AEIMQUYcgkosw]\z#
276             or croak "\"$value\" is not a valid ".
277             "encoded hash";
278 18         131 $self->{hash} = base64_to_block($value);
279             } elsif($attr eq "passphrase") {
280 16 50 33     1137 croak "passphrase specified redundantly"
281             if exists($self->{hash}) ||
282             defined($passphrase);
283 16         54 $passphrase = $value;
284             } else {
285 0         0 croak "unrecognised attribute `$attr'";
286             }
287             }
288 67 100       257 $self->{fold} = !!0 unless exists $self->{fold};
289 67 100       297 $self->{initial} = "\0\0\0\0\0\0\0\0" unless exists $self->{initial};
290 67 100       188 $self->{nrounds} = 25 unless exists $self->{nrounds};
291 67 50       239 croak "salt not specified" unless exists $self->{salt};
292 67 100       186 $self->{hash} = $self->_hash_of($passphrase) if defined $passphrase;
293 67 50       169 croak "hash not specified" unless exists $self->{hash};
294 67         222 return $self;
295             }
296              
297             =item Authen::Passphrase::DESCrypt->from_crypt(PASSWD)
298              
299             Generates a new passphrase recogniser object using the DES-based crypt()
300             algorithm, from a crypt string. Two forms of crypt string are supported.
301              
302             The first form of crypt string must consist of 13 base 64 digits.
303             The first two give the salt, and the next eleven give the hash.
304             Long passphrases are not folded, the initial block is all bits zero,
305             and 25 encryption rounds are performed.
306              
307             The second form of crypt string must consist of an "B<_>" followed
308             by 19 base 64 digits. The first four give the number of encryption
309             rounds, the next four give the salt, and the next eleven give the hash.
310             Long passphrases are folded, and the initial block is all bits zero.
311              
312             =cut
313              
314             sub from_crypt {
315 2     2 1 31 my($class, $passwd) = @_;
316 2 100       24 if($passwd =~ /\A[^\$].{12}\z/s) {
    50          
317 1 50       7 $passwd =~ m#\A([./0-9A-Za-z]{2})([./0-9A-Za-z]{11})\z#
318             or croak "malformed DES crypt data";
319 1         8 my($salt, $hash) = ($1, $2);
320 1         7 return $class->new(salt_base64 => $salt, hash_base64 => $hash);
321             } elsif($passwd =~ /\A_.{19}\z/s) {
322 1 50       9 $passwd =~ m#\A_([./0-9A-Za-z]{4})([./0-9A-Za-z]{4})
323             ([./0-9A-Za-z]{11})\z#x
324             or croak "malformed _ data";
325 1         9 my($nr, $salt, $hash) = ($1, $2, $3);
326 1         6 return $class->new(fold => 1, nrounds_base64 => $nr,
327             salt_base64 => $salt, hash_base64 => $hash);
328             }
329 0         0 return $class->SUPER::from_crypt($passwd);
330             }
331              
332             =item Authen::Passphrase::DESCrypt->from_rfc2307(USERPASSWORD)
333              
334             Generates a new passphrase recogniser object using the DES-based
335             crypt() algorithm, from an RFC 2307 string. The string must consist of
336             "B<{CRYPT}>" (case insensitive) followed by an acceptable crypt string.
337              
338             =back
339              
340             =head1 METHODS
341              
342             =over
343              
344             =item $ppr->fold
345              
346             Returns a truth value indicating whether passphrase folding is used.
347              
348             =cut
349              
350             sub fold {
351 27     27 1 12477 my($self) = @_;
352 27         122 return $self->{fold};
353             }
354              
355             =item $ppr->initial
356              
357             Returns the initial block, as a string of eight bytes.
358              
359             =cut
360              
361             sub initial {
362 8     8 1 22 my($self) = @_;
363 8         38 return $self->{initial};
364             }
365              
366             =item $ppr->initial_base64
367              
368             Returns the initial block, as a string of eleven base 64 digits.
369              
370             =cut
371              
372             sub initial_base64 {
373 23     23 1 45 my($self) = @_;
374 23         151 return block_to_base64($self->{initial});
375             }
376              
377             =item $ppr->nrounds
378              
379             Returns the number of encryption rounds, as a Perl integer.
380              
381             =cut
382              
383             sub nrounds {
384 15     15 1 40 my($self) = @_;
385 15         77 return $self->{nrounds};
386             }
387              
388             =item $ppr->nrounds_base64_4
389              
390             Returns the number of encryption rounds, as a string of four base
391             64 digits.
392              
393             =cut
394              
395             sub nrounds_base64_4 {
396 26     26 1 47 my($self) = @_;
397 26         149 return int24_to_base64($self->{nrounds});
398             }
399              
400             =item $ppr->salt
401              
402             Returns the salt, as a Perl integer.
403              
404             =cut
405              
406             sub salt {
407 14     14 1 30 my($self) = @_;
408 14         66 return $self->{salt};
409             }
410              
411             =item $ppr->salt_base64_2
412              
413             Returns the salt, as a string of two base 64 digits. Cs if it
414             doesn't fit into two digits.
415              
416             =cut
417              
418             sub salt_base64_2 {
419 45     45 1 82 my($self) = @_;
420 45         88 my $salt = $self->{salt};
421 45 100       587 croak "salt $salt doesn't fit into two digits" if $salt >= 4096;
422 43         275 return int12_to_base64($salt);
423             }
424              
425             =item $ppr->salt_base64_4
426              
427             Returns the salt, as a string of four base 64 digits.
428              
429             =cut
430              
431             sub salt_base64_4 {
432 27     27 1 1419 my($self) = @_;
433 27         143 return int24_to_base64($self->{salt});
434             }
435              
436             =item $ppr->hash
437              
438             Returns the hash value, as a string of eight bytes.
439              
440             =cut
441              
442             sub hash {
443 13     13 1 31 my($self) = @_;
444 13         82 return $self->{hash};
445             }
446              
447             =item $ppr->hash_base64
448              
449             Returns the hash value, as a string of eleven base 64 digits.
450              
451             =cut
452              
453             sub hash_base64 {
454 118     118 1 171 my($self) = @_;
455 118         4348 return block_to_base64($self->{hash});
456             }
457              
458             =item $ppr->match(PASSPHRASE)
459              
460             =item $ppr->as_crypt
461              
462             =item $ppr->as_rfc2307
463              
464             These methods are part of the standard L interface.
465              
466             =cut
467              
468              
469              
470             sub _hash_of {
471 170     170   298 my($self, $passphrase) = @_;
472 170 100       914 $passphrase = fold_password($passphrase) if $self->{fold};
473 170         2220512 return crypt_rounds($passphrase, $self->{nrounds}, $self->{salt},
474             $self->{initial});
475             }
476              
477             sub match {
478 154     154 1 746317 my($self, $passphrase) = @_;
479 154         399 return $self->_hash_of($passphrase) eq $self->{hash};
480             }
481              
482             sub as_crypt {
483 36     36 1 71 my($self) = @_;
484 36 100 66     412 if(!$self->{fold} && $self->{initial} eq "\0\0\0\0\0\0\0\0" &&
    100 66        
      33        
      66        
485             $self->{nrounds} == 25 && $self->{salt} < 4096) {
486 16         47 return $self->salt_base64_2.$self->hash_base64;
487             } elsif($self->{fold} && $self->{initial} eq "\0\0\0\0\0\0\0\0") {
488 10         25 return "_".$self->nrounds_base64_4.$self->salt_base64_4.
489             $self->hash_base64;
490             } else {
491 10         1616 croak "passphrase can't be expressed as a crypt string";
492             }
493             }
494              
495             =back
496              
497             =head1 SEE ALSO
498              
499             L,
500             L
501              
502             =head1 AUTHOR
503              
504             Andrew Main (Zefram)
505              
506             =head1 COPYRIGHT
507              
508             Copyright (C) 2006, 2007, 2009, 2010, 2012
509             Andrew Main (Zefram)
510              
511             =head1 LICENSE
512              
513             This module is free software; you can redistribute it and/or modify it
514             under the same terms as Perl itself.
515              
516             =cut
517              
518             1;