net-ssh 4.1.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gitignore +5 -0
  5. data/.rubocop.yml +8 -2
  6. data/.rubocop_todo.yml +405 -552
  7. data/.travis.yml +23 -22
  8. data/CHANGES.txt +112 -1
  9. data/Gemfile +1 -7
  10. data/{Gemfile.norbnacl → Gemfile.noed25519} +1 -1
  11. data/Manifest +4 -5
  12. data/README.md +287 -0
  13. data/Rakefile +40 -29
  14. data/appveyor.yml +12 -6
  15. data/lib/net/ssh.rb +68 -32
  16. data/lib/net/ssh/authentication/agent.rb +234 -222
  17. data/lib/net/ssh/authentication/certificate.rb +175 -164
  18. data/lib/net/ssh/authentication/constants.rb +17 -14
  19. data/lib/net/ssh/authentication/ed25519.rb +162 -141
  20. data/lib/net/ssh/authentication/ed25519_loader.rb +32 -29
  21. data/lib/net/ssh/authentication/key_manager.rb +40 -9
  22. data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
  23. data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
  24. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +1 -1
  25. data/lib/net/ssh/authentication/methods/none.rb +10 -10
  26. data/lib/net/ssh/authentication/methods/password.rb +13 -13
  27. data/lib/net/ssh/authentication/methods/publickey.rb +56 -55
  28. data/lib/net/ssh/authentication/pageant.rb +468 -465
  29. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  30. data/lib/net/ssh/authentication/session.rb +130 -122
  31. data/lib/net/ssh/buffer.rb +345 -312
  32. data/lib/net/ssh/buffered_io.rb +163 -163
  33. data/lib/net/ssh/config.rb +316 -238
  34. data/lib/net/ssh/connection/channel.rb +670 -650
  35. data/lib/net/ssh/connection/constants.rb +30 -26
  36. data/lib/net/ssh/connection/event_loop.rb +108 -105
  37. data/lib/net/ssh/connection/keepalive.rb +54 -50
  38. data/lib/net/ssh/connection/session.rb +682 -671
  39. data/lib/net/ssh/connection/term.rb +180 -176
  40. data/lib/net/ssh/errors.rb +101 -99
  41. data/lib/net/ssh/key_factory.rb +195 -108
  42. data/lib/net/ssh/known_hosts.rb +161 -152
  43. data/lib/net/ssh/loggable.rb +57 -55
  44. data/lib/net/ssh/packet.rb +82 -78
  45. data/lib/net/ssh/prompt.rb +55 -53
  46. data/lib/net/ssh/proxy/command.rb +104 -89
  47. data/lib/net/ssh/proxy/errors.rb +12 -8
  48. data/lib/net/ssh/proxy/http.rb +93 -91
  49. data/lib/net/ssh/proxy/https.rb +42 -39
  50. data/lib/net/ssh/proxy/jump.rb +50 -47
  51. data/lib/net/ssh/proxy/socks4.rb +0 -2
  52. data/lib/net/ssh/proxy/socks5.rb +11 -12
  53. data/lib/net/ssh/service/forward.rb +370 -317
  54. data/lib/net/ssh/test.rb +83 -77
  55. data/lib/net/ssh/test/channel.rb +146 -142
  56. data/lib/net/ssh/test/extensions.rb +150 -146
  57. data/lib/net/ssh/test/kex.rb +35 -31
  58. data/lib/net/ssh/test/local_packet.rb +48 -44
  59. data/lib/net/ssh/test/packet.rb +87 -84
  60. data/lib/net/ssh/test/remote_packet.rb +35 -31
  61. data/lib/net/ssh/test/script.rb +173 -171
  62. data/lib/net/ssh/test/socket.rb +59 -55
  63. data/lib/net/ssh/transport/algorithms.rb +430 -364
  64. data/lib/net/ssh/transport/cipher_factory.rb +95 -91
  65. data/lib/net/ssh/transport/constants.rb +33 -25
  66. data/lib/net/ssh/transport/ctr.rb +33 -11
  67. data/lib/net/ssh/transport/hmac.rb +15 -13
  68. data/lib/net/ssh/transport/hmac/abstract.rb +82 -63
  69. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  70. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  71. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  72. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  73. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  74. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  75. data/lib/net/ssh/transport/identity_cipher.rb +55 -51
  76. data/lib/net/ssh/transport/kex.rb +14 -13
  77. data/lib/net/ssh/transport/kex/abstract.rb +123 -0
  78. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  79. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +38 -0
  80. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  81. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  82. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +112 -217
  83. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -62
  84. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  85. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  86. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  87. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  88. data/lib/net/ssh/transport/key_expander.rb +29 -25
  89. data/lib/net/ssh/transport/openssl.rb +116 -116
  90. data/lib/net/ssh/transport/packet_stream.rb +223 -190
  91. data/lib/net/ssh/transport/server_version.rb +64 -66
  92. data/lib/net/ssh/transport/session.rb +306 -257
  93. data/lib/net/ssh/transport/state.rb +198 -196
  94. data/lib/net/ssh/verifiers/accept_new.rb +35 -0
  95. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +34 -0
  96. data/lib/net/ssh/verifiers/always.rb +56 -0
  97. data/lib/net/ssh/verifiers/never.rb +21 -0
  98. data/lib/net/ssh/version.rb +55 -53
  99. data/net-ssh-public_cert.pem +18 -19
  100. data/net-ssh.gemspec +12 -11
  101. data/support/ssh_tunnel_bug.rb +2 -2
  102. metadata +86 -75
  103. metadata.gz.sig +0 -0
  104. data/Gemfile.norbnacl.lock +0 -41
  105. data/README.rdoc +0 -169
  106. data/lib/net/ssh/ruby_compat.rb +0 -24
  107. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  108. data/lib/net/ssh/verifiers/null.rb +0 -12
  109. data/lib/net/ssh/verifiers/secure.rb +0 -52
  110. data/lib/net/ssh/verifiers/strict.rb +0 -24
  111. data/support/arcfour_check.rb +0 -20
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
- # coding: UTF-8
2
1
  #
