net-ssh 5.2.0 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +7 -4
  6. data/.rubocop_todo.yml +389 -379
  7. data/.travis.yml +13 -14
  8. data/CHANGES.txt +7 -0
  9. data/README.md +286 -0
  10. data/Rakefile +1 -2
  11. data/appveyor.yml +4 -2
  12. data/lib/net/ssh/authentication/key_manager.rb +7 -3
  13. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +3 -1
  14. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +0 -1
  15. data/lib/net/ssh/authentication/session.rb +2 -6
  16. data/lib/net/ssh/buffer.rb +1 -9
  17. data/lib/net/ssh/config.rb +25 -10
  18. data/lib/net/ssh/connection/channel.rb +7 -3
  19. data/lib/net/ssh/connection/session.rb +7 -3
  20. data/lib/net/ssh/key_factory.rb +6 -8
  21. data/lib/net/ssh/known_hosts.rb +26 -29
  22. data/lib/net/ssh/service/forward.rb +2 -1
  23. data/lib/net/ssh/test.rb +3 -2
  24. data/lib/net/ssh/transport/algorithms.rb +67 -42
  25. data/lib/net/ssh/transport/cipher_factory.rb +11 -27
  26. data/lib/net/ssh/transport/constants.rb +10 -6
  27. data/lib/net/ssh/transport/ctr.rb +1 -7
  28. data/lib/net/ssh/transport/hmac.rb +15 -13
  29. data/lib/net/ssh/transport/hmac/abstract.rb +16 -0
  30. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  31. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  32. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  33. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  34. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  35. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  36. data/lib/net/ssh/transport/kex.rb +13 -11
  37. data/lib/net/ssh/transport/kex/abstract.rb +123 -0
  38. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  39. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +38 -0
  40. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  41. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +1 -15
  42. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +9 -118
  43. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +0 -6
  44. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  45. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +18 -79
  46. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +5 -4
  47. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +5 -4
  48. data/lib/net/ssh/transport/openssl.rb +104 -107
  49. data/lib/net/ssh/transport/packet_stream.rb +44 -10
  50. data/lib/net/ssh/transport/state.rb +1 -1
  51. data/lib/net/ssh/version.rb +3 -3
  52. data/net-ssh.gemspec +8 -6
  53. metadata +35 -16
  54. metadata.gz.sig +0 -0
  55. data/Gemfile.noed25519.lock +0 -41
  56. data/README.rdoc +0 -194
  57. data/support/arcfour_check.rb +0 -20
@@ -323,15 +323,7 @@ module Net
323
323
  Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("unsupported key type `#{type}'")
324
324
  key = Net::SSH::Authentication::ED25519::PubKey.read_keyblob(self)
325
325
  when /^ecdsa\-sha2\-(\w*)$/
326
- unless defined?(OpenSSL::PKey::EC)
327
- raise NotImplementedError, "unsupported key type `#{type}'"
328
- else
329
- begin
330
- key = OpenSSL::PKey::EC.read_keyblob($1, self)
331
- rescue OpenSSL::PKey::ECError
332
- raise NotImplementedError, "unsupported key type `#{type}'"
333
- end
334
- end
326
+ key = OpenSSL::PKey::EC.read_keyblob($1, self)
335
327
  else
336
328
  raise NotImplementedError, "unsupported key type `#{type}'"
337
329
  end
@@ -33,6 +33,7 @@ module Net
33
33
  # * ProxyJump => maps to the :proxy option
34
34
  # * PubKeyAuthentication => maps to the :auth_methods option
35
35
  # * RekeyLimit => :rekey_limit
36
+ # * StrictHostKeyChecking => :strict_host_key_checking
36
37
  # * User => :user
37
38
  # * UserKnownHostsFile => :user_known_hosts_file
38
39
  # * NumberOfPasswordPrompts => :number_of_password_prompts
@@ -149,6 +150,13 @@ module Net
149
150
  settings[key] = value unless settings.key?(key)
150
151
  end
151
152
  end
153
+
154
+ # ProxyCommand and ProxyJump override each other so they need to be tracked togeather
155
+ %w[proxyjump proxycommand].each do |proxy_key|
156
+ if (proxy_value = settings.delete(proxy_key))
157
+ settings['proxy'] ||= [proxy_key, proxy_value]
158
+ end
159
+ end
152
160
  end
153
161
 
154
162
  globals.merge(settings) do |key, oldval, newval|
@@ -202,6 +210,7 @@ module Net
202
210
  identityfile: :keys,
203
211
  fingerprinthash: :fingerprint_hash,
