net-ssh 2.7.0 → 7.3.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 (199) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/FUNDING.yml +1 -0
  5. data/.github/config/rubocop_linter_action.yml +4 -0
  6. data/.github/workflows/ci-with-docker.yml +44 -0
  7. data/.github/workflows/ci.yml +94 -0
  8. data/.github/workflows/rubocop.yml +16 -0
  9. data/.gitignore +15 -0
  10. data/.rubocop.yml +22 -0
  11. data/.rubocop_todo.yml +1081 -0
  12. data/CHANGES.txt +387 -0
  13. data/DEVELOPMENT.md +23 -0
  14. data/Dockerfile +29 -0
  15. data/Dockerfile.openssl3 +17 -0
  16. data/Gemfile +13 -0
  17. data/Gemfile.noed25519 +12 -0
  18. data/Gemfile.norbnacl +12 -0
  19. data/ISSUE_TEMPLATE.md +30 -0
  20. data/Manifest +4 -5
  21. data/README.md +303 -0
  22. data/Rakefile +174 -40
  23. data/SECURITY.md +4 -0
  24. data/THANKS.txt +25 -0
  25. data/appveyor.yml +58 -0
  26. data/docker-compose.yml +25 -0
  27. data/lib/net/ssh/authentication/agent.rb +279 -18
  28. data/lib/net/ssh/authentication/certificate.rb +183 -0
  29. data/lib/net/ssh/authentication/constants.rb +17 -15
  30. data/lib/net/ssh/authentication/ed25519.rb +184 -0
  31. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  32. data/lib/net/ssh/authentication/key_manager.rb +125 -54
  33. data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
  34. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  35. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +19 -12
  36. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  37. data/lib/net/ssh/authentication/methods/password.rb +56 -19
  38. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  39. data/lib/net/ssh/authentication/pageant.rb +483 -246
  40. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  41. data/lib/net/ssh/authentication/session.rb +138 -120
  42. data/lib/net/ssh/buffer.rb +399 -300
  43. data/lib/net/ssh/buffered_io.rb +154 -150
  44. data/lib/net/ssh/config.rb +361 -166
  45. data/lib/net/ssh/connection/channel.rb +640 -596
  46. data/lib/net/ssh/connection/constants.rb +29 -29
  47. data/lib/net/ssh/connection/event_loop.rb +123 -0
  48. data/lib/net/ssh/connection/keepalive.rb +59 -0
  49. data/lib/net/ssh/connection/session.rb +628 -548
  50. data/lib/net/ssh/connection/term.rb +125 -123
  51. data/lib/net/ssh/errors.rb +101 -95
  52. data/lib/net/ssh/key_factory.rb +198 -100
  53. data/lib/net/ssh/known_hosts.rb +221 -98
  54. data/lib/net/ssh/loggable.rb +50 -49
  55. data/lib/net/ssh/packet.rb +83 -79
  56. data/lib/net/ssh/prompt.rb +50 -81
  57. data/lib/net/ssh/proxy/command.rb +108 -60
  58. data/lib/net/ssh/proxy/errors.rb +12 -10
  59. data/lib/net/ssh/proxy/http.rb +82 -78
  60. data/lib/net/ssh/proxy/https.rb +50 -0
  61. data/lib/net/ssh/proxy/jump.rb +54 -0
  62. data/lib/net/ssh/proxy/socks4.rb +5 -8
  63. data/lib/net/ssh/proxy/socks5.rb +18 -20
  64. data/lib/net/ssh/service/forward.rb +383 -255
  65. data/lib/net/ssh/test/channel.rb +145 -136
  66. data/lib/net/ssh/test/extensions.rb +131 -110
  67. data/lib/net/ssh/test/kex.rb +34 -32
  68. data/lib/net/ssh/test/local_packet.rb +46 -44
  69. data/lib/net/ssh/test/packet.rb +89 -70
  70. data/lib/net/ssh/test/remote_packet.rb +32 -30
  71. data/lib/net/ssh/test/script.rb +156 -142
  72. data/lib/net/ssh/test/socket.rb +49 -48
  73. data/lib/net/ssh/test.rb +82 -77
  74. data/lib/net/ssh/transport/aes128_gcm.rb +40 -0
  75. data/lib/net/ssh/transport/aes256_gcm.rb +40 -0
  76. data/lib/net/ssh/transport/algorithms.rb +472 -348
  77. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  78. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  79. data/lib/net/ssh/transport/cipher_factory.rb +124 -100
  80. data/lib/net/ssh/transport/constants.rb +32 -24
  81. data/lib/net/ssh/transport/ctr.rb +42 -22
  82. data/lib/net/ssh/transport/gcm_cipher.rb +207 -0
  83. data/lib/net/ssh/transport/hmac/abstract.rb +97 -63
  84. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  85. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  86. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  87. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  88. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  89. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  90. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  91. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  92. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  93. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  94. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  95. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  96. data/lib/net/ssh/transport/hmac.rb +14 -12
  97. data/lib/net/ssh/transport/identity_cipher.rb +54 -44
  98. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  99. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  100. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  101. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  102. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  103. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  104. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
  105. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
  106. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  107. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  108. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  109. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  110. data/lib/net/ssh/transport/kex.rb +15 -12
  111. data/lib/net/ssh/transport/key_expander.rb +24 -20
  112. data/lib/net/ssh/transport/openssl.rb +161 -124
  113. data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  114. data/lib/net/ssh/transport/packet_stream.rb +246 -183
  115. data/lib/net/ssh/transport/server_version.rb +57 -51
  116. data/lib/net/ssh/transport/session.rb +307 -235
  117. data/lib/net/ssh/transport/state.rb +178 -176
  118. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  119. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  120. data/lib/net/ssh/verifiers/always.rb +58 -0
  121. data/lib/net/ssh/verifiers/never.rb +19 -0
  122. data/lib/net/ssh/version.rb +57 -51
  123. data/lib/net/ssh.rb +140 -40
  124. data/net-ssh-public_cert.pem +21 -0
  125. data/net-ssh.gemspec +39 -184
  126. data/support/ssh_tunnel_bug.rb +5 -5
  127. data.tar.gz.sig +0 -0
  128. metadata +205 -99
  129. metadata.gz.sig +0 -0
  130. data/README.rdoc +0 -219
  131. data/Rudyfile +0 -96
  132. data/gem-public_cert.pem +0 -20
  133. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  134. data/lib/net/ssh/authentication/agent/socket.rb +0 -170
  135. data/lib/net/ssh/ruby_compat.rb +0 -51
  136. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  137. data/lib/net/ssh/verifiers/null.rb +0 -12
  138. data/lib/net/ssh/verifiers/secure.rb +0 -54
  139. data/lib/net/ssh/verifiers/strict.rb +0 -24
  140. data/setup.rb +0 -1585
  141. data/support/arcfour_check.rb +0 -20
  142. data/test/README.txt +0 -47
  143. data/test/authentication/methods/common.rb +0 -28
  144. data/test/authentication/methods/test_abstract.rb +0 -51
  145. data/test/authentication/methods/test_hostbased.rb +0 -114
  146. data/test/authentication/methods/test_keyboard_interactive.rb +0 -100
  147. data/test/authentication/methods/test_none.rb +0 -41
  148. data/test/authentication/methods/test_password.rb +0 -52
  149. data/test/authentication/methods/test_publickey.rb +0 -148
  150. data/test/authentication/test_agent.rb +0 -205
  151. data/test/authentication/test_key_manager.rb +0 -218
  152. data/test/authentication/test_session.rb +0 -108
  153. data/test/common.rb +0 -108
  154. data/test/configs/eqsign +0 -3
  155. data/test/configs/exact_match +0 -8
  156. data/test/configs/host_plus +0 -10
  157. data/test/configs/multihost +0 -4
  158. data/test/configs/nohost +0 -19
  159. data/test/configs/numeric_host +0 -4
  160. data/test/configs/send_env +0 -2
  161. data/test/configs/substitutes +0 -8
  162. data/test/configs/wild_cards +0 -14
  163. data/test/connection/test_channel.rb +0 -467
  164. data/test/connection/test_session.rb +0 -526
  165. data/test/known_hosts/github +0 -1
  166. data/test/manual/test_forward.rb +0 -223
  167. data/test/start/test_options.rb +0 -36
  168. data/test/start/test_transport.rb +0 -28
  169. data/test/test_all.rb +0 -11
  170. data/test/test_buffer.rb +0 -433
  171. data/test/test_buffered_io.rb +0 -63
  172. data/test/test_config.rb +0 -151
  173. data/test/test_key_factory.rb +0 -173
  174. data/test/test_known_hosts.rb +0 -13
  175. data/test/transport/hmac/test_md5.rb +0 -41
  176. data/test/transport/hmac/test_md5_96.rb +0 -27
  177. data/test/transport/hmac/test_none.rb +0 -34
  178. data/test/transport/hmac/test_ripemd160.rb +0 -36
  179. data/test/transport/hmac/test_sha1.rb +0 -36
  180. data/test/transport/hmac/test_sha1_96.rb +0 -27
  181. data/test/transport/hmac/test_sha2_256.rb +0 -37
  182. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  183. data/test/transport/hmac/test_sha2_512.rb +0 -37
  184. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  185. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  186. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -146
  187. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -92
  188. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -34
  189. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  190. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  191. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  192. data/test/transport/test_algorithms.rb +0 -330
  193. data/test/transport/test_cipher_factory.rb +0 -443
  194. data/test/transport/test_hmac.rb +0 -34
  195. data/test/transport/test_identity_cipher.rb +0 -40
  196. data/test/transport/test_packet_stream.rb +0 -1755
  197. data/test/transport/test_server_version.rb +0 -78
  198. data/test/transport/test_session.rb +0 -319
  199. data/test/transport/test_state.rb +0 -181
