line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Data::Password::zxcvbn::TimeEstimate; |
2
|
5
|
|
|
5
|
|
123339
|
use strict; |
|
5
|
|
|
|
|
21
|
|
|
5
|
|
|
|
|
146
|
|
3
|
5
|
|
|
5
|
|
25
|
use warnings; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
125
|
|
4
|
5
|
|
|
5
|
|
26
|
use Exporter 'import'; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
2105
|
|
5
|
|
|
|
|
|
|
our @EXPORT_OK=qw(estimate_attack_times guesses_to_score display_time); |
6
|
|
|
|
|
|
|
our $VERSION = '1.1.2'; # VERSION |
7
|
|
|
|
|
|
|
# ABSTRACT: functions to estimate cracking times |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
sub estimate_attack_times { |
11
|
996
|
|
|
996
|
1
|
3037
|
my ($guesses) = @_; |
12
|
996
|
|
|
|
|
7843
|
my %crack_times_seconds = ( |
13
|
|
|
|
|
|
|
online_throttling_100_per_hour => $guesses / (100.0 / 3600.0), |
14
|
|
|
|
|
|
|
online_no_throttling_10_per_second => $guesses / 10.0, |
15
|
|
|
|
|
|
|
offline_slow_hashing_1e4_per_second => $guesses / 1e4, |
16
|
|
|
|
|
|
|
offline_fast_hashing_1e10_per_second => $guesses / 1e10, |
17
|
|
|
|
|
|
|
); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my %crack_times_display = map { |
20
|
996
|
|
|
|
|
3736
|
$_ => display_time($crack_times_seconds{$_}) |
|
3984
|
|
|
|
|
10698
|
|
21
|
|
|
|
|
|
|
} keys %crack_times_seconds; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
return { |
24
|
996
|
|
|
|
|
6723
|
crack_times_seconds => \%crack_times_seconds, |
25
|
|
|
|
|
|
|
crack_times_display => \%crack_times_display, |
26
|
|
|
|
|
|
|
}; |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# the +5 are apparently there to avoid fencepost errors |
31
|
|
|
|
|
|
|
my @score_scales = ( |
32
|
|
|
|
|
|
|
1e3+5, # risky password: "too guessable" |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
1e6+5, # modest protection from throttled online attacks: "very guessable" |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
1e8+5, # modest protection from unthrottled online attacks: |
37
|
|
|
|
|
|
|
# "somewhat guessable" |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
1e10+5, # modest protection from offline attacks: "safely |
40
|
|
|
|
|
|
|
# unguessable" assuming a salted, slow hash function like |
41
|
|
|
|
|
|
|
# bcrypt, scrypt, PBKDF2, argon, etc |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# else: strong protection from offline attacks under same |
44
|
|
|
|
|
|
|
# scenario: "very unguessable" |
45
|
|
|
|
|
|
|
); |
46
|
|
|
|
|
|
|
sub guesses_to_score { |
47
|
1997
|
|
|
1997
|
1
|
8114
|
my ($guesses) = @_; |
48
|
|
|
|
|
|
|
|
49
|
1997
|
|
|
|
|
5407
|
for my $score (0..$#score_scales) { |
50
|
4942
|
100
|
|
|
|
12377
|
if ($guesses < $score_scales[$score]) { |
51
|
1928
|
|
|
|
|
12235
|
return $score |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
69
|
|
|
|
|
490
|
return scalar @score_scales; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
my @display_scales = ( |
60
|
|
|
|
|
|
|
# if it's less than this, use this name |
61
|
|
|
|
|
|
|
# (otherwise divide by the number, and carry on) |
62
|
|
|
|
|
|
|
[ 60 => 'second' ], |
63
|
|
|
|
|
|
|
[ 60 => 'minute' ], |
64
|
|
|
|
|
|
|
[ 24 => 'hour' ], |
65
|
|
|
|
|
|
|
[ 30 => 'day' ], |
66
|
|
|
|
|
|
|
[ 12 => 'month' ], |
67
|
|
|
|
|
|
|
[ 100 => 'year' ], |
68
|
|
|
|
|
|
|
); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub display_time { |
71
|
3991
|
|
|
3991
|
1
|
26408
|
my ($time) = @_; |
72
|
3991
|
100
|
|
|
|
12050
|
return ['less than a second'] |
73
|
|
|
|
|
|
|
if $time < 1; |
74
|
|
|
|
|
|
|
|
75
|
2949
|
|
|
|
|
7052
|
for my $scale (@display_scales) { |
76
|
10441
|
100
|
|
|
|
22436
|
if ($time < $scale->[0]) { |
77
|
2715
|
|
|
|
|
14966
|
return [ "[quant,_1,$scale->[1]]", int($time) ]; |
78
|
|
|
|
|
|
|
} |
79
|
7726
|
|
|
|
|
14179
|
$time /= $scale->[0]; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
234
|
|
|
|
|
1101
|
return ['centuries']; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
1; |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
__END__ |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=pod |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=encoding UTF-8 |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=for :stopwords PBKDF2 scrypt bcrypt un |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=head1 NAME |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
Data::Password::zxcvbn::TimeEstimate - functions to estimate cracking times |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head1 VERSION |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
version 1.1.2 |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head1 SYNOPSIS |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
use Data::Password::zxcvbn::TimeEstimate qw(estimate_attack_times); |
106
|
|
|
|
|
|
|
my $estimates = estimate_attack_times($number_of_guesses); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head1 DESCRIPTION |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
This module provides functions for back-of-the-envelope crack time |
111
|
|
|
|
|
|
|
estimations, in seconds, based on a few scenarios. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=head1 FUNCTIONS |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=head2 C<estimate_attack_times> |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
my $estimates = estimate_attack_times($number_of_guesses); |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Returns a hashref with two keys: |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=over 4 |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=item * |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
C<crack_times_seconds> |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
hashref of back-of-the-envelope crack time estimations, in seconds, |
128
|
|
|
|
|
|
|
based on a few scenarios: |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=over 4 |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item * |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
C<online_throttling_100_per_hour> |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
online attack on a service that rate-limits authentication attempts |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=item * |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
C<online_no_throttling_10_per_second> |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
online attack on a service that doesn't rate-limit, or where an |
143
|
|
|
|
|
|
|
attacker has outsmarted rate-limiting. |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item * |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
C<offline_slow_hashing_1e4_per_second> |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
offline attack. assumes multiple attackers, proper user-unique |
150
|
|
|
|
|
|
|
salting, and a slow hash function with moderate work factor, such as |
151
|
|
|
|
|
|
|
bcrypt, scrypt, PBKDF2. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=item * |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
C<offline_fast_hashing_1e10_per_second> |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
offline attack with user-unique salting but a fast hash function like |
158
|
|
|
|
|
|
|
SHA-1, SHA-256 or MD5. A wide range of reasonable numbers anywhere |
159
|
|
|
|
|
|
|
from one billion - one trillion guesses per second, depending on |
160
|
|
|
|
|
|
|
number of cores and machines; ball-parking at 10B/sec. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=back |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=item * |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
C<crack_times_display> |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
same keys as C<crack_times_seconds>, but more useful for display: the |
169
|
|
|
|
|
|
|
values are arrayrefs C<["english string",$value]> that can be passed |
170
|
|
|
|
|
|
|
to I18N libraries like L<< C<Locale::Maketext> >> to get localised |
171
|
|
|
|
|
|
|
versions with proper plurals |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=back |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head2 C<guesses_to_score> |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
my $score = guesses_to_score($number_of_guesses); |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Returns an integer from 0-4 (useful for implementing a strength bar): |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=over 4 |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=item * |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
C<0> |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
too guessable: risky password. (C<< guesses < 10e3 >>) |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=item * |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
C<1> |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
very guessable: protection from throttled online attacks. (C<< guesses |
194
|
|
|
|
|
|
|
< 10e6 >>) |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=item * |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
C<2> |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
somewhat guessable: protection from un-throttled online attacks. (C<< |
201
|
|
|
|
|
|
|
guesses < 10e8 >>) |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=item * |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
C<3> |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
safely un-guessable: moderate protection from offline slow-hash |
208
|
|
|
|
|
|
|
scenario. (C<< guesses < 10e10 >>) |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=item * |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
C<4> |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
very un-guessable: strong protection from offline slow-hash |
215
|
|
|
|
|
|
|
scenario. (C<< guesses >= 10e10 >>) |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=back |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head2 C<display_time> |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
my ($string,@values) = @{ display_time($time) }; |
222
|
|
|
|
|
|
|
print My::Localise->get_handle->maketext($string,@values); |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Given a C<$time> in seconds, returns an arrayref suitable for |
225
|
|
|
|
|
|
|
L<< C<Locale::Maketext> >>, like: |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
[ 'quant,_1,day', 23 ] |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=head1 AUTHOR |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
Gianni Ceccarelli <gianni.ceccarelli@broadbean.com> |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
This software is copyright (c) 2022 by BroadBean UK, a CareerBuilder Company. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
238
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=cut |