net-ssh 4.2.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/config/rubocop_linter_action.yml +4 -0
  5. data/.github/workflows/ci-with-docker.yml +44 -0
  6. data/.github/workflows/ci.yml +87 -0
  7. data/.github/workflows/rubocop.yml +13 -0
  8. data/.gitignore +7 -0
  9. data/.rubocop.yml +19 -2
  10. data/.rubocop_todo.yml +619 -667
  11. data/CHANGES.txt +110 -1
  12. data/Dockerfile +27 -0
  13. data/Dockerfile.openssl3 +17 -0
  14. data/Gemfile +3 -7
  15. data/{Gemfile.norbnacl → Gemfile.noed25519} +3 -1
  16. data/Manifest +4 -5
  17. data/README.md +293 -0
  18. data/Rakefile +45 -29
  19. data/appveyor.yml +8 -6
  20. data/docker-compose.yml +23 -0
  21. data/lib/net/ssh/authentication/agent.rb +248 -223
  22. data/lib/net/ssh/authentication/certificate.rb +178 -164
  23. data/lib/net/ssh/authentication/constants.rb +17 -15
  24. data/lib/net/ssh/authentication/ed25519.rb +141 -116
  25. data/lib/net/ssh/authentication/ed25519_loader.rb +28 -28
  26. data/lib/net/ssh/authentication/key_manager.rb +79 -36
  27. data/lib/net/ssh/authentication/methods/abstract.rb +62 -47
  28. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  29. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +3 -3
  30. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  31. data/lib/net/ssh/authentication/methods/password.rb +15 -16
  32. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  33. data/lib/net/ssh/authentication/pageant.rb +468 -465
  34. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  35. data/lib/net/ssh/authentication/session.rb +131 -122
  36. data/lib/net/ssh/buffer.rb +385 -332
  37. data/lib/net/ssh/buffered_io.rb +150 -151
  38. data/lib/net/ssh/config.rb +316 -239
  39. data/lib/net/ssh/connection/channel.rb +635 -613
  40. data/lib/net/ssh/connection/constants.rb +29 -29
  41. data/lib/net/ssh/connection/event_loop.rb +104 -95
  42. data/lib/net/ssh/connection/keepalive.rb +55 -51
  43. data/lib/net/ssh/connection/session.rb +614 -611
  44. data/lib/net/ssh/connection/term.rb +125 -123
  45. data/lib/net/ssh/errors.rb +101 -99
  46. data/lib/net/ssh/key_factory.rb +194 -108
  47. data/lib/net/ssh/known_hosts.rb +212 -134
  48. data/lib/net/ssh/loggable.rb +50 -49
  49. data/lib/net/ssh/packet.rb +83 -79
  50. data/lib/net/ssh/prompt.rb +51 -51
  51. data/lib/net/ssh/proxy/command.rb +105 -91
  52. data/lib/net/ssh/proxy/errors.rb +12 -10
  53. data/lib/net/ssh/proxy/http.rb +81 -81
  54. data/lib/net/ssh/proxy/https.rb +37 -36
  55. data/lib/net/ssh/proxy/jump.rb +49 -48
  56. data/lib/net/ssh/proxy/socks4.rb +2 -6
  57. data/lib/net/ssh/proxy/socks5.rb +14 -17
  58. data/lib/net/ssh/service/forward.rb +365 -362
  59. data/lib/net/ssh/test/channel.rb +145 -143
  60. data/lib/net/ssh/test/extensions.rb +131 -127
  61. data/lib/net/ssh/test/kex.rb +34 -32
  62. data/lib/net/ssh/test/local_packet.rb +46 -44
  63. data/lib/net/ssh/test/packet.rb +87 -84
  64. data/lib/net/ssh/test/remote_packet.rb +32 -30
  65. data/lib/net/ssh/test/script.rb +155 -155
  66. data/lib/net/ssh/test/socket.rb +49 -48
  67. data/lib/net/ssh/test.rb +82 -80
  68. data/lib/net/ssh/transport/algorithms.rb +433 -364
  69. data/lib/net/ssh/transport/cipher_factory.rb +95 -91
  70. data/lib/net/ssh/transport/constants.rb +32 -24
  71. data/lib/net/ssh/transport/ctr.rb +37 -15
  72. data/lib/net/ssh/transport/hmac/abstract.rb +81 -63
  73. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  74. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  75. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  76. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  77. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  78. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  79. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  80. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  81. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  82. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  83. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  84. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  85. data/lib/net/ssh/transport/hmac.rb +14 -12
  86. data/lib/net/ssh/transport/identity_cipher.rb +54 -52
  87. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  88. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  89. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  90. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  92. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +112 -217
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -63
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  96. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  97. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  99. data/lib/net/ssh/transport/kex.rb +15 -12
  100. data/lib/net/ssh/transport/key_expander.rb +24 -21
  101. data/lib/net/ssh/transport/openssl.rb +158 -133
  102. data/lib/net/ssh/transport/packet_stream.rb +223 -191
  103. data/lib/net/ssh/transport/server_version.rb +55 -56
  104. data/lib/net/ssh/transport/session.rb +306 -259
  105. data/lib/net/ssh/transport/state.rb +178 -176
  106. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  107. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  108. data/lib/net/ssh/verifiers/always.rb +58 -0
  109. data/lib/net/ssh/verifiers/never.rb +19 -0
  110. data/lib/net/ssh/version.rb +55 -53
  111. data/lib/net/ssh.rb +47 -34
  112. data/net-ssh-public_cert.pem +18 -19
  113. data/net-ssh.gemspec +12 -11
  114. data/support/ssh_tunnel_bug.rb +5 -5
  115. data.tar.gz.sig +0 -0
  116. metadata +78 -73
  117. metadata.gz.sig +0 -0
  118. data/.travis.yml +0 -51
  119. data/Gemfile.norbnacl.lock +0 -41
  120. data/README.rdoc +0 -169
  121. data/lib/net/ssh/ruby_compat.rb +0 -24
  122. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  123. data/lib/net/ssh/verifiers/null.rb +0 -12
  124. data/lib/net/ssh/verifiers/secure.rb +0 -52
  125. data/lib/net/ssh/verifiers/strict.rb +0 -24
  126. data/support/arcfour_check.rb +0 -20