3
2
  # Also in your terminal environment run:
4
3
  # $ export LANG=en_US.UTF-8
@@ -12,7 +11,6 @@ require "bundler/gem_tasks"
12
11
 
13
12
  require "rdoc/task"
14
13
 
15
-
16
14
  desc "When releasing make sure NET_SSH_BUILDGEM_SIGNED is set"
17
15
  task :check_NET_SSH_BUILDGEM_SIGNED do
18
16
  raise "NET_SSH_BUILDGEM_SIGNED should be set to release" unless ENV['NET_SSH_BUILDGEM_SIGNED']
@@ -21,9 +19,8 @@ end
21
19
  Rake::Task[:release].enhance [:check_NET_SSH_BUILDGEM_SIGNED]
22
20
  Rake::Task[:release].prerequisites.unshift(:check_NET_SSH_BUILDGEM_SIGNED)
23
21
 
24
-
25
22
  task default: ["build"]
26
- CLEAN.include [ 'pkg', 'rdoc' ]
23
+ CLEAN.include ['pkg', 'rdoc']
27
24
  name = "net-ssh"
28
25
 
29
26
  require_relative "lib/net/ssh/version"
@@ -34,38 +31,52 @@ RDoc::Task.new do |rdoc|
34
31
  rdoc.rdoc_dir = "rdoc"
35
32
  rdoc.title = "#{name} #{version}"
36
33
  rdoc.generator = 'hanna' # gem install hanna-nouveau
37
- rdoc.main = 'README.rdoc'
34
+ rdoc.main = 'README.md'
38
35
  rdoc.rdoc_files.include("README*")
39
36
  rdoc.rdoc_files.include("bin/*.rb")
40
37
  rdoc.rdoc_files.include("lib/**/*.rb")
41
38
  extra_files.each { |file|
42
- rdoc.rdoc_files.include(file) if File.exists?(file)
39
+ rdoc.rdoc_files.include(file) if File.exist?(file)
43
40
  }
44
41
  end
45
42
 
46
- namespace :rdoc do
47
- desc "Update gh-pages branch"
48
- task :publish do
49
- # copy/checkout
50
- rm_rf "/tmp/net-ssh-rdoc"
51
- rm_rf "/tmp/net-ssh-gh-pages"
52
- cp_r "./rdoc", "/tmp/net-ssh-rdoc"
53
- mkdir "/tmp/net-ssh-gh-pages"
54
- Dir.chdir "/tmp/net-ssh-gh-pages" do
55
- sh "git clone --branch gh-pages --single-branch https://github.com/net-ssh/net-ssh"
56
- rm_rf "/tmp/net-ssh-gh-pages/net-ssh/*"
57
- end
58
- # update
59
- sh "cp -rf ./rdoc/* /tmp/net-ssh-gh-pages/net-ssh/"
60
- Dir.chdir "/tmp/net-ssh-gh-pages/net-ssh" do
61
- sh "git add -A ."
62
- sh "git commit -m \"Update docs\""
63
- end
64
- # publish
65
- Dir.chdir "/tmp/net-ssh-gh-pages/net-ssh" do
66
- sh "git push origin gh-pages"
43
+ namespace :cert do
44
+ desc "Update public cert from private - only run if public is expired"
45
+ task :update_public_when_expired do
46
+ require 'openssl'
47
+ require 'time'
48
+ raw = File.read "net-ssh-public_cert.pem"
49
+ certificate = OpenSSL::X509::Certificate.new raw
50
+ raise Exception, "Not yet expired: #{certificate.not_after}" unless certificate.not_after < Time.now
51
+ sh "gem cert --build netssh@solutious.com --days 365*5 --private-key /mnt/gem/net-ssh-private_key.pem"
52
+ sh "mv gem-public_cert.pem net-ssh-public_cert.pem"
53
+ sh "gem cert --add net-ssh-public_cert.pem"
67
54
  end
68
55
  end
56
+
57
+ namespace :rdoc do
58
+ desc "Update gh-pages branch"
59
+ task :publish do
60
+ # copy/checkout
61
+ rm_rf "/tmp/net-ssh-rdoc"
62
+ rm_rf "/tmp/net-ssh-gh-pages"
63
+ cp_r "./rdoc", "/tmp/net-ssh-rdoc"
64
+ mkdir "/tmp/net-ssh-gh-pages"
65
+ Dir.chdir "/tmp/net-ssh-gh-pages" do
66
+ sh "git clone --branch gh-pages --single-branch https://github.com/net-ssh/net-ssh"
67
+ rm_rf "/tmp/net-ssh-gh-pages/net-ssh/*"
68
+ end
69
+ # update
70
+ sh "cp -rf ./rdoc/* /tmp/net-ssh-gh-pages/net-ssh/"
71
+ Dir.chdir "/tmp/net-ssh-gh-pages/net-ssh" do
72
+ sh "git add -A ."
73
+ sh "git commit -m \"Update docs\""
74
+ end
75
+ # publish
76
+ Dir.chdir "/tmp/net-ssh-gh-pages/net-ssh" do
77
+ sh "git push origin gh-pages"
78
+ end
79
+ end
69
80
  end
70
81
 
71
82
  require 'rake/testtask'
@@ -80,7 +91,7 @@ Rake::TestTask.new do |t|
80
91
  test_files -= FileList['test/manual/test_*.rb']
81
92
  test_files -= FileList['test/test_pageant.rb']
82
93
  test_files -= FileList['test/test/**/test_*.rb']
83
- t.test_files = test_files
94
+ t.test_files = test_files
84
95
  end
85
96
 
86
97
  desc "Run tests of Net::SSH:Test"
@@ -89,5 +100,5 @@ Rake::TestTask.new do |t|
89
100
  # we need to run test/test separatedly as it hacks io + other modules
90
101
  t.libs = ["lib", "test"]
91
102
  test_files = FileList['test/test/**/test_*.rb']
