File Coverage

blib/lib/Crypt/Password/StretchedHash.pm
Criterion Covered Total %
statement 67 67 100.0
branch 18 22 81.8
condition 16 18 88.8
subroutine 11 11 100.0
pod 4 4 100.0
total 116 122 95.0


line stmt bran cond sub pod time code
1             package Crypt::Password::StretchedHash;
2 4     4   78594 use 5.008005;
  4         16  
  4         206  
3 4     4   24 use strict;
  4         12  
  4         235  
4 4     4   36 use warnings;
  4         16  
  4         16036  
5              
6             our $VERSION = "0.05";
7              
8 4         215 use Exporter qw(
9             import
10 4     4   37 );
  4         7  
11 4         370 use Carp qw(
12             croak
13 4     4   28 );
  4         9  
14 4         307 use MIME::Base64 qw(
15             encode_base64
16             decode_base64
17 4     4   4150 );
  4         3516  
18 4         4761 use Params::Validate qw(
19             SCALAR
20 4     4   4489 );
  4         52662  
21              
22             our @EXPORT_OK = qw(
23             crypt
24             verify
25             crypt_with_hashinfo
26             verify_with_hashinfo
27             );
28              
29             sub crypt {
30              
31 37     37 1 26204 my %params = Params::Validate::validate(@_, {
32             password => { type => SCALAR },
33             hash => 1,
34             salt => { type => SCALAR },
35             stretch_count => { type => SCALAR, regex => qr/\A[0-9]+\z/,},
36             format => { type => SCALAR, optional => 1 },
37             });
38              
39 29         893 my $salt = $params{salt};
40 29 100 100     1577 croak "\$params{hash} must be Digest::SHAx Object"
41             unless ( $params{hash}->isa("Digest::SHA") ||
42             $params{hash}->isa("Digest::SHA3"));
43              
44 28         58 my $hash = $params{hash};
45              
46 28 100       204 croak "\$params{stretch_count} must be more than 1"
47             unless ($params{stretch_count} > 0);
48              
49 27         76 my $pwhash = q{};
50 27         109 for (1..$params{stretch_count}) {
51 131000         311245 $hash->add( $pwhash, $params{password}, $salt );
52 131000         629037 $pwhash = $hash->digest;
53             }
54              
55 27 100 100     434 if ( exists $params{format} && $params{format} eq q{hex} ){
    100 66        
56 4         43 $pwhash = unpack("H*", $pwhash);
57             }elsif( exists $params{format} && $params{format} eq q{base64} ){
58 14         104 $pwhash = encode_base64 $pwhash;
59 14         116 $pwhash =~ s/\n//;
60 14         127 chomp($pwhash);
61             }
62              
63 27         277 return $pwhash;
64             }
65              
66             sub verify {
67              
68 9     9 1 29880 my %params = Params::Validate::validate(@_, {
69             password => { type => SCALAR },
70             password_hash => { type => SCALAR },
71             hash => 1,
72             salt => { type => SCALAR },
73             stretch_count => { type => SCALAR, regex => qr/\A[0-9]+\z/,},
74             format => { type => SCALAR, optional => 1 },
75             });
76              
77 9         599 my $pwhash = $params{password_hash};
78 9         32 delete $params{password_hash};
79 9         92 my $calculated_pwhash = Crypt::Password::StretchedHash::crypt( %params );
80 9         132 return ( $calculated_pwhash eq $pwhash );
81             }
82              
83             sub crypt_with_hashinfo {
84              
85 7     7 1 8081 my %params = Params::Validate::validate(@_, {
86             password => { type => SCALAR },
87             hash_info => 1,
88             });
89              
90             # validate hashinfo object
91 7         45 my $hash_info = $params{hash_info};
92 7 50       53 croak "\$params{hash_info} must be Crypt::Password::StretchedHash::HashInfo Object"
93             unless ( $hash_info->isa("Crypt::Password::StretchedHash::HashInfo") );
94              
95 7         29 my $salt = $hash_info->salt;
96              
97 7         58 my $pwhash = Crypt::Password::StretchedHash::crypt(
98             password => $params{password},
99             hash => $hash_info->hash,
100             salt => $salt,
101             stretch_count => $hash_info->stretch_count,
102             format => $hash_info->format,
103             );
104              
105 7 100       69 if ( $hash_info->format eq q{hex} ){
    50          
106 1         44 $salt = unpack("H*", $salt);
107             }elsif( $hash_info->format eq q{base64} ){
108 6         102 $salt = encode_base64 $salt;
109 6         15 chomp($salt);
110             }
111            
112 7         41 return $hash_info->delimiter .
113             $hash_info->identifier.
114             $hash_info->delimiter.
115             $salt.
116             $hash_info->delimiter.
117             $pwhash;
118              
119             }
120              
121             sub verify_with_hashinfo {
122              
123 11     11 1 6050 my %params = Params::Validate::validate(@_, {
124             password => { type => SCALAR },
125             password_hash => { type => SCALAR },
126             hash_info => 1,
127             });
128              
129             # validate hashinfo object
130 11         75 my $hash_info = $params{hash_info};
131 11 50       62 croak "\$params{hash_info} must be Crypt::Password::StretchedHash::HashInfo Object"
132             unless ( $hash_info->isa("Crypt::Password::StretchedHash::HashInfo") );
133              
134             # split password hash
135 11         36 my $delimiter = quotemeta($hash_info->delimiter);
136 11         74 my $identifier = $hash_info->identifier;
137 11         49 my ( $pwhash, $salt );
138              
139 11         124 my @split_password_hash = split( /$delimiter/, $params{password_hash} );
140 11 100 100     163 if ($params{password_hash} =~ /\A$delimiter/ &&
      66        
      100        
141             scalar @split_password_hash == 4 &&
142             $split_password_hash[0] eq q{} &&
143             $identifier eq $split_password_hash[1] ){
144 6         11 $salt = $split_password_hash[2];
145 6         11 $pwhash = $split_password_hash[3];
146             } else {
147 5         24 return;
148             }
149              
150             # obtain law_salt string
151 6 100       25 if ( $hash_info->format eq q{hex} ){
    50          
152 1         14 $salt = pack("H*", $salt);
153             }elsif( $hash_info->format eq q{base64} ){
154 5         87 $salt = decode_base64 $salt;
155             }
156              
157             # generate hashed password
158 6         29 my $expected_pwhash = Crypt::Password::StretchedHash::crypt(
159             password => $params{password},
160             hash => $hash_info->hash,
161             salt => $salt,
162             stretch_count => $hash_info->stretch_count,
163             format => $hash_info->format,
164             );
165 6         68 return ( $expected_pwhash eq $pwhash );
166              
167             }
168              
169             1;
170             __END__