net-ssh 4.1.0 → 6.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 (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