@@ -0,0 +1,23 @@
1
+ version: '3'
2
+
3
+ services:
4
+ ruby-3.1:
5
+ build:
6
+ context: .
7
+ args:
8
+ RUBY_VERSION: 3.1
9
+ ruby-3.0:
10
+ build:
11
+ context: .
12
+ args:
13
+ RUBY_VERSION: 3.0
14
+ ruby-2.7:
15
+ build:
16
+ context: .
17
+ args:
18
+ RUBY_VERSION: 2.7
19
+ ruby-2.6:
20
+ build:
21
+ context: .
22
+ args:
23
+ RUBY_VERSION: 2.6
@@ -8,252 +8,277 @@ 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
11
+ module Net
12
+ module SSH
13
+ module Authentication
14
+ # Class for representing agent-specific errors.
15
+ class AgentError < Net::SSH::Exception; end
16
+
17
+ # An exception for indicating that the SSH agent is not available.
18
+ class AgentNotAvailable < AgentError; end
19
+
20
+ # This class implements a simple client for the ssh-agent protocol. It
21
+ # does not implement any specific protocol, but instead copies the
22
+ # behavior of the ssh-agent functions in the OpenSSH library (3.8).
23
+ #
24
+ # This means that although it behaves like a SSH1 client, it also has
25
+ # some SSH2 functionality (like signing data).
26
+ class Agent
27
+ include Loggable
28
+
29
+ # A simple module for extending keys, to allow comments to be specified
30
+ # for them.
31
+ module Comment
32
+ attr_accessor :comment
33
+ end
31
34
 
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
- SSH_AGENT_RSA_SHA2_256 = 0x02
56
- SSH_AGENT_RSA_SHA2_512 = 0x04
57
-
58
- # The underlying socket being used to communicate with the SSH agent.
59
- attr_reader :socket
60
-
61
- # Instantiates a new agent object, connects to a running SSH agent,
62
- # negotiates the agent protocol version, and returns the agent object.
63
- def self.connect(logger=nil, agent_socket_factory = nil)
64
- agent = new(logger)
65
- agent.connect!(agent_socket_factory)
66
- agent.negotiate!
67
- agent
68
- end
35
+ SSH2_AGENT_REQUEST_VERSION = 1
36
+ SSH2_AGENT_REQUEST_IDENTITIES = 11
37
+ SSH2_AGENT_IDENTITIES_ANSWER = 12
38
+ SSH2_AGENT_SIGN_REQUEST = 13
39
+ SSH2_AGENT_SIGN_RESPONSE = 14
40
+ SSH2_AGENT_ADD_IDENTITY = 17
41
+ SSH2_AGENT_REMOVE_IDENTITY = 18
42
+ SSH2_AGENT_REMOVE_ALL_IDENTITIES = 19
43
+ SSH2_AGENT_LOCK = 22
44
+ SSH2_AGENT_UNLOCK = 23
45
+ SSH2_AGENT_ADD_ID_CONSTRAINED = 25
46
+ SSH2_AGENT_FAILURE = 30
47
+ SSH2_AGENT_VERSION_RESPONSE = 103
48
+
49
+ SSH_COM_AGENT2_FAILURE = 102
50
+
51
+ SSH_AGENT_REQUEST_RSA_IDENTITIES = 1
52
+ SSH_AGENT_RSA_IDENTITIES_ANSWER1 = 2
53
+ SSH_AGENT_RSA_IDENTITIES_ANSWER2 = 5
54
+ SSH_AGENT_FAILURE = 5
55
+ SSH_AGENT_SUCCESS = 6
56
+
57
+ SSH_AGENT_CONSTRAIN_LIFETIME = 1
58
+ SSH_AGENT_CONSTRAIN_CONFIRM = 2
59
+
60
+ SSH_AGENT_RSA_SHA2_256 = 0x02
61
+ SSH_AGENT_RSA_SHA2_512 = 0x04
62
+
63
+ # The underlying socket being used to communicate with the SSH agent.
64
+ attr_reader :socket
65
+
66
+ # Instantiates a new agent object, connects to a running SSH agent,
67
+ # negotiates the agent protocol version, and returns the agent object.
68
+ def self.connect(logger = nil, agent_socket_factory = nil, identity_agent = nil)
69
+ agent = new(logger)
70
+ agent.connect!(agent_socket_factory, identity_agent)
71
+ agent.negotiate!
72
+ agent
73
+ end
69
74
 