92
- t.test_files = test_files
103
+ t.test_files = test_files
93
104
  end
@@ -5,9 +5,15 @@ skip_tags: true
5
5
  environment:
6
6
  matrix:
7
7
  - ruby_version: "jruby-9.1.2.0"
8
+ - ruby_version: "26-x64"
9
+ - ruby_version: "25-x64"
10
+ - ruby_version: "24-x64"
8
11
  - ruby_version: "23"
9
12
  - ruby_version: "23-x64"
10
- - ruby_version: "22-x64"
13
+
14
+ matrix:
15
+ allow_failures:
16
+ - ruby_version: "jruby-9.1.2.0"
11
17
 
12
18
  #init:
13
19
  # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
@@ -25,9 +31,9 @@ install:
25
31
  - if "%ruby_version%" == "jruby-9.1.2.0" ( cinst jruby --version 9.1.2.0 -i --allow-empty-checksums )
26
32
  - if "%ruby_version%" == "jruby-9.1.2.0" ( SET "PATH=C:\jruby-9.1.2.0\bin\;%PATH%" )
27
33
  - ruby --version
28
- - gem install bundler --no-document -v 1.13.5
29
- - SET BUNDLE_GEMFILE=Gemfile.norbnacl
30
- - bundle _1.13.5_ install --retry=3
34
+ - gem install bundler --no-document --user-install -v 1.17
35
+ - SET BUNDLE_GEMFILE=Gemfile.noed25519
36
+ - bundle install --retry=3
31
37
  - cinst freesshd
32
38
  - cinst putty --allow-empty-checksums
33
39
  - ps: |
@@ -45,8 +51,8 @@ install:
45
51
  }
46
52
 
47
53
  test_script:
48
- - SET BUNDLE_GEMFILE=Gemfile.norbnacl
54
+ - SET BUNDLE_GEMFILE=Gemfile.noed25519
49
55
  - SET NET_SSH_RUN_WIN_INTEGRATION_TESTS=YES
50
- - bundle _1.13.5_ exec rake test
56
+ - bundle exec rake test
51
57
 
52
58
  build: off
@@ -4,6 +4,7 @@ ENV['HOME'] ||= ENV['HOMEPATH'] ? "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" : Dir.
4
4
 
5
5
  require 'logger'
6
6
  require 'etc'
7
+ require 'shellwords'
7
8
 
8
9
  require 'net/ssh/config'
9
10
  require 'net/ssh/errors'
@@ -62,17 +63,18 @@ module Net
62
63
  module SSH
63
64
  # This is the set of options that Net::SSH.start recognizes. See
64
65
  # Net::SSH.start for a description of each option.
