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.001';
3 1     1   69535 use strict;
  1         12  
  1         30  
4 1     1   5 use warnings;
  1         1  
  1         42  
5              
6 1     1   490 use parent 'Crypt::Passphrase::Encoder';
  1         323  
  1         5  
7              
8 1     1   1552 use Carp 'croak';
  1         2  
  1         47  
9 1     1   797 use PBKDF2::Tiny qw/derive verify/;
  1         4958  
  1         76  
10 1     1   468 use MIME::Base64 qw/encode_base64 decode_base64/;
  1         644  
  1         623  
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 95 my ($class, %args) = @_;
22 1   50     6 my $type = $args{type} || 'sha256';
23 1 50       5 croak '' unless exists $param_for_type{$type};
24             return bless {
25             salt_size => $args{salt_size} || 16,
26 1   50     13 iterations => $args{iterations} || 10000,
      50        
27             type => $type,
28             }, $class;
29             }
30              
31             sub ab64_encode {
32 2     2 0 7 my $input = shift;
33 2         10 my $output = encode_base64($input);
34 2         8 $output =~ tr/+/./;
35 2         22 $output =~ s/=+$//;
36 2         14 return $output;
37             }
38              
39             sub ab64_decode {
40 7     7 0 14 my $input = shift;
41 7         20 $input =~ tr/./+/;
42 7         42 return decode_base64($input);
43             }
44              
45             sub hash_password {
46 1     1 1 10 my ($self, $password) = @_;
47 1         15 my $salt = $self->random_bytes($self->{salt_size});
48 1         668 my $hash = derive($param_for_type{ $self->{type} }, $password, $salt, $self->{iterations});
49 1         95851 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 250823 my ($self, $hash) = @_;
56 3 50       79 my ($type, $iterations, $salt64, $hash64) = $hash =~ $decode_regex or return 1;
57 3 100 66     39 return 1 if $type ne $self->{type} or $iterations < $self->{iterations};
58 1 50       5 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 715 my ($class, $password, $hash) = @_;
68              
69 3 50       47 my ($type, $iterations, $salt64, $hash64) = $hash =~ $decode_regex or return 0;
70 3 50       46 return 0 unless exists $param_for_type{$type};
71 3         12 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__