@@ -1,120 +1,218 @@
1
1
  require 'net/ssh/transport/openssl'
2
2
  require 'net/ssh/prompt'
3
3
 
4
- module Net; module SSH
5
-
6
- # A factory class for returning new Key classes. It is used for obtaining
7
- # OpenSSL key instances via their SSH names, and for loading both public and
8
- # private keys. It used used primarily by Net::SSH itself, internally, and
9
- # will rarely (if ever) be directly used by consumers of the library.
10
- #
11
- # klass = Net::SSH::KeyFactory.get("rsa")
12
- # assert klass.is_a?(OpenSSL::PKey::RSA)
13
- #
14
- # key = Net::SSH::KeyFactory.load_public_key("~/.ssh/id_dsa.pub")
15
- class KeyFactory
16
- # Specifies the mapping of SSH names to OpenSSL key classes.
17
- MAP = {
18
- "dh" => OpenSSL::PKey::DH,
19
- "rsa" => OpenSSL::PKey::RSA,
20
- "dsa" => OpenSSL::PKey::DSA,
21
- }
22
- if defined?(OpenSSL::PKey::EC)
23
- MAP["ecdsa"] = OpenSSL::PKey::EC
24
- end
4
+ require 'net/ssh/authentication/ed25519_loader'
25
5
 
