net-ssh 5.2.0 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.dockerignore +6 -0
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +87 -0
- data/.github/workflows/rubocop.yml +13 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +16 -2
- data/.rubocop_todo.yml +623 -511
- data/CHANGES.txt +50 -2
- data/Dockerfile +27 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/Manifest +0 -1
- data/README.md +293 -0
- data/Rakefile +6 -2
- data/appveyor.yml +4 -2
- data/docker-compose.yml +23 -0
- data/lib/net/ssh/authentication/agent.rb +29 -13
- data/lib/net/ssh/authentication/certificate.rb +19 -7
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +13 -8
- data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
- data/lib/net/ssh/authentication/key_manager.rb +73 -32
- data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
- data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +5 -3
- data/lib/net/ssh/authentication/methods/none.rb +6 -9
- data/lib/net/ssh/authentication/methods/password.rb +2 -3
- data/lib/net/ssh/authentication/methods/publickey.rb +56 -16
- data/lib/net/ssh/authentication/pageant.rb +97 -97
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -3
- data/lib/net/ssh/authentication/session.rb +27 -23
- data/lib/net/ssh/buffer.rb +51 -40
- data/lib/net/ssh/buffered_io.rb +24 -26
- data/lib/net/ssh/config.rb +82 -50
- data/lib/net/ssh/connection/channel.rb +101 -87
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -25
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +115 -111
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +10 -13
- data/lib/net/ssh/known_hosts.rb +106 -39
- data/lib/net/ssh/loggable.rb +10 -11
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -2
- data/lib/net/ssh/proxy/errors.rb +2 -4
- data/lib/net/ssh/proxy/http.rb +18 -20
- data/lib/net/ssh/proxy/https.rb +8 -10
- data/lib/net/ssh/proxy/jump.rb +8 -10
- data/lib/net/ssh/proxy/socks4.rb +2 -4
- data/lib/net/ssh/proxy/socks5.rb +3 -6
- data/lib/net/ssh/service/forward.rb +9 -8
- data/lib/net/ssh/test/channel.rb +24 -26
- data/lib/net/ssh/test/extensions.rb +35 -35
- data/lib/net/ssh/test/kex.rb +6 -8
- data/lib/net/ssh/test/local_packet.rb +0 -2
- data/lib/net/ssh/test/packet.rb +3 -3
- data/lib/net/ssh/test/remote_packet.rb +6 -8
- data/lib/net/ssh/test/script.rb +25 -27
- data/lib/net/ssh/test/socket.rb +12 -15
- data/lib/net/ssh/test.rb +7 -7
- data/lib/net/ssh/transport/algorithms.rb +100 -58
- data/lib/net/ssh/transport/cipher_factory.rb +34 -50
- data/lib/net/ssh/transport/constants.rb +13 -9
- data/lib/net/ssh/transport/ctr.rb +8 -14
- data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
- data/lib/net/ssh/transport/hmac/md5.rb +0 -2
- data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/none.rb +0 -2
- data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
- data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
- data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac.rb +13 -11
- data/lib/net/ssh/transport/identity_cipher.rb +11 -13
- data/lib/net/ssh/transport/kex/abstract.rb +130 -0
- data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +5 -19
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +30 -139
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -8
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +20 -81
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +5 -4
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +5 -4
- data/lib/net/ssh/transport/kex.rb +15 -10
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +149 -127
- data/lib/net/ssh/transport/packet_stream.rb +50 -16
- data/lib/net/ssh/transport/server_version.rb +17 -16
- data/lib/net/ssh/transport/session.rb +9 -7
- data/lib/net/ssh/transport/state.rb +44 -44
- data/lib/net/ssh/verifiers/accept_new.rb +0 -2
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
- data/lib/net/ssh/verifiers/always.rb +6 -4
- data/lib/net/ssh/verifiers/never.rb +0 -2
- data/lib/net/ssh/version.rb +3 -3
- data/lib/net/ssh.rb +12 -8
- data/net-ssh-public_cert.pem +8 -8
- data/net-ssh.gemspec +9 -7
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +55 -30
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -53
- data/Gemfile.noed25519.lock +0 -41
- data/README.rdoc +0 -194
- data/lib/net/ssh/ruby_compat.rb +0 -13
- data/support/arcfour_check.rb +0 -20
|
@@ -2,7 +2,6 @@ require 'openssl'
|
|
|
2
2
|
require 'net/ssh/authentication/pub_key_fingerprint'
|
|
3
3
|
|
|
4
4
|
module OpenSSL
|
|
5
|
-
|
|
6
5
|
# This class is originally defined in the OpenSSL module. As needed, methods
|
|
7
6
|
# have been added to it by the Net::SSH module for convenience in dealing with
|
|
8
7
|
# SSH functionality.
|
|
@@ -24,7 +23,6 @@ module OpenSSL
|
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
module PKey
|
|
27
|
-
|
|
28
26
|
class PKey
|
|
29
27
|
include Net::SSH::Authentication::PubKeyFingerprint
|
|
30
28
|
end
|
|
@@ -37,6 +35,7 @@ module OpenSSL
|
|
|
37
35
|
# lifted more-or-less directly from OpenSSH, dh.c, dh_pub_is_valid.)
|
|
38
36
|
def valid?
|
|
39
37
|
return false if pub_key.nil? || pub_key < 0
|
|
38
|
+
|
|
40
39
|
bits_set = 0
|
|
41
40
|
pub_key.num_bits.times { |i| bits_set += 1 if pub_key.bit_set?(i) }
|
|
42
41
|
return (bits_set > 1 && pub_key < p)
|
|
@@ -53,9 +52,7 @@ module OpenSSL
|
|
|
53
52
|
"ssh-rsa"
|
|
54
53
|
end
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
ssh_type
|
|
58
|
-
end
|
|
55
|
+
alias ssh_signature_type ssh_type
|
|
59
56
|
|
|
60
57
|
# Converts the key to a blob, according to the SSH2 protocol.
|
|
61
58
|
def to_blob
|
|
@@ -63,13 +60,30 @@ module OpenSSL
|
|
|
63
60
|
end
|
|
64
61
|
|
|
65
62
|
# Verifies the given signature matches the given data.
|
|
66
|
-
def ssh_do_verify(sig, data)
|
|
67
|
-
|
|
63
|
+
def ssh_do_verify(sig, data, options = {})
|
|
64
|
+
digester =
|
|
65
|
+
if options[:host_key] == "rsa-sha2-512"
|
|
66
|
+
OpenSSL::Digest::SHA512.new
|
|
67
|
+
elsif options[:host_key] == "rsa-sha2-256"
|
|
68
|
+
OpenSSL::Digest::SHA256.new
|
|
69
|
+
else
|
|
70
|
+
OpenSSL::Digest::SHA1.new
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
verify(digester, sig, data)
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
# Returns the signature for the given data.
|
|
71
|
-
def ssh_do_sign(data)
|
|
72
|
-
|
|
77
|
+
def ssh_do_sign(data, sig_alg = nil)
|
|
78
|
+
digester =
|
|
79
|
+
if sig_alg == "rsa-sha2-512"
|
|
80
|
+
OpenSSL::Digest::SHA512.new
|
|
81
|
+
elsif sig_alg == "rsa-sha2-256"
|
|
82
|
+
OpenSSL::Digest::SHA256.new
|
|
83
|
+
else
|
|
84
|
+
OpenSSL::Digest::SHA1.new
|
|
85
|
+
end
|
|
86
|
+
sign(digester, data)
|
|
73
87
|
end
|
|
74
88
|
end
|
|
75
89
|
|
|
@@ -83,36 +97,35 @@ module OpenSSL
|
|
|
83
97
|
"ssh-dss"
|
|
84
98
|
end
|
|
85
99
|
|
|
86
|
-
|
|
87
|
-
ssh_type
|
|
88
|
-
end
|
|
100
|
+
alias ssh_signature_type ssh_type
|
|
89
101
|
|
|
90
102
|
# Converts the key to a blob, according to the SSH2 protocol.
|
|
91
103
|
def to_blob
|
|
92
104
|
@blob ||= Net::SSH::Buffer.from(:string, ssh_type,
|
|
93
|
-
|
|
105
|
+
:bignum, p, :bignum, q, :bignum, g, :bignum, pub_key).to_s
|
|
94
106
|
end
|
|
95
107
|
|
|
96
108
|
# Verifies the given signature matches the given data.
|
|
97
|
-
def ssh_do_verify(sig, data)
|
|
98
|
-
sig_r = sig[0,20].unpack("H*")[0].to_i(16)
|
|
99
|
-
sig_s = sig[20,20].unpack("H*")[0].to_i(16)
|
|
109
|
+
def ssh_do_verify(sig, data, options = {})
|
|
110
|
+
sig_r = sig[0, 20].unpack("H*")[0].to_i(16)
|
|
111
|
+
sig_s = sig[20, 20].unpack("H*")[0].to_i(16)
|
|
100
112
|
a1sig = OpenSSL::ASN1::Sequence([
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
OpenSSL::ASN1::Integer(sig_r),
|
|
114
|
+
OpenSSL::ASN1::Integer(sig_s)
|
|
115
|
+
])
|
|
104
116
|
return verify(OpenSSL::Digest::SHA1.new, a1sig.to_der, data)
|
|
105
117
|
end
|
|
106
118
|
|
|
107
119
|
# Signs the given data.
|
|
108
|
-
def ssh_do_sign(data)
|
|
120
|
+
def ssh_do_sign(data, sig_alg = nil)
|
|
109
121
|
sig = sign(OpenSSL::Digest::SHA1.new, data)
|
|
110
122
|
a1sig = OpenSSL::ASN1.decode(sig)
|
|
111
123
|
|
|
112
124
|
sig_r = a1sig.value[0].value.to_s(2)
|
|
113
125
|
sig_s = a1sig.value[1].value.to_s(2)
|
|
114
126
|
|
|
115
|
-
|
|
127
|
+
sig_size = params["q"].num_bits / 8
|
|
128
|
+
raise OpenSSL::PKey::DSAError, "bad sig size" if sig_r.length > sig_size || sig_s.length > sig_size
|
|
116
129
|
|
|
117
130
|
sig_r = "\0" * (20 - sig_r.length) + sig_r if sig_r.length < 20
|
|
118
131
|
sig_s = "\0" * (20 - sig_s.length) + sig_s if sig_s.length < 20
|
|
@@ -121,132 +134,141 @@ module OpenSSL
|
|
|
121
134
|
end
|
|
122
135
|
end
|
|
123
136
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
begin
|
|
146
|
-
key = OpenSSL::PKey::EC.new(OpenSSL::PKey::EC::CurveNameAlias[curve_name_in_key])
|
|
147
|
-
group = key.group
|
|
148
|
-
point = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new(public_key_oct, 2))
|
|
149
|
-
key.public_key = point
|
|
150
|
-
|
|
151
|
-
return key
|
|
152
|
-
rescue OpenSSL::PKey::ECError
|
|
153
|
-
raise NotImplementedError, "unsupported key type `#{type}'"
|
|
154
|
-
end
|
|
137
|
+
# This class is originally defined in the OpenSSL module. As needed, methods
|
|
138
|
+
# have been added to it by the Net::SSH module for convenience in dealing
|
|
139
|
+
# with SSH functionality.
|
|
140
|
+
class EC
|
|
141
|
+
CurveNameAlias = {
|
|
142
|
+
'nistp256' => 'prime256v1',
|
|
143
|
+
'nistp384' => 'secp384r1',
|
|
144
|
+
'nistp521' => 'secp521r1'
|
|
145
|
+
}.freeze
|
|
146
|
+
|
|
147
|
+
CurveNameAliasInv = {
|
|
148
|
+
'prime256v1' => 'nistp256',
|
|
149
|
+
'secp384r1' => 'nistp384',
|
|
150
|
+
'secp521r1' => 'nistp521'
|
|
151
|
+
}.freeze
|
|
152
|
+
|
|
153
|
+
def self.read_keyblob(curve_name_in_type, buffer)
|
|
154
|
+
curve_name_in_key = buffer.read_string
|
|
155
|
+
|
|
156
|
+
unless curve_name_in_type == curve_name_in_key
|
|
157
|
+
raise Net::SSH::Exception, "curve name mismatched (`#{curve_name_in_key}' with `#{curve_name_in_type}')"
|
|
155
158
|
end
|
|
156
159
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
public_key_oct = buffer.read_string
|
|
161
|
+
begin
|
|
162
|
+
curvename = OpenSSL::PKey::EC::CurveNameAlias[curve_name_in_key]
|
|
163
|
+
group = OpenSSL::PKey::EC::Group.new(curvename)
|
|
164
|
+
point = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new(public_key_oct, 2))
|
|
165
|
+
asn1 = OpenSSL::ASN1::Sequence(
|
|
166
|
+
[
|
|
167
|
+
OpenSSL::ASN1::Sequence(
|
|
168
|
+
[
|
|
169
|
+
OpenSSL::ASN1::ObjectId("id-ecPublicKey"),
|
|
170
|
+
OpenSSL::ASN1::ObjectId(curvename)
|
|
171
|
+
]
|
|
172
|
+
),
|
|
173
|
+
OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed))
|
|
174
|
+
]
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
key = OpenSSL::PKey::EC.new(asn1.to_der)
|
|
178
|
+
|
|
179
|
+
return key
|
|
180
|
+
rescue OpenSSL::PKey::ECError
|
|
181
|
+
raise NotImplementedError, "unsupported key type `#{type}'"
|
|
161
182
|
end
|
|
183
|
+
end
|
|
162
184
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
185
|
+
# Returns the description of this key type used by the
|
|
186
|
+
# SSH2 protocol, like "ecdsa-sha2-nistp256"
|
|
187
|
+
def ssh_type
|
|
188
|
+
"ecdsa-sha2-#{CurveNameAliasInv[group.curve_name]}"
|
|
189
|
+
end
|
|
166
190
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
OpenSSL::Digest::SHA384.new
|
|
174
|
-
else
|
|
175
|
-
OpenSSL::Digest::SHA512.new
|
|
176
|
-
end
|
|
177
|
-
else
|
|
191
|
+
alias ssh_signature_type ssh_type
|
|
192
|
+
|
|
193
|
+
def digester
|
|
194
|
+
if group.curve_name =~ /^[a-z]+(\d+)\w*\z/
|
|
195
|
+
curve_size = Regexp.last_match(1).to_i
|
|
196
|
+
if curve_size <= 256
|
|
178
197
|
OpenSSL::Digest::SHA256.new
|
|
198
|
+
elsif curve_size <= 384
|
|
199
|
+
OpenSSL::Digest::SHA384.new
|
|
200
|
+
else
|
|
201
|
+
OpenSSL::Digest::SHA512.new
|
|
179
202
|
end
|
|
203
|
+
else
|
|
204
|
+
OpenSSL::Digest::SHA256.new
|
|
180
205
|
end
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
# Converts the key to a blob, according to the SSH2 protocol.
|
|
184
|
-
def to_blob
|
|
185
|
-
@blob ||= Net::SSH::Buffer.from(:string, ssh_type,
|
|
186
|
-
:string, CurveNameAliasInv[self.group.curve_name],
|
|
187
|
-
:mstring, self.public_key.to_bn.to_s(2)).to_s
|
|
188
|
-
@blob
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Verifies the given signature matches the given data.
|
|
192
|
-
def ssh_do_verify(sig, data)
|
|
193
|
-
digest = digester.digest(data)
|
|
194
|
-
a1sig = nil
|
|
195
|
-
|
|
196
|
-
begin
|
|
197
|
-
sig_r_len = sig[0,4].unpack("H*")[0].to_i(16)
|
|
198
|
-
sig_l_len = sig[4 + sig_r_len,4].unpack("H*")[0].to_i(16)
|
|
206
|
+
end
|
|
207
|
+
private :digester
|
|
199
208
|
|
|
200
|
-
|
|
201
|
-
|
|
209
|
+
# Converts the key to a blob, according to the SSH2 protocol.
|
|
210
|
+
def to_blob
|
|
211
|
+
@blob ||= Net::SSH::Buffer.from(:string, ssh_type,
|
|
212
|
+
:string, CurveNameAliasInv[group.curve_name],
|
|
213
|
+
:mstring, public_key.to_bn.to_s(2)).to_s
|
|
214
|
+
@blob
|
|
215
|
+
end
|
|
202
216
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
217
|
+
# Verifies the given signature matches the given data.
|
|
218
|
+
def ssh_do_verify(sig, data, options = {})
|
|
219
|
+
digest = digester.digest(data)
|
|
220
|
+
a1sig = nil
|
|
221
|
+
|
|
222
|
+
begin
|
|
223
|
+
sig_r_len = sig[0, 4].unpack('H*')[0].to_i(16)
|
|
224
|
+
sig_l_len = sig[4 + sig_r_len, 4].unpack('H*')[0].to_i(16)
|
|
225
|
+
|
|
226
|
+
sig_r = sig[4, sig_r_len].unpack('H*')[0]
|
|
227
|
+
sig_s = sig[4 + sig_r_len + 4, sig_l_len].unpack('H*')[0]
|
|
228
|
+
|
|
229
|
+
a1sig = OpenSSL::ASN1::Sequence([
|
|
230
|
+
OpenSSL::ASN1::Integer(sig_r.to_i(16)),
|
|
231
|
+
OpenSSL::ASN1::Integer(sig_s.to_i(16))
|
|
232
|
+
])
|
|
233
|
+
rescue StandardError
|
|
234
|
+
end
|
|
209
235
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
end
|
|
236
|
+
if a1sig.nil?
|
|
237
|
+
return false
|
|
238
|
+
else
|
|
239
|
+
dsa_verify_asn1(digest, a1sig.to_der)
|
|
215
240
|
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Returns the signature for the given data.
|
|
244
|
+
def ssh_do_sign(data, sig_alg = nil)
|
|
245
|
+
digest = digester.digest(data)
|
|
246
|
+
sig = dsa_sign_asn1(digest)
|
|
247
|
+
a1sig = OpenSSL::ASN1.decode(sig)
|
|
216
248
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
digest = digester.digest(data)
|
|
220
|
-
sig = dsa_sign_asn1(digest)
|
|
221
|
-
a1sig = OpenSSL::ASN1.decode(sig)
|
|
249
|
+
sig_r = a1sig.value[0].value
|
|
250
|
+
sig_s = a1sig.value[1].value
|
|
222
251
|
|
|
223
|
-
|
|
224
|
-
|
|
252
|
+
Net::SSH::Buffer.from(:bignum, sig_r, :bignum, sig_s).to_s
|
|
253
|
+
end
|
|
225
254
|
|
|
226
|
-
|
|
255
|
+
class Point
|
|
256
|
+
# Returns the description of this key type used by the
|
|
257
|
+
# SSH2 protocol, like "ecdsa-sha2-nistp256"
|
|
258
|
+
def ssh_type
|
|
259
|
+
"ecdsa-sha2-#{CurveNameAliasInv[group.curve_name]}"
|
|
227
260
|
end
|
|
228
261
|
|
|
229
|
-
|
|
230
|
-
# Returns the description of this key type used by the
|
|
231
|
-
# SSH2 protocol, like "ecdsa-sha2-nistp256"
|
|
232
|
-
def ssh_type
|
|
233
|
-
"ecdsa-sha2-#{CurveNameAliasInv[self.group.curve_name]}"
|
|
234
|
-
end
|
|
262
|
+
alias ssh_signature_type ssh_type
|
|
235
263
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
end
|
|
264
|
+
# Converts the key to a blob, according to the SSH2 protocol.
|
|
265
|
+
def to_blob
|
|
266
|
+
@blob ||= Net::SSH::Buffer.from(:string, ssh_type,
|
|
267
|
+
:string, CurveNameAliasInv[group.curve_name],
|
|
268
|
+
:mstring, to_bn.to_s(2)).to_s
|
|
269
|
+
@blob
|
|
243
270
|
end
|
|
244
271
|
end
|
|
245
|
-
else
|
|
246
|
-
class OpenSSL::PKey::ECError < RuntimeError
|
|
247
|
-
# for compatibility with interpreters
|
|
248
|
-
# without EC support (i.e. JRuby)
|
|
249
|
-
end
|
|
250
272
|
end
|
|
251
273
|
end
|
|
252
274
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'net/ssh/buffered_io'
|
|
2
2
|
require 'net/ssh/errors'
|
|
3
3
|
require 'net/ssh/packet'
|
|
4
|
-
require 'net/ssh/ruby_compat'
|
|
5
4
|
require 'net/ssh/transport/cipher_factory'
|
|
6
5
|
require 'net/ssh/transport/hmac'
|
|
7
6
|
require 'net/ssh/transport/state'
|
|
@@ -9,7 +8,6 @@ require 'net/ssh/transport/state'
|
|
|
9
8
|
module Net
|
|
10
9
|
module SSH
|
|
11
10
|
module Transport
|
|
12
|
-
|
|
13
11
|
# A module that builds additional functionality onto the Net::SSH::BufferedIo
|
|
14
12
|
# module. It adds SSH encryption, compression, and packet validation, as
|
|
15
13
|
# per the SSH2 protocol. It also adds an abstraction for polling packets,
|
|
@@ -82,7 +80,7 @@ module Net
|
|
|
82
80
|
# available or not, and will return nil if there is no packet ready to be
|
|
83
81
|
# returned. If the mode parameter is :block, then this method will block
|
|
84
82
|
# until a packet is available or timeout seconds have passed.
|
|
85
|
-
def next_packet(mode
|
|
83
|
+
def next_packet(mode = :nonblock, timeout = nil)
|
|
86
84
|
case mode
|
|
87
85
|
when :nonblock then
|
|
88
86
|
packet = poll_next_packet
|
|
@@ -130,7 +128,7 @@ module Net
|
|
|
130
128
|
payload = client.compress(payload)
|
|
131
129
|
|
|
132
130
|
# the length of the packet, minus the padding
|
|
133
|
-
actual_length = 4 + payload.bytesize + 1
|
|
131
|
+
actual_length = (client.hmac.etm ? 0 : 4) + payload.bytesize + 1
|
|
134
132
|
|
|
135
133
|
# compute the padding length
|
|
136
134
|
padding_length = client.block_size - (actual_length % client.block_size)
|
|
@@ -146,11 +144,32 @@ module Net
|
|
|
146
144
|
|
|
147
145
|
padding = Array.new(padding_length) { rand(256) }.pack("C*")
|
|
148
146
|
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
if client.hmac.etm
|
|
148
|
+
debug { "using encrypt-then-mac" }
|
|
149
|
+
|
|
150
|
+
# Encrypt padding_length, payload, and padding. Take MAC
|
|
151
|
+
# from the unencrypted packet_lenght and the encrypted
|
|
152
|
+
# data.
|
|
153
|
+
length_data = [packet_length].pack("N")
|
|
154
|
+
|
|
155
|
+
unencrypted_data = [padding_length, payload, padding].pack("CA*A*")
|
|
156
|
+
|
|
157
|
+
encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
|
|
158
|
+
|
|
159
|
+
mac_data = length_data + encrypted_data
|
|
160
|
+
|
|
161
|
+
mac = client.hmac.digest([client.sequence_number, mac_data].pack("NA*"))
|
|
162
|
+
|
|
163
|
+
message = mac_data + mac
|
|
164
|
+
else
|
|
165
|
+
unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*")
|
|
166
|
+
|
|
167
|
+
mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*"))
|
|
168
|
+
|
|
169
|
+
encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
|
|
151
170
|
|
|
152
|
-
|
|
153
|
-
|
|
171
|
+
message = encrypted_data + mac
|
|
172
|
+
end
|
|
154
173
|
|
|
155
174
|
debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" }
|
|
156
175
|
enqueue(message)
|
|
@@ -195,18 +214,28 @@ module Net
|
|
|
195
214
|
# read, post-processed according to the cipher, hmac, and compression
|
|
196
215
|
# algorithms specified in the server state object, and returned as a
|
|
197
216
|
# new Packet object.
|
|
217
|
+
# rubocop:disable Metrics/AbcSize
|
|
198
218
|
def poll_next_packet
|
|
219
|
+
aad_length = server.hmac.etm ? 4 : 0
|
|
220
|
+
|
|
199
221
|
if @packet.nil?
|
|
200
222
|
minimum = server.block_size < 4 ? 4 : server.block_size
|
|
201
|
-
return nil if available < minimum
|
|
202
|
-
|
|
223
|
+
return nil if available < minimum + aad_length
|
|
224
|
+
|
|
225
|
+
data = read_available(minimum + aad_length)
|
|
203
226
|
|
|
204
227
|
# decipher it
|
|
205
|
-
|
|
206
|
-
|
|
228
|
+
if server.hmac.etm
|
|
229
|
+
@packet_length = data.unpack("N").first
|
|
230
|
+
@mac_data = data
|
|
231
|
+
@packet = Net::SSH::Buffer.new(server.update_cipher(data[aad_length..-1]))
|
|
232
|
+
else
|
|
233
|
+
@packet = Net::SSH::Buffer.new(server.update_cipher(data))
|
|
234
|
+
@packet_length = @packet.read_long
|
|
235
|
+
end
|
|
207
236
|
end
|
|
208
237
|
|
|
209
|
-
need = @packet_length + 4 - server.block_size
|
|
238
|
+
need = @packet_length + 4 - aad_length - server.block_size
|
|
210
239
|
raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0
|
|
211
240
|
|
|
212
241
|
return nil if available < need + server.hmac.mac_length
|
|
@@ -214,6 +243,7 @@ module Net
|
|
|
214
243
|
if need > 0
|
|
215
244
|
# read the remainder of the packet and decrypt it.
|
|
216
245
|
data = read_available(need)
|
|
246
|
+
@mac_data += data if server.hmac.etm
|
|
217
247
|
@packet.append(server.update_cipher(data))
|
|
218
248
|
end
|
|
219
249
|
|
|
@@ -226,8 +256,12 @@ module Net
|
|
|
226
256
|
|
|
227
257
|
payload = @packet.read(@packet_length - padding_length - 1)
|
|
228
258
|
|
|
229
|
-
my_computed_hmac = server.hmac.
|
|
230
|
-
|
|
259
|
+
my_computed_hmac = if server.hmac.etm
|
|
260
|
+
server.hmac.digest([server.sequence_number, @mac_data].pack("NA*"))
|
|
261
|
+
else
|
|
262
|
+
server.hmac.digest([server.sequence_number, @packet.content].pack("NA*"))
|
|
263
|
+
end
|
|
264
|
+
raise Net::SSH::Exception, "corrupted hmac detected #{server.hmac.class}" if real_hmac != my_computed_hmac
|
|
231
265
|
|
|
232
266
|
# try to decompress the payload, in case compression is active
|
|
233
267
|
payload = server.decompress(payload)
|
|
@@ -240,7 +274,7 @@ module Net
|
|
|
240
274
|
return Packet.new(payload)
|
|
241
275
|
end
|
|
242
276
|
end
|
|
243
|
-
|
|
277
|
+
# rubocop:enable Metrics/AbcSize
|
|
244
278
|
end
|
|
245
279
|
end
|
|
246
280
|
end
|
|
@@ -2,10 +2,9 @@ require 'net/ssh/errors'
|
|
|
2
2
|
require 'net/ssh/loggable'
|
|
3
3
|
require 'net/ssh/version'
|
|
4
4
|
|
|
5
|
-
module Net
|
|
6
|
-
module SSH
|
|
5
|
+
module Net
|
|
6
|
+
module SSH
|
|
7
7
|
module Transport
|
|
8
|
-
|
|
9
8
|
# Negotiates the SSH protocol version and trades information about server
|
|
10
9
|
# and client. This is never used directly--it is always called by the
|
|
11
10
|
# transport layer as part of the initialization process of the transport
|
|
@@ -15,40 +14,41 @@ module Net
|
|
|
15
14
|
# the authoritative reference for any queries regarding the version in effect.
|
|
16
15
|
class ServerVersion
|
|
17
16
|
include Loggable
|
|
18
|
-
|
|
17
|
+
|
|
19
18
|
# The SSH version string as reported by Net::SSH
|
|
20
19
|
PROTO_VERSION = "SSH-2.0-Ruby/Net::SSH_#{Net::SSH::Version::CURRENT} #{RUBY_PLATFORM}"
|
|
21
|
-
|
|
20
|
+
|
|
22
21
|
# Any header text sent by the server prior to sending the version.
|
|
23
22
|
attr_reader :header
|
|
24
|
-
|
|
23
|
+
|
|
25
24
|
# The version string reported by the server.
|
|
26
25
|
attr_reader :version
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
# Instantiates a new ServerVersion and immediately (and synchronously)
|
|
29
28
|
# negotiates the SSH protocol in effect, using the given socket.
|
|
30
29
|
def initialize(socket, logger, timeout = nil)
|
|
31
|
-
@header =
|
|
30
|
+
@header = String.new
|
|
32
31
|
@version = nil
|
|
33
32
|
@logger = logger
|
|
34
33
|
negotiate!(socket, timeout)
|
|
35
34
|
end
|
|
36
|
-
|
|
35
|
+
|
|
37
36
|
private
|
|
38
|
-
|
|
37
|
+
|
|
39
38
|
# Negotiates the SSH protocol to use, via the given socket. If the server
|
|
40
39
|
# reports an incompatible SSH version (e.g., SSH1), this will raise an
|
|
41
40
|
# exception.
|
|
42
41
|
def negotiate!(socket, timeout)
|
|
43
42
|
info { "negotiating protocol version" }
|
|
44
|
-
|
|
43
|
+
|
|
45
44
|
debug { "local is `#{PROTO_VERSION}'" }
|
|
46
45
|
socket.write "#{PROTO_VERSION}\r\n"
|
|
47
46
|
socket.flush
|
|
48
|
-
|
|
47
|
+
|
|
49
48
|
raise Net::SSH::ConnectionTimeout, "timeout during server version negotiating" if timeout && !IO.select([socket], nil, nil, timeout)
|
|
49
|
+
|
|
50
50
|
loop do
|
|
51
|
-
@version =
|
|
51
|
+
@version = String.new
|
|
52
52
|
loop do
|
|
53
53
|
begin
|
|
54
54
|
b = socket.readpartial(1)
|
|
@@ -60,14 +60,15 @@ module Net
|
|
|
60
60
|
break if b == "\n"
|
|
61
61
|
end
|
|
62
62
|
break if @version.match(/^SSH-/)
|
|
63
|
+
|
|
63
64
|
@header << @version
|
|
64
65
|
end
|
|
65
|
-
|
|
66
|
+
|
|
66
67
|
@version.chomp!
|
|
67
68
|
debug { "remote is `#{@version}'" }
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
raise Net::SSH::Exception, "incompatible SSH version `#{@version}'" unless @version.match(/^SSH-(1\.99|2\.0)-/)
|
|
70
|
-
|
|
71
|
+
|
|
71
72
|
raise Net::SSH::ConnectionTimeout, "timeout during client version negotiating" if timeout && !IO.select(nil, [socket], nil, timeout)
|
|
72
73
|
end
|
|
73
74
|
end
|
|
@@ -15,7 +15,6 @@ require 'net/ssh/verifiers/never'
|
|
|
15
15
|
module Net
|
|
16
16
|
module SSH
|
|
17
17
|
module Transport
|
|
18
|
-
|
|
19
18
|
# The transport layer represents the lowest level of the SSH protocol, and
|
|
20
19
|
# implements basic message exchanging and protocol initialization. It will
|
|
21
20
|
# never be instantiated directly (unless you really know what you're about),
|
|
@@ -56,7 +55,7 @@ module Net
|
|
|
56
55
|
# Instantiates a new transport layer abstraction. This will block until
|
|
57
56
|
# the initial key exchange completes, leaving you with a ready-to-use
|
|
58
57
|
# transport session.
|
|
59
|
-
def initialize(host, options={})
|
|
58
|
+
def initialize(host, options = {})
|
|
60
59
|
self.logger = options[:logger]
|
|
61
60
|
|
|
62
61
|
@host = host
|
|
@@ -160,6 +159,7 @@ module Net
|
|
|
160
159
|
# one is performed, causing this method to block until it completes.
|
|
161
160
|
def rekey_as_needed
|
|
162
161
|
return if algorithms.pending?
|
|
162
|
+
|
|
163
163
|
socket.if_needs_rekey? { rekey! }
|
|
164
164
|
end
|
|
165
165
|
|
|
@@ -186,7 +186,7 @@ module Net
|
|
|
186
186
|
# received, it will be enqueued and otherwise ignored. When a key-exchange
|
|
187
187
|
# is not in process, and consume_queue is true, packets will be first
|
|
188
188
|
# read from the queue before the socket is queried.
|
|
189
|
-
def poll_message(mode
|
|
189
|
+
def poll_message(mode = :nonblock, consume_queue = true)
|
|
190
190
|
loop do
|
|
191
191
|
return @queue.shift if consume_queue && @queue.any? && algorithms.allow?(@queue.first)
|
|
192
192
|
|
|
@@ -211,6 +211,7 @@ module Net
|
|
|
211
211
|
|
|
212
212
|
else
|
|
213
213
|
return packet if algorithms.allow?(packet)
|
|
214
|
+
|
|
214
215
|
push(packet)
|
|
215
216
|
end
|
|
216
217
|
end
|
|
@@ -222,6 +223,7 @@ module Net
|
|
|
222
223
|
def wait
|
|
223
224
|
loop do
|
|
224
225
|
break if block_given? && yield
|
|
226
|
+
|
|
225
227
|
message = poll_message(:nonblock, false)
|
|
226
228
|
push(message) if message
|
|
227
229
|
break if !block_given?
|
|
@@ -250,27 +252,27 @@ module Net
|
|
|
250
252
|
# Configure's the packet stream's client state with the given set of
|
|
251
253
|
# options. This is typically used to define the cipher, compression, and
|
|
252
254
|
# hmac algorithms to use when sending packets to the server.
|
|
253
|
-
def configure_client(options={})
|
|
255
|
+
def configure_client(options = {})
|
|
254
256
|
socket.client.set(options)
|
|
255
257
|
end
|
|
256
258
|
|
|
257
259
|
# Configure's the packet stream's server state with the given set of
|
|
258
260
|
# options. This is typically used to define the cipher, compression, and
|
|
259
261
|
# hmac algorithms to use when reading packets from the server.
|
|
260
|
-
def configure_server(options={})
|
|
262
|
+
def configure_server(options = {})
|
|
261
263
|
socket.server.set(options)
|
|
262
264
|
end
|
|
263
265
|
|
|
264
266
|
# Sets a new hint for the packet stream, which the packet stream may use
|
|
265
267
|
# to change its behavior. (See PacketStream#hints).
|
|
266
|
-
def hint(which, value=true)
|
|
268
|
+
def hint(which, value = true)
|
|
267
269
|
socket.hints[which] = value
|
|
268
270
|
end
|
|
269
271
|
|
|
270
272
|
public
|
|
271
273
|
|
|
272
274
|
# this method is primarily for use in tests
|
|
273
|
-
attr_reader :queue
|
|
275
|
+
attr_reader :queue # :nodoc:
|
|
274
276
|
|
|
275
277
|
private
|
|
276
278
|
|