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.023';
3 6     6   528067 use strict;
  6         15  
  6         265  
4 6     6   34 use warnings;
  6         13  
  6         370  
5              
6 6     6   58 use Carp ();
  6         13  
  6         106  
7 6     6   27 use Scalar::Util ();
  6         21  
  6         121  
8 6     6   2043 use Encode ();
  6         66900  
  6         189  
9 6     6   3848 use Unicode::Normalize ();
  6         25939  
  6         786  
10              
11             our @CARP_NOT;
12             sub import {
13 8     8   394 my ($class, @args) = @_;
14 8         27 for my $arg (@args) {
15 8         27 my $caller = caller;
16 8 100       42 if ($arg eq '-encoder') {
    50          
    50          
17 5         2407 require Crypt::Passphrase::Encoder;
18 6     6   56 no strict 'refs';
  6         12  
  6         2739  
19 5 50       112 push @{"$caller\::ISA"}, 'Crypt::Passphrase::Encoder' unless $caller->isa('Crypt::Passphrase::Encoder');
  5         84  
20             }
21             elsif ($arg eq '-validator') {
22 0         0 require Crypt::Passphrase::Validator;
23 6     6   49 no strict 'refs';
  6         17  
  6         7556  
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         128565 return;
34             }
35              
36             sub _load_extension {
37 24     24   50 my $short_name = shift;
38 24 50       219 my $module_name = $short_name =~ s/^(\+)?/$1 ? '' : 'Crypt::Passphrase::'/re;
  24         196  
39 24         178 my $file_name = "$module_name.pm" =~ s{::}{/}gr;
40 24         5133 require $file_name;
41 24         936 return $module_name;
42             }
43              
44             sub _load_encoder {
45 20     20   47 my $encoder = shift;
46 20 50       107 if (Scalar::Util::blessed($encoder)) {
    100          
47 0         0 return $encoder;
48             }
49             elsif (ref $encoder) {
50 16         31 my %encoder_conf = %{ $encoder };
  16         70  
51 16         91 my $encoder_module = _load_extension(delete $encoder_conf{module});
52 16         103 return $encoder_module->new(%encoder_conf);
53             }
54             else {
55 4         19 return _load_extension($encoder)->new;
56             }
57             }
58              
59             sub _load_validator {
60 4     4   11 my $validator = shift;
61 4 50       20 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 696256 my ($class, %args) = @_;
81 19 50       96 Carp::croak('No encoder given to Crypt::Passphrase->new') if not $args{encoder};
82 19         81 my $encoder = _load_encoder($args{encoder});
83 19         56 my @validators = map { _load_validator($_) } @{ $args{validators} };
  4         13  
  19         86  
84 19   50     126 my $normalization = $args{normalization} || 'C';
85 19 50       83 Carp::croak("Invalid normalization form $normalization") if not $valid{$normalization};
86              
87 19         107 my $self = bless {
88             encoder => $encoder,
89             validators => [ $encoder, @validators ],
90             normalization => $normalization,
91             }, $class;
92              
93 19         82 return $self;
94             }
95              
96             sub _normalize_password {
97 45     45   142 my ($self, $password) = @_;
98 45   50     343 my $normalized = Unicode::Normalize::normalize($self->{normalization}, $password // '');
99 45         1430 return Encode::encode('utf-8-strict', $normalized);
100             }
101              
102             sub hash_password {
103 18     18 1 20066 my ($self, $password) = @_;
104 18         60 my $normalized = $self->_normalize_password($password);
105 18         592 return $self->{encoder}->hash_password($normalized);
106             }
107              
108             sub needs_rehash {
109 23     23 1 1213 my ($self, $hash) = @_;
110 23         136 return $self->{encoder}->needs_rehash($hash);
111             }
112              
113             sub verify_password {
114 27     27 1 18057 my ($self, $password, $hash) = @_;
115              
116 27         61 for my $validator (@{ $self->{validators} }) {
  27         114  
117 37 100       197 if ($validator->accepts_hash($hash)) {
118 27         110 my $normalized = $self->_normalize_password($password);
119 27         914 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__