26
- class <<self
27
- include Prompt
6
+ module Net
7
+ module SSH
8
+ # A factory class for returning new Key classes. It is used for obtaining
9
+ # OpenSSL key instances via their SSH names, and for loading both public and
10
+ # private keys. It used used primarily by Net::SSH itself, internally, and
11
+ # will rarely (if ever) be directly used by consumers of the library.
12
+ #
13
+ # klass = Net::SSH::KeyFactory.get("rsa")
14
+ # assert klass.is_a?(OpenSSL::PKey::RSA)
15
+ #
16
+ # key = Net::SSH::KeyFactory.load_public_key("~/.ssh/id_dsa.pub")
17
+ class KeyFactory
18
+ # Specifies the mapping of SSH names to OpenSSL key classes.
19
+ MAP = {
20
+ 'dh' => OpenSSL::PKey::DH,
21
+ 'rsa' => OpenSSL::PKey::RSA,
22
+ 'dsa' => OpenSSL::PKey::DSA,
23
+ 'ecdsa' => OpenSSL::PKey::EC
24
+ }
25
+ MAP["ed25519"] = Net::SSH::Authentication::ED25519::PrivKey if defined? Net::SSH::Authentication::ED25519
28
26
 
29
- # Fetch an OpenSSL key instance by its SSH name. It will be a new,
30
- # empty key of the given type.
31
- def get(name)
32
- MAP.fetch(name).new
33
- end
27
+ class << self
28
+ # Fetch an OpenSSL key instance by its SSH name. It will be a new,
29
+ # empty key of the given type.
30
+ def get(name)
31
+ MAP.fetch(name).new
32
+ end
34
33
 
