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
|
@@ -5,13 +5,13 @@ require 'net/ssh/transport/cipher_factory'
|
|
|
5
5
|
require 'net/ssh/transport/constants'
|
|
6
6
|
require 'net/ssh/transport/hmac'
|
|
7
7
|
require 'net/ssh/transport/kex'
|
|
8
|
+
require 'net/ssh/transport/kex/curve25519_sha256_loader'
|
|
8
9
|
require 'net/ssh/transport/server_version'
|
|
9
10
|
require 'net/ssh/authentication/ed25519_loader'
|
|
10
11
|
|
|
11
12
|
module Net
|
|
12
13
|
module SSH
|
|
13
14
|
module Transport
|
|
14
|
-
|
|
15
15
|
# Implements the higher-level logic behind an SSH key-exchange. It handles
|
|
16
16
|
# both the initial exchange, as well as subsequent re-exchanges (as needed).
|
|
17
17
|
# It also encapsulates the negotiation of the algorithms, and provides a
|
|
@@ -23,56 +23,75 @@ module Net
|
|
|
23
23
|
include Loggable
|
|
24
24
|
include Constants
|
|
25
25
|
|
|
26
|
-
# Define the default algorithms, in order of preference, supported by
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
# Define the default algorithms, in order of preference, supported by Net::SSH.
|
|
27
|
+
DEFAULT_ALGORITHMS = {
|
|
28
|
+
host_key: %w[ecdsa-sha2-nistp521-cert-v01@openssh.com
|
|
29
|
+
ecdsa-sha2-nistp384-cert-v01@openssh.com
|
|
30
|
+
ecdsa-sha2-nistp256-cert-v01@openssh.com
|
|
31
|
+
ecdsa-sha2-nistp521
|
|
32
|
+
ecdsa-sha2-nistp384
|
|
33
|
+
ecdsa-sha2-nistp256
|
|
34
|
+
ssh-rsa-cert-v01@openssh.com
|
|
30
35
|
ssh-rsa-cert-v00@openssh.com
|
|
31
|
-
ssh-rsa
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
ssh-rsa
|
|
37
|
+
rsa-sha2-256
|
|
38
|
+
rsa-sha2-512],
|
|
39
|
+
|
|
40
|
+
kex: %w[ecdh-sha2-nistp521
|
|
41
|
+
ecdh-sha2-nistp384
|
|
42
|
+
ecdh-sha2-nistp256
|
|
43
|
+
diffie-hellman-group-exchange-sha256
|
|
44
|
+
diffie-hellman-group14-sha256
|
|
45
|
+
diffie-hellman-group14-sha1],
|
|
46
|
+
|
|
47
|
+
encryption: %w[aes256-ctr aes192-ctr aes128-ctr],
|
|
48
|
+
|
|
49
|
+
hmac: %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
|
|
50
|
+
hmac-sha2-512 hmac-sha2-256
|
|
51
|
+
hmac-sha1]
|
|
52
|
+
}.freeze
|
|
53
|
+
|
|
54
|
+
if Net::SSH::Authentication::ED25519Loader::LOADED
|
|
55
|
+
DEFAULT_ALGORITHMS[:host_key].unshift(
|
|
56
|
+
'ssh-ed25519-cert-v01@openssh.com',
|
|
57
|
+
'ssh-ed25519'
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if Net::SSH::Transport::Kex::Curve25519Sha256Loader::LOADED
|
|
62
|
+
DEFAULT_ALGORITHMS[:kex].unshift(
|
|
63
|
+
'curve25519-sha256',
|
|
64
|
+
'curve25519-sha256@libssh.org'
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Define all algorithms, with the deprecated, supported by Net::SSH.
|
|
69
|
+
ALGORITHMS = {
|
|
70
|
+
host_key: DEFAULT_ALGORITHMS[:host_key] + %w[ssh-dss],
|
|
71
|
+
|
|
72
|
+
kex: DEFAULT_ALGORITHMS[:kex] +
|
|
73
|
+
%w[diffie-hellman-group-exchange-sha1
|
|
35
74
|
diffie-hellman-group1-sha1],
|
|
36
|
-
|
|
37
|
-
|
|
75
|
+
|
|
76
|
+
encryption: DEFAULT_ALGORITHMS[:encryption] +
|
|
77
|
+
%w[aes256-cbc aes192-cbc aes128-cbc
|
|
38
78
|
rijndael-cbc@lysator.liu.se
|
|
39
79
|
blowfish-ctr blowfish-cbc
|
|
40
80
|
cast128-ctr cast128-cbc
|
|
41
81
|
3des-ctr 3des-cbc
|
|
42
|
-
idea-cbc
|
|
82
|
+
idea-cbc
|
|
43
83
|
none],
|
|
44
84
|
|
|
45
|
-
hmac:
|
|
46
|
-
|
|
47
|
-
hmac-sha1
|
|
85
|
+
hmac: DEFAULT_ALGORITHMS[:hmac] +
|
|
86
|
+
%w[hmac-sha2-512-96 hmac-sha2-256-96
|
|
87
|
+
hmac-sha1-96
|
|
48
88
|
hmac-ripemd160 hmac-ripemd160@openssh.com
|
|
49
89
|
hmac-md5 hmac-md5-96
|
|
50
90
|
none],
|
|
51
91
|
|
|
52
92
|
compression: %w[none zlib@openssh.com zlib],
|
|
53
93
|
language: %w[]
|
|
54
|
-
}
|
|
55
|
-
if defined?(OpenSSL::PKey::EC)
|
|
56
|
-
ALGORITHMS[:host_key].unshift(
|
|
57
|
-
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
|
|
58
|
-
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
|
59
|
-
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
|
60
|
-
"ecdsa-sha2-nistp521",
|
|
61
|
-
"ecdsa-sha2-nistp384",
|
|
62
|
-
"ecdsa-sha2-nistp256"
|
|
63
|
-
)
|
|
64
|
-
if Net::SSH::Authentication::ED25519Loader::LOADED
|
|
65
|
-
ALGORITHMS[:host_key].unshift(
|
|
66
|
-
"ssh-ed25519-cert-v01@openssh.com",
|
|
67
|
-
"ssh-ed25519"
|
|
68
|
-
)
|
|
69
|
-
end
|
|
70
|
-
ALGORITHMS[:kex].unshift(
|
|
71
|
-
"ecdh-sha2-nistp521",
|
|
72
|
-
"ecdh-sha2-nistp384",
|
|
73
|
-
"ecdh-sha2-nistp256"
|
|
74
|
-
)
|
|
75
|
-
end
|
|
94
|
+
}.freeze
|
|
76
95
|
|
|
77
96
|
# The underlying transport layer session that supports this object
|
|
78
97
|
attr_reader :session
|
|
@@ -127,7 +146,7 @@ module Net
|
|
|
127
146
|
|
|
128
147
|
# Instantiates a new Algorithms object, and prepares the hash of preferred
|
|
129
148
|
# algorithms based on the options parameter and the ALGORITHMS constant.
|
|
130
|
-
def initialize(session, options={})
|
|
149
|
+
def initialize(session, options = {})
|
|
131
150
|
@session = session
|
|
132
151
|
@logger = session.logger
|
|
133
152
|
@options = options
|
|
@@ -140,6 +159,7 @@ module Net
|
|
|
140
159
|
# Start the algorithm negotation
|
|
141
160
|
def start
|
|
142
161
|
raise ArgumentError, "Cannot call start if it's negotiation started or done" if @pending || @initialized
|
|
162
|
+
|
|
143
163
|
send_kexinit
|
|
144
164
|
end
|
|
145
165
|
|
|
@@ -197,8 +217,8 @@ module Net
|
|
|
197
217
|
|
|
198
218
|
def host_key_format
|
|
199
219
|
case host_key
|
|
200
|
-
when
|
|
201
|
-
|
|
220
|
+
when /^([a-z0-9-]+)-cert-v\d{2}@openssh.com$/
|
|
221
|
+
Regexp.last_match[1]
|
|
202
222
|
else
|
|
203
223
|
host_key
|
|
204
224
|
end
|
|
@@ -239,7 +259,10 @@ module Net
|
|
|
239
259
|
options[:compression] = %w[zlib@openssh.com zlib] if options[:compression] == true
|
|
240
260
|
|
|
241
261
|
ALGORITHMS.each do |algorithm, supported|
|
|
242
|
-
algorithms[algorithm] = compose_algorithm_list(
|
|
262
|
+
algorithms[algorithm] = compose_algorithm_list(
|
|
263
|
+
supported, options[algorithm] || DEFAULT_ALGORITHMS[algorithm],
|
|
264
|
+
options[:append_all_supported_algorithms]
|
|
265
|
+
)
|
|
243
266
|
end
|
|
244
267
|
|
|
245
268
|
# for convention, make sure our list has the same keys as the server
|
|
@@ -255,7 +278,7 @@ module Net
|
|
|
255
278
|
# existing known key for the host has preference.
|
|
256
279
|
|
|
257
280
|
existing_keys = session.host_keys
|
|
258
|
-
host_keys = existing_keys.
|
|
281
|
+
host_keys = existing_keys.flat_map { |key| key.respond_to?(:ssh_types) ? key.ssh_types : [key.ssh_type] }.uniq
|
|
259
282
|
algorithms[:host_key].each do |name|
|
|
260
283
|
host_keys << name unless host_keys.include?(name)
|
|
261
284
|
end
|
|
@@ -270,10 +293,24 @@ module Net
|
|
|
270
293
|
list = []
|
|
271
294
|
option = Array(option).compact.uniq
|
|
272
295
|
|
|
273
|
-
if option.first && option.first.start_with?('+')
|
|
296
|
+
if option.first && option.first.start_with?('+', '-')
|
|
274
297
|
list = supported.dup
|
|
275
|
-
|
|
276
|
-
|
|
298
|
+
|
|
299
|
+
appends = option.select { |opt| opt.start_with?('+') }.map { |opt| opt[1..-1] }
|
|
300
|
+
deletions = option.select { |opt| opt.start_with?('-') }.map { |opt| opt[1..-1] }
|
|
301
|
+
|
|
302
|
+
list.concat(appends)
|
|
303
|
+
|
|
304
|
+
deletions.each do |opt|
|
|
305
|
+
if opt.include?('*')
|
|
306
|
+
opt_escaped = Regexp.escape(opt)
|
|
307
|
+
algo_re = /\A#{opt_escaped.gsub('\*', '[A-Za-z\d\-@\.]*')}\z/
|
|
308
|
+
list.delete_if { |existing_opt| algo_re.match(existing_opt) }
|
|
309
|
+
else
|
|
310
|
+
list.delete(opt)
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
277
314
|
list.uniq!
|
|
278
315
|
else
|
|
279
316
|
list = option
|
|
@@ -332,10 +369,10 @@ module Net
|
|
|
332
369
|
language = algorithms[:language].join(",")
|
|
333
370
|
|
|
334
371
|
Net::SSH::Buffer.from(:byte, KEXINIT,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
372
|
+
:long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)],
|
|
373
|
+
:mstring, [kex, host_key, encryption, encryption, hmac, hmac],
|
|
374
|
+
:mstring, [compression, compression, language, language],
|
|
375
|
+
:bool, false, :long, 0)
|
|
339
376
|
end
|
|
340
377
|
|
|
341
378
|
# Given the parsed server KEX packet, and the client's preferred algorithm
|
|
@@ -356,7 +393,8 @@ module Net
|
|
|
356
393
|
|
|
357
394
|
debug do
|
|
358
395
|
"negotiated:\n" +
|
|
359
|
-
%i[kex host_key encryption_server encryption_client hmac_client hmac_server
|
|
396
|
+
%i[kex host_key encryption_server encryption_client hmac_client hmac_server
|
|
397
|
+
compression_client compression_server language_client language_server].map do |key|
|
|
360
398
|
"* #{key}: #{instance_variable_get("@#{key}")}"
|
|
361
399
|
end.join("\n")
|
|
362
400
|
end
|
|
@@ -368,7 +406,11 @@ module Net
|
|
|
368
406
|
def negotiate(algorithm)
|
|
369
407
|
match = self[algorithm].find { |item| @server_data[algorithm].include?(item) }
|
|
370
408
|
|
|
371
|
-
|
|
409
|
+
if match.nil?
|
|
410
|
+
raise Net::SSH::Exception, "could not settle on #{algorithm} algorithm\n"\
|
|
411
|
+
"Server #{algorithm} preferences: #{@server_data[algorithm].join(',')}\n"\
|
|
412
|
+
"Client #{algorithm} preferences: #{self[algorithm].join(',')}"
|
|
413
|
+
end
|
|
372
414
|
|
|
373
415
|
return match
|
|
374
416
|
end
|
|
@@ -396,13 +438,13 @@ module Net
|
|
|
396
438
|
debug { "exchanging keys" }
|
|
397
439
|
|
|
398
440
|
algorithm = Kex::MAP[kex].new(self, session,
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
441
|
+
client_version_string: Net::SSH::Transport::ServerVersion::PROTO_VERSION,
|
|
442
|
+
server_version_string: session.server_version.version,
|
|
443
|
+
server_algorithm_packet: @server_packet,
|
|
444
|
+
client_algorithm_packet: @client_packet,
|
|
445
|
+
need_bytes: kex_byte_requirement,
|
|
446
|
+
minimum_dh_bits: options[:minimum_dh_bits],
|
|
447
|
+
logger: logger)
|
|
406
448
|
result = algorithm.exchange_keys
|
|
407
449
|
|
|
408
450
|
secret = result[:shared_secret].to_ssh
|
|
@@ -3,68 +3,56 @@ require 'net/ssh/transport/ctr.rb'
|
|
|
3
3
|
require 'net/ssh/transport/key_expander'
|
|
4
4
|
require 'net/ssh/transport/identity_cipher'
|
|
5
5
|
|
|
6
|
-
module Net
|
|
7
|
-
module SSH
|
|
6
|
+
module Net
|
|
7
|
+
module SSH
|
|
8
8
|
module Transport
|
|
9
|
-
|
|
10
9
|
# Implements a factory of OpenSSL cipher algorithms.
|
|
11
10
|
class CipherFactory
|
|
12
11
|
# Maps the SSH name of a cipher to it's corresponding OpenSSL name
|
|
13
12
|
SSH_TO_OSSL = {
|
|
14
|
-
"3des-cbc"
|
|
15
|
-
"blowfish-cbc"
|
|
16
|
-
"aes256-cbc"
|
|
17
|
-
"aes192-cbc"
|
|
18
|
-
"aes128-cbc"
|
|
19
|
-
"idea-cbc"
|
|
20
|
-
"cast128-cbc"
|
|
13
|
+
"3des-cbc" => "des-ede3-cbc",
|
|
14
|
+
"blowfish-cbc" => "bf-cbc",
|
|
15
|
+
"aes256-cbc" => "aes-256-cbc",
|
|
16
|
+
"aes192-cbc" => "aes-192-cbc",
|
|
17
|
+
"aes128-cbc" => "aes-128-cbc",
|
|
18
|
+
"idea-cbc" => "idea-cbc",
|
|
19
|
+
"cast128-cbc" => "cast-cbc",
|
|
21
20
|
"rijndael-cbc@lysator.liu.se" => "aes-256-cbc",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"aes192-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-192-ctr") ? "aes-192-ctr" : "aes-192-ecb",
|
|
32
|
-
"aes128-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-128-ctr") ? "aes-128-ctr" : "aes-128-ecb",
|
|
33
|
-
"cast128-ctr" => "cast5-ecb",
|
|
34
|
-
|
|
35
|
-
"none" => "none"
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# Ruby's OpenSSL bindings always return a key length of 16 for RC4 ciphers
|
|
39
|
-
# resulting in the error: OpenSSL::CipherError: key length too short.
|
|
40
|
-
# The following ciphers will override this key length.
|
|
41
|
-
KEY_LEN_OVERRIDE = {
|
|
42
|
-
"arcfour256" => 32,
|
|
43
|
-
"arcfour512" => 64
|
|
21
|
+
"3des-ctr" => "des-ede3",
|
|
22
|
+
"blowfish-ctr" => "bf-ecb",
|
|
23
|
+
|
|
24
|
+
"aes256-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-256-ctr") ? "aes-256-ctr" : "aes-256-ecb",
|
|
25
|
+
"aes192-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-192-ctr") ? "aes-192-ctr" : "aes-192-ecb",
|
|
26
|
+
"aes128-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-128-ctr") ? "aes-128-ctr" : "aes-128-ecb",
|
|
27
|
+
'cast128-ctr' => 'cast5-ecb',
|
|
28
|
+
|
|
29
|
+
'none' => 'none'
|
|
44
30
|
}
|
|
45
|
-
|
|
31
|
+
|
|
46
32
|
# Returns true if the underlying OpenSSL library supports the given cipher,
|
|
47
33
|
# and false otherwise.
|
|
48
34
|
def self.supported?(name)
|
|
49
35
|
ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
|
|
50
36
|
return true if ossl_name == "none"
|
|
37
|
+
|
|
51
38
|
return OpenSSL::Cipher.ciphers.include?(ossl_name)
|
|
52
39
|
end
|
|
53
|
-
|
|
40
|
+
|
|
54
41
|
# Retrieves a new instance of the named algorithm. The new instance
|
|
55
42
|
# will be initialized using an iv and key generated from the given
|
|
56
43
|
# iv, key, shared, hash and digester values. Additionally, the
|
|
57
44
|
# cipher will be put into encryption or decryption mode, based on the
|
|
58
45
|
# value of the +encrypt+ parameter.
|
|
59
|
-
def self.get(name, options={})
|
|
46
|
+
def self.get(name, options = {})
|
|
60
47
|
ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
|
|
61
48
|
return IdentityCipher if ossl_name == "none"
|
|
49
|
+
|
|
62
50
|
cipher = OpenSSL::Cipher.new(ossl_name)
|
|
63
|
-
|
|
51
|
+
|
|
64
52
|
cipher.send(options[:encrypt] ? :encrypt : :decrypt)
|
|
65
|
-
|
|
53
|
+
|
|
66
54
|
cipher.padding = 0
|
|
67
|
-
|
|
55
|
+
|
|
68
56
|
if name =~ /-ctr(@openssh.org)?$/
|
|
69
57
|
if ossl_name !~ /-ctr/
|
|
70
58
|
cipher.extend(Net::SSH::Transport::CTR)
|
|
@@ -72,16 +60,15 @@ module Net
|
|
|
72
60
|
cipher = Net::SSH::Transport::OpenSSLAESCTR.new(cipher)
|
|
73
61
|
end
|
|
74
62
|
end
|
|
75
|
-
cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options)
|
|
76
|
-
|
|
77
|
-
key_len =
|
|
63
|
+
cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options)
|
|
64
|
+
|
|
65
|
+
key_len = cipher.key_len
|
|
78
66
|
cipher.key_len = key_len
|
|
79
67
|
cipher.key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options)
|
|
80
|
-
|
|
81
|
-
|
|
68
|
+
|
|
82
69
|
return cipher
|
|
83
70
|
end
|
|
84
|
-
|
|
71
|
+
|
|
85
72
|
# Returns a two-element array containing the [ key-length,
|
|
86
73
|
# block-size ] for the named cipher algorithm. If the cipher
|
|
87
74
|
# algorithm is unknown, or is "none", 0 is returned for both elements
|
|
@@ -94,26 +81,23 @@ module Net
|
|
|
94
81
|
result << 0 if options[:iv_len]
|
|
95
82
|
else
|
|
96
83
|
cipher = OpenSSL::Cipher.new(ossl_name)
|
|
97
|
-
key_len =
|
|
84
|
+
key_len = cipher.key_len
|
|
98
85
|
cipher.key_len = key_len
|
|
99
|
-
|
|
86
|
+
|
|
100
87
|
block_size =
|
|
101
88
|
case ossl_name
|
|
102
|
-
when "rc4"
|
|
103
|
-
8
|
|
104
89
|
when /\-ctr/
|
|
105
90
|
Net::SSH::Transport::OpenSSLAESCTR.block_size
|
|
106
91
|
else
|
|
107
92
|
cipher.block_size
|
|
108
93
|
end
|
|
109
|
-
|
|
94
|
+
|
|
110
95
|
result = [key_len, block_size]
|
|
111
96
|
result << cipher.iv_len if options[:iv_len]
|
|
112
97
|
end
|
|
113
98
|
result
|
|
114
99
|
end
|
|
115
100
|
end
|
|
116
|
-
|
|
117
101
|
end
|
|
118
102
|
end
|
|
119
103
|
end
|
|
@@ -1,35 +1,39 @@
|
|
|
1
|
-
module Net
|
|
2
|
-
module SSH
|
|
1
|
+
module Net
|
|
2
|
+
module SSH
|
|
3
3
|
module Transport
|
|
4
4
|
module Constants
|
|
5
|
-
|
|
6
5
|
#--
|
|
7
6
|
# Transport layer generic messages
|
|
8
7
|
#++
|
|
9
|
-
|
|
8
|
+
|
|
10
9
|
DISCONNECT = 1
|
|
11
10
|
IGNORE = 2
|
|
12
11
|
UNIMPLEMENTED = 3
|
|
13
12
|
DEBUG = 4
|
|
14
13
|
SERVICE_REQUEST = 5
|
|
15
14
|
SERVICE_ACCEPT = 6
|
|
16
|
-
|
|
15
|
+
|
|
17
16
|
#--
|
|
18
17
|
# Algorithm negotiation messages
|
|
19
18
|
#++
|
|
20
|
-
|
|
19
|
+
|
|
21
20
|
KEXINIT = 20
|
|
22
21
|
NEWKEYS = 21
|
|
23
|
-
|
|
22
|
+
|
|
24
23
|
#--
|
|
25
24
|
# Key exchange method specific messages
|
|
26
25
|
#++
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
KEXDH_INIT = 30
|
|
29
28
|
KEXDH_REPLY = 31
|
|
30
|
-
|
|
29
|
+
|
|
31
30
|
KEXECDH_INIT = 30
|
|
32
31
|
KEXECDH_REPLY = 31
|
|
32
|
+
|
|
33
|
+
KEXDH_GEX_GROUP = 31
|
|
34
|
+
KEXDH_GEX_INIT = 32
|
|
35
|
+
KEXDH_GEX_REPLY = 33
|
|
36
|
+
KEXDH_GEX_REQUEST = 34
|
|
33
37
|
end
|
|
34
38
|
end
|
|
35
39
|
end
|
|
@@ -2,7 +2,7 @@ require 'openssl'
|
|
|
2
2
|
require 'delegate'
|
|
3
3
|
|
|
4
4
|
module Net::SSH::Transport
|
|
5
|
-
|
|
5
|
+
# :nodoc:
|
|
6
6
|
class OpenSSLAESCTR < SimpleDelegator
|
|
7
7
|
def initialize(original)
|
|
8
8
|
super
|
|
@@ -26,13 +26,13 @@ module Net::SSH::Transport
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# :nodoc:
|
|
30
30
|
# Pure-Ruby implementation of Stateful Decryption Counter(SDCTR) Mode
|
|
31
31
|
# for Block Ciphers. See RFC4344 for detail.
|
|
32
32
|
module CTR
|
|
33
33
|
def self.extended(orig)
|
|
34
34
|
orig.instance_eval {
|
|
35
|
-
@remaining =
|
|
35
|
+
@remaining = String.new
|
|
36
36
|
@counter = nil
|
|
37
37
|
@counter_len = orig.block_size
|
|
38
38
|
orig.encrypt
|
|
@@ -67,13 +67,13 @@ module Net::SSH::Transport
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def reset
|
|
70
|
-
@remaining =
|
|
70
|
+
@remaining = String.new
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def update(data)
|
|
74
74
|
@remaining += data
|
|
75
75
|
|
|
76
|
-
encrypted =
|
|
76
|
+
encrypted = String.new
|
|
77
77
|
|
|
78
78
|
offset = 0
|
|
79
79
|
while (@remaining.bytesize - offset) >= block_size
|
|
@@ -88,20 +88,14 @@ module Net::SSH::Transport
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def final
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
else
|
|
94
|
-
s = ""
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
@remaining = ""
|
|
98
|
-
|
|
91
|
+
s = @remaining.empty? ? '' : xor!(@remaining, _update(@counter))
|
|
92
|
+
@remaining = String.new
|
|
99
93
|
s
|
|
100
94
|
end
|
|
101
95
|
|
|
102
96
|
def xor!(s1, s2)
|
|
103
97
|
s = []
|
|
104
|
-
s1.unpack('Q*').zip(s2.unpack('Q*')) {|a,b| s.push(a ^ b) }
|
|
98
|
+
s1.unpack('Q*').zip(s2.unpack('Q*')) {|a, b| s.push(a ^ b) }
|
|
105
99
|
s.pack('Q*')
|
|
106
100
|
end
|
|
107
101
|
singleton_class.send(:private, :xor!)
|
|
@@ -5,10 +5,21 @@ module Net
|
|
|
5
5
|
module SSH
|
|
6
6
|
module Transport
|
|
7
7
|
module HMAC
|
|
8
|
-
|
|
9
8
|
# The base class of all OpenSSL-based HMAC algorithm wrappers.
|
|
10
9
|
class Abstract
|
|
11
|
-
class <<self
|
|
10
|
+
class << self
|
|
11
|
+
def etm(*v)
|
|
12
|
+
@etm = false if !defined?(@etm)
|
|
13
|
+
if v.empty?
|
|
14
|
+
@etm = superclass.etm if @etm.nil? && superclass.respond_to?(:etm)
|
|
15
|
+
return @etm
|
|
16
|
+
elsif v.length == 1
|
|
17
|
+
@etm = v.first
|
|
18
|
+
else
|
|
19
|
+
raise ArgumentError, "wrong number of arguments (#{v.length} for 1)"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
12
23
|
def key_length(*v)
|
|
13
24
|
@key_length = nil if !defined?(@key_length)
|
|
14
25
|
if v.empty?
|
|
@@ -46,6 +57,10 @@ module Net
|
|
|
46
57
|
end
|
|
47
58
|
end
|
|
48
59
|
|
|
60
|
+
def etm
|
|
61
|
+
self.class.etm
|
|
62
|
+
end
|
|
63
|
+
|
|
49
64
|
def key_length
|
|
50
65
|
self.class.key_length
|
|
51
66
|
end
|
|
@@ -61,19 +76,19 @@ module Net
|
|
|
61
76
|
# The key in use for this instance.
|
|
62
77
|
attr_reader :key
|
|
63
78
|
|
|
64
|
-
def initialize(key=nil)
|
|
79
|
+
def initialize(key = nil)
|
|
65
80
|
self.key = key
|
|
66
81
|
end
|
|
67
82
|
|
|
68
83
|
# Sets the key to the given value, truncating it so that it is the correct
|
|
69
84
|
# length.
|
|
70
85
|
def key=(value)
|
|
71
|
-
@key = value ? value.to_s[0,key_length] : nil
|
|
86
|
+
@key = value ? value.to_s[0, key_length] : nil
|
|
72
87
|
end
|
|
73
88
|
|
|
74
89
|
# Compute the HMAC digest for the given data string.
|
|
75
90
|
def digest(data)
|
|
76
|
-
OpenSSL::HMAC.digest(digest_class.new, key, data)[0,mac_length]
|
|
91
|
+
OpenSSL::HMAC.digest(digest_class.new, key, data)[0, mac_length]
|
|
77
92
|
end
|
|
78
93
|
end
|
|
79
94
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'net/ssh/transport/hmac/abstract'
|
|
2
2
|
|
|
3
3
|
module Net::SSH::Transport::HMAC
|
|
4
|
-
|
|
5
4
|
# The "none" algorithm. This has a key and mac length of 0.
|
|
6
5
|
class None < Abstract
|
|
7
6
|
key_length 0
|
|
@@ -11,5 +10,4 @@ module Net::SSH::Transport::HMAC
|
|
|
11
10
|
""
|
|
12
11
|
end
|
|
13
12
|
end
|
|
14
|
-
|
|
15
13
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'net/ssh/transport/hmac/abstract'
|
|
2
2
|
|
|
3
3
|
module Net::SSH::Transport::HMAC
|
|
4
|
-
|
|
5
4
|
# The RIPEMD-160 HMAC algorithm. This has a mac and key length of 20, and
|
|
6
5
|
# uses the RIPEMD-160 digest algorithm.
|
|
7
6
|
class RIPEMD160 < Abstract
|
|
@@ -9,5 +8,4 @@ module Net::SSH::Transport::HMAC
|
|
|
9
8
|
key_length 20
|
|
10
9
|
digest_class OpenSSL::Digest::RIPEMD160
|
|
11
10
|
end
|
|
12
|
-
|
|
13
11
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'net/ssh/transport/hmac/abstract'
|
|
2
2
|
|
|
3
3
|
module Net::SSH::Transport::HMAC
|
|
4
|
-
|
|
5
4
|
# The SHA1 HMAC algorithm. This has a mac and key length of 20, and
|
|
6
5
|
# uses the SHA1 digest algorithm.
|
|
7
6
|
class SHA1 < Abstract
|
|
@@ -9,5 +8,4 @@ module Net::SSH::Transport::HMAC
|
|
|
9
8
|
key_length 20
|
|
10
9
|
digest_class OpenSSL::Digest::SHA1
|
|
11
10
|
end
|
|
12
|
-
|
|
13
11
|
end
|