File Coverage

blib/lib/Data/Password/zxcvbn/TimeEstimate.pm
Criterion Covered Total %
statement 26 26 100.0
branch 6 6 100.0
condition n/a
subroutine 6 6 100.0
pod 3 3 100.0
total 41 41 100.0


line stmt bran cond sub pod time code
1             package Data::Password::zxcvbn::TimeEstimate;
2 5     5   155340 use strict;
  5         20  
  5         169  
3 5     5   27 use warnings;
  5         12  
  5         146  
4 5     5   33 use Exporter 'import';
  5         9  
  5         2118  
5             our @EXPORT_OK=qw(estimate_attack_times guesses_to_score display_time);
6             our $VERSION = '1.1.0'; # VERSION
7             # ABSTRACT: functions to estimate cracking times
8              
9              
10             sub estimate_attack_times {
11 996     996 1 2996 my ($guesses) = @_;
12 996         8601 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         3996 $_ => display_time($crack_times_seconds{$_})
  3984         10557  
21             } keys %crack_times_seconds;
22              
23             return {
24 996         6202 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 10149 my ($guesses) = @_;
48              
49 1997         6105 for my $score (0..$#score_scales) {
50 4942 100       13231 if ($guesses < $score_scales[$score]) {
51 1928         10429 return $score
52             }
53             }
54              
55 69         400 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 30272 my ($time) = @_;
72 3991 100       14075 return ['less than a second']
73             if $time < 1;
74              
75 2949         6397 for my $scale (@display_scales) {
76 10441 100       22874 if ($time < $scale->[0]) {
77 2715         13625 return [ "[quant,_1,$scale->[1]]", int($time) ];
78             }
79 7726         12995 $time /= $scale->[0];
80             }
81              
82 234         900 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.0
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