70
- # Creates a new Agent object, using the optional logger instance to
71
- # report status.
72
- def initialize(logger=nil)
73
- self.logger = logger
74
- end
75
+ # Creates a new Agent object, using the optional logger instance to
76
+ # report status.
77
+ def initialize(logger = nil)
78
+ self.logger = logger
79
+ end
75
80
 
76
- # Connect to the agent process using the socket factory and socket name
77
- # given by the attribute writers. If the agent on the other end of the
78
- # socket reports that it is an SSH2-compatible agent, this will fail
79
- # (it only supports the ssh-agent distributed by OpenSSH).
80
- def connect!(agent_socket_factory = nil)
81
- debug { "connecting to ssh-agent" }
82
- @socket =
83
- if agent_socket_factory
84
- agent_socket_factory.call
85
- elsif ENV['SSH_AUTH_SOCK'] && unix_socket_class
86
- unix_socket_class.open(ENV['SSH_AUTH_SOCK'])
87
- elsif Gem.win_platform? && RUBY_ENGINE != "jruby"
88
- Pageant::Socket.open
89
- else
90
- raise AgentNotAvailable, "Agent not configured"
81
+ # Connect to the agent process using the socket factory and socket name
82
+ # given by the attribute writers. If the agent on the other end of the
83
+ # socket reports that it is an SSH2-compatible agent, this will fail
84
+ # (it only supports the ssh-agent distributed by OpenSSH).
85
+ def connect!(agent_socket_factory = nil, identity_agent = nil)
86
+ debug { "connecting to ssh-agent" }
87
+ @socket =
88
+ if agent_socket_factory
89
+ agent_socket_factory.call
90
+ elsif identity_agent
91
+ unix_socket_class.open(File.expand_path(identity_agent))
92
+ elsif ENV['SSH_AUTH_SOCK'] && unix_socket_class
93
+ unix_socket_class.open(File.expand_path(ENV['SSH_AUTH_SOCK']))
94
+ elsif Gem.win_platform? && RUBY_ENGINE != "jruby"
95
+ Pageant::Socket.open
96
+ else
97
+ raise AgentNotAvailable, "Agent not configured"
98
+ end
99
+ rescue StandardError => e
100
+ error { "could not connect to ssh-agent: #{e.message}" }
101
+ raise AgentNotAvailable, $!.message
91
102
  end
