net-ssh 2.9.2 → 4.0.0

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 (138) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.gitignore +6 -0
  4. data/.rubocop.yml +5 -0
  5. data/.rubocop_todo.yml +1129 -0
  6. data/.travis.yml +41 -5
  7. data/CHANGES.txt +133 -1
  8. data/Gemfile +13 -0
  9. data/Gemfile.norbnacl +10 -0
  10. data/Gemfile.norbnacl.lock +41 -0
  11. data/ISSUE_TEMPLATE.md +30 -0
  12. data/README.rdoc +26 -81
  13. data/Rakefile +63 -45
  14. data/appveyor.yml +51 -0
  15. data/lib/net/ssh/authentication/agent.rb +174 -14
  16. data/lib/net/ssh/authentication/ed25519.rb +137 -0
  17. data/lib/net/ssh/authentication/ed25519_loader.rb +21 -0
  18. data/lib/net/ssh/authentication/key_manager.rb +36 -30
  19. data/lib/net/ssh/authentication/methods/abstract.rb +4 -0
  20. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +16 -9
  21. data/lib/net/ssh/authentication/methods/password.rb +17 -4
  22. data/lib/net/ssh/authentication/pageant.rb +166 -45
  23. data/lib/net/ssh/authentication/session.rb +3 -2
  24. data/lib/net/ssh/buffer.rb +49 -10
  25. data/lib/net/ssh/buffered_io.rb +17 -12
  26. data/lib/net/ssh/config.rb +39 -8
  27. data/lib/net/ssh/connection/channel.rb +42 -20
  28. data/lib/net/ssh/connection/event_loop.rb +114 -0
  29. data/lib/net/ssh/connection/keepalive.rb +2 -2
  30. data/lib/net/ssh/connection/session.rb +120 -34
  31. data/lib/net/ssh/errors.rb +6 -6
  32. data/lib/net/ssh/key_factory.rb +49 -43
  33. data/lib/net/ssh/known_hosts.rb +49 -3
  34. data/lib/net/ssh/prompt.rb +47 -78
  35. data/lib/net/ssh/proxy/command.rb +31 -5
  36. data/lib/net/ssh/proxy/http.rb +15 -11
  37. data/lib/net/ssh/proxy/https.rb +49 -0
  38. data/lib/net/ssh/proxy/socks4.rb +2 -1
  39. data/lib/net/ssh/proxy/socks5.rb +3 -2
  40. data/lib/net/ssh/ruby_compat.rb +2 -29
  41. data/lib/net/ssh/service/forward.rb +2 -2
  42. data/lib/net/ssh/test/channel.rb +7 -0
  43. data/lib/net/ssh/test/extensions.rb +17 -0
  44. data/lib/net/ssh/test/kex.rb +4 -4
  45. data/lib/net/ssh/test/packet.rb +18 -2
  46. data/lib/net/ssh/test/script.rb +16 -2
  47. data/lib/net/ssh/test/socket.rb +1 -1
  48. data/lib/net/ssh/test.rb +5 -5
  49. data/lib/net/ssh/transport/algorithms.rb +92 -75
  50. data/lib/net/ssh/transport/cipher_factory.rb +19 -26
  51. data/lib/net/ssh/transport/ctr.rb +7 -9
  52. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +20 -9
  53. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +5 -3
  54. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +1 -1
  55. data/lib/net/ssh/transport/key_expander.rb +1 -0
  56. data/lib/net/ssh/transport/openssl.rb +1 -1
  57. data/lib/net/ssh/transport/packet_stream.rb +11 -3
  58. data/lib/net/ssh/transport/server_version.rb +13 -6
  59. data/lib/net/ssh/transport/session.rb +20 -10
  60. data/lib/net/ssh/transport/state.rb +1 -1
  61. data/lib/net/ssh/verifiers/secure.rb +8 -10
  62. data/lib/net/ssh/version.rb +4 -4
  63. data/lib/net/ssh.rb +62 -14
  64. data/net-ssh-public_cert.pem +19 -18
  65. data/net-ssh.gemspec +34 -194
  66. data/support/arcfour_check.rb +1 -1
  67. data/support/ssh_tunnel_bug.rb +1 -1
  68. data.tar.gz.sig +0 -0
  69. metadata +125 -109
  70. metadata.gz.sig +0 -0
  71. data/Rudyfile +0 -96
  72. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  73. data/lib/net/ssh/authentication/agent/socket.rb +0 -178
  74. data/setup.rb +0 -1585
  75. data/test/README.txt +0 -47
  76. data/test/authentication/methods/common.rb +0 -28
  77. data/test/authentication/methods/test_abstract.rb +0 -51
  78. data/test/authentication/methods/test_hostbased.rb +0 -114
  79. data/test/authentication/methods/test_keyboard_interactive.rb +0 -100
  80. data/test/authentication/methods/test_none.rb +0 -41
  81. data/test/authentication/methods/test_password.rb +0 -95
  82. data/test/authentication/methods/test_publickey.rb +0 -148
  83. data/test/authentication/test_agent.rb +0 -224
  84. data/test/authentication/test_key_manager.rb +0 -227
  85. data/test/authentication/test_session.rb +0 -107
  86. data/test/common.rb +0 -108
  87. data/test/configs/auth_off +0 -5
  88. data/test/configs/auth_on +0 -4
  89. data/test/configs/empty +0 -0
  90. data/test/configs/eqsign +0 -3
  91. data/test/configs/exact_match +0 -8
  92. data/test/configs/host_plus +0 -10
  93. data/test/configs/multihost +0 -4
  94. data/test/configs/negative_match +0 -6
  95. data/test/configs/nohost +0 -19
  96. data/test/configs/numeric_host +0 -4
  97. data/test/configs/send_env +0 -2
  98. data/test/configs/substitutes +0 -8
  99. data/test/configs/wild_cards +0 -14
  100. data/test/connection/test_channel.rb +0 -467
  101. data/test/connection/test_session.rb +0 -543
  102. data/test/known_hosts/github +0 -1
  103. data/test/manual/test_forward.rb +0 -285
  104. data/test/manual/test_pageant.rb +0 -37
  105. data/test/start/test_connection.rb +0 -53
  106. data/test/start/test_options.rb +0 -43
  107. data/test/start/test_transport.rb +0 -28
  108. data/test/test_all.rb +0 -11
  109. data/test/test_buffer.rb +0 -433
  110. data/test/test_buffered_io.rb +0 -63
  111. data/test/test_config.rb +0 -221
  112. data/test/test_key_factory.rb +0 -191
  113. data/test/test_known_hosts.rb +0 -13
  114. data/test/transport/hmac/test_md5.rb +0 -41
  115. data/test/transport/hmac/test_md5_96.rb +0 -27
  116. data/test/transport/hmac/test_none.rb +0 -34
  117. data/test/transport/hmac/test_ripemd160.rb +0 -36
  118. data/test/transport/hmac/test_sha1.rb +0 -36
  119. data/test/transport/hmac/test_sha1_96.rb +0 -27
  120. data/test/transport/hmac/test_sha2_256.rb +0 -37
  121. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  122. data/test/transport/hmac/test_sha2_512.rb +0 -37
  123. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  124. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  125. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -146
  126. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -92
  127. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -34
  128. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  129. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  130. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  131. data/test/transport/test_algorithms.rb +0 -324
  132. data/test/transport/test_cipher_factory.rb +0 -443
  133. data/test/transport/test_hmac.rb +0 -34
  134. data/test/transport/test_identity_cipher.rb +0 -40
  135. data/test/transport/test_packet_stream.rb +0 -1761
  136. data/test/transport/test_server_version.rb +0 -78
  137. data/test/transport/test_session.rb +0 -331
  138. data/test/transport/test_state.rb +0 -181
