File Coverage

blib/lib/Crypt/Passphrase.pm
Criterion Covered Total %
statement 73 91 80.2
branch 16 28 57.1
condition 2 4 50.0
subroutine 17 19 89.4
pod 6 6 100.0
total 114 148 77.0


line stmt bran cond sub pod time code
1             package Crypt::Passphrase;
2             $Crypt::Passphrase::VERSION = '0.021';
3 6     6   515923 use strict;
  6         10  
  6         216  
4 6     6   27 use warnings;
  6         11  
  6         415  
5              
6 6     6   34 use Carp ();
  6         9  
  6         82  
7 6     6   24 use Scalar::Util ();
  6         13  
  6         71  
8 6     6   2097 use Encode ();
  6         71336  
  6         200  
9 6     6   3432 use Unicode::Normalize ();
  6         24539  
  6         2642  
10              
11             our @CARP_NOT;
12             sub import {
13 8     8   379 my ($class, @args) = @_;
14 8         24 for my $arg (@args) {
15 8         22 my $caller = caller;
16 8 100       42 if ($arg eq '-encoder') {
    50          
    50          
17 5         2089 require Crypt::Passphrase::Encoder;
18 6     6   64 no strict 'refs';
  6         11  
  6         539  
19 5 50       81 push @{"$caller\::ISA"}, 'Crypt::Passphrase::Encoder' unless $caller->isa('Crypt::Passphrase::Encoder');
  5         92  
20             }
21             elsif ($arg eq '-validator') {
22 0         0 require Crypt::Passphrase::Validator;
23 6     6   40 no strict 'refs';
  6         17  
  6         6483  
24 0 0       0 push @{"$caller\::ISA"}, 'Crypt::Passphrase::Validator' unless $caller->isa('Crypt::Passphrase::Validator');
  0         0  
25             }
26             elsif ($arg eq '-integration') {
27 3         10 push @CARP_NOT, $caller;
28             }
29             else {
30 0         0 Carp::croak("Unknown import argument $arg");
31             }
32             }
33 8         140669 return;
34             }
35              
36             sub _load_extension {
37 24     24   52 my $short_name = shift;
38 24 50       183 my $module_name = $short_name =~ s/^(\+)?/$1 ? '' : 'Crypt::Passphrase::'/re;
  24         171  
39 24         184 my $file_name = "$module_name.pm" =~ s{::}{/}gr;
40 24         5183 require $file_name;
41 24         854 return $module_name;
42             }
43              
44             sub _load_encoder {
45 20     20   49 my $encoder = shift;
46 20 50       99 if (Scalar::Util::blessed($encoder)) {
    100          
47 0         0 return $encoder;
48             }
49             elsif (ref $encoder) {
50 16         36 my %encoder_conf = %{ $encoder };
  16         85  
51 16         81 my $encoder_module = _load_extension(delete $encoder_conf{module});
52 16         142 return $encoder_module->new(%encoder_conf);
53             }
54             else {
55 4         18 return _load_extension($encoder)->new;
56             }
57             }
58              
59             sub _load_validator {
60 4     4   9 my $validator = shift;
61 4 50       24 if (Scalar::Util::blessed($validator)) {
    50          
    50          
62 0         0 return $validator;
63             }
64             elsif (ref($validator) eq 'HASH') {
65 0         0 my %validator_conf = %{ $validator };
  0         0  
66 0         0 my $validator_module = _load_extension(delete $validator_conf{module});
67 0         0 return $validator_module->new(%validator_conf);
68             }
69             elsif (ref($validator) eq 'CODE') {
70 0         0 require Crypt::Passphrase::Fallback;
71 0         0 return Crypt::Passphrase::Fallback->new(callback => $validator);
72             }
73             else {
74 4         11 return _load_extension($validator)->new;
75             }
76             }
77              
78             my %valid = map { $_ => 1 } qw/C D KC KD/;
79             sub new {
80 19     19 1 721420 my ($class, %args) = @_;
81 19 50       118 Carp::croak('No encoder given to Crypt::Passphrase->new') if not $args{encoder};
82 19         81 my $encoder = _load_encoder($args{encoder});
83 19         57 my @validators = map { _load_validator($_) } @{ $args{validators} };
  4         13  
  19         78  
84 19   50     91 my $normalization = $args{normalization} || 'C';
85 19 50       89 Carp::croak("Invalid normalization form $normalization") if not $valid{$normalization};
86              
87 19         101 my $self = bless {
88             encoder => $encoder,
89             validators => [ $encoder, @validators ],
90             normalization => $normalization,
91             }, $class;
92              
93 19         80 return $self;
94             }
95              
96             sub _normalize_password {
97 45     45   129 my ($self, $password) = @_;
98 45   50     391 my $normalized = Unicode::Normalize::normalize($self->{normalization}, $password // '');
99 45         1480 return Encode::encode('utf-8-strict', $normalized);
100             }
101              
102             sub hash_password {
103 18     18 1 21011 my ($self, $password) = @_;
104 18         62 my $normalized = $self->_normalize_password($password);
105 18         641 return $self->{encoder}->hash_password($normalized);
106             }
107              
108             sub needs_rehash {
109 23     23 1 1497 my ($self, $hash) = @_;
110 23         157 return $self->{encoder}->needs_rehash($hash);
111             }
112              
113             sub verify_password {
114 27     27 1 17967 my ($self, $password, $hash) = @_;
115              
116 27         59 for my $validator (@{ $self->{validators} }) {
  27         107  
117 37 100       186 if ($validator->accepts_hash($hash)) {
118 27         108 my $normalized = $self->_normalize_password($password);
119 27         848 return $validator->verify_password($normalized, $hash);
120             }
121             }
122              
123 0           return 0;
124             }
125              
126             sub recode_hash {
127 0     0 1   my ($self, @args) = @_;
128 0           return $self->{encoder}->recode_hash(@args);
129             }
130              
131             sub curry_with_hash {
132 0     0 1   my ($self, $hash) = @_;
133 0           require Crypt::Passphrase::PassphraseHash;
134 0           return Crypt::Passphrase::PassphraseHash->new($self, $hash);
135             }
136              
137             1;
138              
139             # ABSTRACT: A module for managing passwords in a cryptographically agile manner
140              
141             __END__