File Coverage

blib/lib/Crypt/PKCS5.pm
Criterion Covered Total %
statement 50 51 98.0
branch 2 4 50.0
condition 8 21 38.1
subroutine 7 7 100.0
pod 0 2 0.0
total 67 85 78.8


line stmt bran cond sub pod time code
1             package Crypt::PKCS5;
2              
3 3     3   66131 use strict;
  3         7  
  3         110  
4 3     3   15 use warnings;
  3         7  
  3         73  
5              
6 3     3   13 use Carp;
  3         9  
  3         287  
7 3     3   2949 use POSIX;
  3         22698  
  3         20  
8             require Exporter;
9             our @ISA = qw(Exporter);
10             our $VERSION = '0.02';
11              
12             our @EXPORT_OK = qw(pbkdf1);
13              
14              
15             require Digest::MD5;
16             require Digest::HMAC_SHA1;
17              
18              
19             # DK = KDF(P, S)
20              
21             =head1 Key Derivation Functions
22              
23             =head2 PBKDF1
24              
25             PBKDF1($P, $S, $c, $dkLen, [$Hash])
26              
27             Input:
28             P password, an octet string
29             S salt, an eight-octet string
30             c iteration count, a positive integer
31             dkLen intended length in octets of derived key, a positive integer,
32             at most 16 for MD2 or MD5 and 20 for SHA-1
33             Options:
34             Hash underlyting Digest::* instance
35             Output:
36             DK derived key, a dkLen-octet string
37              
38             =cut
39              
40             sub pbkdf1 {
41 17     17 0 6568 my $P = shift; # password, an octet string
42 17         24 my $S = shift; # salt, an eight-octet string
43 17         20 my $c = shift; # iteration count, a positive integer
44 17         19 my $dk_len = shift; # intended length in octets of derived key
45 17   50     66 my $class = shift || 'Digest::MD5';
46              
47             # Step 1
48 17 50 33     140 if (($class eq 'Digest::MD2' && $dk_len > 16)
      33        
      33        
      33        
      33        
49             || ($class eq 'Digest::MD5' && $dk_len > 16)
50             || ($class eq 'Digest::SHA1' && $dk_len > 20))
51             {
52 0         0 croak 'derived key too long';
53             }
54              
55             # Step 2
56 17         84 my $hash = $class->new;
57 17         97 my $dk = $hash->add($P. $S)->digest();
58 17         51 for (my $i = 1; $i < $c; $i++) {
59 11107         42329 $dk = $hash->add($dk)->digest();
60             }
61             # Step 3
62 17         106 return substr $dk, 0, $dk_len;
63             }
64              
65              
66             =head2 PBKDF2
67              
68             PBKDF2($P, $S, $c, $dkLen, [$PRF])
69            
70             Input:
71             P password, an octet string
72             S salt, an octet string
73             c iteration count, a positive integer
74             dkLen intended length in octets of the derived key, a positive integer,
75             at most (2**32 -1) x hLen
76             Output:
77             DK derived key, a dkLen-octet string
78             Options:
79             PRF underlying pseudorandom function (hLen denotes the length in
80             octets of the pseudorandom function output)
81              
82             =cut
83              
84             sub _pbkdf2_F {
85 28     28   31 my $PRF = shift; # include P
86 28         30 my $S = shift;
87 28         23 my $c = shift;
88 28         31 my $i = shift;
89 28   50     83 my $h_len = shift || 20;
90              
91 28         65 $PRF->reset();
92 28         389 my $U = $PRF->add($S. pack 'N', $i)->digest();
93 28         584 my $U_last = $U;
94 28         101 for (my $j = 1; $j < $c; $j++) {
95 22713         48397 $PRF->reset();
96 22713         241513 $U_last = $PRF->add($U_last)->digest();
97 22713         426908 $U ^= $U_last;
98             }
99 28         114 return $U;
100             }
101              
102              
103             sub pbkdf2 {
104 15     15 0 7099 my $P = shift; # password, an octet string
105 15         22 my $S = shift; # salt, an octet string
106 15         19 my $c = shift; # iteration count, a positive integer
107 15         16 my $dk_len = shift; # intended length in octets of derived key
108 15   50     60 my $class = shift || 'Digest::HMAC_SHA1';
109              
110 15         16 my $h_len = 20;
111 15 50       31 if ($class eq 'Digest::HMAC_SHA1') {
112 15         18 $h_len = 20;
113             }
114 15         53 my $prf = $class->new($P);
115              
116             # check $dk_len
117              
118 15         539 my $l = POSIX::ceil($dk_len / $h_len);
119              
120 15         18 my $T = '';
121 15         36 for (my $i = 1; $i <= $l; $i++) {
122 28         48 $T .= _pbkdf2_F($prf, $S, $c, $i);
123             }
124              
125             # Step 4
126 15         85 return substr $T, 0, $dk_len;
127             }
128              
129              
130             1;
131             __END__