File Coverage

blib/lib/PBKDF2/Tiny.pm
Criterion Covered Total %
statement 51 51 100.0
branch 8 14 57.1
condition 3 8 37.5
subroutine 11 11 100.0
pod 6 6 100.0
total 79 90 87.7


line stmt bran cond sub pod time code
1 1     1   14618 use strict;
  1         2  
  1         33  
2 1     1   4 use warnings;
  1         1  
  1         47  
3              
4             package PBKDF2::Tiny;
5             # ABSTRACT: Minimalist PBKDF2 (RFC 2898) with HMAC-SHA1 or HMAC-SHA2
6              
7             our $VERSION = '0.003';
8              
9 1     1   4 use Carp ();
  1         1  
  1         17  
10 1     1   3 use Exporter 5.57 qw/import/;
  1         13  
  1         129  
11              
12             our @EXPORT_OK = qw/derive derive_hex verify verify_hex hmac digest_fcn/;
13              
14             my ( $BACKEND, $LOAD_ERR );
15             for my $mod (qw/Digest::SHA Digest::SHA::PurePerl/) {
16             $BACKEND = $mod, last if eval "require $mod; 1";
17             $LOAD_ERR ||= $@;
18             }
19             die $LOAD_ERR if !$BACKEND;
20              
21             #--------------------------------------------------------------------------#
22             # constants and lookup tables
23             #--------------------------------------------------------------------------#
24              
25             # function coderef placeholder, block size in bytes, digest size in bytes
26             my %DIGEST_TYPES = (
27             'SHA-1' => [ undef, 64, 20 ],
28             'SHA-224' => [ undef, 64, 28 ],
29             'SHA-256' => [ undef, 64, 32 ],
30             'SHA-384' => [ undef, 128, 48 ],
31             'SHA-512' => [ undef, 128, 64 ],
32             );
33              
34             for my $type ( keys %DIGEST_TYPES ) {
35 1     1   4 no strict 'refs';
  1         2  
  1         504  
36             ( my $name = lc $type ) =~ s{-}{};
37             $DIGEST_TYPES{$type}[0] = \&{"$BACKEND\::$name"};
38             }
39              
40             my %INT = map { $_ => pack( "N", $_ ) } 1 .. 16;
41              
42             #--------------------------------------------------------------------------#
43             # public functions
44             #--------------------------------------------------------------------------#
45              
46             #pod =func derive
47             #pod
48             #pod $dk = derive( $type, $password, $salt, $iterations, $dk_length )
49             #pod
50             #pod The C function outputs a binary string with the derived key.
51             #pod The first argument indicates the digest function to use. It must be one
52             #pod of: SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512.
53             #pod
54             #pod If a password or salt are not provided, they default to the empty string, so
55             #pod don't do that! L
56             #pod recommends|https://tools.ietf.org/html/rfc2898#section-4.1> a random salt of at
57             #pod least 8 octets. If you need a cryptographically strong salt, consider
58             #pod L.
59             #pod
60             #pod The number of iterations defaults to 1000 if not provided. If the derived
61             #pod key length is not provided, it defaults to the output size of the digest
62             #pod function.
63             #pod
64             #pod =cut
65              
66             sub derive {
67 56     56 1 5923 my ( $type, $passwd, $salt, $iterations, $dk_length ) = @_;
68              
69 56         101 my ( $digester, $block_size, $digest_length ) = digest_fcn($type);
70              
71 56 50       106 $passwd = '' unless defined $passwd;
72 56 50       120 $salt = '' unless defined $salt;
73 56   50     107 $iterations ||= 1000;
74 56   33     93 $dk_length ||= $digest_length;
75              
76 56 50       120 my $key = ( length($passwd) > $block_size ) ? $digester->($passwd) : $passwd;
77 56         102 my $passes = int( $dk_length / $digest_length );
78 56 100       114 $passes++ if $dk_length % $digest_length; # need part of an extra pass
79              
80 56         80 my $dk = "";
81 56         94 for my $i ( 1 .. $passes ) {
82 60   33     173 $INT{$i} ||= pack( "N", $i );
83 60         147 my $digest = my $result =
84             "" . hmac( $salt . $INT{$i}, $key, $digester, $block_size );
85 60         120 for my $iter ( 2 .. $iterations ) {
86 81516         86433 $digest = hmac( $digest, $key, $digester, $block_size );
87 81516         84127 $result ^= $digest;
88             }
89 60         116 $dk .= $result;
90             }
91              
92 56         217 return substr( $dk, 0, $dk_length );
93             }
94              
95             #pod =func derive_hex
96             #pod
97             #pod Works just like L but outputs a hex string.
98             #pod
99             #pod =cut
100              
101 14     14 1 8218 sub derive_hex { unpack( "H*", &derive ) }
102              
103             #pod =func verify
104             #pod
105             #pod $bool = verify( $dk, $type, $password, $salt, $iterations, $dk_length );
106             #pod
107             #pod The C function checks that a given derived key (in binary form) matches
108             #pod the password and other parameters provided using a constant-time comparison
109             #pod function.
110             #pod
111             #pod The first parameter is the derived key to check. The remaining parameters
112             #pod are the same as for L.
113             #pod
114             #pod =cut
115              
116             sub verify {
117 28     28 1 7282 my ( $dk1, $type, $password, $salt, $iterations, $dk_length ) = @_;
118              
119 28         51 my $dk2 = derive( $type, $password, $salt, $iterations, $dk_length );
120              
121             # shortcut if input dk is the wrong length entirely; this is not
122             # constant time, but this doesn't really give much away as
123             # the keys are of different types anyway
124              
125 28 50       94 return unless length($dk1) == length($dk2);
126              
127             # if lengths match, do constant time comparison to avoid timing attacks
128 28         30 my $match = 1;
129 28         51 for my $offset ( 0 .. $dk_length ) {
130 958 50       1261 $match &= ( substr( $dk1, $offset, 1 ) eq substr( $dk2, $offset, 1 ) ) ? 1 : 0;
131             }
132              
133 28         163 return $match;
134             }
135              
136             #pod =func verify_hex
137             #pod
138             #pod Works just like L but the derived key must be a hex string (without a
139             #pod leading "0x").
140             #pod
141             #pod =cut
142              
143             sub verify_hex {
144 14     14 1 66 my $dk = pack( "H*", shift );
145 14         27 return verify( $dk, @_ );
146             }
147              
148             #pod =func digest_fcn
149             #pod
150             #pod ($fcn, $block_size, $digest_length) = digest_fcn('SHA-1');
151             #pod $digest = $fcn->($data);
152             #pod
153             #pod This function is used internally by PBKDF2::Tiny, but made available in case
154             #pod it's useful to someone.
155             #pod
156             #pod Given one of the valid digest types, it returns a function reference that
157             #pod digests a string of data. It also returns block size and digest length for that
158             #pod digest type.
159             #pod
160             #pod =cut
161              
162             sub digest_fcn {
163 56     56 1 72 my ($type) = @_;
164              
165 56 50       159 Carp::croak("Digest function '$type' not supported")
166             unless exists $DIGEST_TYPES{$type};
167              
168 56         49 return @{ $DIGEST_TYPES{$type} };
  56         147  
169             }
170              
171             #pod =func hmac
172             #pod
173             #pod $key = $digest_fcn->($key) if length($key) > $block_sizes;
174             #pod $hmac = hmac( $data, $key, $digest_fcn, $block_size );
175             #pod
176             #pod This function is used internally by PBKDF2::Tiny, but made available in case
177             #pod it's useful to someone.
178             #pod
179             #pod The first two arguments are the data and key inputs to the HMAC function.
180             #pod B: if the key is longer than the digest block size, it must be
181             #pod preprocessed using the digesting function.
182             #pod
183             #pod The third and fourth arguments must be a digesting code reference (from
184             #pod L) and block size.
185             #pod
186             #pod =cut
187              
188             # hmac function adapted from Digest::HMAC by Graham Barr and Gisle Aas.
189             # Compared to that implementation, this *requires* a preprocessed
190             # key and block size, which makes iterative hmac slightly more efficient.
191             sub hmac {
192 81576     81576 1 75719 my ( $data, $key, $digest_func, $block_size ) = @_;
193              
194 81576         82718 my $k_ipad = $key ^ ( chr(0x36) x $block_size );
195 81576         71276 my $k_opad = $key ^ ( chr(0x5c) x $block_size );
196              
197 81576         453799 &$digest_func( $k_opad, &$digest_func( $k_ipad, $data ) );
198             }
199              
200             1;
201              
202              
203             # vim: ts=4 sts=4 sw=4 et:
204              
205             __END__