@@ -31,10 +31,10 @@ module Net; module SSH; module Test
31
31
  buffer = @connection.next_message
32
32
  raise Net::SSH::Exception, "expected NEWKEYS" unless buffer.type == NEWKEYS
33
33
 
34
- { :session_id => "abc-xyz",
35
- :server_key => OpenSSL::PKey::RSA.new(512),
36
- :shared_secret => OpenSSL::BN.new("1234567890", 10),
37
- :hashing_algorithm => OpenSSL::Digest::SHA1 }
34
+ { session_id: "abc-xyz",
35
+ server_key: OpenSSL::PKey::RSA.new(512),
36
+ shared_secret: OpenSSL::BN.new("1234567890", 10),
37
+ hashing_algorithm: OpenSSL::Digest::SHA1 }
38
38
  end
39
39
  end
40
40
 
@@ -17,6 +17,17 @@ module Net; module SSH; module Test
17
17
  include Net::SSH::Transport::Constants
18
18
  include Net::SSH::Connection::Constants
19
19
 
20
+ # Register a custom channel request. extra_parts is an array of types
21
+ # of extra parameters
22
+ def self.register_channel_request(request, extra_parts)
23
+ @registered_requests ||= {}
24
+ @registered_requests[request] = {extra_parts: extra_parts}
25
+ end
26
+
27
+ def self.registered_channel_requests(request)
28
+ @registered_requests && @registered_requests[request]
29
+ end
30
+
20
31
  # Ceate a new packet of the given +type+, and with +args+ being a list of
21
32
  # data elements in the order expected for packets of the given +type+
22
33
  # (see #types).
@@ -70,9 +81,14 @@ module Net; module SSH; module Test
70
81
  when CHANNEL_REQUEST
71
82
  parts = [:long, :string, :bool]
72
83
  case @data[1]
73
- when "exec", "subsystem" then parts << :string
84
+ when "exec", "subsystem","shell" then parts << :string
74
85
  when "exit-status" then parts << :long