92
- rescue StandardError => e
93
- error { "could not connect to ssh-agent: #{e.message}" }
94
- raise AgentNotAvailable, $!.message
95
- end
96
103
 
97
- # Attempts to negotiate the SSH agent protocol version. Raises an error
98
- # if the version could not be negotiated successfully.
99
- def negotiate!
100
- # determine what type of agent we're communicating with
101
- type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
102
-
103
- raise AgentNotAvailable, "SSH2 agents are not yet supported" if type == SSH2_AGENT_VERSION_RESPONSE
104
- if type == SSH2_AGENT_FAILURE
105
- debug { "Unexpected response type==#{type}, this will be ignored" }
106
- elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
107
- raise AgentNotAvailable, "unknown response from agent: #{type}, #{body.to_s.inspect}"
108
- end
109
- end
104
+ # Attempts to negotiate the SSH agent protocol version. Raises an error
105
+ # if the version could not be negotiated successfully.
106
+ def negotiate!
107
+ # determine what type of agent we're communicating with
108
+ type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
109
+
110
+ raise AgentNotAvailable, "SSH2 agents are not yet supported" if type == SSH2_AGENT_VERSION_RESPONSE
110
111
 
111
- # Return an array of all identities (public keys) known to the agent.
112
- # Each key returned is augmented with a +comment+ property which is set
113
- # to the comment returned by the agent for that key.
114
- def identities
115
- type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES)
116
- raise AgentError, "could not get identity count" if agent_failed(type)
117
- raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER
118
-
119
- identities = []
120
- body.read_long.times do
121
- key_str = body.read_string
122
- comment_str = body.read_string
123
- begin
124
- key = Buffer.new(key_str).read_key
125
- key.extend(Comment)
126
- key.comment = comment_str
127
- identities.push key
128
- rescue NotImplementedError => e
129
- error { "ignoring unimplemented key:#{e.message} #{comment_str}" }
112
+ if type == SSH2_AGENT_FAILURE
113
+ debug { "Unexpected response type==#{type}, this will be ignored" }
114
+ elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
115
+ raise AgentNotAvailable, "unknown response from agent: #{type}, #{body.to_s.inspect}"
116
+ end
130
117
  end
131
- end
132
118
 
133
- return identities
134
- end
119
+ # Return an array of all identities (public keys) known to the agent.
120
+ # Each key returned is augmented with a +comment+ property which is set
121
+ # to the comment returned by the agent for that key.
122
+ def identities
123
+ type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES)
124
+ raise AgentError, "could not get identity count" if agent_failed(type)
125
+ raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER
126
+
127
+ identities = []
128
+ body.read_long.times do
129
+ key_str = body.read_string
130
+ comment_str = body.read_string
131
+ begin
132
+ key = Buffer.new(key_str).read_key
133
+ if key.nil?
134
+ error { "ignoring invalid key: #{comment_str}" }
135
+ next
136
+ end
137
+ key.extend(Comment)
138
+ key.comment = comment_str
139
+ identities.push key
140
+ rescue NotImplementedError => e
141
+ error { "ignoring unimplemented key:#{e.message} #{comment_str}" }
142
+ end
143
+ end
144
+
145
+ return identities
146
+ end
135
147
 
136
- # Closes this socket. This agent reference is no longer able to
137
- # query the agent.
138
- def close
139
- @socket.close
140
- end
148
+ # Closes this socket. This agent reference is no longer able to
149
+ # query the agent.
150
+ def close
151
+ @socket.close
152
+ end
141
153
 
142
- # Using the agent and the given public key, sign the given data. The
143
- # signature is returned in SSH2 format.
144
- def sign(key, data, flags = 0)
145
- type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, flags)
154
+ # Using the agent and the given public key, sign the given data. The
155
+ # signature is returned in SSH2 format.
156
+ def sign(key, data, flags = 0)
157
+ type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, flags)
146
158
 
