net-ssh 5.0.2 → 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.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/config/rubocop_linter_action.yml +4 -0
  5. data/.github/workflows/ci-with-docker.yml +44 -0
  6. data/.github/workflows/ci.yml +87 -0
  7. data/.github/workflows/rubocop.yml +13 -0
  8. data/.gitignore +3 -0
  9. data/.rubocop.yml +19 -2
  10. data/.rubocop_todo.yml +623 -511
  11. data/CHANGES.txt +76 -0
  12. data/Dockerfile +27 -0
  13. data/Dockerfile.openssl3 +17 -0
  14. data/Gemfile +2 -0
  15. data/Gemfile.noed25519 +2 -0
  16. data/Manifest +0 -1
  17. data/README.md +293 -0
  18. data/Rakefile +6 -2
  19. data/appveyor.yml +4 -2
  20. data/docker-compose.yml +23 -0
  21. data/lib/net/ssh/authentication/agent.rb +36 -14
  22. data/lib/net/ssh/authentication/certificate.rb +19 -7
  23. data/lib/net/ssh/authentication/constants.rb +0 -1
  24. data/lib/net/ssh/authentication/ed25519.rb +83 -50
  25. data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
  26. data/lib/net/ssh/authentication/key_manager.rb +74 -33
  27. data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
  28. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
  29. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +5 -3
  30. data/lib/net/ssh/authentication/methods/none.rb +6 -9
  31. data/lib/net/ssh/authentication/methods/password.rb +2 -3
  32. data/lib/net/ssh/authentication/methods/publickey.rb +58 -16
  33. data/lib/net/ssh/authentication/pageant.rb +97 -97
  34. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -3
  35. data/lib/net/ssh/authentication/session.rb +27 -23
  36. data/lib/net/ssh/buffer.rb +91 -40
  37. data/lib/net/ssh/buffered_io.rb +24 -26
  38. data/lib/net/ssh/config.rb +99 -53
  39. data/lib/net/ssh/connection/channel.rb +101 -87
  40. data/lib/net/ssh/connection/constants.rb +0 -4
  41. data/lib/net/ssh/connection/event_loop.rb +30 -25
  42. data/lib/net/ssh/connection/keepalive.rb +12 -12
  43. data/lib/net/ssh/connection/session.rb +115 -111
  44. data/lib/net/ssh/connection/term.rb +56 -58
  45. data/lib/net/ssh/errors.rb +12 -12
  46. data/lib/net/ssh/key_factory.rb +108 -22
  47. data/lib/net/ssh/known_hosts.rb +120 -36
  48. data/lib/net/ssh/loggable.rb +10 -11
  49. data/lib/net/ssh/packet.rb +1 -1
  50. data/lib/net/ssh/prompt.rb +9 -11
  51. data/lib/net/ssh/proxy/command.rb +1 -2
  52. data/lib/net/ssh/proxy/errors.rb +2 -4
  53. data/lib/net/ssh/proxy/http.rb +18 -20
  54. data/lib/net/ssh/proxy/https.rb +8 -10
  55. data/lib/net/ssh/proxy/jump.rb +8 -10
  56. data/lib/net/ssh/proxy/socks4.rb +2 -4
  57. data/lib/net/ssh/proxy/socks5.rb +3 -6
  58. data/lib/net/ssh/service/forward.rb +9 -8
  59. data/lib/net/ssh/test/channel.rb +24 -26
  60. data/lib/net/ssh/test/extensions.rb +37 -35
  61. data/lib/net/ssh/test/kex.rb +6 -8
  62. data/lib/net/ssh/test/local_packet.rb +0 -2
  63. data/lib/net/ssh/test/packet.rb +3 -3
  64. data/lib/net/ssh/test/remote_packet.rb +6 -8
  65. data/lib/net/ssh/test/script.rb +25 -27
  66. data/lib/net/ssh/test/socket.rb +12 -15
  67. data/lib/net/ssh/test.rb +12 -12
  68. data/lib/net/ssh/transport/algorithms.rb +177 -118
  69. data/lib/net/ssh/transport/cipher_factory.rb +34 -50
  70. data/lib/net/ssh/transport/constants.rb +13 -9
  71. data/lib/net/ssh/transport/ctr.rb +8 -14
  72. data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
  73. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  74. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  75. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  76. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  77. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  78. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  79. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  80. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  81. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  82. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  83. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  84. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  85. data/lib/net/ssh/transport/hmac.rb +13 -11
  86. data/lib/net/ssh/transport/identity_cipher.rb +11 -13
  87. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  88. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  89. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  90. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +5 -19
  92. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +30 -139
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -8
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  96. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +20 -81
  97. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +5 -4
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +5 -4
  99. data/lib/net/ssh/transport/kex.rb +15 -10
  100. data/lib/net/ssh/transport/key_expander.rb +7 -8
  101. data/lib/net/ssh/transport/openssl.rb +149 -111
  102. data/lib/net/ssh/transport/packet_stream.rb +53 -22
  103. data/lib/net/ssh/transport/server_version.rb +17 -16
  104. data/lib/net/ssh/transport/session.rb +35 -11
  105. data/lib/net/ssh/transport/state.rb +44 -44
  106. data/lib/net/ssh/verifiers/accept_new.rb +7 -2
  107. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  108. data/lib/net/ssh/verifiers/always.rb +10 -4
  109. data/lib/net/ssh/verifiers/never.rb +4 -2
  110. data/lib/net/ssh/version.rb +2 -2
  111. data/lib/net/ssh.rb +17 -9
  112. data/net-ssh-public_cert.pem +18 -19
  113. data/net-ssh.gemspec +9 -7
  114. data/support/ssh_tunnel_bug.rb +3 -3
  115. data.tar.gz.sig +0 -0
  116. metadata +65 -41
  117. metadata.gz.sig +0 -0
  118. data/.travis.yml +0 -52
  119. data/Gemfile.noed25519.lock +0 -41
  120. data/README.rdoc +0 -169
  121. data/lib/net/ssh/ruby_compat.rb +0 -13
  122. 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