204
212
  port: :port,
213
+ stricthostkeychecking: :strict_host_key_checking,
205
214
  user: :user,
206
215
  userknownhostsfile: :user_known_hosts_file,
207
216
  checkhostip: :check_host_ip
@@ -250,15 +259,9 @@ module Net
250
259
  end
251
260
  when :preferredauthentications
252
261
  hash[:auth_methods] = value.split(/,/) # TODO we should place to preferred_auth_methods rather than auth_methods
253
- when :proxycommand
254
- if value and value !~ /^none$/
255
- require 'net/ssh/proxy/command'
256
- hash[:proxy] = Net::SSH::Proxy::Command.new(value)
257
- end
258
- when :proxyjump
259
- if value
260
- require 'net/ssh/proxy/jump'
261
- hash[:proxy] = Net::SSH::Proxy::Jump.new(value)
262
+ when :proxy
263
+ if (proxy = setup_proxy(*value))
264
+ hash[:proxy] = proxy
262
265
  end
263
266
  when :pubkeyauthentication
264
267
  if value
@@ -278,6 +281,19 @@ module Net
278
281
  end
279
282
  end
280
283
 
284
+ def setup_proxy(type, value)
285
+ case type
286
+ when 'proxycommand'
287
+ if value !~ /^none$/
288
+ require 'net/ssh/proxy/command'
289
+ Net::SSH::Proxy::Command.new(value)
290
+ end
291
+ when 'proxyjump'
292
+ require 'net/ssh/proxy/jump'
293
+ Net::SSH::Proxy::Jump.new(value)
294
+ end
295
+ end
296
+
281
297
  # Converts an ssh_config pattern into a regex for matching against
282
298
  # host names.
283
299
  def pattern2regex(pattern)
@@ -369,6 +385,5 @@ module Net
369
385
  end
370
386
  end
371
387
  end
372
-
373
388
  end
374
389
  end
@@ -648,10 +648,14 @@ module Net
648
648
  def update_local_window_size(size)
649
649
  @local_window_size -= size
650
650
  if local_window_size < local_maximum_window_size / 2
651
- connection.send_message(Buffer.from(:byte, CHANNEL_WINDOW_ADJUST,
652
- :long, remote_id, :long, LOCAL_WINDOW_SIZE_INCREMENT))
651
+ connection.send_message(
652
+ Buffer.from(:byte, CHANNEL_WINDOW_ADJUST, :long, remote_id, :long, LOCAL_WINDOW_SIZE_INCREMENT)
653
+ )
653
654
  @local_window_size += LOCAL_WINDOW_SIZE_INCREMENT
654
- @local_maximum_window_size += LOCAL_WINDOW_SIZE_INCREMENT if @local_maximum_window_size < @local_window_size || @local_maximum_window_size < GOOD_LOCAL_MAXIUMUM_WINDOW_SIZE
655
+
656
+ if @local_maximum_window_size < @local_window_size || @local_maximum_window_size < GOOD_LOCAL_MAXIUMUM_WINDOW_SIZE
657
+ @local_maximum_window_size += LOCAL_WINDOW_SIZE_INCREMENT
658
+ end
655
659
  end
656
660
  end
657
661
 
@@ -580,8 +580,10 @@ module Net
580
580
  info { "global request received: #{packet[:request_type]} #{packet[:want_reply]}" }
581
581
  callback = @on_global_request[packet[:request_type]]
582
582
  result = callback ? callback.call(packet[:request_data], packet[:want_reply]) : false
583
-
584
- raise "expected global request handler for `#{packet[:request_type]}' to return true, false, or :sent, but got #{result.inspect}" if result != :sent && result != true && result != false
583
+
584
+ if result != :sent && result != true && result != false
585
+ raise "expected global request handler for `#{packet[:request_type]}' to return true, false, or :sent, but got #{result.inspect}"
586
+ end
585
587
 
586
588
  if packet[:want_reply] && result != :sent
587
589
  msg = Buffer.from(:byte, result ? REQUEST_SUCCESS : REQUEST_FAILURE)
@@ -624,7 +626,9 @@ module Net
624
626
  failure = [err.code, err.reason]
625
627
  else
626
628
  channels[local_id] = channel
627
- msg = Buffer.from(:byte, CHANNEL_OPEN_CONFIRMATION, :long, channel.remote_id, :long, channel.local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size)
629
+ msg = Buffer.from(:byte, CHANNEL_OPEN_CONFIRMATION, :long, channel.remote_id, :long,
630
+ channel.local_id, :long, channel.local_maximum_window_size, :long,
631
+ channel.local_maximum_packet_size)
628
632
  end