147
- raise AgentError, "agent could not sign data with requested identity" if agent_failed(type)
148
- raise AgentError, "bad authentication response #{type}" if type != SSH2_AGENT_SIGN_RESPONSE
159
+ raise AgentError, "agent could not sign data with requested identity" if agent_failed(type)
160
+ raise AgentError, "bad authentication response #{type}" if type != SSH2_AGENT_SIGN_RESPONSE
149
161
 
150
- return reply.read_string
151
- end
162
+ return reply.read_string
163
+ end
152
164
 
153
- # Adds the private key with comment to the agent.
154
- # If lifetime is given, the key will automatically be removed after lifetime
155
- # seconds.
156
- # If confirm is true, confirmation will be required for each agent signing
157
- # operation.
158
- def add_identity(priv_key, comment, lifetime: nil, confirm: false)
159
- constraints = Buffer.new
160
- if lifetime
161
- constraints.write_byte(SSH_AGENT_CONSTRAIN_LIFETIME)
162
- constraints.write_long(lifetime)
163
- end
164
- constraints.write_byte(SSH_AGENT_CONSTRAIN_CONFIRM) if confirm
165
+ # Adds the private key with comment to the agent.
166
+ # If lifetime is given, the key will automatically be removed after lifetime
167
+ # seconds.
168
+ # If confirm is true, confirmation will be required for each agent signing
169
+ # operation.
170
+ def add_identity(priv_key, comment, lifetime: nil, confirm: false)
171
+ constraints = Buffer.new
172
+ if lifetime
173
+ constraints.write_byte(SSH_AGENT_CONSTRAIN_LIFETIME)
174
+ constraints.write_long(lifetime)
175
+ end
176
+ constraints.write_byte(SSH_AGENT_CONSTRAIN_CONFIRM) if confirm
177
+
178
+ req_type = constraints.empty? ? SSH2_AGENT_ADD_IDENTITY : SSH2_AGENT_ADD_ID_CONSTRAINED
179
+ type, = send_and_wait(req_type, :string, priv_key.ssh_type, :raw, blob_for_add(priv_key),
180
+ :string, comment, :raw, constraints)
181
+ raise AgentError, "could not add identity to agent" if type != SSH_AGENT_SUCCESS
182
+ end
165
183
 
166
- req_type = constraints.empty? ? SSH2_AGENT_ADD_IDENTITY : SSH2_AGENT_ADD_ID_CONSTRAINED
167
- type, = send_and_wait(req_type, :string, priv_key.ssh_type, :raw, blob_for_add(priv_key),
168
- :string, comment, :raw, constraints)
169
- raise AgentError, "could not add identity to agent" if type != SSH_AGENT_SUCCESS
170
- end
184
+ # Removes key from the agent.
185
+ def remove_identity(key)
186
+ type, = send_and_wait(SSH2_AGENT_REMOVE_IDENTITY, :string, key.to_blob)
187
+ raise AgentError, "could not remove identity from agent" if type != SSH_AGENT_SUCCESS
188
+ end
171
189
 
172
- # Removes key from the agent.
173
- def remove_identity(key)
174
- type, = send_and_wait(SSH2_AGENT_REMOVE_IDENTITY, :string, key.to_blob)
175
- raise AgentError, "could not remove identity from agent" if type != SSH_AGENT_SUCCESS
176
- end
190
+ # Removes all identities from the agent.
191
+ def remove_all_identities
192
+ type, = send_and_wait(SSH2_AGENT_REMOVE_ALL_IDENTITIES)
193
+ raise AgentError, "could not remove all identity from agent" if type != SSH_AGENT_SUCCESS
194
+ end
177
195
 
178
- # Removes all identities from the agent.
179
- def remove_all_identities
180
- type, = send_and_wait(SSH2_AGENT_REMOVE_ALL_IDENTITIES)
181
- raise AgentError, "could not remove all identity from agent" if type != SSH_AGENT_SUCCESS
182
- end
196
+ # lock the ssh agent with password
197
+ def lock(password)
198
+ type, = send_and_wait(SSH2_AGENT_LOCK, :string, password)
199
+ raise AgentError, "could not lock agent" if type != SSH_AGENT_SUCCESS
200
+ end
183
201
 