35
- # Loads a private key from a file. It will correctly determine
36
- # whether the file describes an RSA or DSA key, and will load it
37
- # appropriately. The new key is returned. If the key itself is
38
- # encrypted (requiring a passphrase to use), the user will be
39
- # prompted to enter their password unless passphrase works.
40
- def load_private_key(filename, passphrase=nil, ask_passphrase=true)
41
- data = File.read(File.expand_path(filename))
42
- load_data_private_key(data, passphrase, ask_passphrase, filename)
43
- end
34
+ # Loads a private key from a file. It will correctly determine
35
+ # whether the file describes an RSA or DSA key, and will load it
36
+ # appropriately. The new key is returned. If the key itself is
37
+ # encrypted (requiring a passphrase to use), the user will be
38
+ # prompted to enter their password unless passphrase works.
39
+ def load_private_key(filename, passphrase = nil, ask_passphrase = true, prompt = Prompt.default)
40
+ data = File.read(File.expand_path(filename))
41
+ load_data_private_key(data, passphrase, ask_passphrase, filename, prompt)
42
+ end
44
43
 
45
- # Loads a private key. It will correctly determine
46
- # whether the file describes an RSA or DSA key, and will load it
47
- # appropriately. The new key is returned. If the key itself is
48
- # encrypted (requiring a passphrase to use), the user will be
49
- # prompted to enter their password unless passphrase works.
50
- def load_data_private_key(data, passphrase=nil, ask_passphrase=true, filename="")
51
- if OpenSSL::PKey.respond_to?(:read)
52
- pkey_read = true
53
- error_class = ArgumentError
54
- else
55
- pkey_read = false
56
- if data.match(/-----BEGIN DSA PRIVATE KEY-----/)
57
- key_type = OpenSSL::PKey::DSA
58
- error_class = OpenSSL::PKey::DSAError
59
- elsif data.match(/-----BEGIN RSA PRIVATE KEY-----/)
60
- key_type = OpenSSL::PKey::RSA
61
- error_class = OpenSSL::PKey::RSAError
62
- elsif data.match(/-----BEGIN EC PRIVATE KEY-----/) && defined?(OpenSSL::PKey::EC)
63
- key_type = OpenSSL::PKey::EC
64
- error_class = OpenSSL::PKey::ECError
65
- elsif data.match(/-----BEGIN (.+) PRIVATE KEY-----/)
66
- raise OpenSSL::PKey::PKeyError, "not a supported key type '#{$1}'"
67
- else
68
- raise OpenSSL::PKey::PKeyError, "not a private key (#{filename})"
44
+ # Loads a private key. It will correctly determine
45
+ # whether the file describes an RSA or DSA key, and will load it
46
+ # appropriately. The new key is returned. If the key itself is
47
+ # encrypted (requiring a passphrase to use), the user will be
48
+ # prompted to enter their password unless passphrase works.
49
+ def load_data_private_key(data, passphrase = nil, ask_passphrase = true, filename = "", prompt = Prompt.default)
50
+ key_type = classify_key(data, filename)
51
+
52
+ encrypted_key = nil
53
+ tries = 0
54
+
55
+ prompter = nil
56
+ result =
57
+ begin
58
+ key_type.read(data, passphrase || 'invalid')
59
+ rescue *key_type.error_classes => e
60
+ encrypted_key = !!key_type.encrypted_key?(data, e) if encrypted_key.nil?
61
+ if encrypted_key && ask_passphrase
62
+ tries += 1
63
+ if tries <= 3
64
+ prompter ||= prompt.start(type: 'private_key', filename: filename, sha: Digest::SHA256.digest(data))
65
+ passphrase = prompter.ask("Enter passphrase for #{filename}:", false)
66
+ retry
67
+ else
68
+ raise
69
+ end
70
+ else
71
+ raise
72
+ end
73
+ end
74
+ prompter.success if prompter
75
+ result
76
+ end
77
+
78
+ # Loads a public key from a file. It will correctly determine whether
79
+ # the file describes an RSA or DSA key, and will load it
80
+ # appropriately. The new public key is returned.
81
+ def load_public_key(filename)
82
+ data = File.read(File.expand_path(filename))
83
+ load_data_public_key(data, filename)
84
+ end
85
+
86
+ # Loads a public key. It will correctly determine whether
87
+ # the file describes an RSA or DSA key, and will load it
88
+ # appropriately. The new public key is returned.
89
+ def load_data_public_key(data, filename = "")
90
+ fields = data.split(/ /)
91
+
92
+ blob = nil
93
+ begin
94
+ blob = fields.shift
95
+ end while !blob.nil? && !/^(ssh-(rsa|dss|ed25519)|ecdsa-sha2-nistp\d+)(-cert-v01@openssh\.com)?$/.match(blob)
96
+ blob = fields.shift
97
+
98
+ raise Net::SSH::Exception, "public key at #{filename} is not valid" if blob.nil?
99
+
100
+ blob = blob.unpack("m*").first
101
+ reader = Net::SSH::Buffer.new(blob)
102
+ reader.read_key or raise OpenSSL::PKey::PKeyError, "not a public key #{filename.inspect}"
103
+ end
104
+
105
+ private
106
+
107
+ # rubocop:disable Style/Documentation, Lint/DuplicateMethods
108
+ class KeyType
109
+ def self.read(key_data, passphrase)
110
+ raise Exception, "TODO subclasses should implement read"
111
+ end
112
+
113
+ def self.error_classes
114
+ raise Exception, "TODO subclasses should implement read"
115
+ end
116
+
117
+ def self.encrypted_key?(data, error)
118
+ raise Exception, "TODO subclasses should implement is_encrypted_key"
69
119
  end
