File Coverage

blib/lib/Authen/Passphrase/BigCrypt.pm
Criterion Covered Total %
statement 80 83 96.3
branch 33 48 68.7
condition 5 15 33.3
subroutine 16 16 100.0
pod 7 7 100.0
total 141 169 83.4


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Authen::Passphrase::BigCrypt - passphrases using bigcrypt algorithm
4              
5             =head1 SYNOPSIS
6              
7             use Authen::Passphrase::BigCrypt;
8              
9             $ppr = Authen::Passphrase::BigCrypt->new(
10             salt_base64 => "qi",
11             hash_base64 => "yh4XPJGsOZ2MEAyLkfWqeQ");
12              
13             $ppr = Authen::Passphrase::BigCrypt->new(
14             salt_random => 12,
15             passphrase => "passphrase");
16              
17             $salt = $ppr->salt;
18             $salt_base64 = $ppr->salt_base64_2;
19             $hash = $ppr->hash;
20             $hash_base64 = $ppr->hash_base64;
21              
22             $pprs = $ppr->sections;
23              
24             if($ppr->match($passphrase)) { ...
25              
26             =head1 DESCRIPTION
27              
28             An object of this class encapsulates a passphrase hashed using the
29             "bigcrypt" hash function found in HP-UX, Digital Unix, OSF/1, and some
30             other flavours of Unix. Do not confuse this with the "crypt16" found
31             on Ultrix and Tru64 (for which see L).
32             This is a subclass of L, and this document assumes
33             that the reader is familiar with the documentation for that class.
34              
35             This is a derivation of the original DES-based crypt function found on all
36             Unices (see L). The first eight bytes of
37             the passphrase are used as a DES key to encrypt the all-bits-zero block
38             through 25 rounds of (12-bit) salted DES, just like the original crypt.
39             Then, if the passphrase is longer than eight bytes, the next eight bytes
40             are used as a DES key to encrypt the all-bits-zero block through 25
41             rounds of salted DES, using as salt the first 12 bits of the hash of the
42             first section. Then, if the passphrase is longer than sixteen bytes,
43             the next eight bytes are used, with salt consisting of the first 12
44             bits of the hash of the second section. This repeats until the entire
45             passphrase has been used. The hashes of all the sections are concatenated
46             to form the final hash.
47              
48             A password hash of this scheme is conventionally represented in ASCII
49             using the base 64 encoding of the underlying DES-based crypt function.
50             The first two characters give the salt for the first section, the next
51             eleven give the hash of the first section, the next eleven give the hash
52             of the second section, and so on. A hash thus encoded is used as a crypt
53             string, on those systems where the bigcrypt algorithm is part of crypt(),
54             but the syntax clashes with that of crypt16. This module does not treat
55             it as a crypt string syntax.
56              
57             Because the sections of the passphrase are hashed separately, it is
58             possible to manipulate (e.g., crack) a section hash in isolation.
59             See L for handling of a single section.
60              
61             I This is a fatally flawed design, often providing I
62             security than the plain DES scheme alone. Do not use seriously.
63              
64             =cut
65              
66             package Authen::Passphrase::BigCrypt;
67              
68 1     1   80279 { use 5.006; }
  1         3  
  1         56  
69 1     1   6 use warnings;
  1         2  
  1         47  
70 1     1   10 use strict;
  1         3  
  1         41  
71              
72 1     1   660 use Authen::Passphrase 0.003;
  1         18  
  1         28  
73 1     1   107889 use Authen::Passphrase::DESCrypt;
  1         3  
  1         40  
74 1     1   8 use Carp qw(croak);
  1         3  
  1         63  
75 1     1   6 use Crypt::UnixCrypt_XS 0.08 qw(base64_to_block base64_to_int12);
  1         28  
  1         55  
76 1     1   7 use Data::Entropy::Algorithms 0.000 qw(rand_int);
  1         16  
  1         65  
77              
78             our $VERSION = "0.008";
79              
80 1     1   5 use parent "Authen::Passphrase";
  1         1  
  1         5  
81              
82             =head1 CONSTRUCTOR
83              
84             =over
85              
86             =item Authen::Passphrase::BigCrypt->new(ATTR => VALUE, ...)
87              
88             Generates a new passphrase recogniser object using the bigcrypt hash
89             algorithm. The following attributes may be given:
90              
91             =over
92              
93             =item B
94              
95             The salt for the first section, as an integer in the range [0, 4096).
96              
97             =item B
98              
99             The salt for the first section, as a string of two base 64 digits.
100              
101             =item B
102              
103             Causes salt for the first section to be generated randomly. The value
104             given for this attribute must be 12, indicating generation of 12 bits
105             of salt. The source of randomness may be controlled by the facility
106             described in L.
107              
108             =item B
109              
110             The hash, as a string of bytes.
111              
112             =item B
113              
114             The hash, as a string of base 64 digits.
115              
116             =item B
117              
118             A passphrase that will be accepted.
119              
120             =back
121              
122             The salt for the first section must be given, and either the hash or
123             the passphrase.
124              
125             =cut
126              
127             sub new {
128 14     14 1 3576 my $class = shift;
129 14         22 my $salt;
130             my @hashes;
131 0         0 my $passphrase;
132 14         50 while(@_) {
133 28         25104 my $attr = shift;
134 28         40 my $value = shift;
135 28 100       144 if($attr eq "salt") {
    100          
    100          
    100          
    100          
    50          
136 4 50       12 croak "salt specified redundantly"
137             if defined $salt;
138 4 50 33     52 croak "\"$value\" is not a valid salt"
      33        
139             unless $value == int($value) &&
140             $value >= 0 && $value < 4096;
141 4         12 $salt = $value;
142             } elsif($attr eq "salt_base64") {
143 9 50       25 croak "salt specified redundantly"
144             if defined $salt;
145 9 50       33 $value =~ m#\A[./0-9A-Za-z]{2}\z#
146             or croak "\"$value\" is not a valid salt";
147 9         36 $salt = base64_to_int12($value);
148             } elsif($attr eq "salt_random") {
149 1 50       5 croak "salt specified redundantly"
150             if defined $salt;
151 1 50       5 croak "\"$value\" is not a valid salt size"
152             unless $value == 12;
153 1         9 $salt = rand_int(1 << $value);
154             } elsif($attr eq "hash") {
155 1 50 33     9 croak "hash specified redundantly"
156             if @hashes || defined($passphrase);
157 1 50       13 $value =~ m#\A(?:[\x00-\xff]{8})+\z#
158             or croak "not a valid bigcrypt hash";
159 1         20 push @hashes, $1 while $value =~ /(.{8})/sg;
160             } elsif($attr eq "hash_base64") {
161 9 50 33     53 croak "hash specified redundantly"
162             if @hashes || defined($passphrase);
163 9 50       50 $value =~ m#\A(?:[./0-9A-Za-z]{10}[.26AEIMQUYcgkosw])
164             +\z#x
165             or croak "\"$value\" is not a valid ".
166             "encoded hash";
167 9         39 while($value =~ /(.{11})/sg) {
168 16         27 my $b64 = $1;
169 16         382 push @hashes, base64_to_block($b64);
170             }
171             } elsif($attr eq "passphrase") {
172 4 50 33     28 croak "passphrase specified redundantly"
173             if @hashes || defined($passphrase);
174 4         14 $passphrase = $value;
175             } else {
176 0         0 croak "unrecognised attribute `$attr'";
177             }
178             }
179 14 50       38 croak "salt not specified" unless defined $salt;
180 14         20 my @sections;
181 14 100       41 if(defined $passphrase) {
    50          
182 4 50       17 my $nsegs = $passphrase eq "" ? 1 :
183             ((length($passphrase) + 7) >> 3);
184 4         17 for(my $i = 0; $i != $nsegs; $i++) {
185 8         56 push @sections,
186             Authen::Passphrase::DESCrypt
187             ->new(salt => $salt,
188             passphrase =>
189             substr($passphrase, $i << 3, 8));
190 8         35 $salt = base64_to_int12(
191             substr($sections[-1]->hash_base64, 0, 2));
192             }
193             } elsif(@hashes) {
194 10         15 foreach my $hash (@hashes) {
195 18         89 push @sections, Authen::Passphrase::DESCrypt
196             ->new(salt => $salt, hash => $hash);
197 18         71 $salt = base64_to_int12(
198             substr($sections[-1]->hash_base64, 0, 2));
199             }
200             } else {
201 0         0 croak "hash not specified";
202             }
203 14         74 return bless(\@sections, $class);
204             }
205              
206             =back
207              
208             =head1 METHODS
209              
210             =over
211              
212             =item $ppr->salt
213              
214             Returns the salt for the first section, as a Perl integer.
215              
216             =cut
217              
218 4     4 1 1769 sub salt { $_[0]->[0]->salt }
219              
220             =item $ppr->salt_base64_2
221              
222             Returns the salt for the first section, as a string of two base 64 digits.
223              
224             =cut
225              
226 11     11 1 2995 sub salt_base64_2 { $_[0]->[0]->salt_base64_2 }
227              
228             =item $ppr->hash
229              
230             Returns the hash value, as a string of bytes.
231              
232             =cut
233              
234 2     2 1 5 sub hash { join("", map { $_->hash } @{$_[0]}) }
  4         17  
  2         7  
235              
236             =item $ppr->hash_base64
237              
238             Returns the hash value, as a string of base 64 digits. This is the
239             concatenation of the base 64 encodings of the section hashes, rather
240             than a base64 encoding of the combined hash.
241              
242             =cut
243              
244 13     13 1 23 sub hash_base64 { join("", map { $_->hash_base64 } @{$_[0]}) }
  24         62  
  13         36  
245              
246             =item $ppr->sections
247              
248             Returns a reference to an array of L
249             passphrase recognisers for the sections of the passphrase.
250              
251             =cut
252              
253 11     11 1 19 sub sections { [ @{$_[0]} ] }
  11         72  
254              
255             =item $ppr->match(PASSPHRASE)
256              
257             This method is part of the standard L interface.
258              
259             =cut
260              
261             sub match {
262 82     82 1 21322 my Authen::Passphrase::BigCrypt $self = shift;
263 82         113 my($passphrase) = @_;
264 82 100       208 my $nsegs = $passphrase eq "" ? 1 : ((length($passphrase) + 7) >> 3);
265 82 100       247 return 0 unless $nsegs == @$self;
266 36         83 for(my $i = $nsegs; $i--; ) {
267 46 100       186 return 0 unless $self->[$i]
268             ->match(substr($passphrase, $i << 3, 8));
269             }
270 10         26 return 1;
271             }
272              
273             =back
274              
275             =head1 SEE ALSO
276              
277             L,
278             L
279              
280             =head1 AUTHOR
281              
282             Andrew Main (Zefram)
283              
284             =head1 COPYRIGHT
285              
286             Copyright (C) 2006, 2007, 2009, 2010, 2012
287             Andrew Main (Zefram)
288              
289             =head1 LICENSE
290              
291             This module is free software; you can redistribute it and/or modify it
292             under the same terms as Perl itself.
293              
294             =cut
295              
296             1;