629
633
  else
630
634
  failure = [3, "unknown channel type #{channel.type}"]
@@ -18,14 +18,12 @@ module Net
18
18
  class KeyFactory
19
19
  # Specifies the mapping of SSH names to OpenSSL key classes.
20
20
  MAP = {
21
- "dh" => OpenSSL::PKey::DH,
22
- "rsa" => OpenSSL::PKey::RSA,
23
- "dsa" => OpenSSL::PKey::DSA
21
+ 'dh' => OpenSSL::PKey::DH,
22
+ 'rsa' => OpenSSL::PKey::RSA,
23
+ 'dsa' => OpenSSL::PKey::DSA,
24
+ 'ecdsa' => OpenSSL::PKey::EC
24
25
  }
25
- if defined?(OpenSSL::PKey::EC)
26
- MAP["ecdsa"] = OpenSSL::PKey::EC
27
- MAP["ed25519"] = Net::SSH::Authentication::ED25519::PrivKey if defined? Net::SSH::Authentication::ED25519
28
- end
26
+ MAP["ed25519"] = Net::SSH::Authentication::ED25519::PrivKey if defined? Net::SSH::Authentication::ED25519
29
27
 
30
28
  class <<self
31
29
  # Fetch an OpenSSL key instance by its SSH name. It will be a new,
@@ -207,7 +205,7 @@ module Net
207
205
  return OpenSSLDSAKeyType
208
206
  elsif data.match(/-----BEGIN RSA PRIVATE KEY-----/)
209
207
  return OpenSSLRSAKeyType
210
- elsif data.match(/-----BEGIN EC PRIVATE KEY-----/) && defined?(OpenSSL::PKey::EC)
208
+ elsif data.match(/-----BEGIN EC PRIVATE KEY-----/)
211
209
  return OpenSSLECKeyType
212
210
  elsif data.match(/-----BEGIN (.+) PRIVATE KEY-----/)
213
211
  raise OpenSSL::PKey::PKeyError, "not a supported key type '#{$1}'"
@@ -41,14 +41,11 @@ module Net
41
41
  # This is used internally by Net::SSH, and will never need to be used directly
42
42
  # by consumers of the library.
43
43
  class KnownHosts
44
- if defined?(OpenSSL::PKey::EC)
45
- SUPPORTED_TYPE = %w[ssh-rsa ssh-dss
46
- ecdsa-sha2-nistp256
47
- ecdsa-sha2-nistp384
48
- ecdsa-sha2-nistp521]
49
- else
50
- SUPPORTED_TYPE = %w[ssh-rsa ssh-dss]
51
- end
44
+ SUPPORTED_TYPE = %w[ssh-rsa ssh-dss
45
+ ecdsa-sha2-nistp256
46
+ ecdsa-sha2-nistp384
47
+ ecdsa-sha2-nistp521]
48
+
52
49
  SUPPORTED_TYPE.push('ssh-ed25519') if Net::SSH::Authentication::ED25519Loader::LOADED
53
50
 
54
51
  class <<self
@@ -78,7 +75,9 @@ module Net
78
75
 
79
76
  files += Array(options[:user_known_hosts_file] || %w[~/.ssh/known_hosts ~/.ssh/known_hosts2]) if which == :all || which == :user
80
77
 
81
- files += Array(options[:global_known_hosts_file] || %w[/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2]) if which == :all || which == :global
78
+ if which == :all || which == :global
79
+ files += Array(options[:global_known_hosts_file] || %w[/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2])
80
+ end
82
81
 
83
82
  return files
84
83
  end
@@ -130,27 +129,21 @@ module Net
130
129
  host_ip = entries[1]
131
130
 
132
131
  File.open(source) do |file|
133
- scanner = StringScanner.new("")
134
132
  file.each_line do |line|
135
- scanner.string = line
133
+ hosts, type, key_content = line.split(' ')
134
+ next unless hosts || hosts.start_with?('#')
136
135
 