184
- private
202
+ # unlock the ssh agent with password
203
+ def unlock(password)
204
+ type, = send_and_wait(SSH2_AGENT_UNLOCK, :string, password)
205
+ raise AgentError, "could not unlock agent" if type != SSH_AGENT_SUCCESS
206
+ end
185
207
 
186
- def unix_socket_class
187
- defined?(UNIXSocket) && UNIXSocket
188
- end
208
+ private
189
209
 
190
- # Send a new packet of the given type, with the associated data.
191
- def send_packet(type, *args)
192
- buffer = Buffer.from(*args)
193
- data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*")
194
- debug { "sending agent request #{type} len #{buffer.length}" }
195
- @socket.send data, 0
196
- end
210
+ def unix_socket_class
211
+ defined?(UNIXSocket) && UNIXSocket
212
+ end
197
213
 
198
- # Read the next packet from the agent. This will return a two-part
199
- # tuple consisting of the packet type, and the packet's body (which
200
- # is returned as a Net::SSH::Buffer).
201
- def read_packet
202
- buffer = Net::SSH::Buffer.new(@socket.read(4))
203
- buffer.append(@socket.read(buffer.read_long))
204
- type = buffer.read_byte
205
- debug { "received agent packet #{type} len #{buffer.length-4}" }
206
- return type, buffer
207
- end
214
+ # Send a new packet of the given type, with the associated data.
215
+ def send_packet(type, *args)
216
+ buffer = Buffer.from(*args)
217
+ data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*")
218
+ debug { "sending agent request #{type} len #{buffer.length}" }
219
+ @socket.send data, 0
220
+ end
208
221
 
209
- # Send the given packet and return the subsequent reply from the agent.
210
- # (See #send_packet and #read_packet).
211
- def send_and_wait(type, *args)
212
- send_packet(type, *args)
213
- read_packet
214
- end
222
+ # Read the next packet from the agent. This will return a two-part
223
+ # tuple consisting of the packet type, and the packet's body (which
224
+ # is returned as a Net::SSH::Buffer).
225
+ def read_packet
226
+ buffer = Net::SSH::Buffer.new(@socket.read(4))
227
+ buffer.append(@socket.read(buffer.read_long))
228
+ type = buffer.read_byte
229
+ debug { "received agent packet #{type} len #{buffer.length - 4}" }
230
+ return type, buffer
231
+ end
215
232
 
216
- # Returns +true+ if the parameter indicates a "failure" response from
217
- # the agent, and +false+ otherwise.
218
- def agent_failed(type)
219
- type == SSH_AGENT_FAILURE ||
220
- type == SSH2_AGENT_FAILURE ||
221
- type == SSH_COM_AGENT2_FAILURE
222
- end
233
+ # Send the given packet and return the subsequent reply from the agent.
234
+ # (See #send_packet and #read_packet).
235
+ def send_and_wait(type, *args)
236
+ send_packet(type, *args)
237
+ read_packet
238
+ end
223
239
 
