File Coverage

blib/lib/Authen/Passphrase/BigCrypt.pm
Criterion Covered Total %
statement 80 82 97.5
branch 33 48 68.7
condition 5 15 33.3
subroutine 16 16 100.0
pod 7 7 100.0
total 141 168 83.9


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 2     2   86243 { use 5.006; }
  2         7  
69 2     2   64 use warnings;
  2         5  
  2         119  
70 2     2   10 use strict;
  2         3  
  2         70  
71              
72 2     2   384 use Authen::Passphrase 0.003;
  2         35  
  2         68  
73 2     2   1037 use Authen::Passphrase::DESCrypt;
  2         6  
  2         120  
74 2     2   12 use Carp qw(croak);
  2         3  
  2         117  
75 2     2   11 use Crypt::UnixCrypt_XS 0.08 qw(base64_to_block base64_to_int12);
  2         30  
  2         82  
76 2     2   8 use Crypt::SysRandom 'random_bytes';
  2         6  
  2         88  
77              
78             our $VERSION = "0.009";
79              
80 2     2   9 use parent "Authen::Passphrase";
  2         3  
  2         7  
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.
106              
107             =item B
108              
109             The hash, as a string of bytes.
110              
111             =item B
112              
113             The hash, as a string of base 64 digits.
114              
115             =item B
116              
117             A passphrase that will be accepted.
118              
119             =back
120              
121             The salt for the first section must be given, and either the hash or
122             the passphrase.
123              
124             =cut
125              
126             sub new {
127 14     14 1 154151 my $class = shift;
128 14         30 my $salt;
129             my @hashes;
130 14         0 my $passphrase;
131 14         36 while(@_) {
132 28         53 my $attr = shift;
133 28         32 my $value = shift;
134 28 100       81 if($attr eq "salt") {
    100          
    100          
    100          
    100          
    50          
135 4 50       17 croak "salt specified redundantly"
136             if defined $salt;
137 4 50 33     23 croak "\"$value\" is not a valid salt"
      33        
138             unless $value == int($value) &&
139             $value >= 0 && $value < 4096;
140 4         23 $salt = $value;
141             } elsif($attr eq "salt_base64") {
142 9 50       19 croak "salt specified redundantly"
143             if defined $salt;
144 9 50       27 $value =~ m#\A[./0-9A-Za-z]{2}\z#
145             or croak "\"$value\" is not a valid salt";
146 9         29 $salt = base64_to_int12($value);
147             } elsif($attr eq "salt_random") {
148 1 50       4 croak "salt specified redundantly"
149             if defined $salt;
150 1 50       2 croak "\"$value\" is not a valid salt size"
151             unless $value == 12;
152 1         12 $salt = unpack("S", random_bytes(2)) % 4096;
153             } elsif($attr eq "hash") {
154 1 50 33     5 croak "hash specified redundantly"
155             if @hashes || defined($passphrase);
156 1 50       7 $value =~ m#\A(?:[\x00-\xff]{8})+\z#
157             or croak "not a valid bigcrypt hash";
158 1         9 push @hashes, $1 while $value =~ /(.{8})/sg;
159             } elsif($attr eq "hash_base64") {
160 9 50 33     48 croak "hash specified redundantly"
161             if @hashes || defined($passphrase);
162 9 50       35 $value =~ m#\A(?:[./0-9A-Za-z]{10}[.26AEIMQUYcgkosw])
163             +\z#x
164             or croak "\"$value\" is not a valid ".
165             "encoded hash";
166 9         26 while($value =~ /(.{11})/sg) {
167 16         22 my $b64 = $1;
168 16         54 push @hashes, base64_to_block($b64);
169             }
170             } elsif($attr eq "passphrase") {
171 4 50 33     14 croak "passphrase specified redundantly"
172             if @hashes || defined($passphrase);
173 4         9 $passphrase = $value;
174             } else {
175 0         0 croak "unrecognised attribute `$attr'";
176             }
177             }
178 14 50       18 croak "salt not specified" unless defined $salt;
179 14         16 my @sections;
180 14 100       28 if(defined $passphrase) {
    50          
181 4 50       7 my $nsegs = $passphrase eq "" ? 1 :
182             ((length($passphrase) + 7) >> 3);
183 4         9 for(my $i = 0; $i != $nsegs; $i++) {
184 8         26 push @sections,
185             Authen::Passphrase::DESCrypt
186             ->new(salt => $salt,
187             passphrase =>
188             substr($passphrase, $i << 3, 8));
189 8         18 $salt = base64_to_int12(
190             substr($sections[-1]->hash_base64, 0, 2));
191             }
192             } elsif(@hashes) {
193 10         15 foreach my $hash (@hashes) {
194 18         40 push @sections, Authen::Passphrase::DESCrypt
195             ->new(salt => $salt, hash => $hash);
196 18         38 $salt = base64_to_int12(
197             substr($sections[-1]->hash_base64, 0, 2));
198             }
199             } else {
200 0         0 croak "hash not specified";
201             }
202 14         52 return bless(\@sections, $class);
203             }
204              
205             =back
206              
207             =head1 METHODS
208              
209             =over
210              
211             =item $ppr->salt
212              
213             Returns the salt for the first section, as a Perl integer.
214              
215             =cut
216              
217 4     4 1 1088 sub salt { $_[0]->[0]->salt }
218              
219             =item $ppr->salt_base64_2
220              
221             Returns the salt for the first section, as a string of two base 64 digits.
222              
223             =cut
224              
225 11     11 1 2257 sub salt_base64_2 { $_[0]->[0]->salt_base64_2 }
226              
227             =item $ppr->hash
228              
229             Returns the hash value, as a string of bytes.
230              
231             =cut
232              
233 2     2 1 4 sub hash { join("", map { $_->hash } @{$_[0]}) }
  4         51  
  2         5  
