line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
use strict; |
2
|
10
|
|
|
10
|
|
50
|
use warnings; |
|
10
|
|
|
|
|
17
|
|
|
10
|
|
|
|
|
205
|
|
3
|
10
|
|
|
10
|
|
38
|
use Carp qw(croak); |
|
10
|
|
|
|
|
18
|
|
|
10
|
|
|
|
|
191
|
|
4
|
10
|
|
|
10
|
|
39
|
use Digest; |
|
10
|
|
|
|
|
16
|
|
|
10
|
|
|
|
|
428
|
|
5
|
10
|
|
|
10
|
|
4032
|
use MIME::Base64 qw(decode_base64 encode_base64); |
|
10
|
|
|
|
|
4373
|
|
|
10
|
|
|
|
|
270
|
|
6
|
10
|
|
|
10
|
|
3266
|
use Data::Entropy::Algorithms qw(rand_bits rand_int); |
|
10
|
|
|
|
|
5217
|
|
|
10
|
|
|
|
|
537
|
|
7
|
10
|
|
|
10
|
|
3571
|
|
|
10
|
|
|
|
|
110578
|
|
|
10
|
|
|
|
|
10410
|
|
8
|
|
|
|
|
|
|
# ABSTRACT: Passphrases and Passwords as objects for Dancer2 |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
Dancer2::Plugin::Passphrase::Core - Core package for Dancer2::Plugin::Passphrase. |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 DESCRIPTION |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
B<FOR INTERNAL USE ONLY> |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 AUTHOR |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Maintainer: Henk van Oers <hvoers@cpan.org> |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
This software is copyright (c) 2012 by James Aitken. |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
27
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=cut |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my $class = shift; |
32
|
|
|
|
|
|
|
my @args = @_; |
33
|
98
|
|
|
98
|
0
|
2193
|
return bless { @args == 1 ? %{$args[0]} : @args }, $class; |
34
|
98
|
|
|
|
|
233
|
} |
35
|
98
|
50
|
|
|
|
627
|
|
|
0
|
|
|
|
|
0
|
|
36
|
|
|
|
|
|
|
# { algorithm => '...', this => '...' } |
37
|
|
|
|
|
|
|
my $self = shift; |
38
|
|
|
|
|
|
|
my $options = shift; |
39
|
|
|
|
|
|
|
my $algorithm = $self->{'algorithm'}; |
40
|
26
|
|
|
26
|
|
41
|
my $settings = {}; |
41
|
26
|
|
|
|
|
36
|
|
42
|
26
|
|
|
|
|
52
|
# if we got options |
43
|
26
|
|
|
|
|
37
|
if ($options) { |
44
|
|
|
|
|
|
|
$algorithm = delete $options->{'algorithm'}; |
45
|
|
|
|
|
|
|
$settings = |
46
|
26
|
100
|
|
|
|
61
|
defined $options->{$algorithm} |
47
|
24
|
|
|
|
|
42
|
? $options->{$algorithm} |
48
|
|
|
|
|
|
|
: $self->{$algorithm}; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
24
|
50
|
|
|
|
66
|
# Specify empty string to get an unsalted hash |
52
|
|
|
|
|
|
|
# Leaving it undefs results in 128 random bits being used as salt |
53
|
|
|
|
|
|
|
# bcrypt requires this amount, and is reasonable for other algorithms |
54
|
|
|
|
|
|
|
$settings->{'salt'} = rand_bits(128) |
55
|
|
|
|
|
|
|
unless defined $settings->{'salt'}; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# RFC 2307 scheme is based on the algorithm, with a prefixed 'S' for salted |
58
|
26
|
50
|
|
|
|
108
|
$settings->{'scheme'} = join '', $algorithm =~ /[\w]+/g; |
59
|
|
|
|
|
|
|
$settings->{'scheme'} = 'S'. $settings->{'scheme'} |
60
|
|
|
|
|
|
|
if $settings->{'salt'}; |
61
|
26
|
|
|
|
|
28219
|
|
62
|
|
|
|
|
|
|
if ( $settings->{'scheme'} eq 'SHA1' ) { |
63
|
26
|
50
|
|
|
|
93
|
$settings->{'scheme'} = 'SHA'; |
64
|
|
|
|
|
|
|
} elsif ( $settings->{'scheme'} eq 'SSHA1' ) { |
65
|
26
|
50
|
|
|
|
90
|
$settings->{'scheme'} = 'SSHA'; |
|
|
100
|
|
|
|
|
|
66
|
0
|
|
|
|
|
0
|
} |
67
|
|
|
|
|
|
|
|
68
|
6
|
|
|
|
|
15
|
# Bcrypt requires a cost parameter |
69
|
|
|
|
|
|
|
if ( $algorithm eq 'Bcrypt' ) { |
70
|
|
|
|
|
|
|
$settings->{'scheme'} = 'CRYPT'; |
71
|
|
|
|
|
|
|
$settings->{'type'} = '2a'; |
72
|
26
|
100
|
|
|
|
53
|
|
73
|
5
|
|
|
|
|
12
|
$settings->{'cost'} //= $self->{'Bcrypt'}{'cost'} || 4; |
74
|
5
|
|
|
|
|
9
|
$settings->{'cost'} = 31 if $settings->{'cost'} > 31; |
75
|
|
|
|
|
|
|
$settings->{'cost'} = sprintf '%02d', $settings->{'cost'}; |
76
|
5
|
|
50
|
|
|
46
|
} |
|
|
|
33
|
|
|
|
|
77
|
5
|
50
|
|
|
|
14
|
|
78
|
5
|
|
|
|
|
22
|
$settings->{'algorithm'} = $algorithm; |
79
|
|
|
|
|
|
|
$settings->{'plaintext'} = $self->{'plaintext'}; |
80
|
|
|
|
|
|
|
|
81
|
26
|
|
|
|
|
40
|
return $settings; |
82
|
26
|
|
|
|
|
46
|
} |
83
|
|
|
|
|
|
|
|
84
|
26
|
|
|
|
|
47
|
# From Crypt::Eksblowfish::Bcrypt. |
85
|
|
|
|
|
|
|
# Bcrypt uses it's own variation on base64 |
86
|
|
|
|
|
|
|
my ($octets) = @_; |
87
|
|
|
|
|
|
|
my $text = encode_base64($octets, ''); |
88
|
|
|
|
|
|
|
$text =~ tr{A-Za-z0-9+/=}{./A-Za-z0-9}d; |
89
|
|
|
|
|
|
|
return $text; |
90
|
84
|
|
|
84
|
|
160
|
} |
91
|
84
|
|
|
|
|
201
|
|
92
|
84
|
|
|
|
|
143
|
|
93
|
84
|
|
|
|
|
189
|
# And the decoder of bcrypt's custom base64 |
94
|
|
|
|
|
|
|
my ($text) = @_; |
95
|
|
|
|
|
|
|
$text =~ tr{./A-Za-z0-9}{A-Za-z0-9+/}; |
96
|
|
|
|
|
|
|
$text .= "=" x (3 - (length($text) + 3) % 4); |
97
|
|
|
|
|
|
|
return decode_base64($text); |
98
|
|
|
|
|
|
|
} |
99
|
38
|
|
|
38
|
|
110
|
|
100
|
38
|
|
|
|
|
92
|
# Extracts the settings from an RFC 2307 string |
101
|
38
|
|
|
|
|
171
|
my ($self, $rfc2307_string) = @_; |
102
|
38
|
|
|
|
|
161
|
my $settings = {}; |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
my ($scheme, $rfc_settings) = ($rfc2307_string =~ m/^{(\w+)}(.*)/s); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
unless ($scheme && $rfc_settings) { |
107
|
69
|
|
|
69
|
|
146
|
croak "An RFC 2307 compliant string must be passed to matches()"; |
108
|
69
|
|
|
|
|
111
|
} |
109
|
|
|
|
|
|
|
|
110
|
69
|
|
|
|
|
331
|
if ($scheme eq 'CRYPT') { |
111
|
|
|
|
|
|
|
if ($rfc_settings =~ m/^\$2(?:a|x|y)\$/) { |
112
|
69
|
100
|
66
|
|
|
365
|
$scheme = 'Bcrypt'; |
113
|
1
|
|
|
|
|
142
|
$rfc_settings =~ m{\A\$(2a|2x|2y)\$([0-9]{2})\$([./A-Za-z0-9]{22})}x; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
@{$settings}{qw<type cost salt>} = ( $1, $2, _de_bcrypt_base64($3) ); |
116
|
68
|
100
|
|
|
|
202
|
} else { |
117
|
39
|
100
|
|
|
|
177
|
croak "Unknown CRYPT format"; |
118
|
38
|
|
|
|
|
75
|
} |
119
|
38
|
|
|
|
|
149
|
} |
120
|
|
|
|
|
|
|
|
121
|
38
|
|
|
|
|
160
|
my $scheme_meta = { |
|
38
|
|
|
|
|
173
|
|
122
|
|
|
|
|
|
|
'MD5' => { algorithm => 'MD5', octets => 128 / 8 }, |
123
|
1
|
|
|
|
|
58
|
'SMD5' => { algorithm => 'MD5', octets => 128 / 8 }, |
124
|
|
|
|
|
|
|
'SHA' => { algorithm => 'SHA-1', octets => 160 / 8 }, |
125
|
|
|
|
|
|
|
'SSHA' => { algorithm => 'SHA-1', octets => 160 / 8 }, |
126
|
|
|
|
|
|
|
'SHA224' => { algorithm => 'SHA-224', octets => 224 / 8 }, |
127
|
67
|
|
|
|
|
1288
|
'SSHA224' => { algorithm => 'SHA-224', octets => 224 / 8 }, |
128
|
|
|
|
|
|
|
'SHA256' => { algorithm => 'SHA-256', octets => 256 / 8 }, |
129
|
|
|
|
|
|
|
'SSHA256' => { algorithm => 'SHA-256', octets => 256 / 8 }, |
130
|
|
|
|
|
|
|
'SHA384' => { algorithm => 'SHA-384', octets => 384 / 8 }, |
131
|
|
|
|
|
|
|
'SSHA384' => { algorithm => 'SHA-384', octets => 384 / 8 }, |
132
|
|
|
|
|
|
|
'SHA512' => { algorithm => 'SHA-512', octets => 512 / 8 }, |
133
|
|
|
|
|
|
|
'SSHA512' => { algorithm => 'SHA-512', octets => 512 / 8 }, |
134
|
|
|
|
|
|
|
'Bcrypt' => { algorithm => 'Bcrypt', octets => 128 / 8 }, |
135
|
|
|
|
|
|
|
}; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
$settings->{'scheme'} = $scheme; |
138
|
|
|
|
|
|
|
$settings->{'algorithm'} = $scheme_meta->{$scheme}{algorithm}; |
139
|
|
|
|
|
|
|
$settings->{'plaintext'} = $self->{'plaintext'};; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
if ( !defined $settings->{'salt'} ) { |
142
|
|
|
|
|
|
|
$settings->{'salt'} = substr( |
143
|
67
|
|
|
|
|
163
|
decode_base64($rfc_settings), |
144
|
67
|
|
|
|
|
144
|
$scheme_meta->{$scheme}{octets}, |
145
|
67
|
|
|
|
|
129
|
); |
146
|
|
|
|
|
|
|
} |
147
|
67
|
100
|
|
|
|
181
|
|
148
|
|
|
|
|
|
|
return $settings; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
29
|
|
|
|
|
112
|
my ( $self, $settings ) = @_; |
152
|
|
|
|
|
|
|
my $hasher = Digest->new( $settings->{'algorithm'} ); |
153
|
|
|
|
|
|
|
my ( $hash, $rfc2307 ); |
154
|
67
|
|
|
|
|
305
|
|
155
|
|
|
|
|
|
|
if ( $settings->{'algorithm'} eq 'Bcrypt' ) { |
156
|
|
|
|
|
|
|
$hasher->add( $settings->{'plaintext'} ); |
157
|
|
|
|
|
|
|
$hasher->salt( $settings->{'salt'} ); |
158
|
93
|
|
|
93
|
|
172
|
$hasher->cost( $settings->{'cost'} ); |
159
|
93
|
|
|
|
|
400
|
|
160
|
93
|
|
|
|
|
25772
|
$hash = $hasher->digest; |
161
|
|
|
|
|
|
|
$rfc2307 = '{CRYPT}$' |
162
|
93
|
100
|
|
|
|
247
|
. $settings->{'type'} . '$' |
163
|
43
|
|
|
|
|
167
|
. $settings->{'cost'} . '$' |
164
|
43
|
|
|
|
|
374
|
. _en_bcrypt_base64( $settings->{'salt'} ) |
165
|
43
|
|
|
|
|
4243
|
. _en_bcrypt_base64($hash); |
166
|
|
|
|
|
|
|
} else { |
167
|
43
|
|
|
|
|
937
|
$hasher->add( $settings->{'plaintext'} ); |
168
|
|
|
|
|
|
|
$hasher->add( $settings->{'salt'} ); |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
$hash = $hasher->digest; |
171
|
42
|
|
|
|
|
61407
|
$rfc2307 = '{' . $settings->{'scheme'} . '}' |
172
|
|
|
|
|
|
|
. encode_base64( |
173
|
|
|
|
|
|
|
$hash . $settings->{'salt'}, |
174
|
50
|
|
|
|
|
164
|
'' |
175
|
47
|
|
|
|
|
92
|
); |
176
|
|
|
|
|
|
|
} |
177
|
47
|
|
|
|
|
182
|
|
178
|
|
|
|
|
|
|
return Dancer2::Plugin::Passphrase::Hashed->new( |
179
|
|
|
|
|
|
|
hash => $hash, |
180
|
47
|
|
|
|
|
204
|
rfc2307 => $rfc2307, |
181
|
|
|
|
|
|
|
%{$settings}, |
182
|
|
|
|
|
|
|
); |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
my $self = shift; |
186
|
|
|
|
|
|
|
my $options = shift; |
187
|
|
|
|
|
|
|
my $settings = $self->_merge_options($options); |
188
|
89
|
|
|
|
|
180
|
|
|
89
|
|
|
|
|
521
|
|
189
|
|
|
|
|
|
|
return $self->_calculate_hash($settings); |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
my ($self, $options) = @_; |
193
|
26
|
|
|
26
|
0
|
46
|
|
194
|
26
|
|
|
|
|
34
|
# Default is 16 URL-safe base64 chars. Supported everywhere and a reasonable length |
195
|
26
|
|
|
|
|
53
|
my $length = $options->{length} || 16; |
196
|
|
|
|
|
|
|
my $charset = $options->{charset} || ['a'..'z', 'A'..'Z', '0'..'9', '-', '_']; |
197
|
26
|
|
|
|
|
64
|
|
198
|
|
|
|
|
|
|
return join '', map { @$charset[rand_int scalar @$charset] } 1..$length; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
3
|
|
|
3
|
0
|
7
|
my ($self, $stored_hash) = @_; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
my $settings = $self->_extract_settings($stored_hash); |
204
|
3
|
|
100
|
|
|
9
|
my $new_hash = $self->_calculate_hash($settings)->rfc2307; |
205
|
3
|
|
100
|
|
|
23
|
|
206
|
|
|
|
|
|
|
return ($new_hash eq $stored_hash) ? 1 : undef; |
207
|
3
|
|
|
|
|
7
|
} |
|
51
|
|
|
|
|
6961
|
|
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
1; |