65
- VALID_OPTIONS = [
66
- :auth_methods, :bind_address, :compression, :compression_level, :config,
67
- :encryption, :forward_agent, :hmac, :host_key, :remote_user,
68
- :keepalive, :keepalive_interval, :keepalive_maxcount, :kex, :keys, :key_data,
69
- :languages, :logger, :paranoid, :password, :port, :proxy,
70
- :rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
71
- :known_hosts, :global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
72
- :host_name, :user, :properties, :passphrase, :keys_only, :max_pkt_size,
73
- :max_win_size, :send_env, :use_agent, :number_of_password_prompts,
74
- :append_supported_algorithms, :non_interactive, :password_prompt, :agent_socket_factory,
75
- :minimum_dh_bits
66
+ VALID_OPTIONS = %i[
67
+ auth_methods bind_address compression compression_level config
68
+ encryption forward_agent hmac host_key remote_user
69
+ keepalive keepalive_interval keepalive_maxcount kex keys key_data
70
+ keycerts languages logger paranoid password port proxy
71
+ rekey_blocks_limit rekey_limit rekey_packet_limit timeout verbose
72
+ known_hosts global_known_hosts_file user_known_hosts_file host_key_alias
73
+ host_name user properties passphrase keys_only max_pkt_size
74
+ max_win_size send_env set_env use_agent number_of_password_prompts
75
+ append_all_supported_algorithms non_interactive password_prompt
76
+ agent_socket_factory minimum_dh_bits verify_host_key
77
+ fingerprint_hash check_host_ip
76
78
  ]
77
79
 
78
80
  # The standard means of starting a new SSH connection. When used with a
@@ -107,6 +109,8 @@ module Net
107
109
  # * :bind_address => the IP address on the connecting machine to use in
108
110
  # establishing connection. (:bind_address is discarded if :proxy
109
111
  # is set.)
112
+ # * :check_host_ip => Also ckeck IP address when connecting to remote host.
113
+ # Defaults to +true+.
110
114
  # * :compression => the compression algorithm to use, or +true+ to use
111
115
  # whatever is supported.
112
116
  # * :compression_level => the compression level to use when sending data
@@ -141,6 +145,8 @@ module Net
141
145
  # * :kex => the key exchange algorithm (or algorithms) to use
142
146
  # * :keys => an array of file names of private keys to use for publickey
143
147
  # and hostbased authentication
148
+ # * :keycerts => an array of file names of key certificates to use
149
+ # with publickey authentication
144
150
  # * :key_data => an array of strings, with each element of the array being
145
151
  # a raw private key in PEM format.
146
152
  # * :keys_only => set to +true+ to use only private keys from +keys+ and
@@ -157,12 +163,7 @@ module Net
157
163
  # authentication failure vs password prompt. Non-interactive applications
158
164
  # should set it to true to prefer failing a password/etc auth methods vs.
159
165
  # asking for password.
160
- # * :paranoid => either false, true, :very, or :secure specifying how
161
- # strict host-key verification should be (in increasing order here).
162
- # You can also provide an own Object which responds to +verify+. The argument
163
- # given to +verify+ is a hash consisting of the +:key+, the +:key_blob+,
164
- # the +:fingerprint+ and the +:session+. Returning true accepts the host key,
165
- # returning false declines it and closes the connection.
166
+ # * :paranoid => deprecated alias for :verify_host_key
166
167
  # * :passphrase => the passphrase to use when loading a private key (default
167
168
  # is +nil+, for no passphrase)
168
169
  # * :password => the password to use to login
@@ -175,6 +176,8 @@ module Net
175
176
  # * :rekey_packet_limit => the max number of packets to process before rekeying
176
177
  # * :send_env => an array of local environment variable names to export to the
177
178
  # remote environment. Names may be given as String or Regexp.
179
+ # * :set_env => a hash of environment variable names and values to set to the
180
+ # remote environment. Override the ones if specified in +send_env+.
178
181
  # * :timeout => how long to wait for the initial connection to be made
179
182
  # * :user => the user name to log in as; this overrides the +user+
180
183
  # parameter, and is primarily only useful when provided via an SSH
@@ -197,8 +200,19 @@ module Net
197
200
  # * :password_prompt => a custom prompt object with ask method. See Net::SSH::Prompt
198
201
  #
199
202
  # * :agent_socket_factory => enables the user to pass a lambda/block that will serve as the socket factory
200
- # Net::SSH::start(user,host,agent_socket_factory: ->{ UNIXSocket.open('/foo/bar') })
203
+ # Net::SSH.start(host,user,agent_socket_factory: ->{ UNIXSocket.open('/foo/bar') })
201
204
  # example: ->{ UNIXSocket.open('/foo/bar')}
205
+ # * :verify_host_key => specify how strict host-key verification should be.
206
+ # In order of increasing strictness:
207
+ # * :never (very insecure) ::Net::SSH::Verifiers::Never
208
+ # * :accept_new_or_local_tunnel (insecure) ::Net::SSH::Verifiers::AcceptNewOrLocalTunnel
209
+ # * :accept_new (insecure) ::Net::SSH::Verifiers::AcceptNew
210
+ # * :always (secure) ::Net::SSH::Verifiers::Always
211
+ # You can also provide an own Object which responds to +verify+. The argument
212
+ # given to +verify+ is a hash consisting of the +:key+, the +:key_blob+,
213
+ # the +:fingerprint+ and the +:session+. Returning true accepts the host key,
214
+ # returning false declines it and closes the connection.
215
+ # * :fingerprint_hash => 'MD5' or 'SHA256', defaults to 'SHA256'
202
216
  # If +user+ parameter is nil it defaults to USER from ssh_config, or
203
217
  # local username
204
218
  def self.start(host, user=nil, options={}, &block)
@@ -214,26 +228,30 @@ module Net
214
228
  options = configuration_for(host, options.fetch(:config, true)).merge(options)
215
229
  host = options.fetch(:host_name, host)
216
230
 
231
+ options[:check_host_ip] = true unless options.key?(:check_host_ip)
232
+
217
233
  if options[:non_interactive]
218
234
  options[:number_of_password_prompts] = 0
219
235
  end
220
236
 
237
+ _support_deprecated_option_paranoid(options)
238
+
221
239
  if options[:verbose]
222
240
  options[:logger].level = case options[:verbose]
223
- when Integer then options[:verbose]
224
- when :debug then Logger::DEBUG
225
- when :info then Logger::INFO
226
- when :warn then Logger::WARN
227
- when :error then Logger::ERROR
228
- when :fatal then Logger::FATAL
229
- else raise ArgumentError, "can't convert #{options[:verbose].inspect} to any of the Logger level constants"
230
- end
241
+ when Integer then options[:verbose]
242
+ when :debug then Logger::DEBUG
243
+ when :info then Logger::INFO
244
+ when :warn then Logger::WARN
245
+ when :error then Logger::ERROR
246
+ when :fatal then Logger::FATAL
247
+ else raise ArgumentError, "can't convert #{options[:verbose].inspect} to any of the Logger level constants"
248
+ end
231
249
  end
232
250
 
233
251
  transport = Transport::Session.new(host, options)
234
252
  auth = Authentication::Session.new(transport, options)
235
253
 
236
- user = options.fetch(:user, user) || Etc.getlogin
254
+ user = options.fetch(:user, user) || Etc.getpwuid.name
237
255
  if auth.authenticate("ssh-connection", user, options[:password])
238
256
  connection = Connection::Session.new(transport, options)
239
257
  if block_given?
@@ -262,10 +280,10 @@ module Net
262
280
  # See Net::SSH::Config for the full description of all supported options.
263
281
  def self.configuration_for(host, use_ssh_config)
264
282
  files = case use_ssh_config
265
- when true then Net::SSH::Config.expandable_default_files
266
- when false, nil then return {}
267
- else Array(use_ssh_config)
268
- end
283
+ when true then Net::SSH::Config.expandable_default_files
284
+ when false, nil then return {}
285
+ else Array(use_ssh_config)
286
+ end
269
287
 
270
288
  Net::SSH::Config.for(host, files)
271
289
  end
@@ -278,7 +296,7 @@ module Net
278
296
 
279
297
  options[:password_prompt] ||= Prompt.default(options)
280
298
 
281
- [:password, :passphrase].each do |key|
299
+ %i[password passphrase].each do |key|
282
300
  options.delete(key) if options.key?(key) && options[key].nil?
283
301
  end
284
302
  end
@@ -291,5 +309,23 @@ module Net
291
309
  end
292
310
  end
293
311
  private_class_method :_sanitize_options
312
+
313
+ def self._support_deprecated_option_paranoid(options)
314
+ if options.key?(:paranoid)
315
+ Kernel.warn(
316
+ ":paranoid is deprecated, please use :verify_host_key. Supported " \
317
+ "values are exactly the same, only the name of the option has changed."
318
+ )
319
+ if options.key?(:verify_host_key)
320
+ Kernel.warn(
321
+ "Both :paranoid and :verify_host_key were specified. " \
322
+ ":verify_host_key takes precedence, :paranoid will be ignored."
323
+ )
324
+ else
325
+ options[:verify_host_key] = options.delete(:paranoid)
326
+ end
327
+ end
328
+ end
329
+ private_class_method :_support_deprecated_option_paranoid
294
330
  end
295
331
  end
@@ -8,249 +8,261 @@ require 'rubygems'
8
8
 
9
9
  require 'net/ssh/authentication/pageant' if Gem.win_platform? && RUBY_PLATFORM != "java"
10
10
 
11
- module Net; module SSH; module Authentication
12
- # Class for representing agent-specific errors.
13
- class AgentError < Net::SSH::Exception; end
14
- # An exception for indicating that the SSH agent is not available.
15
- class AgentNotAvailable < AgentError; end
16
-
17
- # This class implements a simple client for the ssh-agent protocol. It
18
- # does not implement any specific protocol, but instead copies the
19
- # behavior of the ssh-agent functions in the OpenSSH library (3.8).
20
- #
21
- # This means that although it behaves like a SSH1 client, it also has
22
- # some SSH2 functionality (like signing data).
23
- class Agent
24
- include Loggable
25
-
26
- # A simple module for extending keys, to allow comments to be specified
27
- # for them.
28
- module Comment
29
- attr_accessor :comment
30
- end
31
-
32
- SSH2_AGENT_REQUEST_VERSION = 1
33
- SSH2_AGENT_REQUEST_IDENTITIES = 11
34
- SSH2_AGENT_IDENTITIES_ANSWER = 12
35
- SSH2_AGENT_SIGN_REQUEST = 13
36
- SSH2_AGENT_SIGN_RESPONSE = 14
37
- SSH2_AGENT_ADD_IDENTITY = 17
38
- SSH2_AGENT_REMOVE_IDENTITY = 18
39
- SSH2_AGENT_REMOVE_ALL_IDENTITIES = 19
40
- SSH2_AGENT_ADD_ID_CONSTRAINED = 25
41
- SSH2_AGENT_FAILURE = 30
42
- SSH2_AGENT_VERSION_RESPONSE = 103
43
-
44
- SSH_COM_AGENT2_FAILURE = 102
45
-
46
- SSH_AGENT_REQUEST_RSA_IDENTITIES = 1
47
- SSH_AGENT_RSA_IDENTITIES_ANSWER1 = 2
48
- SSH_AGENT_RSA_IDENTITIES_ANSWER2 = 5
49
- SSH_AGENT_FAILURE = 5
50
- SSH_AGENT_SUCCESS = 6
51
-
52
- SSH_AGENT_CONSTRAIN_LIFETIME = 1
53
- SSH_AGENT_CONSTRAIN_CONFIRM = 2
54
-
55
- # The underlying socket being used to communicate with the SSH agent.
56
- attr_reader :socket
57
-
58
- # Instantiates a new agent object, connects to a running SSH agent,
59
- # negotiates the agent protocol version, and returns the agent object.
60
- def self.connect(logger=nil, agent_socket_factory = nil)
61
- agent = new(logger)
62
- agent.connect!(agent_socket_factory)
63
- agent.negotiate!
64
- agent
65
- end
66
-
67
- # Creates a new Agent object, using the optional logger instance to
68
- # report status.
69
- def initialize(logger=nil)
70
- self.logger = logger
71
- end
11
+ module Net
12
+ module SSH
13
+ module Authentication
14
+ # Class for representing agent-specific errors.
15
+ class AgentError < Net::SSH::Exception; end
16
+ # An exception for indicating that the SSH agent is not available.
17
+ class AgentNotAvailable < AgentError; end
18
+
19
+ # This class implements a simple client for the ssh-agent protocol. It
20
+ # does not implement any specific protocol, but instead copies the
21
+ # behavior of the ssh-agent functions in the OpenSSH library (3.8).
22
+ #
23
+ # This means that although it behaves like a SSH1 client, it also has
24
+ # some SSH2 functionality (like signing data).
25
+ class Agent
26
+ include Loggable
27
+
28
+ # A simple module for extending keys, to allow comments to be specified
29
+ # for them.
30
+ module Comment
31
+ attr_accessor :comment
32
+ end
72
33
 
73
- # Connect to the agent process using the socket factory and socket name
74
- # given by the attribute writers. If the agent on the other end of the
75
- # socket reports that it is an SSH2-compatible agent, this will fail
76
- # (it only supports the ssh-agent distributed by OpenSSH).
77
- def connect!(agent_socket_factory = nil)
78
- debug { "connecting to ssh-agent" }
79
- @socket =
80
- if agent_socket_factory
81
- agent_socket_factory.call
82
- elsif ENV['SSH_AUTH_SOCK'] && unix_socket_class
83
- unix_socket_class.open(ENV['SSH_AUTH_SOCK'])
84
- elsif Gem.win_platform? && RUBY_ENGINE != "jruby"
85
- Pageant::Socket.open
86
- else
87
- raise AgentNotAvailable, "Agent not configured"
34
+ SSH2_AGENT_REQUEST_VERSION = 1
35
+ SSH2_AGENT_REQUEST_IDENTITIES = 11
36
+ SSH2_AGENT_IDENTITIES_ANSWER = 12
37
+ SSH2_AGENT_SIGN_REQUEST = 13
38
+ SSH2_AGENT_SIGN_RESPONSE = 14
39
+ SSH2_AGENT_ADD_IDENTITY = 17
40
+ SSH2_AGENT_REMOVE_IDENTITY = 18
41
+ SSH2_AGENT_REMOVE_ALL_IDENTITIES = 19
42
+ SSH2_AGENT_ADD_ID_CONSTRAINED = 25
43
+ SSH2_AGENT_FAILURE = 30
44
+ SSH2_AGENT_VERSION_RESPONSE = 103
45
+
46
+ SSH_COM_AGENT2_FAILURE = 102
47
+
48
+ SSH_AGENT_REQUEST_RSA_IDENTITIES = 1
49
+ SSH_AGENT_RSA_IDENTITIES_ANSWER1 = 2
50
+ SSH_AGENT_RSA_IDENTITIES_ANSWER2 = 5
51
+ SSH_AGENT_FAILURE = 5
52
+ SSH_AGENT_SUCCESS = 6
53
+
54
+ SSH_AGENT_CONSTRAIN_LIFETIME = 1
55
+ SSH_AGENT_CONSTRAIN_CONFIRM = 2
56
+
57
+ SSH_AGENT_RSA_SHA2_256 = 0x02
58
+ SSH_AGENT_RSA_SHA2_512 = 0x04
59
+
60
+ # The underlying socket being used to communicate with the SSH agent.
61
+ attr_reader :socket
62
+
63
+ # Instantiates a new agent object, connects to a running SSH agent,
64
+ # negotiates the agent protocol version, and returns the agent object.
65
+ def self.connect(logger=nil, agent_socket_factory = nil, identity_agent = nil)
66
+ agent = new(logger)
67
+ agent.connect!(agent_socket_factory, identity_agent)
68
+ agent.negotiate!
69
+ agent
88
70
  end
89
- rescue StandardError => e
90
- error { "could not connect to ssh-agent: #{e.message}" }
91
- raise AgentNotAvailable, $!.message
92
- end
93
71
 
94
- # Attempts to negotiate the SSH agent protocol version. Raises an error
95
- # if the version could not be negotiated successfully.
96
- def negotiate!
97
- # determine what type of agent we're communicating with
98
- type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
99
-
100
- raise AgentNotAvailable, "SSH2 agents are not yet supported" if type == SSH2_AGENT_VERSION_RESPONSE
101
- if type == SSH2_AGENT_FAILURE
102
- debug { "Unexpected response type==#{type}, this will be ignored" }
103
- elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
104
- raise AgentNotAvailable, "unknown response from agent: #{type}, #{body.to_s.inspect}"
105
- end
106
- end
72
+ # Creates a new Agent object, using the optional logger instance to
73
+ # report status.
74
+ def initialize(logger=nil)
75
+ self.logger = logger
76
+ end
107
77
 
108
- # Return an array of all identities (public keys) known to the agent.
109
- # Each key returned is augmented with a +comment+ property which is set
110
- # to the comment returned by the agent for that key.
111
- def identities
112
- type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES)
113
- raise AgentError, "could not get identity count" if agent_failed(type)
114
- raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER
115
-
116
- identities = []
117
- body.read_long.times do
118
- key_str = body.read_string
119
- comment_str = body.read_string
120
- begin
121
- key = Buffer.new(key_str).read_key
122
- key.extend(Comment)
123
- key.comment = comment_str
124
- identities.push key
125
- rescue NotImplementedError => e
126
- error { "ignoring unimplemented key:#{e.message} #{comment_str}" }
78
+ # Connect to the agent process using the socket factory and socket name
79
+ # given by the attribute writers. If the agent on the other end of the
80
+ # socket reports that it is an SSH2-compatible agent, this will fail
81
+ # (it only supports the ssh-agent distributed by OpenSSH).
82
+ def connect!(agent_socket_factory = nil, identity_agent = nil)
83
+ debug { "connecting to ssh-agent" }
84
+ @socket =
85
+ if agent_socket_factory
86
+ agent_socket_factory.call
87
+ elsif identity_agent
88
+ unix_socket_class.open(identity_agent)
89
+ elsif ENV['SSH_AUTH_SOCK'] && unix_socket_class
90
+ unix_socket_class.open(ENV['SSH_AUTH_SOCK'])
91
+ elsif Gem.win_platform? && RUBY_ENGINE != "jruby"
92
+ Pageant::Socket.open
93
+ else
94
+ raise AgentNotAvailable, "Agent not configured"
95
+ end
96
+ rescue StandardError => e
97
+ error { "could not connect to ssh-agent: #{e.message}" }
98
+ raise AgentNotAvailable, $!.message
127
99
  end