75
- else raise "don't know what to do about #{@data[1]} channel request"
86
+ when "pty-req" then parts.concat([:string, :long, :long, :long, :long, :string])
87
+ when "env" then parts.contact([:string,:string])
88
+ else
89
+ request = Packet.registered_channel_requests(@data[1])
90
+ raise "don't know what to do about #{@data[1]} channel request" unless request
91
+ parts.concat(request[:extra_parts])
76
92
  end
77
93
  else raise "don't know how to parse packet type #{@type}"
78
94
  end
@@ -63,7 +63,8 @@ module Net; module SSH; module Test
63
63
  # indicating whether a response to this packet is required , and +data+
64
64
  # is any additional request-specific data that this packet should send.
65
65
  # +success+ indicates whether the response (if one is required) should be
66
- # success or failure.
66
+ # success or failure. If +data+ is an array it will be treated as multiple
67
+ # data.
67
68
  #
68
69
  # If a reply is desired, a remote packet will also be queued, :channel_success
69
70
  # if +success+ is true, or :channel_failure if +success+ is false.
@@ -71,7 +72,11 @@ module Net; module SSH; module Test
71
72
  # This will typically be called via Net::SSH::Test::Channel#sends_exec or
72
73
  # Net::SSH::Test::Channel#sends_subsystem.
73
74
  def sends_channel_request(channel, request, reply, data, success=true)
74
- events << LocalPacket.new(:channel_request, channel.remote_id, request, reply, data)
75
+ if data.is_a? Array
76
+ events << LocalPacket.new(:channel_request, channel.remote_id, request, reply, *data)
77
+ else
78
+ events << LocalPacket.new(:channel_request, channel.remote_id, request, reply, data)
79
+ end
75
80
  if reply
76
81
  if success
77
82
  events << RemotePacket.new(:channel_success, channel.local_id)
@@ -104,6 +109,15 @@ module Net; module SSH; module Test
104
109
  events << LocalPacket.new(:channel_close, channel.remote_id)
105
110
  end
106
111
 
112
+ # Scripts the sending of a channel request pty packets from the given
113
+ # Net::SSH::Test::Channel +channel+. This will typically be called via
114
+ # Net::SSH::Test::Channel#sends_request_pty.
115
+ def sends_channel_request_pty(channel)
116
+ data = ['pty-req', false]
117
+ data += Net::SSH::Connection::Channel::VALID_PTY_OPTIONS.merge(modes: "\0").values
118
+ events << LocalPacket.new(:channel_request, channel.remote_id, *data)
119
+ end
120
+
107
121
  # Scripts the reception of a channel data packet from the remote host by
108
122
  # the given Net::SSH::Test::Channel +channel+. This will typically be
109
123
  # called via Net::SSH::Test::Channel#gets_data.
@@ -25,8 +25,8 @@ module Net; module SSH; module Test
25
25
 
26
26
  @script = Script.new
27
27
 
28
- script.gets(:kexinit, 1, 2, 3, 4, "test", "ssh-rsa", "none", "none", "none", "none", "none", "none", "", "", false)
29
28
  script.sends(:kexinit)
29
+ script.gets(:kexinit, 1, 2, 3, 4, "test", "ssh-rsa", "none", "none", "none", "none", "none", "none", "", "", false)
30
30
  script.sends(:newkeys)
31
31
  script.gets(:newkeys)
32
32
  end
data/lib/net/ssh/test.rb CHANGED
@@ -10,10 +10,10 @@ module Net; module SSH
10
10
  # typically include this module in your unit test class, and then build a
11
11
  # "story" of expected sends and receives:
12
12
  #
13
- # require 'test/unit'
13
+ # require 'minitest/autorun'
14
14
  # require 'net/ssh/test'
15
15
  #
16
- # class MyTest < Test::Unit::TestCase
16
+ # class MyTest < Minitest::Test
17
17
  # include Net::SSH::Test
18
18
  #
19
19
  # def test_exec_via_channel_works
@@ -50,7 +50,7 @@ module Net; module SSH
50
50
  # If a block is given, yields the script for the test socket (#socket).
51
51
  # Otherwise, simply returns the socket's script. See Net::SSH::Test::Script.
52
52
  def story
53
- yield socket.script if block_given?
53
+ Net::SSH::Test::Extensions::IO.with_test_extension { yield socket.script if block_given? }
54
54
  return socket.script
55
55
  end
56
56
 
@@ -71,7 +71,7 @@ module Net; module SSH
71
71
  # in these tests. It is a fully functional SSH transport session, operating
72
72
  # over a mock socket (#socket).
73
73
  def transport(options={})
74
- @transport ||= Net::SSH::Transport::Session.new(options[:host] || "localhost", options.merge(:kex => "test", :host_key => "ssh-rsa", :paranoid => false, :proxy => socket(options)))
74
+ @transport ||= Net::SSH::Transport::Session.new(options[:host] || "localhost", options.merge(kex: "test", host_key: "ssh-rsa", paranoid: false, proxy: socket(options)))
75
75
  end