- module Net
12
- module SSH
12
+ module Net
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
@@ -22,95 +22,131 @@ module Net
22
22
  class Algorithms
23
23
  include Loggable
24
24
  include Constants
25
-
26
- # Define the default algorithms, in order of preference, supported by
27
- # Net::SSH.
28
- ALGORITHMS = {
29
- host_key: %w[ssh-rsa ssh-dss
25
+
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
30
34
  ssh-rsa-cert-v01@openssh.com
31
- ssh-rsa-cert-v00@openssh.com],
32
- kex: %w[diffie-hellman-group-exchange-sha1
33
- diffie-hellman-group1-sha1
34
- diffie-hellman-group14-sha1
35
- diffie-hellman-group-exchange-sha256],
36
- encryption: %w[aes128-cbc 3des-cbc blowfish-cbc cast128-cbc
37
- aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se
38
- idea-cbc arcfour128 arcfour256 arcfour
39
- aes128-ctr aes192-ctr aes256-ctr
40
- cast128-ctr blowfish-ctr 3des-ctr none],
41
-
42
- hmac: %w[hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96
35
+ ssh-rsa-cert-v00@openssh.com
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
74
+ diffie-hellman-group1-sha1],
75
+
76
+ encryption: DEFAULT_ALGORITHMS[:encryption] +
77
+ %w[aes256-cbc aes192-cbc aes128-cbc
78
+ rijndael-cbc@lysator.liu.se
79
+ blowfish-ctr blowfish-cbc
80
+ cast128-ctr cast128-cbc
81
+ 3des-ctr 3des-cbc
82
+ idea-cbc
83
+ none],
84
+
85
+ hmac: DEFAULT_ALGORITHMS[:hmac] +
86
+ %w[hmac-sha2-512-96 hmac-sha2-256-96
87
+ hmac-sha1-96
43
88
  hmac-ripemd160 hmac-ripemd160@openssh.com
44
- hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96
45
- hmac-sha2-512-96 none],
46
-
89
+ hmac-md5 hmac-md5-96
90
+ none],
91
+
47
92
  compression: %w[none zlib@openssh.com zlib],
