line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Crypt::Juniper; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
114584
|
use warnings; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
116
|
|
4
|
4
|
|
|
4
|
|
22
|
use strict; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
128
|
|
5
|
4
|
|
|
4
|
|
24
|
use Carp; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
372
|
|
6
|
|
|
|
|
|
|
|
7
|
4
|
|
|
4
|
|
21
|
use base 'Exporter'; |
|
4
|
|
|
|
|
19
|
|
|
4
|
|
|
|
|
4534
|
|
8
|
|
|
|
|
|
|
our @EXPORT = qw( juniper_encrypt juniper_decrypt ); |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
Crypt::Juniper - Encrypt/decrypt Juniper $9$ secrets |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 VERSION |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
Version 0.02 |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=cut |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 SYNOPSIS |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
use Crypt::Juniper; |
26
|
|
|
|
|
|
|
my $secret = juniper_decrypt('$9$LbHX-wg4Z'); ## $secret="lc"; |
27
|
|
|
|
|
|
|
my $crypt = juniper_encrypt('lc'); ## encrypt it |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=cut |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
################################################################# |
32
|
|
|
|
|
|
|
## globals |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my $MAGIC = q{$9$}; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
################################### |
37
|
|
|
|
|
|
|
## letter families |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my @FAMILY = qw[ QzF3n6/9CAtpu0O B1IREhcSyrleKvMW8LXx 7N-dVbwsY2g4oaJZGUDj iHkq.mPf5T ]; |
40
|
|
|
|
|
|
|
my %EXTRA; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
for my $fam (0..$#FAMILY) |
43
|
|
|
|
|
|
|
{ |
44
|
|
|
|
|
|
|
for my $c (split //, $FAMILY[$fam]) |
45
|
|
|
|
|
|
|
{ |
46
|
|
|
|
|
|
|
$EXTRA{$c} = (3-$fam); |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
my $VALID = do { |
51
|
|
|
|
|
|
|
my $letters = join '', @FAMILY; |
52
|
|
|
|
|
|
|
my $end = "[$letters]{4,}\$"; |
53
|
|
|
|
|
|
|
$end =~ s/-/\\-/; |
54
|
|
|
|
|
|
|
qr/^\Q$MAGIC\E$end/; |
55
|
|
|
|
|
|
|
}; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
################################### |
58
|
|
|
|
|
|
|
## forward and reverse dictionaries |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my @NUM_ALPHA = split //, join '', @FAMILY; |
61
|
|
|
|
|
|
|
my %ALPHA_NUM = map { $NUM_ALPHA[$_] => $_ } 0..$#NUM_ALPHA; |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
################################### |
64
|
|
|
|
|
|
|
## encoding moduli by position |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
my @ENCODING = ( |
67
|
|
|
|
|
|
|
[ 1, 4, 32 ], |
68
|
|
|
|
|
|
|
[ 1, 16, 32 ], |
69
|
|
|
|
|
|
|
[ 1, 8, 32 ], |
70
|
|
|
|
|
|
|
[ 1, 64 ], |
71
|
|
|
|
|
|
|
[ 1, 32 ], |
72
|
|
|
|
|
|
|
[ 1, 4, 16, 128 ], |
73
|
|
|
|
|
|
|
[ 1, 32, 64 ], |
74
|
|
|
|
|
|
|
); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
################################################################# |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=head1 EXPORTED FUNCTIONS |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=head2 juniper_decrypt($crypt) |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
Decrypt the string C<$crypt>, returning the corresponding plain-text. |
83
|
|
|
|
|
|
|
Input string must be of the format "$9$blahblah". This function will |
84
|
|
|
|
|
|
|
die() if there any processing errors. |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=cut |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub juniper_decrypt { |
89
|
3690
|
|
|
3690
|
1
|
1246310
|
my ($crypt) = @_; |
90
|
|
|
|
|
|
|
|
91
|
3690
|
100
|
100
|
|
|
38284
|
croak "Invalid Juniper crypt string!" |
92
|
|
|
|
|
|
|
unless (defined $crypt and $crypt =~ $VALID); |
93
|
|
|
|
|
|
|
|
94
|
3686
|
|
|
|
|
21985
|
my ($chars) = $crypt =~ /^\Q$MAGIC\E(\S+)/; |
95
|
|
|
|
|
|
|
|
96
|
3686
|
|
|
|
|
8648
|
my $first = _nibble(\$chars, 1); |
97
|
3686
|
|
|
|
|
9964
|
_nibble(\$chars, $EXTRA{$first}); |
98
|
|
|
|
|
|
|
|
99
|
3686
|
|
|
|
|
4645
|
my $prev = $first; |
100
|
3686
|
|
|
|
|
4419
|
my $decrypt = ''; |
101
|
|
|
|
|
|
|
|
102
|
3686
|
|
|
|
|
8680
|
while ($chars) |
103
|
|
|
|
|
|
|
{ |
104
|
69830
|
|
|
|
|
105142
|
my $decode = $ENCODING[ length($decrypt) % @ENCODING ]; |
105
|
69830
|
|
|
|
|
83657
|
my $len = @$decode; |
106
|
|
|
|
|
|
|
|
107
|
69830
|
|
|
|
|
130020
|
my @nibble = split //, _nibble(\$chars, $len); |
108
|
69828
|
|
|
|
|
119912
|
my @gaps = map { my $g = _gap($prev, $_); $prev = $_ ; $g } @nibble; |
|
200080
|
|
|
|
|
280945
|
|
|
200080
|
|
|
|
|
229474
|
|
|
200080
|
|
|
|
|
307565
|
|
109
|
|
|
|
|
|
|
|
110
|
69828
|
|
|
|
|
120122
|
$decrypt .= _gap_decode(\@gaps, $decode); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
3684
|
|
|
|
|
16184
|
return $decrypt; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub _nibble { |
117
|
77202
|
|
|
77202
|
|
88739
|
my ($cref, $len) = @_; |
118
|
77202
|
|
|
|
|
132637
|
my $nib = substr($$cref, 0, $len, ''); |
119
|
77202
|
100
|
|
|
|
154444
|
length($nib) == $len |
120
|
|
|
|
|
|
|
or croak "Ran out of characters: hit '$nib', expecting $len chars"; |
121
|
77200
|
|
|
|
|
237289
|
return $nib; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
################################### |
125
|
|
|
|
|
|
|
## calculate the distance between two characters |
126
|
|
|
|
|
|
|
sub _gap { |
127
|
200080
|
|
|
200080
|
|
243529
|
my ($c1, $c2) = @_; |
128
|
|
|
|
|
|
|
|
129
|
200080
|
|
|
|
|
416556
|
return ($ALPHA_NUM{$c2} - $ALPHA_NUM{$c1}) % @NUM_ALPHA - 1; |
130
|
|
|
|
|
|
|
}; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
################################### |
133
|
|
|
|
|
|
|
## given a series of gaps and moduli, calculate the resulting plaintext |
134
|
|
|
|
|
|
|
sub _gap_decode { |
135
|
69828
|
|
|
69828
|
|
77793
|
my ($gaps, $dec) = @_; |
136
|
69828
|
|
|
|
|
68081
|
my $num = 0; |
137
|
69828
|
50
|
|
|
|
147088
|
@$gaps == @$dec or die "Nibble and decode size not the same!"; |
138
|
69828
|
|
|
|
|
119217
|
for (0..$#$gaps) |
139
|
|
|
|
|
|
|
{ |
140
|
200080
|
|
|
|
|
311432
|
$num += $gaps->[$_] * $dec->[$_]; |
141
|
|
|
|
|
|
|
} |
142
|
69828
|
|
|
|
|
277753
|
chr( $num % 256 ); |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=head2 juniper_encrypt($secret) |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Encrypt the plain text C<$secret>, returning a result suitable for |
148
|
|
|
|
|
|
|
inclusion in a Juniper configuration. |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=cut |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub juniper_encrypt { |
153
|
3684
|
|
|
3684
|
1
|
743589
|
my ($plain, $salt) = @_; |
154
|
|
|
|
|
|
|
|
155
|
3684
|
100
|
|
|
|
9649
|
defined $salt or $salt = _randc(1); |
156
|
3684
|
|
|
|
|
8161
|
my $rand = _randc($EXTRA{$salt}); |
157
|
|
|
|
|
|
|
|
158
|
3684
|
|
|
|
|
4735
|
my $pos = 0; |
159
|
3684
|
|
|
|
|
4105
|
my $prev = $salt; |
160
|
3684
|
|
|
|
|
5862
|
my $crypt = "$MAGIC$salt$rand"; |
161
|
|
|
|
|
|
|
|
162
|
3684
|
|
|
|
|
21347
|
for my $p (split //, $plain) |
163
|
|
|
|
|
|
|
{ |
164
|
69815
|
|
|
|
|
104029
|
my $encode = $ENCODING[ $pos % @ENCODING ]; |
165
|
69815
|
|
|
|
|
112919
|
$crypt .= _gap_encode($p, $prev, $encode); |
166
|
69815
|
|
|
|
|
103633
|
$prev = substr($crypt, -1); |
167
|
69815
|
|
|
|
|
96364
|
$pos++; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
3684
|
|
|
|
|
16954
|
return $crypt; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
## return a random number of characters from our alphabet |
174
|
|
|
|
|
|
|
sub _randc { |
175
|
4684
|
|
100
|
4684
|
|
12105
|
my $cnt = shift || 0; |
176
|
4684
|
|
|
|
|
5945
|
my $r = ''; |
177
|
|
|
|
|
|
|
|
178
|
4684
|
|
|
|
|
25427
|
$r .= $NUM_ALPHA[ int rand $#NUM_ALPHA ] |
179
|
|
|
|
|
|
|
while ($cnt-- > 0); |
180
|
|
|
|
|
|
|
|
181
|
4684
|
|
|
|
|
10544
|
$r; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
## encode a plain-text character with a series of gaps, |
185
|
|
|
|
|
|
|
## according to the current encoder. |
186
|
|
|
|
|
|
|
sub _gap_encode { |
187
|
69815
|
|
|
69815
|
|
101941
|
my ($pc, $prev, $enc) = @_; |
188
|
69815
|
|
|
|
|
80341
|
my $ord = ord($pc); |
189
|
|
|
|
|
|
|
|
190
|
69815
|
|
|
|
|
74242
|
my $crypt = ''; |
191
|
69815
|
|
|
|
|
62013
|
my @gaps; |
192
|
|
|
|
|
|
|
|
193
|
69815
|
|
|
|
|
91148
|
for my $mod (reverse @$enc) |
194
|
|
|
|
|
|
|
{ |
195
|
200043
|
|
|
|
|
273231
|
unshift @gaps, int($ord/$mod); |
196
|
200043
|
|
|
|
|
274607
|
$ord %= $mod; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
69815
|
|
|
|
|
103115
|
for my $gap (@gaps) |
200
|
|
|
|
|
|
|
{ |
201
|
200043
|
|
|
|
|
237127
|
$gap += $ALPHA_NUM{$prev} + 1; |
202
|
200043
|
|
|
|
|
275187
|
my $c = $prev = $NUM_ALPHA[ $gap % @NUM_ALPHA ]; |
203
|
200043
|
|
|
|
|
309056
|
$crypt .= $c; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
69815
|
|
|
|
|
153802
|
return $crypt; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head1 AUTHOR |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
kevin brintnall, C<< >> |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Copyright 2008 kevin brintnall, all rights reserved. |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
218
|
|
|
|
|
|
|
under the same terms as Perl itself. |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=cut |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
1; # End of Crypt::Juniper |