128
- end
129
100
 
130
- return identities
131
- end
101
+ # Attempts to negotiate the SSH agent protocol version. Raises an error
102
+ # if the version could not be negotiated successfully.
103
+ def negotiate!
104
+ # determine what type of agent we're communicating with
105
+ type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
106
+
107
+ raise AgentNotAvailable, "SSH2 agents are not yet supported" if type == SSH2_AGENT_VERSION_RESPONSE
108
+ if type == SSH2_AGENT_FAILURE
109
+ debug { "Unexpected response type==#{type}, this will be ignored" }
110
+ elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
111
+ raise AgentNotAvailable, "unknown response from agent: #{type}, #{body.to_s.inspect}"
112
+ end
113
+ end
132
114
 
133
- # Closes this socket. This agent reference is no longer able to
134
- # query the agent.
135
- def close
136
- @socket.close
137
- end
115
+ # Return an array of all identities (public keys) known to the agent.
116
+ # Each key returned is augmented with a +comment+ property which is set
117
+ # to the comment returned by the agent for that key.
118
+ def identities
119
+ type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES)
120
+ raise AgentError, "could not get identity count" if agent_failed(type)
121
+ raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER
122
+
123
+ identities = []
124
+ body.read_long.times do
125
+ key_str = body.read_string
126
+ comment_str = body.read_string
127
+ begin
128
+ key = Buffer.new(key_str).read_key
129
+ if key.nil?
130
+ error { "ignoring invalid key: #{comment_str}" }
131
+ next
132
+ end
133
+ key.extend(Comment)
134
+ key.comment = comment_str
135
+ identities.push key
136
+ rescue NotImplementedError => e
137
+ error { "ignoring unimplemented key:#{e.message} #{comment_str}" }
138
+ end
139
+ end
140
+
141
+ return identities
142
+ end
138
143
 