48
93
  language: %w[]
49
- }
50
- if defined?(OpenSSL::PKey::EC)
51
- ALGORITHMS[:host_key] += %w[ecdsa-sha2-nistp256
52
- ecdsa-sha2-nistp384
53
- ecdsa-sha2-nistp521]
54
- ALGORITHMS[:host_key] += %w[ssh-ed25519] if Net::SSH::Authentication::ED25519Loader::LOADED
55
- ALGORITHMS[:kex] += %w[ecdh-sha2-nistp256
56
- ecdh-sha2-nistp384
57
- ecdh-sha2-nistp521]
58
- end
59
-
94
+ }.freeze
95
+
60
96
  # The underlying transport layer session that supports this object
61
97
  attr_reader :session
62
-
98
+
63
99
  # The hash of options used to initialize this object
64
100
  attr_reader :options
65
-
101
+
66
102
  # The kex algorithm to use settled on between the client and server.
67
103
  attr_reader :kex
68
-
104
+
69
105
  # The type of host key that will be used for this session.
70
106
  attr_reader :host_key
71
-
107
+
72
108
  # The type of the cipher to use to encrypt packets sent from the client to
73
109
  # the server.
74
110
  attr_reader :encryption_client
75
-
111
+
76
112
  # The type of the cipher to use to decrypt packets arriving from the server.
77
113
  attr_reader :encryption_server
78
-
114
+
79
115
  # The type of HMAC to use to sign packets sent by the client.
80
116
  attr_reader :hmac_client
81
-
117
+
82
118
  # The type of HMAC to use to validate packets arriving from the server.
83
119
  attr_reader :hmac_server
84
-
120
+
85
121
  # The type of compression to use to compress packets being sent by the client.
86
122
  attr_reader :compression_client
87
-
123
+
88
124
  # The type of compression to use to decompress packets arriving from the server.
89
125
  attr_reader :compression_server
90
-
126
+
91
127
  # The language that will be used in messages sent by the client.
92
128
  attr_reader :language_client
93
-
129
+
94
130
  # The language that will be used in messages sent from the server.
95
131
  attr_reader :language_server
96
-
132
+
97
133
  # The hash of algorithms preferred by the client, which will be told to
98
134
  # the server during algorithm negotiation.
99
135
  attr_reader :algorithms
100
-
136
+
101
137
  # The session-id for this session, as decided during the initial key exchange.
102
138
  attr_reader :session_id
103
-
139
+
104
140
  # Returns true if the given packet can be processed during a key-exchange.
105
141
  def self.allowed_packet?(packet)
106
142
  (1..4).include?(packet.type) ||
107
143
  (6..19).include?(packet.type) ||
108
144
  (21..49).include?(packet.type)
109
145
  end
110
-
146
+
111
147
  # Instantiates a new Algorithms object, and prepares the hash of preferred
112
148
  # algorithms based on the options parameter and the ALGORITHMS constant.
113
- def initialize(session, options={})
149
+ def initialize(session, options = {})
114
150
  @session = session
115
151
  @logger = session.logger
116
152
  @options = options
@@ -119,13 +155,14 @@ module Net
119
155
  @client_packet = @server_packet = nil
120
156
  prepare_preferred_algorithms!
121
157
  end
122
-
158
+
123
159
  # Start the algorithm negotation
124
160
  def start
125
161
  raise ArgumentError, "Cannot call start if it's negotiation started or done" if @pending || @initialized
162
+
126
163
  send_kexinit
127
164
  end
128
-
165
+
129
166
  # Request a rekey operation. This will return immediately, and does not
130
167
  # actually perform the rekey operation. It does cause the session to change
131
168
  # state, however--until the key exchange finishes, no new packets will be
@@ -135,7 +172,7 @@ module Net
135
172
  @initialized = false
136
173
  send_kexinit
137
174
  end