76
76
 
77
77
  # First asserts that a story has been described (see #story). Then yields,
@@ -81,7 +81,7 @@ module Net; module SSH
81
81
  # the block passed to this assertion.
82
82
  def assert_scripted
83
83
  raise "there is no script to be processed" if socket.script.events.empty?
84
- yield
84
+ Net::SSH::Test::Extensions::IO.with_test_extension { yield }
85
85
  assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still #{socket.script.events.length} pending"
86
86
  end
87
87
  end
@@ -6,6 +6,7 @@ require 'net/ssh/transport/constants'
6
6
  require 'net/ssh/transport/hmac'
7
7
  require 'net/ssh/transport/kex'
8
8
  require 'net/ssh/transport/server_version'
9
+ require 'net/ssh/authentication/ed25519_loader'
9
10
 
10
11
  module Net; module SSH; module Transport
11
12
 
@@ -22,40 +23,34 @@ module Net; module SSH; module Transport
22
23
  # Define the default algorithms, in order of preference, supported by
23
24
  # Net::SSH.
24
25
  ALGORITHMS = {
25
- :host_key => %w(ssh-rsa ssh-dss
26
- ssh-rsa-cert-v01@openssh.com
27
- ssh-rsa-cert-v00@openssh.com),
28
- :kex => %w(diffie-hellman-group-exchange-sha1
29
- diffie-hellman-group1-sha1
30
- diffie-hellman-group14-sha1
31
- diffie-hellman-group-exchange-sha256),
32
- :encryption => %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc
33
- aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se
34
- idea-cbc none arcfour128 arcfour256 arcfour
35
- aes128-ctr aes192-ctr aes256-ctr
36
- camellia128-cbc camellia192-cbc camellia256-cbc
37
- camellia128-cbc@openssh.org
38
- camellia192-cbc@openssh.org
39
- camellia256-cbc@openssh.org
40
- camellia128-ctr camellia192-ctr camellia256-ctr
41
- camellia128-ctr@openssh.org
42
- camellia192-ctr@openssh.org
43
- camellia256-ctr@openssh.org
44
- cast128-ctr blowfish-ctr 3des-ctr
45
- ),
46
-
47
- :hmac => %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96
48
- hmac-ripemd160 hmac-ripemd160@openssh.com
49
- hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96
50
- hmac-sha2-512-96 none),
51
-
52
- :compression => %w(none zlib@openssh.com zlib),
53
- :language => %w()
26
+ host_key: %w(ssh-rsa ssh-dss
27
+ ssh-rsa-cert-v01@openssh.com
28
+ ssh-rsa-cert-v00@openssh.com),
29
+ kex: %w(diffie-hellman-group-exchange-sha1
30
+ diffie-hellman-group1-sha1
31
+ diffie-hellman-group14-sha1
32
+ diffie-hellman-group-exchange-sha256),
33
+ encryption: %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc
34
+ aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se
35
+ idea-cbc none arcfour128 arcfour256 arcfour
36
+ aes128-ctr aes192-ctr aes256-ctr
37
+ cast128-ctr blowfish-ctr 3des-ctr),
38
+
39
+ hmac: %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96
40
+ hmac-ripemd160 hmac-ripemd160@openssh.com
41
+ hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96
42
+ hmac-sha2-512-96 none),
43
+
44
+ compression: %w(none zlib@openssh.com zlib),
45
+ language: %w()
54
46
  }
55
47
  if defined?(OpenSSL::PKey::EC)
56
48
  ALGORITHMS[:host_key] += %w(ecdsa-sha2-nistp256
57
49
  ecdsa-sha2-nistp384
58
50
  ecdsa-sha2-nistp521)
51
+ if Net::SSH::Authentication::ED25519Loader::LOADED
52
+ ALGORITHMS[:host_key] += %w(ssh-ed25519)
53
+ end
59
54
  ALGORITHMS[:kex] += %w(ecdh-sha2-nistp256
60
55
  ecdh-sha2-nistp384
61
56
  ecdh-sha2-nistp521)
@@ -124,6 +119,12 @@ module Net; module SSH; module Transport
124
119
  prepare_preferred_algorithms!
125
120
  end
126
121
 
122
+ # Start the algorithm negotation
123
+ def start
124
+ raise ArgumentError, "Cannot call start if it's negoitation started or done" if @pending || @initialized
125
+ send_kexinit
126
+ end
127
+
127
128
  # Request a rekey operation. This will return immediately, and does not
128
129
  # actually perform the rekey operation. It does cause the session to change
129
130
  # state, however--until the key exchange finishes, no new packets will be
@@ -210,25 +211,8 @@ module Net; module SSH; module Transport
210
211
  def prepare_preferred_algorithms!
211
212
  options[:compression] = %w(zlib@openssh.com zlib) if options[:compression] == true