139
- # Using the agent and the given public key, sign the given data. The
140
- # signature is returned in SSH2 format.
141
- def sign(key, data)
142
- type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, 0)
144
+ # Closes this socket. This agent reference is no longer able to
145
+ # query the agent.
146
+ def close
147
+ @socket.close
148
+ end
143
149
 
144
- raise AgentError, "agent could not sign data with requested identity" if agent_failed(type)
145
- raise AgentError, "bad authentication response #{type}" if type != SSH2_AGENT_SIGN_RESPONSE
150
+ # Using the agent and the given public key, sign the given data. The
151
+ # signature is returned in SSH2 format.
152
+ def sign(key, data, flags = 0)
153
+ type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, flags)
146
154
 
147
- return reply.read_string
148
- end
155
+ raise AgentError, "agent could not sign data with requested identity" if agent_failed(type)
156
+ raise AgentError, "bad authentication response #{type}" if type != SSH2_AGENT_SIGN_RESPONSE
149
157
 
150
- # Adds the private key with comment to the agent.
151
- # If lifetime is given, the key will automatically be removed after lifetime
152
- # seconds.
153
- # If confirm is true, confirmation will be required for each agent signing
154
- # operation.
155
- def add_identity(priv_key, comment, lifetime: nil, confirm: false)
156
- constraints = Buffer.new
157
- if lifetime
158
- constraints.write_byte(SSH_AGENT_CONSTRAIN_LIFETIME)
159
- constraints.write_long(lifetime)
160
- end
161
- constraints.write_byte(SSH_AGENT_CONSTRAIN_CONFIRM) if confirm
158
+ return reply.read_string
159
+ end
162
160
 
