File Coverage

blib/lib/Crypt/Passphrase/System.pm
Criterion Covered Total %
statement 46 53 86.7
branch 9 16 56.2
condition 10 27 37.0
subroutine 11 11 100.0
pod 5 6 83.3
total 81 113 71.6


line stmt bran cond sub pod time code
1             package Crypt::Passphrase::System;
2             $Crypt::Passphrase::System::VERSION = '0.021';
3 2     2   213819 use strict;
  2         12  
  2         81  
4 2     2   9 use warnings;
  2         4  
  2         174  
5              
6 2     2   13 use parent 'Crypt::Passphrase::Encoder';
  2         23  
  2         15  
7              
8 2     2   104 use Carp 'croak';
  2         3  
  2         2398  
9              
10             my @possibilities = (
11             ['3' , '$3$' , 0, '$3$$8846f7eaee8fb117ad06bdd830b7586c'],
12             ['', '' , 2, 'abJnggxhB/yWI' , '%s%s'],
13             ['_', '_EQ0.' , 3, '_EQ0.jzhSVeUyoSqLupI', '%s%s'],
14             ['1' , '$1$' , 6, '$1$aaaaaa$FuYJ957Lgsw.eVsENqOok1' ],
15             ['md5', '$md5,rounds=5000$', 6, '$md5,rounds=5000$GUBv0xjJ$$PI9W.MLvhYh0hQMCkz1CH/', '%s%s$'],
16             ['sha1', '$sha1$40000$' , 6, '$sha1$40000$jtNX3nZ2$hBNaIXkt4wBI2o5rsi8KejSjNqIq'],
17             ['5' , '$5$rounds=535000$', 12, '$5$aaaaaa$9hHgJfCniK4.dU43ykArHVETrhKDDElbS.cioeCajw.' ],
18             ['6' , '$6$rounds=656000$', 12, '$6$aaaaaa$RgJSheuY/DBadaBm/5gQ.s3M9a/2n8gubwCE41kMiz1P4KcxORD6LxY2NUCuOQNZawfiD8tWWfRKg9v0CQjbH0'],
19             ['2x', '$2x$12$' , 16, '$2x$08$......................qrjEXaz4RUVmquy3IT5eLKXLB28ahI2' ],
20             ['2a', '$2a$12$' , 16, '$2a$08$......................qrjEXaz4RUVmquy3IT5eLKXLB28ahI2' ],
21             ['2y', '$2y$12$' , 16, '$2y$08$......................qrjEXaz4RUVmquy3IT5eLKXLB28ahI2' ],
22             ['2b', '$2b$12$' , 16, '$2b$08$......................qrjEXaz4RUVmquy3IT5eLKXLB28ahI2' ],
23             ['7' , '$7$DU..../....' , 16, '$7$AU..../....2Q9obwLhin8qvQl6sisAO/$E1HizYWxBmnIH4sdPkd1UOML9t62Gf.wvNTnt5XFzs8' ],
24             ['gy', '$gy$j8T$' , 16, '$gy$j9T$......................$5.2XCu2DhNfGzpifM7X8goEG2Wkio9cWIMtyWnX4tp2' ],
25             ['y' , '$y$j8T$' , 16, '$y$j9T$F5Jx5fExrKuPp53xLKQ..1$tnSYvahCwPBHKZUspmcxMfb0.WiB9W.zEaKlOBL35rC' ],
26             );
27              
28             my (%algorithm, $default);
29              
30             for my $row (@possibilities) {
31             my ($name, $setting, $salt_size, $value, $format) = @{$row};
32             my $hash = eval { crypt 'password', $value };
33             if (defined $hash and $hash eq $value) {
34             $algorithm{$name} = { settings => $setting, salt_size => $salt_size, format => $format };
35             $default = $name;
36             }
37             }
38              
39             sub new {
40 16     16 0 60 my ($class, %args) = @_;
41              
42 16         58 my ($settings, $salt_size, $format);
43 16 50       62 if (defined($settings = $args{settings})) {
44 0 0       0 if ($settings eq '') {
    0          
45 0         0 ($salt_size, $format) = (2, '%s%s');
46             } elsif ($settings eq '_') {
47 0         0 ($salt_size, $format) = (3, '%s%s');
48             } else {
49 0 0       0 my $type = $settings =~ /\A \$ ([^\$]+) \$ /x or croak "Invalid settings string '$settings'";
50 0   0     0 $salt_size = $args{salt_size} // $algorithm{$type}{salt_size} // 16;
      0        
51 0   0     0 $format = $args{format} // $algorithm{$type}{format} // '%s%s$';
      0        
52             }
53             }
54             else {
55 16   66     78 my $type = $args{type} // $default // croak 'No known crypt type found';
      33        
56 16   33     70 $settings = $algorithm{$type}{settings} // croak "No such crypt type '$type' known";
57 16   66     124 $salt_size = $args{salt_size} // $algorithm{$type}{salt_size};
58 16   66     120 $format = $args{format} // $algorithm{$type}{format} // '%s%s$';
      100        
59             }
60              
61 16         139 return bless {
62             settings => $settings,
63             salt_size => $salt_size,
64             format => $format,
65             }, $class;
66             }
67              
68             my $base64_digits = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
69             sub _encode_crypt64 {
70 16     16   29 my $bytes = shift;
71 16         34 my $nbytes = length $bytes;
72 16         61 my $npadbytes = 2 - ($nbytes + 2) % 3;
73 16         48 $bytes .= "\0" x $npadbytes;
74 16         36 my $digits = '';
75 16         62 for (my $i = 0; $i < $nbytes; $i += 3) {
76 64         186 my $v = ord(substr $bytes, $i, 1) |
77             (ord(substr $bytes, $i + 1, 1) << 8) |
78             (ord(substr $bytes, $i + 2, 1) << 16);
79 64         225 $digits .= substr($base64_digits, $v & 0x3f, 1) .
80             substr($base64_digits, ($v >> 6) & 0x3f, 1) .
81             substr($base64_digits, ($v >> 12) & 0x3f, 1) .
82             substr($base64_digits, ($v >> 18) & 0x3f, 1);
83             }
84 16         40 substr $digits, -$npadbytes, $npadbytes, '';
85 16         39 return $digits;
86             }
87              
88              
89             sub hash_password {
90 16     16 1 42 my ($self, $password) = @_;
91 16         86 my $salt = $self->random_bytes($self->{salt_size});
92 16         52 my $encoded_salt = _encode_crypt64($salt);
93 16 100       70 substr $encoded_salt, 2, 1, '' if $self->{salt_size} == 2; # descrypt
94              
95 16         128 my $settings = sprintf $self->{format}, $self->{settings}, $encoded_salt;
96 16         4035483 return crypt $password, $settings;
97             }
98              
99             my $descrypt = qr{ \A [./0-9A-Za-z]{13} \z }x;
100              
101             my @formats;
102             push @formats, $descrypt if $algorithm{''};
103             push @formats, qr{ \A _[./0-9A-Za-z]{19} \z }x if $algorithm{'_'};
104              
105             sub accepts_hash {
106 20     20 1 61 my ($self, $hash) = @_;
107 20 100       154 return 1 if $self->SUPER::accepts_hash($hash);
108 3         9 for my $format (@formats) {
109 4 100       37 return 1 if $hash =~ $format;
110             }
111 0         0 return 0;
112             }
113              
114             sub crypt_subtypes {
115 18     18 1 364 return sort keys %algorithm;
116             }
117              
118             sub needs_rehash {
119 16     16 1 64 my ($self, $hash) = @_;
120 16 100       145 return length $self->{settings} ? substr($hash, 0, length $self->{settings}) ne $self->{settings} : $hash !~ $descrypt;
121             }
122              
123             sub verify_password {
124 20     20 1 50 my ($class, $password, $hash) = @_;
125 20         3864536 my $new_hash = crypt $password, $hash;
126 20         268 return $class->secure_compare($hash, $new_hash);
127             }
128              
129             #ABSTRACT: An system crypt() encoder for Crypt::Passphrase
130              
131             1;
132              
133             __END__