212
213
 
213
- ALGORITHMS.each do |algorithm, list|
214
- algorithms[algorithm] = list.dup
215
-
216
- # apply the preferred algorithm order, if any
217
- if options[algorithm]
218
- algorithms[algorithm] = Array(options[algorithm]).compact.uniq
219
- unsupported = []
220
- algorithms[algorithm].select! do |name|
221
- supported = ALGORITHMS[algorithm].include?(name)
222
- unsupported << name unless supported
223
- supported
224
- end
225
- lwarn { "unsupported #{algorithm} algorithm: `#{unsupported}'" } unless unsupported.empty?
226
-
227
- # make sure all of our supported algorithms are tacked onto the
228
- # end, so that if the user tries to give a list of which none are
229
- # supported, we can still proceed.
230
- list.each { |name| algorithms[algorithm] << name unless algorithms[algorithm].include?(name) }
231
- end
214
+ ALGORITHMS.each do |algorithm, supported|
215
+ algorithms[algorithm] = compose_algorithm_list(supported, options[algorithm], options[:append_all_supported_algorithms])
232
216
  end
233
217
 
234
218
  # for convention, make sure our list has the same keys as the server
@@ -243,7 +227,7 @@ module Net; module SSH; module Transport
243
227
  # make sure the host keys are specified in preference order, where any
244
228
  # existing known key for the host has preference.
245
229
 
246
- existing_keys = KnownHosts.search_for(options[:host_key_alias] || session.host_as_string, options)
230
+ existing_keys = session.host_keys
247
231
  host_keys = existing_keys.map { |key| key.ssh_type }.uniq
248
232
  algorithms[:host_key].each do |name|
249
233
  host_keys << name unless host_keys.include?(name)
@@ -252,9 +236,41 @@ module Net; module SSH; module Transport
252
236
  end
253
237
  end
254
238
 
239
+ # Composes the list of algorithms by taking supported algorithms and matching with supplied options.
240
+ def compose_algorithm_list(supported, option, append_all_supported_algorithms = false)
241
+ return supported.dup unless option
242
+
243
+ list = []
244
+ option = Array(option).compact.uniq
245
+
246
+ if option.first && option.first.start_with?('+')
247
+ list = supported.dup
248
+ list << option.first[1..-1]
249
+ list.concat(option[1..-1])
250
+ list.uniq!
251
+ else
252
+ list = option
253
+
254
+ if append_all_supported_algorithms
255
+ supported.each { |name| list << name unless list.include?(name) }
256
+ end
257
+ end
258
+
259
+ unsupported = []
260
+ list.select! do |name|
261
+ is_supported = supported.include?(name)
262
+ unsupported << name unless is_supported
263
+ is_supported
264
+ end
265
+
266
+ lwarn { "unsupported #{algorithm} algorithm: `#{unsupported}'" } unless unsupported.empty?
267
+
268
+ list
269
+ end
270
+
255
271
  # Parses a KEXINIT packet from the server.
256
272
  def parse_server_algorithm_packet(packet)
257
- data = { :raw => packet.content }
273
+ data = { raw: packet.content }
258
274
 
259
275
  packet.read(16) # skip the cookie value
260
276
 
@@ -290,8 +306,8 @@ module Net; module SSH; module Transport
290
306
 
291
307
  Net::SSH::Buffer.from(:byte, KEXINIT,
292
308
  :long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)],
293
- :string, [kex, host_key, encryption, encryption, hmac, hmac],
294
- :string, [compression, compression, language, language],
309
+ :mstring, [kex, host_key, encryption, encryption, hmac, hmac],
310
+ :mstring, [compression, compression, language, language],
295
311
  :bool, false, :long, 0)
296
312
  end
297
313
 
@@ -355,12 +371,13 @@ module Net; module SSH; module Transport
355
371
  debug { "exchanging keys" }
356
372
 
357
373
  algorithm = Kex::MAP[kex].new(self, session,
358
- :client_version_string => Net::SSH::Transport::ServerVersion::PROTO_VERSION,
359
- :server_version_string => session.server_version.version,
360
- :server_algorithm_packet => @server_packet,
361
- :client_algorithm_packet => @client_packet,
362
- :need_bytes => kex_byte_requirement,
363
- :logger => logger)
374
+ client_version_string: Net::SSH::Transport::ServerVersion::PROTO_VERSION,
375
+ server_version_string: session.server_version.version,
376
+ server_algorithm_packet: @server_packet,
377
+ client_algorithm_packet: @client_packet,
378
+ need_bytes: kex_byte_requirement,
379
+ minimum_dh_bits: options[:minimum_dh_bits],
380
+ logger: logger)
364
381
  result = algorithm.exchange_keys
365
382
 
366
383
  secret = result[:shared_secret].to_ssh
@@ -370,7 +387,7 @@ module Net; module SSH; module Transport
370
387
  @session_id ||= hash