163
- req_type = constraints.empty? ? SSH2_AGENT_ADD_IDENTITY : SSH2_AGENT_ADD_ID_CONSTRAINED
164
- type, = send_and_wait(req_type, :string, priv_key.ssh_type, :raw, blob_for_add(priv_key),
165
- :string, comment, :raw, constraints)
166
- raise AgentError, "could not add identity to agent" if type != SSH_AGENT_SUCCESS
167
- end
161
+ # Adds the private key with comment to the agent.
162
+ # If lifetime is given, the key will automatically be removed after lifetime
163
+ # seconds.
164
+ # If confirm is true, confirmation will be required for each agent signing
165
+ # operation.
166
+ def add_identity(priv_key, comment, lifetime: nil, confirm: false)
167
+ constraints = Buffer.new
168
+ if lifetime
169
+ constraints.write_byte(SSH_AGENT_CONSTRAIN_LIFETIME)
170
+ constraints.write_long(lifetime)
171
+ end
172
+ constraints.write_byte(SSH_AGENT_CONSTRAIN_CONFIRM) if confirm
173
+
174
+ req_type = constraints.empty? ? SSH2_AGENT_ADD_IDENTITY : SSH2_AGENT_ADD_ID_CONSTRAINED
175
+ type, = send_and_wait(req_type, :string, priv_key.ssh_type, :raw, blob_for_add(priv_key),
176
+ :string, comment, :raw, constraints)
177
+ raise AgentError, "could not add identity to agent" if type != SSH_AGENT_SUCCESS
178
+ end
168
179
 
169
- # Removes key from the agent.
170
- def remove_identity(key)
171
- type, = send_and_wait(SSH2_AGENT_REMOVE_IDENTITY, :string, key.to_blob)
172
- raise AgentError, "could not remove identity from agent" if type != SSH_AGENT_SUCCESS
173
- end
180
+ # Removes key from the agent.
181
+ def remove_identity(key)
182
+ type, = send_and_wait(SSH2_AGENT_REMOVE_IDENTITY, :string, key.to_blob)
183
+ raise AgentError, "could not remove identity from agent" if type != SSH_AGENT_SUCCESS
184
+ end
174
185
 
175
- # Removes all identities from the agent.
176
- def remove_all_identities
177
- type, = send_and_wait(SSH2_AGENT_REMOVE_ALL_IDENTITIES)
178
- raise AgentError, "could not remove all identity from agent" if type != SSH_AGENT_SUCCESS
179
- end
186
+ # Removes all identities from the agent.
187
+ def remove_all_identities
188
+ type, = send_and_wait(SSH2_AGENT_REMOVE_ALL_IDENTITIES)
189
+ raise AgentError, "could not remove all identity from agent" if type != SSH_AGENT_SUCCESS
190
+ end
180
191
 
181
- private
192
+ private
182
193
 
183
- def unix_socket_class
184
- defined?(UNIXSocket) && UNIXSocket
185
- end
194
+ def unix_socket_class
195
+ defined?(UNIXSocket) && UNIXSocket
196
+ end
186
197
 
187
- # Send a new packet of the given type, with the associated data.
188
- def send_packet(type, *args)
189
- buffer = Buffer.from(*args)
190
- data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*")
191
- debug { "sending agent request #{type} len #{buffer.length}" }
192
- @socket.send data, 0
193
- end
198
+ # Send a new packet of the given type, with the associated data.
199
+ def send_packet(type, *args)
200
+ buffer = Buffer.from(*args)
201
+ data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*")
202
+ debug { "sending agent request #{type} len #{buffer.length}" }
203
+ @socket.send data, 0
204
+ end
194
205
 
195
- # Read the next packet from the agent. This will return a two-part
196
- # tuple consisting of the packet type, and the packet's body (which
197
- # is returned as a Net::SSH::Buffer).
198
- def read_packet
199
- buffer = Net::SSH::Buffer.new(@socket.read(4))
200
- buffer.append(@socket.read(buffer.read_long))
201
- type = buffer.read_byte
202
- debug { "received agent packet #{type} len #{buffer.length-4}" }
203
- return type, buffer
204
- end
206
+ # Read the next packet from the agent. This will return a two-part
207
+ # tuple consisting of the packet type, and the packet's body (which
208
+ # is returned as a Net::SSH::Buffer).
209
+ def read_packet
210
+ buffer = Net::SSH::Buffer.new(@socket.read(4))
211
+ buffer.append(@socket.read(buffer.read_long))
212
+ type = buffer.read_byte
213
+ debug { "received agent packet #{type} len #{buffer.length - 4}" }
214
+ return type, buffer
215
+ end
205
216
 
