File Coverage

blib/lib/Crypt/Passphrase/PBKDF2.pm
Criterion Covered Total %
statement 43 45 95.5
branch 7 12 58.3
condition 5 9 55.5
subroutine 12 13 92.3
pod 5 7 71.4
total 72 86 83.7


line stmt bran cond sub pod time code
1             package Crypt::Passphrase::PBKDF2;
2             $Crypt::Passphrase::PBKDF2::VERSION = '0.002';
3 1     1   67247 use strict;
  1         10  
  1         28  
4 1     1   9 use warnings;
  1         2  
  1         25  
5              
6 1     1   426 use parent 'Crypt::Passphrase::Encoder';
  1         331  
  1         5  
7              
8 1     1   9482 use Carp 'croak';
  1         2  
  1         73  
9 1     1   457 use PBKDF2::Tiny qw/derive verify/;
  1         5336  
  1         64  
10 1     1   498 use MIME::Base64 qw/encode_base64 decode_base64/;
  1         596  
  1         791  
11              
12             my %param_for_type =(
13             sha1 => 'SHA-1',
14             sha224 => 'SHA-224',
15             sha256 => 'SHA-256',
16             sha384 => 'SHA-384',
17             sha512 => 'SHA-512',
18             );
19              
20             sub new {
21 1     1 1 96 my ($class, %args) = @_;
22 1   50     5 my $type = $args{type} || 'sha256';
23 1 50       4 croak "Hash type $type not supported" unless exists $param_for_type{$type};
24             return bless {
25             salt_size => $args{salt_size} || 16,
26 1   50     12 iterations => $args{iterations} || 100_000,
      50        
27             type => $type,
28             }, $class;
29             }
30              
31             sub ab64_encode {
32 2     2 0 6 my $input = shift;
33 2         9 my $output = encode_base64($input, '');
34 2         7 $output =~ tr/+/./;
35 2         18 $output =~ s/=+$//;
36 2         28 return $output;
37             }
38              
39             sub ab64_decode {
40 7     7 0 13 my $input = shift;
41 7         16 $input =~ tr/./+/;
42 7         40 return decode_base64($input);
43             }
44              
45             sub hash_password {
46 1     1 1 9 my ($self, $password) = @_;
47 1         17 my $salt = $self->random_bytes($self->{salt_size});
48 1         10210 my $hash = derive($param_for_type{ $self->{type} }, $password, $salt, $self->{iterations});
49 1         97691 return join '$', "\$pbkdf2-$self->{type}", $self->{iterations}, ab64_encode($salt), ab64_encode($hash);
50             }
51              
52             my $decode_regex = qr/ \A \$ pbkdf2- (\w+) \$ (\d+) \$ ([^\$]+) \$ ([^\$]*) \z /x;
53              
54             sub needs_rehash {
55 3     3 1 255287 my ($self, $hash) = @_;
56 3 50       49 my ($type, $iterations, $salt64, $hash64) = $hash =~ $decode_regex or return 1;
57 3 100 66     36 return 1 if $type ne $self->{type} or $iterations != $self->{iterations};
58 1 50       6 return 1 if length ab64_decode($salt64) != $self->{salt_size};
59 1         6 return;
60             }
61              
62             sub crypt_types {
63 0     0 1 0 return map { "pbkdf2-$_" } keys %param_for_type;
  0         0  
64             }
65              
66             sub verify_password {
67 3     3 1 17 my ($class, $password, $hash) = @_;
68              
69 3 50       47 my ($type, $iterations, $salt64, $hash64) = $hash =~ $decode_regex or return 0;
70 3 50       21 return 0 unless exists $param_for_type{$type};
71 3         11 return verify(ab64_decode($hash64), $param_for_type{$type}, $password, ab64_decode($salt64), $iterations);
72             }
73              
74             1;
75              
76             # ABSTRACT: A PBKDF2 encoder for Crypt::Passphrase
77              
78             __END__