line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Crypt::RSA::Key::Private::SSH::Buffer; |
2
|
2
|
|
|
2
|
|
1198
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
47
|
|
3
|
2
|
|
|
2
|
|
9
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
49
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
## Crypt::RSA::Key::Private::SSH |
6
|
|
|
|
|
|
|
## |
7
|
|
|
|
|
|
|
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved. |
8
|
|
|
|
|
|
|
## This code is free software; you can redistribute it and/or modify |
9
|
|
|
|
|
|
|
## it under the same terms as Perl itself. |
10
|
|
|
|
|
|
|
|
11
|
2
|
|
|
2
|
|
9
|
use Crypt::RSA::DataFormat qw( os2ip bitsize i2osp ); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
79
|
|
12
|
2
|
|
|
2
|
|
843
|
use Data::Buffer; |
|
2
|
|
|
|
|
2134
|
|
|
2
|
|
|
|
|
50
|
|
13
|
2
|
|
|
2
|
|
11
|
use base qw( Data::Buffer ); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
296
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub get_mp_int { |
16
|
8
|
|
|
8
|
|
15
|
my $buf = shift; |
17
|
8
|
|
|
|
|
14
|
my $off = $buf->{offset}; |
18
|
8
|
|
|
|
|
23
|
my $bits = unpack "n", $buf->bytes($off, 2); |
19
|
8
|
|
|
|
|
87
|
my $bytes = int(($bits+7)/8); |
20
|
8
|
|
|
|
|
21
|
my $p = os2ip( $buf->bytes($off+2, $bytes) ); |
21
|
8
|
|
|
|
|
11389
|
$buf->{offset} += 2 + $bytes; |
22
|
8
|
|
|
|
|
50
|
$p; |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub put_mp_int { |
26
|
6
|
|
|
6
|
|
11
|
my $buf = shift; |
27
|
6
|
|
|
|
|
10
|
my $int = shift; |
28
|
6
|
|
|
|
|
19
|
my $bits = bitsize($int); |
29
|
6
|
|
|
|
|
24
|
$buf->put_int16($bits); |
30
|
6
|
|
|
|
|
45
|
$buf->put_chars( i2osp($int) ); |
31
|
|
|
|
|
|
|
} |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
package Crypt::RSA::Key::Private::SSH; |
35
|
2
|
|
|
2
|
|
11
|
use strict; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
30
|
|
36
|
2
|
|
|
2
|
|
8
|
use warnings; |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
67
|
|
37
|
2
|
|
|
2
|
|
12
|
use constant PRIVKEY_ID => "SSH PRIVATE KEY FILE FORMAT 1.1\n"; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
146
|
|
38
|
2
|
|
|
2
|
|
10
|
use vars qw( %CIPHERS %CIPHERS_TEXT ); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
190
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# Having to name all the ciphers here is not extensible, but we're stuck |
41
|
|
|
|
|
|
|
# with it given the RSA1 format. I don't think any of this is standardized. |
42
|
|
|
|
|
|
|
# OpenSSH supports only: none, des, 3des, and blowfish here. This set of |
43
|
|
|
|
|
|
|
# numbers below 10 match. Values above 10 are well supported by Perl modules. |
44
|
|
|
|
|
|
|
BEGIN { |
45
|
|
|
|
|
|
|
# CIPHERS : Used by deserialize to map numbers to modules. |
46
|
2
|
|
|
2
|
|
35
|
%CIPHERS = ( |
47
|
|
|
|
|
|
|
# 0 = none |
48
|
|
|
|
|
|
|
1 => [ 'IDEA' ], |
49
|
|
|
|
|
|
|
2 => [ 'DES', 'DES_PP' ], |
50
|
|
|
|
|
|
|
3 => [ 'DES_EDE3' ], |
51
|
|
|
|
|
|
|
# From what I can see, none of the 3+ RC4 modules are CBC compatible |
52
|
|
|
|
|
|
|
# 5 => [ 'RC4' ], |
53
|
|
|
|
|
|
|
6 => [ 'Blowfish', 'Blowfish_PP' ], |
54
|
|
|
|
|
|
|
10 => [ 'Twofish2' ], |
55
|
|
|
|
|
|
|
11 => [ 'CAST5', 'CAST5_PP' ], |
56
|
|
|
|
|
|
|
12 => [ 'Rijndael', 'OpenSSL::AES' ], |
57
|
|
|
|
|
|
|
13 => [ 'RC6' ], |
58
|
|
|
|
|
|
|
14 => [ 'Camellia', 'Camellia_PP' ], |
59
|
|
|
|
|
|
|
# Crypt::Serpent is broken and abandonded. |
60
|
|
|
|
|
|
|
); |
61
|
|
|
|
|
|
|
# CIPHERS_TEXT : Used by serialize to map names to modules to numbers |
62
|
2
|
|
|
|
|
66
|
%CIPHERS_TEXT = ( |
63
|
|
|
|
|
|
|
'NONE' => 0, |
64
|
|
|
|
|
|
|
'IDEA' => 1, |
65
|
|
|
|
|
|
|
'DES' => 2, |
66
|
|
|
|
|
|
|
'DES_EDE3' => 3, |
67
|
|
|
|
|
|
|
'DES3' => 3, |
68
|
|
|
|
|
|
|
'3DES' => 3, |
69
|
|
|
|
|
|
|
'TRIPLEDES' => 3, |
70
|
|
|
|
|
|
|
# 'RC4' => 5, |
71
|
|
|
|
|
|
|
# 'ARC4' => 5, |
72
|
|
|
|
|
|
|
# 'ARCFOUR' => 5, |
73
|
|
|
|
|
|
|
'BLOWFISH' => 6, |
74
|
|
|
|
|
|
|
'TWOFISH' => 10, |
75
|
|
|
|
|
|
|
'TWOFISH2' => 10, |
76
|
|
|
|
|
|
|
'CAST5' => 11, |
77
|
|
|
|
|
|
|
'CAST5_PP' => 11, |
78
|
|
|
|
|
|
|
'CAST5PP' => 11, |
79
|
|
|
|
|
|
|
'CAST-5' => 11, |
80
|
|
|
|
|
|
|
'CAST-128' => 11, |
81
|
|
|
|
|
|
|
'CAST128' => 11, |
82
|
|
|
|
|
|
|
'RIJNDAEL' => 12, |
83
|
|
|
|
|
|
|
'AES' => 12, |
84
|
|
|
|
|
|
|
'OPENSSL::AES'=>12, |
85
|
|
|
|
|
|
|
'RC6' => 13, |
86
|
|
|
|
|
|
|
'CAMELLIA' => 14, |
87
|
|
|
|
|
|
|
); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
2
|
|
|
2
|
|
15
|
use Carp qw( croak ); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
77
|
|
91
|
2
|
|
|
2
|
|
9
|
use Data::Buffer; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
34
|
|
92
|
2
|
|
|
2
|
|
11
|
use Crypt::CBC 2.17; # We want a good version |
|
2
|
|
|
|
|
46
|
|
|
2
|
|
|
|
|
39
|
|
93
|
2
|
|
|
2
|
|
10
|
use Crypt::RSA::Key::Private; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
36
|
|
94
|
2
|
|
|
2
|
|
8
|
use base qw( Crypt::RSA::Key::Private ); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
1470
|
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub deserialize { |
97
|
2
|
|
|
2
|
1
|
1052
|
my($key, %params) = @_; |
98
|
|
|
|
|
|
|
my $passphrase = defined $params{Password} ? $params{Password} |
99
|
2
|
0
|
|
|
|
10
|
: defined $key->Password ? $key->Password |
|
|
50
|
|
|
|
|
|
100
|
|
|
|
|
|
|
: ''; |
101
|
2
|
|
|
|
|
5
|
my $string = $params{String}; |
102
|
2
|
50
|
|
|
|
7
|
croak "Must supply String=>'blob' to deserialize" unless defined $string; |
103
|
2
|
50
|
|
|
|
18
|
$string = join('', @$string) if ref($string) eq 'ARRAY'; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
croak "Cowardly refusing to deserialize on top of a hidden key" |
106
|
2
|
50
|
|
|
|
14
|
if $key->{Hidden}; |
107
|
|
|
|
|
|
|
|
108
|
2
|
|
|
|
|
15
|
my $buffer = new Crypt::RSA::Key::Private::SSH::Buffer; |
109
|
2
|
|
|
|
|
24
|
$buffer->append($string); |
110
|
|
|
|
|
|
|
|
111
|
2
|
|
|
|
|
17
|
my $id = $buffer->bytes(0, length(PRIVKEY_ID), ''); |
112
|
2
|
50
|
|
|
|
29
|
croak "Bad key file format" unless $id eq PRIVKEY_ID; |
113
|
2
|
|
|
|
|
7
|
$buffer->bytes(0, 1, ''); |
114
|
|
|
|
|
|
|
|
115
|
2
|
|
|
|
|
24
|
my $cipher_type = $buffer->get_int8; |
116
|
2
|
|
|
|
|
39
|
$buffer->get_int32; ## Reserved data. |
117
|
|
|
|
|
|
|
|
118
|
2
|
|
|
|
|
38
|
$buffer->get_int32; ## Private key bits. |
119
|
2
|
|
|
|
|
29
|
$key->n( $buffer->get_mp_int ); |
120
|
2
|
|
|
|
|
6
|
$key->e( $buffer->get_mp_int ); |
121
|
|
|
|
|
|
|
|
122
|
2
|
|
|
|
|
11
|
$key->Identity( $buffer->get_str ); ## Comment. |
123
|
|
|
|
|
|
|
|
124
|
2
|
50
|
|
|
|
8
|
if ($cipher_type != 0) { |
125
|
2
|
50
|
|
|
|
10
|
my $cipher_names = $CIPHERS{$cipher_type} or |
126
|
|
|
|
|
|
|
croak "Unknown cipher '$cipher_type' used in key file"; |
127
|
2
|
|
|
|
|
3
|
my $cipher_name; |
128
|
2
|
|
|
|
|
6
|
foreach my $name (@$cipher_names) { |
129
|
2
|
|
|
|
|
5
|
my $class = "Crypt::$name"; |
130
|
2
|
|
|
|
|
13
|
(my $file = $class) =~ s=::|'=/=g; |
131
|
2
|
50
|
|
|
|
5
|
if ( eval { require "$file.pm"; 1 } ) { |
|
2
|
|
|
|
|
17
|
|
|
2
|
|
|
|
|
7
|
|
132
|
2
|
|
|
|
|
5
|
$cipher_name = $name; last; |
|
2
|
|
|
|
|
5
|
|
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
} |
135
|
2
|
50
|
|
|
|
6
|
if (!defined $cipher_name) { |
136
|
0
|
|
|
|
|
0
|
croak "Unsupported cipher '$cipher_names->[0]': $@"; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
2
|
|
|
|
|
13
|
my $cipher = Crypt::CBC->new( -key => $passphrase, |
140
|
|
|
|
|
|
|
-cipher => $cipher_name ); |
141
|
2
|
|
|
|
|
264
|
my $decrypted = |
142
|
|
|
|
|
|
|
$cipher->decrypt($buffer->bytes($buffer->offset)); |
143
|
2
|
|
|
|
|
1073
|
$buffer->empty; |
144
|
2
|
|
|
|
|
17
|
$buffer->append($decrypted); |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
2
|
|
|
|
|
17
|
my $check1 = $buffer->get_int8; |
148
|
2
|
|
|
|
|
33
|
my $check2 = $buffer->get_int8; |
149
|
2
|
100
|
66
|
|
|
28
|
unless ($check1 == $buffer->get_int8 && |
150
|
|
|
|
|
|
|
$check2 == $buffer->get_int8) { |
151
|
1
|
|
|
|
|
203
|
croak "Bad passphrase supplied for key file"; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
1
|
|
|
|
|
33
|
$key->d( $buffer->get_mp_int ); |
155
|
1
|
|
|
|
|
4
|
$key->u( $buffer->get_mp_int ); |
156
|
1
|
|
|
|
|
44
|
$key->p( $buffer->get_mp_int ); |
157
|
1
|
|
|
|
|
4
|
$key->q( $buffer->get_mp_int ); |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# Restore other variables. |
160
|
1
|
|
|
|
|
5
|
$key->phi( ($key->p - 1) * ($key->q - 1) ); |
161
|
1
|
|
|
|
|
7
|
$key->dp( $key->d % ($key->p - 1) ); |
162
|
1
|
|
|
|
|
6
|
$key->dq( $key->d % ($key->q - 1) ); |
163
|
|
|
|
|
|
|
# Our passphrase may be just temporary for the serialization, and have |
164
|
|
|
|
|
|
|
# nothing to do with the key. So don't store it. |
165
|
|
|
|
|
|
|
#$key->{Password} = $passphrase unless defined $key->{Password}; |
166
|
|
|
|
|
|
|
|
167
|
1
|
|
|
|
|
6
|
$key; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub serialize { |
172
|
1
|
|
|
1
|
1
|
14
|
my($key, %params) = @_; |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# We could reveal it, but (1) what if it was hidden with a different |
175
|
|
|
|
|
|
|
# password, and (2) they may not want to revealed (even if hidden after). |
176
|
|
|
|
|
|
|
croak "Cowardly refusing to serialize a hidden key" |
177
|
1
|
50
|
|
|
|
5
|
if $key->{Hidden}; |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
my $passphrase = defined $params{Password} ? $params{Password} |
180
|
1
|
0
|
|
|
|
5
|
: defined $key->Password ? $key->Password |
|
|
50
|
|
|
|
|
|
181
|
|
|
|
|
|
|
: ''; |
182
|
|
|
|
|
|
|
my $cipher_name = defined $params{Cipher} ? $params{Cipher} |
183
|
1
|
0
|
|
|
|
6
|
: defined $key->Cipher ? $key->Cipher |
|
|
50
|
|
|
|
|
|
184
|
|
|
|
|
|
|
: 'Blowfish'; |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# If they've given us no passphrase, we will be unencrypted. |
187
|
1
|
|
|
|
|
2
|
my $cipher_type = 0; |
188
|
|
|
|
|
|
|
|
189
|
1
|
50
|
|
|
|
5
|
if ($passphrase ne '') { |
190
|
1
|
|
|
|
|
6
|
$cipher_type = $CIPHERS_TEXT{ uc $cipher_name }; |
191
|
1
|
50
|
|
|
|
4
|
croak "Unknown cipher: '$cipher_name'" unless defined $cipher_type; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
1
|
|
|
|
|
38
|
my $buffer = new Crypt::RSA::Key::Private::SSH::Buffer; |
195
|
1
|
|
|
|
|
15
|
my($check1, $check2); |
196
|
1
|
|
|
|
|
89
|
$buffer->put_int8($check1 = int rand 255); |
197
|
1
|
|
|
|
|
18
|
$buffer->put_int8($check2 = int rand 255); |
198
|
1
|
|
|
|
|
8
|
$buffer->put_int8($check1); |
199
|
1
|
|
|
|
|
22
|
$buffer->put_int8($check2); |
200
|
|
|
|
|
|
|
|
201
|
1
|
|
|
|
|
11
|
$buffer->put_mp_int($key->d); |
202
|
1
|
|
|
|
|
15
|
$buffer->put_mp_int($key->u); |
203
|
1
|
|
|
|
|
14
|
$buffer->put_mp_int($key->p); |
204
|
1
|
|
|
|
|
12
|
$buffer->put_mp_int($key->q); |
205
|
|
|
|
|
|
|
|
206
|
1
|
|
|
|
|
14
|
$buffer->put_int8(0) |
207
|
|
|
|
|
|
|
while $buffer->length % 8; |
208
|
|
|
|
|
|
|
|
209
|
1
|
|
|
|
|
38
|
my $encrypted = new Crypt::RSA::Key::Private::SSH::Buffer; |
210
|
1
|
|
|
|
|
25
|
$encrypted->put_chars(PRIVKEY_ID); |
211
|
1
|
|
|
|
|
10
|
$encrypted->put_int8(0); |
212
|
1
|
|
|
|
|
8
|
$encrypted->put_int8($cipher_type); |
213
|
1
|
|
|
|
|
10
|
$encrypted->put_int32(0); |
214
|
|
|
|
|
|
|
|
215
|
1
|
|
|
|
|
12
|
$encrypted->put_int32(Crypt::RSA::DataFormat::bitsize($key->n)); |
216
|
1
|
|
|
|
|
10
|
$encrypted->put_mp_int($key->n); |
217
|
1
|
|
|
|
|
14
|
$encrypted->put_mp_int($key->e); |
218
|
1
|
|
50
|
|
|
12
|
$encrypted->put_str($key->Identity || ''); |
219
|
|
|
|
|
|
|
|
220
|
1
|
50
|
|
|
|
17
|
if ($cipher_type) { |
221
|
1
|
|
|
|
|
4
|
my $cipher_names = $CIPHERS{$cipher_type}; |
222
|
1
|
|
|
|
|
3
|
my $cipher_name; |
223
|
1
|
|
|
|
|
3
|
foreach my $name (@$cipher_names) { |
224
|
1
|
|
|
|
|
3
|
my $class = "Crypt::$name"; |
225
|
1
|
|
|
|
|
9
|
(my $file = $class) =~ s=::|'=/=g; |
226
|
1
|
50
|
|
|
|
5
|
if ( eval { require "$file.pm"; 1 } ) { |
|
1
|
|
|
|
|
628
|
|
|
1
|
|
|
|
|
793
|
|
227
|
1
|
|
|
|
|
3
|
$cipher_name = $name; last; |
|
1
|
|
|
|
|
3
|
|
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
} |
230
|
1
|
50
|
|
|
|
5
|
if (!defined $cipher_name) { |
231
|
0
|
|
|
|
|
0
|
croak "Unsupported cipher '$cipher_names->[0]': $@"; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
1
|
|
|
|
|
12
|
my $cipher = Crypt::CBC->new( -key => $passphrase, |
235
|
|
|
|
|
|
|
-cipher => $cipher_name ); |
236
|
1
|
|
|
|
|
163
|
$encrypted->append( $cipher->encrypt($buffer->bytes) ); |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
else { |
239
|
0
|
|
|
|
|
0
|
$encrypted->append($buffer->bytes); |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
1
|
|
|
|
|
1333
|
$encrypted->bytes; |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
0
|
1
|
|
sub hide {} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=head1 NAME |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Crypt::RSA::Key::Private::SSH - SSH Private Key Import |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=head1 SYNOPSIS |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
Crypt::RSA::Key::Private::SSH is a class derived from |
255
|
|
|
|
|
|
|
Crypt::RSA::Key::Private that provides serialize() and |
256
|
|
|
|
|
|
|
deserialize() methods for SSH keys in the SSH1 format. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
Alternative formats (SSH2, PEM) are not implemented. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=head1 AUTHOR |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
Vipul Ved Prakash, Email@vipul.netE wrote the original version. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
Dana Jacobsen Edana@acm.orgE wrote the new version. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=cut |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
1; |