206
- # Send the given packet and return the subsequent reply from the agent.
207
- # (See #send_packet and #read_packet).
208
- def send_and_wait(type, *args)
209
- send_packet(type, *args)
210
- read_packet
211
- end
217
+ # Send the given packet and return the subsequent reply from the agent.
218
+ # (See #send_packet and #read_packet).
219
+ def send_and_wait(type, *args)
220
+ send_packet(type, *args)
221
+ read_packet
222
+ end
212
223
 
213
- # Returns +true+ if the parameter indicates a "failure" response from
214
- # the agent, and +false+ otherwise.
215
- def agent_failed(type)
216
- type == SSH_AGENT_FAILURE ||
217
- type == SSH2_AGENT_FAILURE ||
218
- type == SSH_COM_AGENT2_FAILURE
219
- end
224
+ # Returns +true+ if the parameter indicates a "failure" response from
225
+ # the agent, and +false+ otherwise.
226
+ def agent_failed(type)
227
+ type == SSH_AGENT_FAILURE ||
228
+ type == SSH2_AGENT_FAILURE ||
229
+ type == SSH_COM_AGENT2_FAILURE
230
+ end
220
231
 
221
- def blob_for_add(priv_key)
222
- # Ideally we'd have something like `to_private_blob` on the various key types, but the
223
- # nuances with encoding (e.g. `n` and `e` are reversed for RSA keys) make this impractical.
224
- case priv_key.ssh_type
225
- when /^ssh-dss$/
226
- Net::SSH::Buffer.from(:bignum, priv_key.p, :bignum, priv_key.q, :bignum, priv_key.g,
227
- :bignum, priv_key.pub_key, :bignum, priv_key.priv_key).to_s
228
- when /^ssh-dss-cert-v01@openssh\.com$/
229
- Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.priv_key).to_s
230
- when /^ecdsa\-sha2\-(\w*)$/
231
- curve_name = OpenSSL::PKey::EC::CurveNameAliasInv[priv_key.group.curve_name]
232
- Net::SSH::Buffer.from(:string, curve_name, :mstring, priv_key.public_key.to_bn.to_s(2),
233
- :bignum, priv_key.private_key).to_s
234
- when /^ecdsa\-sha2\-(\w*)-cert-v01@openssh\.com$/
235
- Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.private_key).to_s
236
- when /^ssh-ed25519$/
237
- Net::SSH::Buffer.from(:string, priv_key.public_key.verify_key.to_bytes,
238
- :string, priv_key.sign_key.keypair_bytes).to_s
239
- when /^ssh-ed25519-cert-v01@openssh\.com$/
240
- # Unlike the other certificate types, the public key is included after the certifiate.
241
- Net::SSH::Buffer.from(:string, priv_key.to_blob,
242
- :string, priv_key.key.public_key.verify_key.to_bytes,
243
- :string, priv_key.key.sign_key.keypair_bytes).to_s
244
- when /^ssh-rsa$/
245
- # `n` and `e` are reversed compared to the ordering in `OpenSSL::PKey::RSA#to_blob`.
246
- Net::SSH::Buffer.from(:bignum, priv_key.n, :bignum, priv_key.e, :bignum, priv_key.d,
247
- :bignum, priv_key.iqmp, :bignum, priv_key.p, :bignum, priv_key.q).to_s
248
- when /^ssh-rsa-cert-v01@openssh\.com$/
249
- Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.d,
250
- :bignum, priv_key.key.iqmp, :bignum, priv_key.key.p,
251
- :bignum, priv_key.key.q).to_s
232
+ def blob_for_add(priv_key)
233
+ # Ideally we'd have something like `to_private_blob` on the various key types, but the
234
+ # nuances with encoding (e.g. `n` and `e` are reversed for RSA keys) make this impractical.
235
+ case priv_key.ssh_type
236
+ when /^ssh-dss$/
237
+ Net::SSH::Buffer.from(:bignum, priv_key.p, :bignum, priv_key.q, :bignum, priv_key.g,
238
+ :bignum, priv_key.pub_key, :bignum, priv_key.priv_key).to_s
239
+ when /^ssh-dss-cert-v01@openssh\.com$/
240
+ Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.priv_key).to_s
241
+ when /^ecdsa\-sha2\-(\w*)$/
242
+ curve_name = OpenSSL::PKey::EC::CurveNameAliasInv[priv_key.group.curve_name]
243
+ Net::SSH::Buffer.from(:string, curve_name, :mstring, priv_key.public_key.to_bn.to_s(2),
244
+ :bignum, priv_key.private_key).to_s
245
+ when /^ecdsa\-sha2\-(\w*)-cert-v01@openssh\.com$/
246
+ Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.private_key).to_s
247
+ when /^ssh-ed25519$/
248
+ Net::SSH::Buffer.from(:string, priv_key.public_key.verify_key.to_bytes,
249
+ :string, priv_key.sign_key.keypair).to_s
250
+ when /^ssh-ed25519-cert-v01@openssh\.com$/
251
+ # Unlike the other certificate types, the public key is included after the certifiate.
252
+ Net::SSH::Buffer.from(:string, priv_key.to_blob,
253
+ :string, priv_key.key.public_key.verify_key.to_bytes,
254
+ :string, priv_key.key.sign_key.keypair).to_s
255
+ when /^ssh-rsa$/
256
+ # `n` and `e` are reversed compared to the ordering in `OpenSSL::PKey::RSA#to_blob`.
257
+ Net::SSH::Buffer.from(:bignum, priv_key.n, :bignum, priv_key.e, :bignum, priv_key.d,
258
+ :bignum, priv_key.iqmp, :bignum, priv_key.p, :bignum, priv_key.q).to_s
259
+ when /^ssh-rsa-cert-v01@openssh\.com$/
260
+ Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.d,
261
+ :bignum, priv_key.key.iqmp, :bignum, priv_key.key.p,
262
+ :bignum, priv_key.key.q).to_s
263
+ end
264
+ end
252
265
  end
253
266
  end
254
267
  end
255
-
256
- end; end; end
268
+ end