File Coverage

blib/lib/Crypt/PBE/PBKDF2.pm
Criterion Covered Total %
statement 95 120 79.1
branch 9 14 64.2
condition 13 31 41.9
subroutine 21 29 72.4
pod 12 13 92.3
total 150 207 72.4


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