234              
235             =item $ppr->hash_base64
236              
237             Returns the hash value, as a string of base 64 digits. This is the
238             concatenation of the base 64 encodings of the section hashes, rather
239             than a base64 encoding of the combined hash.
240              
241             =cut
242              
243 13     13 1 23 sub hash_base64 { join("", map { $_->hash_base64 } @{$_[0]}) }
  24         45  
  13         28  
244              
245             =item $ppr->sections
246              
247             Returns a reference to an array of L
248             passphrase recognisers for the sections of the passphrase.
249              
250             =cut
251              
252 11     11 1 21 sub sections { [ @{$_[0]} ] }
  11         47  
253              
254             =item $ppr->match(PASSPHRASE)
255              
256             This method is part of the standard L interface.
257              
258             =cut
259              
260             sub match {
261 82     82 1 15396 my Authen::Passphrase::BigCrypt $self = shift;
262 82         129 my($passphrase) = @_;
263 82 100       144 my $nsegs = $passphrase eq "" ? 1 : ((length($passphrase) + 7) >> 3);
264 82 100       179 return 0 unless $nsegs == @$self;
265 36         63 for(my $i = $nsegs; $i--; ) {
266 46 100       130 return 0 unless $self->[$i]
267             ->match(substr($passphrase, $i << 3, 8));
268             }
269 10         19 return 1;
270             }
271              
272             =back
273              
274             =head1 SEE ALSO
275              
276             L,
277             L
278              
279             =head1 AUTHOR
280              
281             Andrew Main (Zefram)
282              
283             =head1 COPYRIGHT
284              
285             Copyright (C) 2006, 2007, 2009, 2010, 2012
286             Andrew Main (Zefram)
287              
288             =head1 LICENSE
289              
290             This module is free software; you can redistribute it and/or modify it
291             under the same terms as Perl itself.
292              
293             =cut
294              
295             1;