138
-
175
+
139
176
  # Called by the transport layer when a KEXINIT packet is received, indicating
140
177
  # that the server wants to exchange keys. This can be spontaneous, or it
141
178
  # can be in response to a client-initiated rekey request (see #rekey!). Either
@@ -150,13 +187,13 @@ module Net
150
187
  proceed!
151
188
  end
152
189
  end
153
-
190
+
154
191
  # A convenience method for accessing the list of preferred types for a
155
192
  # specific algorithm (see #algorithms).
156
193
  def [](key)
157
194
  algorithms[key]
158
195
  end
159
-
196
+
160
197
  # Returns +true+ if a key-exchange is pending. This will be true from the
161
198
  # moment either the client or server requests the key exchange, until the
162
199
  # exchange completes. While an exchange is pending, only a limited number
@@ -180,8 +217,8 @@ module Net
180
217
 
181
218
  def host_key_format
182
219
  case host_key
183
- when "ssh-rsa-cert-v01@openssh.com", "ssh-rsa-cert-v00@openssh.com"
184
- "ssh-rsa"
220
+ when /^([a-z0-9-]+)-cert-v\d{2}@openssh.com$/
221
+ Regexp.last_match[1]
185
222
  else
186
223
  host_key
187
224
  end
@@ -201,7 +238,7 @@ module Net
201
238
  session.send_message(packet)
202
239
  proceed! if @server_packet
203
240
  end
204
-
241
+
205
242
  # After both client and server have sent their KEXINIT packets, this
206
243
  # will do the algorithm negotiation and key exchange. Once both finish,
207
244
  # the object leaves the pending state and the method returns.
@@ -211,7 +248,7 @@ module Net
211
248
  exchange_keys
212
249
  @pending = false
213
250
  end
214
-
251
+
215
252
  # Prepares the list of preferred algorithms, based on the options hash
216
253
  # that was given when the object was constructed, and the ALGORITHMS
217
254
  # constant. Also, when determining the host_key type to use, the known
@@ -220,70 +257,87 @@ module Net
220
257
  # communicating with this server.
221
258
  def prepare_preferred_algorithms!
222
259
  options[:compression] = %w[zlib@openssh.com zlib] if options[:compression] == true
223
-
260
+
224
261
  ALGORITHMS.each do |algorithm, supported|
225
- algorithms[algorithm] = compose_algorithm_list(supported, options[algorithm], options[:append_all_supported_algorithms])
262
+ algorithms[algorithm] = compose_algorithm_list(
263
+ supported, options[algorithm] || DEFAULT_ALGORITHMS[algorithm],
264
+ options[:append_all_supported_algorithms]
265
+ )
226
266
  end
227
-
267
+
228
268
  # for convention, make sure our list has the same keys as the server
229
269
  # list
230
-
270
+
231
271
  algorithms[:encryption_client ] = algorithms[:encryption_server ] = algorithms[:encryption]
232
272
  algorithms[:hmac_client ] = algorithms[:hmac_server ] = algorithms[:hmac]
233
273
  algorithms[:compression_client] = algorithms[:compression_server] = algorithms[:compression]
234
274
  algorithms[:language_client ] = algorithms[:language_server ] = algorithms[:language]
235
-
275
+
236
276
  if !options.key?(:host_key)
237
277
  # make sure the host keys are specified in preference order, where any
238
278
  # existing known key for the host has preference.
239
-
279
+
240
280
  existing_keys = session.host_keys
241
- host_keys = existing_keys.map { |key| key.ssh_type }.uniq
281
+ host_keys = existing_keys.flat_map { |key| key.respond_to?(:ssh_types) ? key.ssh_types : [key.ssh_type] }.uniq
242
282
  algorithms[:host_key].each do |name|
243
283
  host_keys << name unless host_keys.include?(name)
244
284
  end
245
285
  algorithms[:host_key] = host_keys
246
286
  end
247
287
  end
248
-
288
+
249
289
  # Composes the list of algorithms by taking supported algorithms and matching with supplied options.
