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