371
388
 
372
389
  key = Proc.new { |salt| digester.digest(secret + hash + salt + @session_id) }
373
-
390
+
374
391
  iv_client = key["A"]
375
392
  iv_server = key["B"]
376
393
  key_client = key["C"]
@@ -378,26 +395,26 @@ module Net; module SSH; module Transport
378
395
  mac_key_client = key["E"]
379
396
  mac_key_server = key["F"]
380
397
 
381
- parameters = { :shared => secret, :hash => hash, :digester => digester }
382
-
383
- cipher_client = CipherFactory.get(encryption_client, parameters.merge(:iv => iv_client, :key => key_client, :encrypt => true))
384
- cipher_server = CipherFactory.get(encryption_server, parameters.merge(:iv => iv_server, :key => key_server, :decrypt => true))
398
+ parameters = { shared: secret, hash: hash, digester: digester }
399
+
400
+ cipher_client = CipherFactory.get(encryption_client, parameters.merge(iv: iv_client, key: key_client, encrypt: true))
401
+ cipher_server = CipherFactory.get(encryption_server, parameters.merge(iv: iv_server, key: key_server, decrypt: true))
385
402
 
386
403
  mac_client = HMAC.get(hmac_client, mac_key_client, parameters)
387
404
  mac_server = HMAC.get(hmac_server, mac_key_server, parameters)
388
405
 
389
- session.configure_client :cipher => cipher_client, :hmac => mac_client,
390
- :compression => normalize_compression_name(compression_client),
391
- :compression_level => options[:compression_level],
392
- :rekey_limit => options[:rekey_limit],
393
- :max_packets => options[:rekey_packet_limit],
394
- :max_blocks => options[:rekey_blocks_limit]
395
-
396
- session.configure_server :cipher => cipher_server, :hmac => mac_server,
397
- :compression => normalize_compression_name(compression_server),
398
- :rekey_limit => options[:rekey_limit],
399
- :max_packets => options[:rekey_packet_limit],
400
- :max_blocks => options[:rekey_blocks_limit]
406
+ session.configure_client cipher: cipher_client, hmac: mac_client,
407
+ compression: normalize_compression_name(compression_client),
408
+ compression_level: options[:compression_level],
409
+ rekey_limit: options[:rekey_limit],
410
+ max_packets: options[:rekey_packet_limit],
411
+ max_blocks: options[:rekey_blocks_limit]
412
+
413
+ session.configure_server cipher: cipher_server, hmac: mac_server,
414
+ compression: normalize_compression_name(compression_server),
415
+ rekey_limit: options[:rekey_limit],
416
+ max_packets: options[:rekey_packet_limit],
417
+ max_blocks: options[:rekey_blocks_limit]
401
418
 
402
419
  @initialized = true
403
420
  end
@@ -21,12 +21,6 @@ module Net; module SSH; module Transport
21
21
  "arcfour256" => "rc4",
22
22
  "arcfour512" => "rc4",
23
23
  "arcfour" => "rc4",
24
- "camellia128-cbc" => "camellia-128-cbc",
25
- "camellia192-cbc" => "camellia-192-cbc",
26
- "camellia256-cbc" => "camellia-256-cbc",
27
- "camellia128-cbc@openssh.org" => "camellia-128-cbc",
28
- "camellia192-cbc@openssh.org" => "camellia-192-cbc",
29
- "camellia256-cbc@openssh.org" => "camellia-256-cbc",
30
24
 
31
25
  "3des-ctr" => "des-ede3",
32
26
  "blowfish-ctr" => "bf-ecb",
@@ -34,19 +28,13 @@ module Net; module SSH; module Transport
34
28
  "aes192-ctr" => "aes-192-ecb",
35
29
  "aes128-ctr" => "aes-128-ecb",
36
30
  "cast128-ctr" => "cast5-ecb",
37
- "camellia128-ctr" => "camellia-128-ecb",
38
- "camellia192-ctr" => "camellia-192-ecb",
39
- "camellia256-ctr" => "camellia-256-ecb",
40
- "camellia128-ctr@openssh.org" => "camellia-128-ecb",
41
- "camellia192-ctr@openssh.org" => "camellia-192-ecb",
42
- "camellia256-ctr@openssh.org" => "camellia-256-ecb",
43
31
 
44
32
  "none" => "none",
45
33
  }
46
34
 
47
35
  # Ruby's OpenSSL bindings always return a key length of 16 for RC4 ciphers
