| 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 |