File Coverage

blib/lib/Authen/Passphrase/Crypt16.pm
Criterion Covered Total %
statement 72 74 97.3
branch 28 42 66.6
condition 7 18 38.8
subroutine 17 17 100.0
pod 8 8 100.0
total 132 159 83.0


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