48
- # resulting in the error: OpenSSL::CipherError: key length too short.
49
- # The following ciphers will override this key length.
36
+ # resulting in the error: OpenSSL::CipherError: key length too short.
37
+ # The following ciphers will override this key length.
50
38
  KEY_LEN_OVERRIDE = {
51
39
  "arcfour256" => 32,
52
40
  "arcfour512" => 64
@@ -69,19 +57,18 @@ module Net; module SSH; module Transport
69
57
  def self.get(name, options={})
70
58
  ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
71
59
  return IdentityCipher if ossl_name == "none"
72
- cipher = OpenSSL::Cipher::Cipher.new(ossl_name)
60
+ cipher = OpenSSL::Cipher.new(ossl_name)
73
61
 
74
62
  cipher.send(options[:encrypt] ? :encrypt : :decrypt)
75
63
 
76
64
  cipher.padding = 0
77
65
 
78
66
  cipher.extend(Net::SSH::Transport::CTR) if (name =~ /-ctr(@openssh.org)?$/)
79
-
80
- cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options) if ossl_name != "rc4"
67
+ cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options) if ossl_name != "rc4"
81
68
 
82
69
  key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len
83
70
  cipher.key_len = key_len
84
- cipher.key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options)
71
+ cipher.key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options)
85
72
  cipher.update(" " * 1536) if (ossl_name == "rc4" && name != "arcfour")
86
73
 
87
74
  return cipher
@@ -91,15 +78,21 @@ module Net; module SSH; module Transport
91
78
  # block-size ] for the named cipher algorithm. If the cipher
92
79
  # algorithm is unknown, or is "none", 0 is returned for both elements
93
80
  # of the tuple.
94
- def self.get_lengths(name)
81
+ # if :iv_len option is supplied the third return value will be ivlen
82
+ def self.get_lengths(name, options = {})
95
83
  ossl_name = SSH_TO_OSSL[name]
96
- return [0, 0] if ossl_name.nil? || ossl_name == "none"
97
-
98
- cipher = OpenSSL::Cipher::Cipher.new(ossl_name)
99
- key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len
100
- cipher.key_len = key_len
101
-
102
- return [key_len, ossl_name=="rc4" ? 8 : cipher.block_size]
84
+ if ossl_name.nil? || ossl_name == "none"
85
+ result = [0, 0]
86
+ result << 0 if options[:iv_len]
87
+ else
88
+ cipher = OpenSSL::Cipher.new(ossl_name)
89
+ key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len
90
+ cipher.key_len = key_len
91
+
92
+ result = [key_len, ossl_name=="rc4" ? 8 : cipher.block_size]
93
+ result << cipher.iv_len if options[:iv_len]
94
+ end
95
+ result
103
96
  end
104
97
  end
105
98
 
@@ -12,12 +12,10 @@ module Net::SSH::Transport
12
12
  @counter_len = orig.block_size
13
13
  orig.encrypt
14
14
  orig.padding = 0
15
- }
16
-
17
- class <<orig
18
- alias :_update :update
19
- private :_update
20
- undef :update
15
+
16
+ singleton_class.send(:alias_method, :_update, :update)
17
+ singleton_class.send(:private, :_update)
18
+ singleton_class.send(:undef_method, :update)
21
19
 
22
20
  def iv
23
21
  @counter
@@ -73,13 +71,12 @@ module Net::SSH::Transport
73
71
  s
74
72
  end
75
73
 
76
- private
77
-
78
74
  def xor!(s1, s2)
79
75
  s = []
80
76
  s1.unpack('Q*').zip(s2.unpack('Q*')) {|a,b| s.push(a^b) }
81
77
  s.pack('Q*')
82
78
  end
79
+ singleton_class.send(:private, :xor!)
83
80
 
84
81
  def increment_counter!
85
82
  c = @counter_len
@@ -89,7 +86,8 @@ module Net::SSH::Transport
89
86
  end
90
87
  end
91
88
  end
92
- end
89
+ singleton_class.send(:private, :increment_counter!)
90
+ }
93
91
  end
94
92
  end
95
93
  end
@@ -69,10 +69,10 @@ module Net; module SSH; module Transport; module Kex
69
69
  session_id = verify_signature(result)
70
70
  confirm_newkeys
71
71
 
72
- return { :session_id => session_id,
73
- :server_key => result[:server_key],
74
- :shared_secret => result[:shared_secret],
75
- :hashing_algorithm => digester }
72
+ return { session_id: session_id,
73
+ server_key: result[:server_key],
74
+ shared_secret: result[:shared_secret],
75
+ hashing_algorithm: digester }
76
76
  end
77
77
 
78
78
  private
@@ -115,11 +115,22 @@ module Net; module SSH; module Transport; module Kex
115
115
  def generate_key #:nodoc:
116
116
  dh = OpenSSL::PKey::DH.new
117
117
 
118
- dh.p, dh.g = get_parameters
119
- dh.priv_key = OpenSSL::BN.rand(data[:need_bytes] * 8)
120
-
121
- dh.generate_key! until dh.valid?
118
+ if dh.respond_to?(:set_pqg)
119
+ p, g = get_parameters
120
+ dh.set_pqg(p, nil, g)
121
+ else
122
+ dh.p, dh.g = get_parameters
123
+ end
122
124
 