70
120
  end
71
121
 
72
- encrypted_key = data.match(/ENCRYPTED/)
73
- tries = 0
122
+ class OpenSSHPrivateKeyType < KeyType
123
+ def self.read(key_data, passphrase)
124
+ Net::SSH::Authentication::ED25519::OpenSSHPrivateKeyLoader.read(key_data, passphrase)
125
+ end
74
126
 
75
- begin
76
- if pkey_read
77
- return OpenSSL::PKey.read(data, passphrase || 'invalid')
78
- else
79
- return key_type.new(data, passphrase || 'invalid')
80
- end
81
- rescue error_class
82
- if encrypted_key && ask_passphrase
83
- tries += 1
84
- if tries <= 3
85
- passphrase = prompt("Enter passphrase for #{filename}:", false)
86
- retry
87
- else
88
- raise
89
- end
90
- else
91
- raise
127
+ def self.error_classes
128
+ [Net::SSH::Authentication::ED25519::OpenSSHPrivateKeyLoader::DecryptError]
129
+ end
130
+
131
+ def self.encrypted_key?(key_data, decode_error)
132
+ decode_error.is_a?(Net::SSH::Authentication::ED25519::OpenSSHPrivateKeyLoader::DecryptError) && decode_error.encrypted_key?
92
133
  end
93
134
  end
94
- end
95
135
 