137
- scanner.skip(/\s*/)
138
- next if scanner.match?(/$|#/)
136
+ hostlist = hosts.split(',')
137
+
138
+ next unless SUPPORTED_TYPE.include?(type)
139
139
 
140
- hostlist = scanner.scan(/\S+/).split(/,/)
141
140
  found = hostlist.any? { |pattern| match(host_name, pattern) } || known_host_hash?(hostlist, entries)
142
141
  next unless found
143
142
 
144
143
  found = hostlist.include?(host_ip) if options[:check_host_ip] && entries.size > 1 && hostlist.size > 1
145
144
  next unless found
146
145
 
147
- scanner.skip(/\s*/)
148
- type = scanner.scan(/\S+/)
149
-
150
- next unless SUPPORTED_TYPE.include?(type)
151
-
152
- scanner.skip(/\s*/)
153
- blob = scanner.rest.unpack("m*").first
146
+ blob = key_content.unpack("m*").first
154
147
  keys << Net::SSH::Buffer.new(blob).read_key
155
148
  end
156
149
  end
@@ -159,14 +152,18 @@ module Net
159
152
  end
160
153
 
161
154
  def match(host, pattern)
162
- # see man 8 sshd for pattern details
163
- pattern_regexp = pattern.split('*').map do |x|
164
- x.split('?').map do |y|
165
- Regexp.escape(y)
166
- end.join('.')
167
- end.join('[^.]*')
168
-
169
- host =~ Regexp.new("\\A#{pattern_regexp}\\z")
155
+ if pattern.include?('*') || pattern.include?('?')
156
+ # see man 8 sshd for pattern details
157
+ pattern_regexp = pattern.split('*').map do |x|
158
+ x.split('?').map do |y|
159
+ Regexp.escape(y)
160
+ end.join('.')
161
+ end.join('[^.]*')
162
+
163
+ host =~ Regexp.new("\\A#{pattern_regexp}\\z")
164
+ else
165
+ host == pattern
166
+ end
170
167
  end
171
168
 
172
169
  # Indicates whether one of the entries matches an hostname that has been
@@ -85,7 +85,8 @@ module Net
85
85
  client = server.accept
86
86
  debug { "received connection on #{socket}" }
87
87
 
88
- channel = session.open_channel("direct-tcpip", :string, remote_host, :long, remote_port, :string, bind_address, local_port_type, local_port) do |achannel|
88
+ channel = session.open_channel("direct-tcpip", :string, remote_host, :long,
89
+ remote_port, :string, bind_address, local_port_type, local_port) do |achannel|
89
90
  achannel.info { "direct channel established" }
90
91
  end
91
92
 
@@ -74,7 +74,7 @@ module Net
74
74
  def transport(options={})
75
75
  @transport ||= Net::SSH::Transport::Session.new(
76
76
  options[:host] || "localhost",
77
- options.merge(kex: "test", host_key: "ssh-rsa", verify_host_key: :never, proxy: socket(options))
77
+ options.merge(kex: "test", host_key: "ssh-rsa", append_all_supported_algorithms: true, verify_host_key: :never, proxy: socket(options))
78
78
  )
79
79
  end
80
80
 
@@ -86,7 +86,8 @@ module Net
86
86
  def assert_scripted
87
87
  raise "there is no script to be processed" if socket.script.events.empty?
88
88
  Net::SSH::Test::Extensions::IO.with_test_extension { yield }
89
- assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still #{socket.script.events.length} pending"
89
+ assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still" \
90
+ "#{socket.script.events.length} pending"
90
91
  end
91
92
  end
92
93
 
@@ -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,72 @@ module Net
23
23
  include Loggable
24
24
  include Constants
25
25
 
26
- # Define the default algorithms, in order of preference, supported by
27
- # Net::SSH.
28
- ALGORITHMS = {
29
- host_key: %w[ssh-rsa-cert-v01@openssh.com
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 ssh-dss],
32
- kex: %w[diffie-hellman-group-exchange-sha256
33
- diffie-hellman-group-exchange-sha1
34
- diffie-hellman-group14-sha1
36
+ ssh-rsa],
37
+
38
+ kex: %w[ecdh-sha2-nistp521
39
+ ecdh-sha2-nistp384
40
+ ecdh-sha2-nistp256
41
+ diffie-hellman-group-exchange-sha256
42
+ diffie-hellman-group14-sha1],
43
+
44
+ encryption: %w[aes256-ctr aes192-ctr aes128-ctr],
45
+
46
+ hmac: %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
47
+ hmac-sha2-512 hmac-sha2-256
48
+ hmac-sha1]
49
+ }.freeze
50
+
51
+ if Net::SSH::Authentication::ED25519Loader::LOADED
52
+ DEFAULT_ALGORITHMS[:host_key].unshift(
53
+ 'ssh-ed25519-cert-v01@openssh.com',
54
+ 'ssh-ed25519'
55
+ )
56
+ end
57
+
58
+ if Net::SSH::Transport::Kex::Curve25519Sha256Loader::LOADED
59
+ DEFAULT_ALGORITHMS[:kex].unshift(
60
+ 'curve25519-sha256',
61
+ 'curve22519-sha256@libssh.org'
62
+ )
63
+ end
64
+
65
+ # Define all algorithms, with the deprecated, supported by Net::SSH.
66
+ ALGORITHMS = {
67
+ host_key: DEFAULT_ALGORITHMS[:host_key] + %w[ssh-dss],
68
+
69
+ kex: DEFAULT_ALGORITHMS[:kex] +
70
+ %w[diffie-hellman-group-exchange-sha1
35
71
  diffie-hellman-group1-sha1],
36
- encryption: %w[aes256-ctr aes192-ctr aes128-ctr
37
- aes256-cbc aes192-cbc aes128-cbc
72
+
73
+ encryption: DEFAULT_ALGORITHMS[:encryption] +
74
+ %w[aes256-cbc aes192-cbc aes128-cbc
38
75
  rijndael-cbc@lysator.liu.se
39
76
  blowfish-ctr blowfish-cbc
40
77
  cast128-ctr cast128-cbc
41
78
  3des-ctr 3des-cbc
42
- idea-cbc arcfour256 arcfour128 arcfour
79
+ idea-cbc
43
80
  none],
44
81
 
45
- hmac: %w[hmac-sha2-512 hmac-sha2-256
46
- hmac-sha2-512-96 hmac-sha2-256-96
47
- hmac-sha1 hmac-sha1-96
82
+ hmac: DEFAULT_ALGORITHMS[:hmac] +
83
+ %w[hmac-sha2-512-96 hmac-sha2-256-96
84
+ hmac-sha1-96
48
85
  hmac-ripemd160 hmac-ripemd160@openssh.com
49
86
  hmac-md5 hmac-md5-96
50
87
  none],
51
88
 
52
89
  compression: %w[none zlib@openssh.com zlib],
53
90
  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
91
+ }.freeze
76
92
 
77
93
  # The underlying transport layer session that supports this object
78
94
  attr_reader :session
@@ -140,6 +156,7 @@ module Net
140
156
  # Start the algorithm negotation
141
157
  def start
142
158
  raise ArgumentError, "Cannot call start if it's negotiation started or done" if @pending || @initialized
159
+
143
160
  send_kexinit
144
161
  end
145
162
 
@@ -197,8 +214,8 @@ module Net
197
214
 
198
215
  def host_key_format
199
216
  case host_key
200
- when "ssh-rsa-cert-v01@openssh.com", "ssh-rsa-cert-v00@openssh.com"
201
- "ssh-rsa"
217
+ when /^([a-z0-9-]+)-cert-v\d{2}@openssh.com$/
218
+ Regexp.last_match[1]
202
219
  else
203
220
  host_key
204
221
  end
@@ -239,7 +256,10 @@ module Net
239
256
  options[:compression] = %w[zlib@openssh.com zlib] if options[:compression] == true
240
257
 
241
258
  ALGORITHMS.each do |algorithm, supported|
242
- algorithms[algorithm] = compose_algorithm_list(supported, options[algorithm], options[:append_all_supported_algorithms])
259
+ algorithms[algorithm] = compose_algorithm_list(
260
+ supported, options[algorithm] || DEFAULT_ALGORITHMS[algorithm],
261
+ options[:append_all_supported_algorithms]
262
+ )
243
263
  end
244
264
 
245
265
  # for convention, make sure our list has the same keys as the server
@@ -356,7 +376,8 @@ module Net
356
376
 
357
377
  debug do
358
378
  "negotiated:\n" +
359
- %i[kex host_key encryption_server encryption_client hmac_client hmac_server compression_client compression_server language_client language_server].map do |key|
379
+ %i[kex host_key encryption_server encryption_client hmac_client hmac_server
380
+ compression_client compression_server language_client language_server].map do |key|
360
381
  "* #{key}: #{instance_variable_get("@#{key}")}"
361
382
  end.join("\n")
362
383
  end
@@ -368,7 +389,11 @@ module Net
368
389
  def negotiate(algorithm)
369
390
  match = self[algorithm].find { |item| @server_data[algorithm].include?(item) }
370
391
 
371
- raise Net::SSH::Exception, "could not settle on #{algorithm} algorithm" if match.nil?
392
+ if match.nil?
393
+ raise Net::SSH::Exception, "could not settle on #{algorithm} algorithm\n"\
394
+ "Server #{algorithm} preferences: #{@server_data[algorithm].join(',')}\n"\
395
+ "Client #{algorithm} preferences: #{self[algorithm].join(',')}"
396
+ end
372
397
 
373
398
  return match
374
399
  end