File Coverage

blib/lib/Crypt/Passphrase/PBKDF2.pm
Criterion Covered Total %
statement 41 41 100.0
branch 7 12 58.3
condition 5 9 55.5
subroutine 13 13 100.0
pod 4 7 57.1
total 70 82 85.3


line stmt bran cond sub pod time code
1             package Crypt::Passphrase::PBKDF2;
2             $Crypt::Passphrase::PBKDF2::VERSION = '0.004';
3 2     2   525639 use strict;
  2         4  
  2         83  
4 2     2   14 use warnings;
  2         5  
  2         193  
5              
6 2     2   819 use Crypt::Passphrase 0.010 -encoder;
  2         6404  
  2         43  
7              
8 2     2   8605 use Carp 'croak';
  2         10  
  2         110  
9 2     2   937 use PBKDF2::Tiny qw/derive verify/;
  2         11497  
  2         159  
10 2     2   1061 use MIME::Base64 qw/encode_base64 decode_base64/;
  2         1509  
  2         1755  
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 0 13 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     13 iterations => $args{iterations} || 100_000,
      50        
27             type => $type,
28             }, $class;
29             }
30              
31             sub ab64_encode {
32 2     2 0 5 my $input = shift;
33 2         19 return encode_base64($input, '') =~ tr/+=/./dr;
34             }
35              
36             sub ab64_decode {
37 8     8 0 11 my $input = shift;
38 8         39 return decode_base64($input =~ tr/./+/r);
39             }
40              
41             sub hash_password {
42 1     1 1 84 my ($self, $password) = @_;
43 1         10 my $salt = $self->random_bytes($self->{salt_size});
44 1         13 my $hash = derive($param_for_type{ $self->{type} }, $password, $salt, $self->{iterations});
45 1         59016 return join '$', "\$pbkdf2-$self->{type}", $self->{iterations}, ab64_encode($salt), ab64_encode($hash);
46             }
47              
48             my $decode_regex = qr/ \A \$ pbkdf2- (\w+) \$ (\d+) \$ ([^\$]+) \$ ([^\$]*) \z /x;
49              
50             sub needs_rehash {
51 3     3 1 203963 my ($self, $hash) = @_;
52 3 50       46 my ($type, $iterations, $salt64, $hash64) = $hash =~ $decode_regex or return 1;
53 3 100 66     23 return 1 if $type ne $self->{type} or $iterations != $self->{iterations};
54 2 50       13 return 1 if length ab64_decode($salt64) != $self->{salt_size};
55 2         9 return;
56             }
57              
58             sub crypt_subtypes {
59 1     1 1 36 return map { "pbkdf2-$_" } keys %param_for_type;
  5         11  
60             }
61              
62             sub verify_password {
63 3     3 1 304 my ($class, $password, $hash) = @_;
64              
65 3 50       25 my ($type, $iterations, $salt64, $hash64) = $hash =~ $decode_regex or return 0;
66 3 50       10 return 0 unless exists $param_for_type{$type};
67 3         8 return verify(ab64_decode($hash64), $param_for_type{$type}, $password, ab64_decode($salt64), $iterations);
68             }
69              
70             1;
71              
72             # ABSTRACT: A PBKDF2 encoder for Crypt::Passphrase
73              
74             __END__