224
- def blob_for_add(priv_key)
225
- # Ideally we'd have something like `to_private_blob` on the various key types, but the
226
- # nuances with encoding (e.g. `n` and `e` are reversed for RSA keys) make this impractical.
227
- case priv_key.ssh_type
228
- when /^ssh-dss$/
229
- Net::SSH::Buffer.from(:bignum, priv_key.p, :bignum, priv_key.q, :bignum, priv_key.g,
230
- :bignum, priv_key.pub_key, :bignum, priv_key.priv_key).to_s
231
- when /^ssh-dss-cert-v01@openssh\.com$/
232
- Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.priv_key).to_s
233
- when /^ecdsa\-sha2\-(\w*)$/
234
- curve_name = OpenSSL::PKey::EC::CurveNameAliasInv[priv_key.group.curve_name]
235
- Net::SSH::Buffer.from(:string, curve_name, :mstring, priv_key.public_key.to_bn.to_s(2),
236
- :bignum, priv_key.private_key).to_s
237
- when /^ecdsa\-sha2\-(\w*)-cert-v01@openssh\.com$/
238
- Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.private_key).to_s
239
- when /^ssh-ed25519$/
240
- Net::SSH::Buffer.from(:string, priv_key.public_key.verify_key.to_bytes,
241
- :string, priv_key.sign_key.keypair_bytes).to_s
242
- when /^ssh-ed25519-cert-v01@openssh\.com$/
243
- # Unlike the other certificate types, the public key is included after the certifiate.
244
- Net::SSH::Buffer.from(:string, priv_key.to_blob,
245
- :string, priv_key.key.public_key.verify_key.to_bytes,
246
- :string, priv_key.key.sign_key.keypair_bytes).to_s
247
- when /^ssh-rsa$/
248
- # `n` and `e` are reversed compared to the ordering in `OpenSSL::PKey::RSA#to_blob`.
249
- Net::SSH::Buffer.from(:bignum, priv_key.n, :bignum, priv_key.e, :bignum, priv_key.d,
250
- :bignum, priv_key.iqmp, :bignum, priv_key.p, :bignum, priv_key.q).to_s
251
- when /^ssh-rsa-cert-v01@openssh\.com$/
252
- Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.d,
253
- :bignum, priv_key.key.iqmp, :bignum, priv_key.key.p,
254
- :bignum, priv_key.key.q).to_s
240
+ # Returns +true+ if the parameter indicates a "failure" response from
241
+ # the agent, and +false+ otherwise.
242
+ def agent_failed(type)
243
+ type == SSH_AGENT_FAILURE ||
244
+ type == SSH2_AGENT_FAILURE ||
245
+ type == SSH_COM_AGENT2_FAILURE
246
+ end
247
+
248
+ def blob_for_add(priv_key)
249
+ # Ideally we'd have something like `to_private_blob` on the various key types, but the
250
+ # nuances with encoding (e.g. `n` and `e` are reversed for RSA keys) make this impractical.
251
+ case priv_key.ssh_type
252
+ when /^ssh-dss$/
253
+ Net::SSH::Buffer.from(:bignum, priv_key.p, :bignum, priv_key.q, :bignum, priv_key.g,
254
+ :bignum, priv_key.pub_key, :bignum, priv_key.priv_key).to_s
255
+ when /^ssh-dss-cert-v01@openssh\.com$/
256
+ Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.priv_key).to_s
257
+ when /^ecdsa\-sha2\-(\w*)$/
258
+ curve_name = OpenSSL::PKey::EC::CurveNameAliasInv[priv_key.group.curve_name]
259
+ Net::SSH::Buffer.from(:string, curve_name, :mstring, priv_key.public_key.to_bn.to_s(2),
260
+ :bignum, priv_key.private_key).to_s
261
+ when /^ecdsa\-sha2\-(\w*)-cert-v01@openssh\.com$/
262
+ Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.private_key).to_s
263
+ when /^ssh-ed25519$/
264
+ Net::SSH::Buffer.from(:string, priv_key.public_key.verify_key.to_bytes,
265
+ :string, priv_key.sign_key.keypair).to_s
266
+ when /^ssh-ed25519-cert-v01@openssh\.com$/
267
+ # Unlike the other certificate types, the public key is included after the certifiate.
268
+ Net::SSH::Buffer.from(:string, priv_key.to_blob,
269
+ :string, priv_key.key.public_key.verify_key.to_bytes,
270
+ :string, priv_key.key.sign_key.keypair).to_s
271
+ when /^ssh-rsa$/
272
+ # `n` and `e` are reversed compared to the ordering in `OpenSSL::PKey::RSA#to_blob`.
273
+ Net::SSH::Buffer.from(:bignum, priv_key.n, :bignum, priv_key.e, :bignum, priv_key.d,
274
+ :bignum, priv_key.iqmp, :bignum, priv_key.p, :bignum, priv_key.q).to_s
275
+ when /^ssh-rsa-cert-v01@openssh\.com$/
276
+ Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.d,
277
+ :bignum, priv_key.key.iqmp, :bignum, priv_key.key.p,
278
+ :bignum, priv_key.key.q).to_s
279
+ end
280
+ end
255
281
  end
256
282
  end
257
283
  end
258
-
259
- end; end; end
284
+ end