125
+ dh.generate_key!
126
+ until dh.valid? && dh.priv_key.num_bytes == data[:need_bytes]
127
+ if dh.respond_to?(:set_key)
128
+ dh.set_key(nil, OpenSSL::BN.rand(data[:need_bytes] * 8))
129
+ else
130
+ dh.priv_key = OpenSSL::BN.rand(data[:need_bytes] * 8)
131
+ end
132
+ dh.generate_key!
133
+ end
123
134
  dh
124
135
  end
125
136
 
@@ -170,7 +181,7 @@ module Net; module SSH; module Transport; module Kex
170
181
 
171
182
  blob, fingerprint = generate_key_fingerprint(key)
172
183
 
173
- unless connection.host_key_verifier.verify(:key => key, :key_blob => blob, :fingerprint => fingerprint, :session => connection)
184
+ unless connection.host_key_verifier.verify(key: key, key_blob: blob, fingerprint: fingerprint, session: connection)
174
185
  raise Net::SSH::Exception, "host key verification failed"
175
186
  end
176
187
  end
@@ -23,8 +23,10 @@ module Net::SSH::Transport::Kex
23
23
  # for Compatibility: OpenSSH requires (need_bits * 2 + 1) length of parameter
24
24
  need_bits = data[:need_bytes] * 8 * 2 + 1
25
25
 
26
- if need_bits < MINIMUM_BITS
27
- need_bits = MINIMUM_BITS
26
+ data[:minimum_dh_bits] ||= MINIMUM_BITS
27
+
28
+ if need_bits < data[:minimum_dh_bits]
29
+ need_bits = data[:minimum_dh_bits]
28
30
  elsif need_bits > MAXIMUM_BITS
29
31
  need_bits = MAXIMUM_BITS
30
32
  end
@@ -38,7 +40,7 @@ module Net::SSH::Transport::Kex
38
40
  compute_need_bits
39
41
 
40
42
  # request the DH key parameters for the given number of bits.
41
- buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, MINIMUM_BITS,
43
+ buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, data[:minimum_dh_bits],
42
44
  :long, data[:need_bits], :long, MAXIMUM_BITS)
43
45
  connection.send_message(buffer)
44
46
 
@@ -57,7 +57,7 @@ module Net; module SSH; module Transport; module Kex
57
57
  # send the KEXECDH_INIT message
58
58
  ## byte SSH_MSG_KEX_ECDH_INIT
59
59
  ## string Q_C, client's ephemeral public key octet string
60
- buffer = Net::SSH::Buffer.from(:byte, init, :string, ecdh.public_key.to_bn.to_s(2))
60
+ buffer = Net::SSH::Buffer.from(:byte, init, :mstring, ecdh.public_key.to_bn.to_s(2))
61
61
  connection.send_message(buffer)
62
62
 
63
63
  # expect the following KEXECDH_REPLY message
@@ -9,6 +9,7 @@ module Net; module SSH; module Transport
9
9
  end
10
10
 
11
11
  k = start[0, bytes]
12
+ return k if k.length >= bytes
12
13
 
13
14
  digester = options[:digester] or raise 'No digester supplied'
14
15
  shared = options[:shared] or raise 'No shared secret supplied'
@@ -185,7 +185,7 @@ module OpenSSL
185
185
  def to_blob
186
186
  @blob ||= Net::SSH::Buffer.from(:string, ssh_type,
187
187
  :string, CurveNameAliasInv[self.group.curve_name],
188
- :string, self.public_key.to_bn.to_s(2)).to_s
188
+ :mstring, self.public_key.to_bn.to_s(2)).to_s
189
189
  @blob
190
190
  end
191
191
 
@@ -69,13 +69,13 @@ module Net; module SSH; module Transport
69
69
  PROXY_COMMAND_HOST_IP
70
70
  end
71
71
  end
72
-
72
+
73
73
  # Returns true if the IO is available for reading, and false otherwise.
74
74
  def available_for_read?
75
75
  result = Net::SSH::Compat.io_select([self], nil, nil, 0)
76
76
  result && result.first.any?
77
77
  end
78
-
78
+
79
79
  # Returns the next full packet. If the mode parameter is :nonblock (the
80
80
  # default), then this will return immediately, whether a packet is
81
81
  # available or not, and will return nil if there is no packet ready to be
@@ -84,9 +84,17 @@ module Net; module SSH; module Transport
84
84
  def next_packet(mode=:nonblock)
85
85
  case mode
86
86
  when :nonblock then
87
+ packet = poll_next_packet
88
+ return packet if packet
89
+
87
90
  if available_for_read?
88
91
  if fill <= 0
89
- raise Net::SSH::Disconnect, "connection closed by remote host"
92
+ result = poll_next_packet
93
+ if result.nil?
94
+ raise Net::SSH::Disconnect, "connection closed by remote host"
95
+ else
96
+ return result
97
+ end
90
98
  end
91
99
  end
92
100
  poll_next_packet