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