line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Data::Password::zxcvbn::Match::Sequence; |
2
|
3
|
|
|
3
|
|
9282
|
use Moo; |
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
21
|
|
3
|
|
|
|
|
|
|
with 'Data::Password::zxcvbn::Match'; |
4
|
|
|
|
|
|
|
our $VERSION = '1.1.2'; # VERSION |
5
|
|
|
|
|
|
|
# ABSTRACT: match class for sequences of uniformly-spaced codepoints |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
has ascending => (is => 'ro', default => 1); |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
sub estimate_guesses { |
12
|
654
|
|
|
654
|
1
|
15346
|
my ($self,$min_guesses) = @_; |
13
|
|
|
|
|
|
|
|
14
|
654
|
|
|
|
|
2783
|
my $first_char = substr($self->token,0,1); |
15
|
|
|
|
|
|
|
|
16
|
654
|
|
|
|
|
1181
|
my $guesses; |
17
|
|
|
|
|
|
|
# lower guesses for obvious starting points |
18
|
654
|
100
|
|
|
|
3988
|
if ($first_char =~ m{[aAzZ019]}) { |
|
|
100
|
|
|
|
|
|
19
|
179
|
|
|
|
|
413
|
$guesses = 4; |
20
|
|
|
|
|
|
|
} |
21
|
|
|
|
|
|
|
elsif ($first_char =~ m{[0-9]}) { |
22
|
180
|
|
|
|
|
434
|
$guesses = 10; # digits |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
else { |
25
|
|
|
|
|
|
|
# could give a higher base for uppercase, assigning 26 to both |
26
|
|
|
|
|
|
|
# upper and lower sequences is more conservative. |
27
|
295
|
|
|
|
|
678
|
$guesses = 26; |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
654
|
100
|
|
|
|
2849
|
$guesses *= 2 unless $self->ascending; |
31
|
|
|
|
|
|
|
|
32
|
654
|
|
|
|
|
3084
|
return $guesses * length($self->token); |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub feedback_warning { |
37
|
5
|
|
|
5
|
1
|
16
|
my ($self) = @_; |
38
|
|
|
|
|
|
|
|
39
|
5
|
|
|
|
|
37
|
return 'Sequences like abc or 6543 are easy to guess'; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub feedback_suggestions { |
43
|
5
|
|
|
5
|
1
|
28
|
return [ 'Avoid sequences' ]; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
my $MAX_DELTA = 5; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub make { |
50
|
1506
|
|
|
1506
|
1
|
93363
|
my ($class, $password) = @_; |
51
|
|
|
|
|
|
|
# Identifies sequences by looking for repeated differences in |
52
|
|
|
|
|
|
|
# unicode codepoint. this allows skipping, such as 9753, and also |
53
|
|
|
|
|
|
|
# matches some extended unicode sequences such as Greek and |
54
|
|
|
|
|
|
|
# Cyrillic alphabets. |
55
|
|
|
|
|
|
|
# |
56
|
|
|
|
|
|
|
# for example, consider the input 'abcdb975zy' |
57
|
|
|
|
|
|
|
# |
58
|
|
|
|
|
|
|
# password: a b c d b 9 7 5 z y |
59
|
|
|
|
|
|
|
# index: 0 1 2 3 4 5 6 7 8 9 |
60
|
|
|
|
|
|
|
# delta: 1 1 1 -2 -41 -2 -2 69 1 |
61
|
|
|
|
|
|
|
# |
62
|
|
|
|
|
|
|
# expected result: |
63
|
|
|
|
|
|
|
# [(i, j, delta), ...] = [(0, 3, 1), (5, 7, -2), (8, 9, 1)] |
64
|
|
|
|
|
|
|
|
65
|
1506
|
|
|
|
|
3337
|
my $length = length($password); |
66
|
1506
|
100
|
|
|
|
5594
|
return [] if $length <= 1; |
67
|
|
|
|
|
|
|
|
68
|
1055
|
|
|
|
|
2547
|
my @matches; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
my $update = sub { |
71
|
6378
|
|
|
6378
|
|
12025
|
my ($i,$j,$delta) = @_; |
72
|
6378
|
|
100
|
|
|
15581
|
my $abs_delta = abs($delta||0); |
73
|
6378
|
100
|
100
|
|
|
22761
|
return unless $j-$i>1 or $abs_delta == 1; |
74
|
733
|
100
|
|
|
|
2919
|
return if $abs_delta == 0; |
75
|
690
|
100
|
|
|
|
2704
|
return if $abs_delta > $MAX_DELTA; |
76
|
|
|
|
|
|
|
|
77
|
662
|
|
|
|
|
2109
|
my $token = substr($password,$i,$j-$i+1); |
78
|
662
|
|
|
|
|
18559
|
push @matches, $class->new({ |
79
|
|
|
|
|
|
|
token => $token, |
80
|
|
|
|
|
|
|
i => $i, j => $j, |
81
|
|
|
|
|
|
|
ascending => !!($delta>0), |
82
|
|
|
|
|
|
|
}); |
83
|
1055
|
|
|
|
|
10246
|
}; |
84
|
|
|
|
|
|
|
|
85
|
1055
|
|
|
|
|
3258
|
my $i=0; |
86
|
1055
|
|
|
|
|
2495
|
my $last_delta; |
87
|
1055
|
|
|
|
|
4416
|
for my $k (1..$length-1) { |
88
|
6611
|
|
|
|
|
15326
|
my $delta = ord(substr($password,$k,1)) - ord(substr($password,$k-1,1)); |
89
|
6611
|
100
|
|
|
|
13791
|
$last_delta = $delta unless defined($last_delta); |
90
|
6611
|
100
|
|
|
|
14034
|
next if $delta == $last_delta; |
91
|
5323
|
|
|
|
|
8949
|
my $j = $k-1; |
92
|
5323
|
|
|
|
|
13645
|
$update->($i,$j,$last_delta); |
93
|
5323
|
|
|
|
|
30406
|
$i = $j; $last_delta = $delta; |
|
5323
|
|
|
|
|
9452
|
|
94
|
|
|
|
|
|
|
} |
95
|
1055
|
|
|
|
|
5587
|
$update->($i,$length-1,$last_delta); |
96
|
|
|
|
|
|
|
|
97
|
1055
|
|
|
|
|
19328
|
return \@matches; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
around fields_for_json => sub { |
102
|
|
|
|
|
|
|
my ($orig,$self) = @_; |
103
|
|
|
|
|
|
|
( $self->$orig(), qw(ascending) ) |
104
|
|
|
|
|
|
|
}; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
1; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
__END__ |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=pod |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=encoding UTF-8 |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head1 NAME |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Data::Password::zxcvbn::Match::Sequence - match class for sequences of uniformly-spaced codepoints |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=head1 VERSION |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
version 1.1.2 |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head1 DESCRIPTION |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
This class represents the guess that a certain substring of a |
125
|
|
|
|
|
|
|
password, consisting of uniformly-spaced codepoints, is easy to guess. |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=head2 C<ascending> |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Boolean, true if the sequence starts at a lower codepoint and ends at |
132
|
|
|
|
|
|
|
a higher one (e.g. C<acegi> is ascending, C<86420> is not). |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head1 METHODS |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=head2 C<estimate_guesses> |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
The number of guesses is I<linear> with the length of the |
139
|
|
|
|
|
|
|
sequence. Descending sequences get a higher estimate, sequences that |
140
|
|
|
|
|
|
|
start at obvious points (e.g. C<A> or C<1>) get lower estimates. |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=head2 C<feedback_warning> |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head2 C<feedback_suggestions> |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
This class suggests not using sequences. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=head2 C<make> |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
my @matches = @{ Data::Password::zxcvbn::Match::Sequence->make( |
151
|
|
|
|
|
|
|
$password, |
152
|
|
|
|
|
|
|
) }; |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
Scans the C<$password> for sequences of characters whose codepoints |
155
|
|
|
|
|
|
|
increase or decrease by a constant. |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head2 C<fields_for_json> |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
The JSON serialisation for matches of this class will contain C<token |
160
|
|
|
|
|
|
|
i j guesses guesses_log10 ascending>. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head1 AUTHOR |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Gianni Ceccarelli <gianni.ceccarelli@broadbean.com> |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
This software is copyright (c) 2022 by BroadBean UK, a CareerBuilder Company. |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
171
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=cut |