File Coverage

blib/lib/Crypt/Passphrase/Pepper/Base.pm
Criterion Covered Total %
statement 51 53 96.2
branch 12 18 66.6
condition 2 6 33.3
subroutine 12 12 100.0
pod 6 6 100.0
total 83 95 87.3


line stmt bran cond sub pod time code
1             package Crypt::Passphrase::Pepper::Base;
2             $Crypt::Passphrase::Pepper::Base::VERSION = '0.016';
3 1     1   482 use strict;
  1         2  
  1         29  
4 1     1   4 use warnings;
  1         2  
  1         34  
5              
6 1     1   6 use Carp 'croak';
  1         2  
  1         61  
7 1     1   6 use Crypt::Passphrase -encoder, -integration;
  1         2  
  1         5  
8 1     1   451 use MIME::Base64 'encode_base64';
  1         629  
  1         962  
9              
10             sub new {
11 1     1 1 4 my ($class, %args) = @_;
12 1   33     17 my $inner = delete $args{inner} // croak('No inner encoder given to pepper');
13 1         7 my $encoder = Crypt::Passphrase::_load_encoder($inner);
14              
15 1 50       10 croak('No peppers given') if not defined $args{active};
16 1 50       9 croak("Invalid pepper name '$args{active}'") if $args{active} =~ /\W/;
17              
18 1         14 my $self = bless {
19             %args,
20             inner => $encoder,
21             }, $class;
22              
23 1         10 return $self;
24             }
25              
26             sub _to_inner {
27 4     4   5 my $hash = shift;
28 4 100       33 if ($hash =~ s/ (?<= \A \$) ([\w-]+?)-pepper-([\w-]+) \$ v=1 , id=([^\$,]+) /$1/x) {
    50          
29 2         15 return ($hash, $2, $3);
30             } elsif ($hash =~ s/ (?<= \A \$) peppered-(\w+) \$ v=1 , alg=([^\$,]+) , id=([^\$,]+) /$1/x) {
31 0         0 return ($hash, $2, $3);
32             } else {
33 2         11 return;
34             }
35             }
36              
37             sub supported_hashes {
38 1     1 1 4 my $self = shift;
39 1 50       16 @{ $self->{supported_hashes} || [] };
  1         9  
40             }
41              
42             sub prehash_password;
43              
44             sub hash_password {
45 1     1 1 2 my ($self, $password) = @_;
46              
47 1         10 my $prehashed = $self->prehash_password($password, $self->{algorithm}, $self->{active});
48 1         9 my $wrapped = encode_base64($prehashed, '') =~ tr/=//dr;
49 1         4 my $hash = $self->{inner}->hash_password($wrapped);
50 1         52 return $hash =~ s/ (?<= \A \$) ([^\$]+) /$1-pepper-$self->{algorithm}\$v=1,id=$self->{active}/rx;
51             }
52              
53             sub crypt_subtypes {
54 1     1 1 3 my $self = shift;
55 1         1 my @result;
56 1         4 my @supported = $self->supported_hashes;
57 1         5 for my $inner ($self->{inner}->crypt_subtypes) {
58 1         4 push @result, $inner, map { "$inner-pepper-$_" } @supported
  5         15  
59             }
60 1         7 return @result;
61             }
62              
63             sub needs_rehash {
64 2     2 1 4 my ($self, $hash) = @_;
65 2 100       5 my ($primary, $type, $id) = _to_inner($hash) or return 1;
66 1   33     3 return "$type,$id" ne join(',', @{$self}{qw/algorithm active/}) || $self->{inner}->needs_rehash($primary);
67             }
68              
69             sub verify_password {
70 2     2 1 5 my ($self, $password, $hash) = @_;
71              
72 2 100       5 if (my ($primary, $type, $id) = _to_inner($hash)) {
    50          
73 1 50       2 my $prehashed = eval { $self->prehash_password($password, $type, $id) } or return !!0;
  1         3  
74 1         5 my $wrapped = encode_base64($prehashed, '') =~ tr/=//dr;
75 1         7 return $self->{inner}->verify_password($wrapped, $primary);
76             }
77             elsif ($self->{inner}->accepts_hash($hash)) {
78 1         4 return $self->{inner}->verify_password($password, $hash);
79             }
80             else {
81 0           return !!0;
82             }
83             }
84              
85             1;
86              
87             # ABSTRACT: A base class for pre-hashing pepper implementations
88              
89             __END__