net-ssh 5.2.0 → 6.0.0.beta1

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