96
- # Loads a public key from a file. It will correctly determine whether
97
- # the file describes an RSA or DSA key, and will load it
98
- # appropriately. The new public key is returned.
99
- def load_public_key(filename)
100
- data = File.read(File.expand_path(filename))
101
- load_data_public_key(data, filename)
102
- end
136
+ class OpenSSLKeyTypeBase < KeyType
137
+ def self.open_ssl_class
138
+ raise Exception, "TODO: subclasses should implement"
139
+ end
140
+
141
+ def self.read(key_data, passphrase)
142
+ open_ssl_class.new(key_data, passphrase)
143
+ end
144
+
145
+ def self.encrypted_key?(key_data, error)
146
+ key_data.match(/ENCRYPTED/)
147
+ end
148
+ end
149
+
150
+ class OpenSSLPKeyType < OpenSSLKeyTypeBase
151
+ def self.read(key_data, passphrase)
152
+ open_ssl_class.read(key_data, passphrase)
153
+ end
154
+
155
+ def self.open_ssl_class
156
+ OpenSSL::PKey
157
+ end
103
158
 
104
- # Loads a public key. It will correctly determine whether
105
- # the file describes an RSA or DSA key, and will load it
106
- # appropriately. The new public key is returned.
107
- def load_data_public_key(data, filename="")
108
- _, blob = data.split(/ /)
159
+ def self.error_classes
160
+ [ArgumentError, OpenSSL::PKey::PKeyError]
161
+ end
162
+ end
163
+
164
+ class OpenSSLDSAKeyType < OpenSSLKeyTypeBase
165
+ def self.open_ssl_class
166
+ OpenSSL::PKey::DSA
167
+ end
109
168
 
110
- raise Net::SSH::Exception, "public key at #{filename} is not valid" if blob.nil?
169
+ def self.error_classes
170
+ [OpenSSL::PKey::DSAError]
171
+ end
172
+ end
173
+
174
+ class OpenSSLRSAKeyType < OpenSSLKeyTypeBase
175
+ def self.open_ssl_class
176
+ OpenSSL::PKey::RSA
177
+ end
111
178
 
112
- blob = blob.unpack("m*").first
113
- reader = Net::SSH::Buffer.new(blob)
114
- reader.read_key or raise OpenSSL::PKey::PKeyError, "not a public key #{filename.inspect}"
179
+ def self.error_classes
180
+ [OpenSSL::PKey::RSAError]
181
+ end
182
+ end
183
+
184
+ class OpenSSLECKeyType < OpenSSLKeyTypeBase
185
+ def self.open_ssl_class
186
+ OpenSSL::PKey::EC
187
+ end
188
+
189
+ def self.error_classes
190
+ [OpenSSL::PKey::ECError]
191
+ end
192
+ end
193
+ # rubocop:enable Style/Documentation, Lint/DuplicateMethods
194
+
195
+ # Determine whether the file describes an RSA or DSA key, and return how load it
196
+ # appropriately.
197
+ def classify_key(data, filename)
198
+ if data.match(/-----BEGIN OPENSSH PRIVATE KEY-----/)
199
+ Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("OpenSSH keys only supported if ED25519 is available")
200
+ return OpenSSHPrivateKeyType
201
+ elsif OpenSSL::PKey.respond_to?(:read)
202
+ return OpenSSLPKeyType
203
+ elsif data.match(/-----BEGIN DSA PRIVATE KEY-----/)
204
+ return OpenSSLDSAKeyType
205
+ elsif data.match(/-----BEGIN RSA PRIVATE KEY-----/)
206
+ return OpenSSLRSAKeyType
207
+ elsif data.match(/-----BEGIN EC PRIVATE KEY-----/)
208
+ return OpenSSLECKeyType
209
+ elsif data.match(/-----BEGIN (.+) PRIVATE KEY-----/)
210
+ raise OpenSSL::PKey::PKeyError, "not a supported key type '#{$1}'"
211
+ else
212
+ raise OpenSSL::PKey::PKeyError, "not a private key (#{filename})"
213
+ end
214
+ end
115
215
  end
116
216
  end
117
-
118
217
  end
119
-
120
- end; end
218
+ end