250
290
  def compose_algorithm_list(supported, option, append_all_supported_algorithms = false)
251
291
  return supported.dup unless option
252
-
292
+
253
293
  list = []
254
294
  option = Array(option).compact.uniq
255
-
256
- if option.first && option.first.start_with?('+')
295
+
296
+ if option.first && option.first.start_with?('+', '-')
257
297
  list = supported.dup
258
- list << option.first[1..-1]
259
- list.concat(option[1..-1])
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
+
260
314
  list.uniq!
261
315
  else
262
316
  list = option
263
-
317
+
264
318
  if append_all_supported_algorithms
265
319
  supported.each { |name| list << name unless list.include?(name) }
266
320
  end
267
321
  end
268
-
322
+
269
323
  unsupported = []
270
324
  list.select! do |name|
271
325
  is_supported = supported.include?(name)
272
326
  unsupported << name unless is_supported
273
327
  is_supported
274
328
  end
275
-
329
+
276
330
  lwarn { %(unsupported algorithm: `#{unsupported}') } unless unsupported.empty?
277
-
331
+
278
332
  list
279
333
  end
280
-
334
+
281
335
  # Parses a KEXINIT packet from the server.
282
336
  def parse_server_algorithm_packet(packet)
283
337
  data = { raw: packet.content }
284
-
338
+
285
339
  packet.read(16) # skip the cookie value
286
-
340
+
287
341
  data[:kex] = packet.read_string.split(/,/)
288
342
  data[:host_key] = packet.read_string.split(/,/)
289
343
  data[:encryption_client] = packet.read_string.split(/,/)
@@ -294,15 +348,15 @@ module Net
294
348
  data[:compression_server] = packet.read_string.split(/,/)
295
349
  data[:language_client] = packet.read_string.split(/,/)
296
350
  data[:language_server] = packet.read_string.split(/,/)
297
-
351
+
298
352
  # TODO: if first_kex_packet_follows, we need to try to skip the
299
353
  # actual kexinit stuff and try to guess what the server is doing...
300
354
  # need to read more about this scenario.
301
355
  # first_kex_packet_follows = packet.read_bool
302
-
356
+
303
357
  return data
304
358
  end
305
-
359
+
306
360
  # Given the #algorithms map of preferred algorithm types, this constructs
307
361
  # a KEXINIT packet to send to the server. It does not actually send it,
308
362
  # it simply builds the packet and returns it.
@@ -313,14 +367,14 @@ module Net
313
367
  hmac = algorithms[:hmac].join(",")
314
368
  compression = algorithms[:compression].join(",")
315
369
  language = algorithms[:language].join(",")
316
-
370
+
317
371
  Net::SSH::Buffer.from(:byte, KEXINIT,
318
- :long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)],
319
- :mstring, [kex, host_key, encryption, encryption, hmac, hmac],
320
- :mstring, [compression, compression, language, language],
321
- :bool, false, :long, 0)
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)
322
376
  end
323
-
377
+
324
378
  # Given the parsed server KEX packet, and the client's preferred algorithm
325
379
  # lists in #algorithms, determine which preferred algorithms each has
326
380
  # in common and set those as the selected algorithms. If, for any algorithm,
@@ -336,97 +390,102 @@ module Net
336
390
  @compression_server = negotiate(:compression_server)
337
391
  @language_client = negotiate(:language_client) rescue ""
338
392
  @language_server = negotiate(:language_server) rescue ""
339
-
393
+
340
394
  debug do
341
395
  "negotiated:\n" +
342
- %i[kex host_key encryption_server encryption_client hmac_client hmac_server compression_client compression_server language_client language_server].map do |key|
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|
343
398
  "* #{key}: #{instance_variable_get("@#{key}")}"
344
399
  end.join("\n")
345
400
  end
346
401
  end
347
-
402
+
348
403
  # Negotiates a single algorithm based on the preferences reported by the
349
404
  # server and those set by the client. This is called by
350
405
  # #negotiate_algorithms.
351
406
  def negotiate(algorithm)
352
407
  match = self[algorithm].find { |item| @server_data[algorithm].include?(item) }
353
-
354
- raise Net::SSH::Exception, "could not settle on #{algorithm} algorithm" if match.nil?
355
-
408
+
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
414
+
356
415
  return match
357
416
  end
358
-
417
+
359
418
  # Considers the sizes of the keys and block-sizes for the selected ciphers,
360
419
  # and the lengths of the hmacs, and returns the largest as the byte requirement
361
420
  # for the key-exchange algorithm.
362
421
  def kex_byte_requirement
363
422
  sizes = [8] # require at least 8 bytes
364
-
423
+
365
424
  sizes.concat(CipherFactory.get_lengths(encryption_client))
366
425
  sizes.concat(CipherFactory.get_lengths(encryption_server))
367
-
426
+
368
427
  sizes << HMAC.key_length(hmac_client)
369
428
  sizes << HMAC.key_length(hmac_server)
370
-
429
+
371
430
  sizes.max
372
431
  end
373
-
432
+
374
433
  # Instantiates one of the Transport::Kex classes (based on the negotiated
375
434
  # kex algorithm), and uses it to exchange keys. Then, the ciphers and
376
435
  # HMACs are initialized and fed to the transport layer, to be used in
377
436
  # further communication with the server.
378
437
  def exchange_keys
379
438
  debug { "exchanging keys" }
380
-
439
+
381
440
  algorithm = Kex::MAP[kex].new(self, session,
382
- client_version_string: Net::SSH::Transport::ServerVersion::PROTO_VERSION,
383
- server_version_string: session.server_version.version,
384
- server_algorithm_packet: @server_packet,
385
- client_algorithm_packet: @client_packet,
386
- need_bytes: kex_byte_requirement,
387
- minimum_dh_bits: options[:minimum_dh_bits],
388
- logger: logger)
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)
389
448
  result = algorithm.exchange_keys
390
-
449
+
391
450
  secret = result[:shared_secret].to_ssh
392
451
  hash = result[:session_id]
393
452
  digester = result[:hashing_algorithm]
394
-
453
+
395
454
  @session_id ||= hash
396
-
455
+
397
456
  key = Proc.new { |salt| digester.digest(secret + hash + salt + @session_id) }
398
-
457
+
399
458
  iv_client = key["A"]
400
459
  iv_server = key["B"]
401
460
  key_client = key["C"]
402
461
  key_server = key["D"]
403
462
  mac_key_client = key["E"]
404
463
  mac_key_server = key["F"]
405
-
464
+
406
465
  parameters = { shared: secret, hash: hash, digester: digester }
407
-
466
+
408
467
  cipher_client = CipherFactory.get(encryption_client, parameters.merge(iv: iv_client, key: key_client, encrypt: true))
409
468
  cipher_server = CipherFactory.get(encryption_server, parameters.merge(iv: iv_server, key: key_server, decrypt: true))
410
-
469
+
411
470
  mac_client = HMAC.get(hmac_client, mac_key_client, parameters)
412
471
  mac_server = HMAC.get(hmac_server, mac_key_server, parameters)
413
-
472
+
414
473
  session.configure_client cipher: cipher_client, hmac: mac_client,
415
474
  compression: normalize_compression_name(compression_client),
416
475
  compression_level: options[:compression_level],
417
476
  rekey_limit: options[:rekey_limit],
418
477
  max_packets: options[:rekey_packet_limit],
419
478
  max_blocks: options[:rekey_blocks_limit]
420
-
479
+
421
480
  session.configure_server cipher: cipher_server, hmac: mac_server,
422
481
  compression: normalize_compression_name(compression_server),
423
482
  rekey_limit: options[:rekey_limit],
424
483
  max_packets: options[:rekey_packet_limit],
425
484
  max_blocks: options[:rekey_blocks_limit]
426
-
485
+
427
486
  @initialized = true
428
487
  end
429
-
488
+
430
489
  # Given the SSH name for some compression algorithm, return a normalized
431
490
  # name as a symbol.
432
491
  def normalize_compression_name(name)