File Coverage

blib/lib/Crypt/PBE/PBKDF2.pm
Criterion Covered Total %
statement 61 115 53.0
branch 6 14 42.8
condition 10 29 34.4
subroutine 13 27 48.1
pod 12 13 92.3
total 102 198 51.5


line stmt bran cond sub pod time code
1             package Crypt::PBE::PBKDF2;
2              
3 5     5   1648 use strict;
  5         10  
  5         148  
4 5     5   28 use warnings;
  5         8  
  5         123  
5              
6 5     5   24 use Carp;
  5         9  
  5         310  
7 5     5   2705 use POSIX;
  5         32787  
  5         30  
8              
9 5     5   15236 use MIME::Base64;
  5         653  
  5         343  
10              
11 5     5   1671 use Digest::SHA qw(hmac_sha1 hmac_sha224 hmac_sha256 hmac_sha384 hmac_sha512);
  5         9550  
  5         438  
12              
13 5     5   36 use Exporter qw(import);
  5         10  
  5         4517  
14              
15             our $VERSION = '0.101';
16              
17             our @EXPORT = qw(
18             pbkdf2
19             pbkdf2_base64
20             pbkdf2_hex
21             pbkdf2_ldap
22             );
23              
24             our @EXPORT_OK = qw(
25             pbkdf2_hmac_sha1
26             pbkdf2_hmac_sha1_base64
27             pbkdf2_hmac_sha1_hex
28             pbkdf2_hmac_sha1_ldap
29              
30             pbkdf2_hmac_sha224
31             pbkdf2_hmac_sha224_base64
32             pbkdf2_hmac_sha224_hex
33             pbkdf2_hmac_sha224_ldap
34              
35             pbkdf2_hmac_sha256
36             pbkdf2_hmac_sha256_base64
37             pbkdf2_hmac_sha256_hex
38             pbkdf2_hmac_sha256_ldap
39              
40             pbkdf2_hmac_sha384
41             pbkdf2_hmac_sha384_base64
42             pbkdf2_hmac_sha384_hex
43             pbkdf2_hmac_sha384_ldap
44              
45             pbkdf2_hmac_sha512
46             pbkdf2_hmac_sha512_base64
47             pbkdf2_hmac_sha512_hex
48             pbkdf2_hmac_sha512_ldap
49             );
50              
51             sub new {
52              
53 0     0 1 0 my ( $class, %params ) = @_;
54              
55 0   0     0 my $password = delete $params{password} || croak('Specify password');
56 0   0     0 my $salt = delete $params{salt} || croak('Specify salt');
57 0   0     0 my $count = delete $params{count} || 1_000;
58 0   0     0 my $prf = delete $params{prf} || 'hmac-sha1';
59 0         0 my $dk_len = delete $params{dk_len};
60              
61 0         0 $prf =~ s/-/_/;
62              
63 0         0 my $self = {
64             password => $password,
65             salt => $salt,
66             count => $count,
67             prf => $prf,
68             dk_len => $dk_len
69             };
70              
71 0         0 bless $self, $class;
72              
73 0         0 return $self;
74              
75             }
76              
77             sub prf {
78 0     0 1 0 my ($self) = @_;
79 0         0 return $self->{prf};
80             }
81              
82             sub count {
83 0     0 1 0 my ($self) = @_;
84 0         0 return $self->{count};
85             }
86              
87             sub derived_key_length {
88 0     0 1 0 my ($self) = @_;
89 0         0 return $self->{dk_len};
90             }
91              
92             sub validate {
93 0     0 1 0 my ( $self, $derived_key, $password ) = @_;
94 0         0 my $check = pbkdf2( $self->{prf}, $password, $self->{salt}, $self->{count}, $self->{dk_len} );
95 0         0 return ( $derived_key eq $check );
96             }
97              
98             sub derived_key {
99 0     0 1 0 my ($self) = @_;
100             return pbkdf2(
101             prf => $self->{prf},
102             password => $self->{password},
103             salt => $self->{salt},
104             count => $self->{count},
105             dk_len => $self->{dk_len}
106 0         0 );
107             }
108              
109             sub derived_key_base64 {
110 0     0 1 0 my ($self) = @_;
111             return pbkdf2_base64(
112             prf => $self->{prf},
113             password => $self->{password},
114             salt => $self->{salt},
115             count => $self->{count},
116             dk_len => $self->{dk_len}
117 0         0 );
118             }
119              
120             sub derived_key_hex {
121 0     0 1 0 my ($self) = @_;
122             return pbkdf2_hex(
123             prf => $self->{prf},
124             password => $self->{password},
125             salt => $self->{salt},
126             count => $self->{count},
127             dk_len => $self->{dk_len}
128 0         0 );
129             }
130              
131             # PBKDF2 (P, S, c, dkLen)
132             #
133             # Options: PRF underlying pseudorandom function (hLen
134             # denotes the length in octets of the
135             # pseudorandom function output)
136             #
137             # Input: P password, an octet string
138             # S salt, an octet string
139             # c iteration count, a positive integer
140             # dkLen intended length in octets of the derived
141             # key, a positive integer, at most
142             # (2^32 - 1) * hLen
143             #
144             # Output: DK derived key, a dkLen-octet string
145              
146             sub pbkdf2 {
147              
148 52     52 1 2923 my (%params) = @_;
149              
150 52   33     169 my $P = delete( $params{password} ) || croak 'Specify password';
151 52   33     145 my $S = delete( $params{salt} ) || croak 'Specify salt';
152 52   50     114 my $c = delete( $params{count} ) || 1_000;
153 52   100     138 my $dkLen = delete( $params{dk_len} ) || 0;
154 52   100     123 my $PRF = delete( $params{prf} ) || 'hmac-sha1';
155              
156 52         188 $PRF =~ s/-/_/;
157              
158 52         85 my $hLen = 20;
159 52   100     124 $dkLen ||= 0;
160 52   50     101 $c ||= 1_000;
161              
162 52         176 my %hmac_length = (
163             'hmac_sha1' => ( 160 / 8 ),
164             'hmac_sha224' => ( 224 / 8 ),
165             'hmac_sha256' => ( 256 / 8 ),
166             'hmac_sha384' => ( 384 / 8 ),
167             'hmac_sha512' => ( 512 / 8 ),
168             );
169              
170 52 50       108 if ( !defined( $hmac_length{$PRF} ) ) {
171 0         0 croak 'unknown PRF';
172             }
173              
174 52         86 $hLen = $hmac_length{$PRF};
175              
176 52 50       198 if ( $dkLen > ( 2**32 - 1 ) * $hLen ) {
177 0         0 croak 'derived key too long';
178             }
179              
180 52 100       237 my $l = ( $dkLen > 0 ) ? POSIX::ceil( $dkLen / $hLen ) : 1;
181              
182 52         109 my $r = $dkLen - ( $l - 1 ) * $hLen;
183 52         90 my $T = undef;
184              
185 52         120 for ( my $i = 1; $i <= $l; $i++ ) {
186 59         120 $T .= _pbkdf2_F( $PRF, $P, $S, $c, $i );
187             }
188              
189 52         92 my $DK = $T;
190              
191 52 100       104 if ( $dkLen > 0 ) {
192 37         336 return substr( $DK, 0, $dkLen );
193             }
194              
195 15         223 return $DK;
196              
197             }
198              
199             sub _pbkdf2_F {
200              
201 59     59   137 my ( $PRF, $P, $S, $c, $i ) = @_;
202              
203 5     5   42 no strict 'refs'; ## no critic
  5         9  
  5         733  
204              
205 59         207 my $U = &{$PRF}( $S . pack( 'N', $i ), $P );
  59         615  
206              
207 59         119 my $U_x = $U;
208              
209 59         126 for ( my $x = 1; $x < $c; $x++ ) {
210 69328         83111 $U_x = &{$PRF}( $U_x, $P );
  69328         433977  
211 69328         141419 $U ^= $U_x;
212             }
213              
214 59         280 return $U;
215              
216             }
217              
218             for my $hmac (qw/hmac_sha1 hmac_sha224 hmac_sha256 hmac_sha384 hmac_sha512/) {
219              
220             my $sub_name = 'pbkdf2_' . $hmac;
221              
222 5     5   59 no strict 'refs'; ## no critic
  5         19  
  5         2891  
223              
224             *{$sub_name} = sub {
225 0     0   0 my (%params) = @_;
226 0         0 $params{prf} = $hmac;
227 0         0 return pbkdf2(%params);
228             };
229              
230             *{ $sub_name . '_base64' } = sub {
231 0     0   0 my (%params) = @_;
232 0         0 $params{prf} = $hmac;
233 0         0 return encode_base64 pbkdf2(%params), '';
234             };
235              
236             *{ $sub_name . '_hex' } = sub {
237 0     0   0 my (%params) = @_;
238 0         0 $params{prf} = $hmac;
239 0         0 return join '', unpack '(H2)*', pbkdf2(%params);
240             };
241              
242             *{ $sub_name . '_ldap' } = sub {
243 0     0   0 my (%params) = @_;
244 0         0 $params{prf} = $hmac;
245 0         0 return pbkdf2_ldap(%params);
246             };
247             }
248              
249             sub pbkdf2_hex {
250 10     10 1 2849 return join '', unpack '(H2)*', pbkdf2(@_);
251             }
252              
253             sub pbkdf2_base64 {
254 5     5 1 17 return encode_base64 pbkdf2(@_), '';
255             }
256              
257             sub pbkdf2_ldap {
258              
259 0     0 1   my ( $hmac, $password, $salt, $count ) = @_;
260              
261 0 0 0       if ( $hmac eq 'sha224' || $hmac eq 'sha384' ) {
262 0           croak "$hmac not supported LDAP scheme";
263             }
264              
265 0           my $derived_key = pbkdf2( $hmac, $password, $salt, $count );
266              
267 0           my $scheme = 'PBKDF2';
268 0           my $b64_salt = b64_to_ab64( encode_base64( $salt, '' ) );
269 0           my $b64_derived_key = b64_to_ab64( encode_base64( $derived_key, '' ) );
270              
271 0 0         $scheme = 'PBKDF2-SHA256' if ( $hmac eq 'sha256' );
272 0 0         $scheme = 'PBKDF2-SHA512' if ( $hmac eq 'sha512' );
273              
274 0           return "{$scheme}$count\$$b64_salt\$$b64_derived_key";
275              
276             }
277              
278             sub b64_to_ab64 {
279              
280 0     0 0   my ($string) = @_;
281              
282 0           $string =~ s/\+/./g;
283 0           $string =~ s/=//g;
284 0           $string =~ s/\s//g;
285              
286 0           return $string;
287              
288             }
289              
290             1;
291             __END__