hrr_rb_ssh 0.3.0.pre3 → 0.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.
- checksums.yaml +4 -4
- data/README.md +61 -3
- data/demo/client.rb +58 -0
- data/hrr_rb_ssh.gemspec +2 -2
- data/lib/hrr_rb_ssh/authentication/method/keyboard_interactive.rb +34 -0
- data/lib/hrr_rb_ssh/authentication/method/none.rb +13 -0
- data/lib/hrr_rb_ssh/authentication/method/password.rb +18 -0
- data/lib/hrr_rb_ssh/authentication/method/publickey/algorithm/functionable.rb +22 -0
- data/lib/hrr_rb_ssh/authentication/method/publickey.rb +49 -0
- data/lib/hrr_rb_ssh/authentication.rb +47 -1
- data/lib/hrr_rb_ssh/client.rb +198 -0
- data/lib/hrr_rb_ssh/connection/channel/channel_type/direct_tcpip.rb +6 -3
- data/lib/hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip.rb +6 -3
- data/lib/hrr_rb_ssh/connection/channel/channel_type/session.rb +7 -1
- data/lib/hrr_rb_ssh/connection/channel.rb +308 -79
- data/lib/hrr_rb_ssh/connection.rb +99 -38
- data/lib/hrr_rb_ssh/logger.rb +5 -5
- data/lib/hrr_rb_ssh/server.rb +3 -3
- data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman.rb +37 -32
- data/lib/hrr_rb_ssh/transport/kex_algorithm/diffie_hellman_group_exchange.rb +80 -46
- data/lib/hrr_rb_ssh/transport/kex_algorithm/elliptic_curve_diffie_hellman.rb +37 -32
- data/lib/hrr_rb_ssh/transport.rb +46 -10
- data/lib/hrr_rb_ssh/version.rb +1 -1
- data/lib/hrr_rb_ssh.rb +1 -0
- metadata +9 -8
@@ -11,12 +11,14 @@ module HrrRbSsh
|
|
11
11
|
attr_reader \
|
12
12
|
:username,
|
13
13
|
:variables,
|
14
|
-
:options
|
14
|
+
:options,
|
15
|
+
:mode
|
15
16
|
|
16
|
-
def initialize authentication, options={}
|
17
|
+
def initialize authentication, mode, options={}
|
17
18
|
@logger = Logger.new self.class.name
|
18
19
|
|
19
20
|
@authentication = authentication
|
21
|
+
@mode = mode
|
20
22
|
@options = options
|
21
23
|
|
22
24
|
@global_request_handler = GlobalRequestHandler.new self
|
@@ -38,21 +40,31 @@ module HrrRbSsh
|
|
38
40
|
def assign_channel
|
39
41
|
i = 0
|
40
42
|
res = nil
|
41
|
-
|
43
|
+
while true
|
42
44
|
break unless @channels.keys.include?(i)
|
43
45
|
i += 1
|
44
46
|
end
|
45
47
|
i
|
46
48
|
end
|
47
49
|
|
48
|
-
def start
|
50
|
+
def start foreground: true
|
51
|
+
@logger.info { "start connection" }
|
49
52
|
@authentication.start
|
50
53
|
@closed = false
|
51
|
-
|
54
|
+
@connection_loop_thread = connection_loop_thread
|
55
|
+
if foreground
|
56
|
+
@connection_loop_thread.join
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def loop
|
61
|
+
@connection_loop_thread.join
|
52
62
|
end
|
53
63
|
|
54
64
|
def close
|
65
|
+
@logger.info { "closing connection" }
|
55
66
|
@closed = true
|
67
|
+
@authentication.close
|
56
68
|
@channels.values.each do |channel|
|
57
69
|
begin
|
58
70
|
channel.close
|
@@ -62,47 +74,58 @@ module HrrRbSsh
|
|
62
74
|
end
|
63
75
|
@channels.clear
|
64
76
|
@global_request_handler.close
|
77
|
+
@connection_loop_thread.join unless @connection_loop_thread == Thread.current
|
78
|
+
@logger.info { "connection closed" }
|
65
79
|
end
|
66
80
|
|
67
81
|
def closed?
|
68
82
|
@closed
|
69
83
|
end
|
70
84
|
|
71
|
-
def
|
72
|
-
@logger.info { "start connection" }
|
73
|
-
|
85
|
+
def connection_loop_thread
|
86
|
+
@logger.info { "start connection loop" }
|
87
|
+
Thread.new do
|
74
88
|
begin
|
75
|
-
|
76
|
-
|
89
|
+
while true
|
90
|
+
begin
|
91
|
+
payload = @authentication.receive
|
92
|
+
rescue Error::ClosedAuthentication => e
|
93
|
+
@logger.info { "authentication closed" }
|
94
|
+
break
|
95
|
+
end
|
96
|
+
@username ||= @authentication.username
|
97
|
+
@variables ||= @authentication.variables
|
98
|
+
case payload[0,1].unpack("C")[0]
|
99
|
+
when Message::SSH_MSG_GLOBAL_REQUEST::VALUE
|
100
|
+
global_request payload
|
101
|
+
when Message::SSH_MSG_CHANNEL_OPEN::VALUE
|
102
|
+
channel_open payload
|
103
|
+
when Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE
|
104
|
+
channel_open_confirmation payload
|
105
|
+
when Message::SSH_MSG_CHANNEL_REQUEST::VALUE
|
106
|
+
channel_request payload
|
107
|
+
when Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
|
108
|
+
channel_window_adjust payload
|
109
|
+
when Message::SSH_MSG_CHANNEL_DATA::VALUE
|
110
|
+
channel_data payload
|
111
|
+
when Message::SSH_MSG_CHANNEL_EXTENDED_DATA::VALUE
|
112
|
+
channel_extended_data payload
|
113
|
+
when Message::SSH_MSG_CHANNEL_EOF::VALUE
|
114
|
+
channel_eof payload
|
115
|
+
when Message::SSH_MSG_CHANNEL_CLOSE::VALUE
|
116
|
+
channel_close payload
|
117
|
+
else
|
118
|
+
@logger.warn { "received unsupported message: id: #{payload[0,1].unpack("C")[0]}" }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
rescue => e
|
122
|
+
@logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
123
|
+
ensure
|
77
124
|
@logger.info { "closing connection loop" }
|
78
|
-
|
79
|
-
|
80
|
-
@username ||= @authentication.username
|
81
|
-
@variables ||= @authentication.variables
|
82
|
-
case payload[0,1].unpack("C")[0]
|
83
|
-
when Message::SSH_MSG_GLOBAL_REQUEST::VALUE
|
84
|
-
global_request payload
|
85
|
-
when Message::SSH_MSG_CHANNEL_OPEN::VALUE
|
86
|
-
channel_open payload
|
87
|
-
when Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE
|
88
|
-
channel_open_confirmation payload
|
89
|
-
when Message::SSH_MSG_CHANNEL_REQUEST::VALUE
|
90
|
-
channel_request payload
|
91
|
-
when Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
|
92
|
-
channel_window_adjust payload
|
93
|
-
when Message::SSH_MSG_CHANNEL_DATA::VALUE
|
94
|
-
channel_data payload
|
95
|
-
when Message::SSH_MSG_CHANNEL_EOF::VALUE
|
96
|
-
channel_eof payload
|
97
|
-
when Message::SSH_MSG_CHANNEL_CLOSE::VALUE
|
98
|
-
channel_close payload
|
99
|
-
else
|
100
|
-
@logger.warn { "received unsupported message: id: #{payload[0,1].unpack("C")[0]}" }
|
125
|
+
close
|
126
|
+
@logger.info { "connection loop closed" }
|
101
127
|
end
|
102
128
|
end
|
103
|
-
@logger.info { "closing connection" }
|
104
|
-
close
|
105
|
-
@logger.info { "connection closed" }
|
106
129
|
end
|
107
130
|
|
108
131
|
def global_request payload
|
@@ -149,11 +172,40 @@ module HrrRbSsh
|
|
149
172
|
channel.start
|
150
173
|
send_channel_open_confirmation channel
|
151
174
|
rescue => e
|
175
|
+
@logger.error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
|
152
176
|
recipient_channel = message[:'sender channel']
|
153
177
|
send_channel_open_failure recipient_channel, Message::SSH_MSG_CHANNEL_OPEN_FAILURE::ReasonCode::SSH_OPEN_CONNECT_FAILED, e.message
|
154
178
|
end
|
155
179
|
end
|
156
180
|
|
181
|
+
def request_channel_open channel_type, channel_specific_message={}, wait_response=true
|
182
|
+
@logger.info { 'request channel open' }
|
183
|
+
case channel_type
|
184
|
+
when "session"
|
185
|
+
channel = Channel.new self, {:'channel type' => channel_type}
|
186
|
+
@channels[channel.local_channel] = channel
|
187
|
+
end
|
188
|
+
message = {
|
189
|
+
:'message number' => Message::SSH_MSG_CHANNEL_OPEN::VALUE,
|
190
|
+
:'channel type' => channel_type,
|
191
|
+
:'sender channel' => channel.local_channel,
|
192
|
+
:'initial window size' => channel.local_window_size,
|
193
|
+
:'maximum packet size' => channel.local_maximum_packet_size,
|
194
|
+
}
|
195
|
+
send_channel_open message.merge(channel_specific_message)
|
196
|
+
@logger.info { 'sent channel open' }
|
197
|
+
if wait_response
|
198
|
+
@logger.info { 'wait response' }
|
199
|
+
channel.wait_until_started
|
200
|
+
end
|
201
|
+
unless channel.closed?
|
202
|
+
@logger.info { 'channel opened' }
|
203
|
+
channel
|
204
|
+
else
|
205
|
+
raise "Faild opening channel"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
157
209
|
def channel_open_confirmation payload
|
158
210
|
@logger.info { 'received ' + Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::ID }
|
159
211
|
message = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.decode payload
|
@@ -183,12 +235,18 @@ module HrrRbSsh
|
|
183
235
|
@channels[local_channel].receive_message_queue.enq message
|
184
236
|
end
|
185
237
|
|
238
|
+
def channel_extended_data payload
|
239
|
+
@logger.info { 'received ' + Message::SSH_MSG_CHANNEL_EXTENDED_DATA::ID }
|
240
|
+
message = Message::SSH_MSG_CHANNEL_EXTENDED_DATA.decode payload
|
241
|
+
local_channel = message[:'recipient channel']
|
242
|
+
@channels[local_channel].receive_message_queue.enq message
|
243
|
+
end
|
244
|
+
|
186
245
|
def channel_eof payload
|
187
246
|
@logger.info { 'received ' + Message::SSH_MSG_CHANNEL_EOF::ID }
|
188
247
|
message = Message::SSH_MSG_CHANNEL_EOF.decode payload
|
189
248
|
local_channel = message[:'recipient channel']
|
190
|
-
|
191
|
-
channel.receive_message_queue.close
|
249
|
+
@channels[local_channel].receive_message_queue.enq message
|
192
250
|
end
|
193
251
|
|
194
252
|
def channel_close payload
|
@@ -197,6 +255,9 @@ module HrrRbSsh
|
|
197
255
|
local_channel = message[:'recipient channel']
|
198
256
|
channel = @channels[local_channel]
|
199
257
|
channel.close
|
258
|
+
@logger.info { "wait until threads closed in channel" }
|
259
|
+
channel.wait_until_closed
|
260
|
+
@logger.info { "channel closed" }
|
200
261
|
@logger.info { "deleting channel" }
|
201
262
|
@channels.delete local_channel
|
202
263
|
@logger.info { "channel deleted" }
|
data/lib/hrr_rb_ssh/logger.rb
CHANGED
@@ -25,31 +25,31 @@ module HrrRbSsh
|
|
25
25
|
|
26
26
|
def fatal
|
27
27
|
if @@logger
|
28
|
-
@@logger.fatal { "#{@name}: #{yield}" }
|
28
|
+
@@logger.fatal { "p#{Process.pid}.t#{Thread.current.object_id}: #{@name}: #{yield}" }
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
def error
|
33
33
|
if @@logger
|
34
|
-
@@logger.error { "#{@name}: #{yield}" }
|
34
|
+
@@logger.error { "p#{Process.pid}.t#{Thread.current.object_id}: #{@name}: #{yield}" }
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
def warn
|
39
39
|
if @@logger
|
40
|
-
@@logger.warn { "#{@name}: #{yield}" }
|
40
|
+
@@logger.warn { "p#{Process.pid}.t#{Thread.current.object_id}: #{@name}: #{yield}" }
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
def info
|
45
45
|
if @@logger
|
46
|
-
@@logger.info { "#{@name}: #{yield}" }
|
46
|
+
@@logger.info { "p#{Process.pid}.t#{Thread.current.object_id}: #{@name}: #{yield}" }
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
50
|
def debug
|
51
51
|
if @@logger
|
52
|
-
@@logger.debug { "#{@name}: #{yield}" }
|
52
|
+
@@logger.debug { "p#{Process.pid}.t#{Thread.current.object_id}: #{@name}: #{yield}" }
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
data/lib/hrr_rb_ssh/server.rb
CHANGED
@@ -20,9 +20,9 @@ module HrrRbSsh
|
|
20
20
|
|
21
21
|
def start io
|
22
22
|
@logger.info { "start server service" }
|
23
|
-
transport =
|
24
|
-
authentication =
|
25
|
-
connection =
|
23
|
+
transport = Transport.new io, Mode::SERVER, @options
|
24
|
+
authentication = Authentication.new transport, Mode::SERVER, @options
|
25
|
+
connection = Connection.new authentication, Mode::SERVER, @options
|
26
26
|
connection.start
|
27
27
|
end
|
28
28
|
end
|
@@ -22,74 +22,79 @@ module HrrRbSsh
|
|
22
22
|
@dh.g = OpenSSL::BN.new(self.class::G)
|
23
23
|
end
|
24
24
|
@dh.generate_key!
|
25
|
+
@public_key = @dh.pub_key.to_i
|
25
26
|
end
|
26
27
|
|
27
|
-
def start transport
|
28
|
-
case mode
|
28
|
+
def start transport
|
29
|
+
case transport.mode
|
29
30
|
when Mode::SERVER
|
30
|
-
|
31
|
+
@k_s = transport.server_host_key_algorithm.server_public_host_key
|
32
|
+
@f = @public_key
|
33
|
+
message = receive_kexdh_init transport.receive
|
34
|
+
@e = message[:'e']
|
35
|
+
@shared_secret = OpenSSL::BN.new(@dh.compute_key(OpenSSL::BN.new(@e)), 2).to_i
|
31
36
|
send_kexdh_reply transport
|
32
|
-
|
33
|
-
|
37
|
+
when Mode::CLIENT
|
38
|
+
@e = @public_key
|
39
|
+
send_kexdh_init transport
|
40
|
+
message = receive_kexdh_reply transport.receive
|
41
|
+
@k_s = message[:'server public host key and certificates (K_S)']
|
42
|
+
@f = message[:'f']
|
43
|
+
@shared_secret = OpenSSL::BN.new(@dh.compute_key(OpenSSL::BN.new(@f)), 2).to_i
|
34
44
|
end
|
35
45
|
end
|
36
46
|
|
37
|
-
def set_e e
|
38
|
-
@e = e
|
39
|
-
end
|
40
|
-
|
41
47
|
def shared_secret
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
def pub_key
|
46
|
-
f = @dh.pub_key.to_i
|
48
|
+
@shared_secret
|
47
49
|
end
|
48
50
|
|
49
51
|
def hash transport
|
50
|
-
e = @e
|
51
|
-
k = shared_secret
|
52
|
-
f = pub_key
|
53
|
-
|
54
52
|
h0_payload = {
|
55
53
|
:'V_C' => transport.v_c,
|
56
54
|
:'V_S' => transport.v_s,
|
57
55
|
:'I_C' => transport.i_c,
|
58
56
|
:'I_S' => transport.i_s,
|
59
|
-
:'K_S' =>
|
60
|
-
:'e' => e,
|
61
|
-
:'f' => f,
|
62
|
-
:'k' =>
|
57
|
+
:'K_S' => @k_s,
|
58
|
+
:'e' => @e,
|
59
|
+
:'f' => @f,
|
60
|
+
:'k' => @shared_secret,
|
63
61
|
}
|
64
62
|
h0 = H0.encode h0_payload
|
65
|
-
|
66
|
-
h = OpenSSL::Digest.digest self.class::DIGEST, h0
|
67
|
-
|
68
|
-
h
|
63
|
+
h = OpenSSL::Digest.digest self.class::DIGEST, h0
|
69
64
|
end
|
70
65
|
|
71
66
|
def sign transport
|
72
67
|
h = hash transport
|
73
68
|
s = transport.server_host_key_algorithm.sign h
|
74
|
-
|
75
|
-
s
|
76
69
|
end
|
77
70
|
|
78
71
|
def receive_kexdh_init payload
|
79
|
-
|
80
|
-
set_e message[:'e']
|
72
|
+
Message::SSH_MSG_KEXDH_INIT.decode payload
|
81
73
|
end
|
82
74
|
|
83
75
|
def send_kexdh_reply transport
|
84
76
|
message = {
|
85
77
|
:'message number' => Message::SSH_MSG_KEXDH_REPLY::VALUE,
|
86
|
-
:'server public host key and certificates (K_S)' =>
|
87
|
-
:'f' =>
|
78
|
+
:'server public host key and certificates (K_S)' => @k_s,
|
79
|
+
:'f' => @f,
|
88
80
|
:'signature of H' => sign(transport),
|
89
81
|
}
|
90
82
|
payload = Message::SSH_MSG_KEXDH_REPLY.encode message
|
91
83
|
transport.send payload
|
92
84
|
end
|
85
|
+
|
86
|
+
def send_kexdh_init transport
|
87
|
+
message = {
|
88
|
+
:'message number' => Message::SSH_MSG_KEXDH_INIT::VALUE,
|
89
|
+
:'e' => @e,
|
90
|
+
}
|
91
|
+
payload = Message::SSH_MSG_KEXDH_INIT.encode message
|
92
|
+
transport.send payload
|
93
|
+
end
|
94
|
+
|
95
|
+
def receive_kexdh_reply payload
|
96
|
+
Message::SSH_MSG_KEXDH_REPLY.decode payload
|
97
|
+
end
|
93
98
|
end
|
94
99
|
end
|
95
100
|
end
|
@@ -16,25 +16,50 @@ module HrrRbSsh
|
|
16
16
|
@logger = Logger.new(self.class.name)
|
17
17
|
end
|
18
18
|
|
19
|
-
def start transport
|
20
|
-
case mode
|
19
|
+
def start transport
|
20
|
+
case transport.mode
|
21
21
|
when Mode::SERVER
|
22
|
-
receive_kex_dh_gex_request transport.receive
|
23
|
-
|
22
|
+
message = receive_kex_dh_gex_request transport.receive
|
23
|
+
@min = message[:'min']
|
24
|
+
@n = message[:'n']
|
25
|
+
@max = message[:'max']
|
26
|
+
initialize_dh
|
27
|
+
@p = @dh.p.to_i
|
28
|
+
@g = @dh.g.to_i
|
24
29
|
send_kex_dh_gex_group transport
|
25
|
-
|
30
|
+
@k_s = transport.server_host_key_algorithm.server_public_host_key
|
31
|
+
@f = @public_key
|
32
|
+
message = receive_kex_dh_gex_init transport.receive
|
33
|
+
@e = message[:'e']
|
34
|
+
@shared_secret = OpenSSL::BN.new(@dh.compute_key(OpenSSL::BN.new(@e)), 2).to_i
|
26
35
|
send_kex_dh_gex_reply transport
|
27
|
-
|
28
|
-
|
36
|
+
when Mode::CLIENT
|
37
|
+
@min = 1024
|
38
|
+
@n = 2048
|
39
|
+
@max = 8192
|
40
|
+
send_kex_dh_gex_request transport
|
41
|
+
message = receive_kex_dh_gex_group transport.receive
|
42
|
+
@p = message[:'p']
|
43
|
+
@g = message[:'g']
|
44
|
+
initialize_dh [@p, @g]
|
45
|
+
@e = @public_key
|
46
|
+
send_kex_dh_gex_init transport
|
47
|
+
message = receive_kex_dh_gex_reply transport.receive
|
48
|
+
@k_s = message[:'server public host key and certificates (K_S)']
|
49
|
+
@f = message[:'f']
|
50
|
+
@shared_secret = OpenSSL::BN.new(@dh.compute_key(OpenSSL::BN.new(@f)), 2).to_i
|
29
51
|
end
|
30
52
|
end
|
31
53
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
54
|
+
def initialize_dh pg=nil
|
55
|
+
unless pg
|
56
|
+
p_list = KexAlgorithm.list_supported.map{ |e| KexAlgorithm[e] }.select{ |e| e.const_defined?(:P) }.map{ |e| [OpenSSL::BN.new(e::P,16).num_bits, e::P] }.sort_by{ |e| e[0] }.reverse
|
57
|
+
candidate = p_list.find{ |e| e[0] <= @n }
|
58
|
+
raise unless (@min .. @max).include?(candidate[0])
|
59
|
+
p, g = candidate[1], 2
|
60
|
+
else
|
61
|
+
p, g = pg
|
62
|
+
end
|
38
63
|
@dh = OpenSSL::PKey::DH.new
|
39
64
|
if @dh.respond_to?(:set_pqg)
|
40
65
|
@dh.set_pqg OpenSSL::BN.new(p, 16), nil, OpenSSL::BN.new(g)
|
@@ -43,85 +68,94 @@ module HrrRbSsh
|
|
43
68
|
@dh.g = OpenSSL::BN.new(g)
|
44
69
|
end
|
45
70
|
@dh.generate_key!
|
46
|
-
|
47
|
-
|
48
|
-
def set_e e
|
49
|
-
@e = e
|
71
|
+
@public_key = @dh.pub_key.to_i
|
50
72
|
end
|
51
73
|
|
52
74
|
def shared_secret
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def pub_key
|
57
|
-
f = @dh.pub_key.to_i
|
75
|
+
@shared_secret
|
58
76
|
end
|
59
77
|
|
60
78
|
def hash transport
|
61
|
-
e = @e
|
62
|
-
k = shared_secret
|
63
|
-
f = pub_key
|
64
|
-
|
65
79
|
h0_payload = {
|
66
80
|
:'V_C' => transport.v_c,
|
67
81
|
:'V_S' => transport.v_s,
|
68
82
|
:'I_C' => transport.i_c,
|
69
83
|
:'I_S' => transport.i_s,
|
70
|
-
:'K_S' =>
|
84
|
+
:'K_S' => @k_s,
|
71
85
|
:'min' => @min,
|
72
86
|
:'n' => @n,
|
73
87
|
:'max' => @max,
|
74
|
-
:'p' => @
|
75
|
-
:'g' => @
|
76
|
-
:'e' => e,
|
77
|
-
:'f' => f,
|
78
|
-
:'k' =>
|
88
|
+
:'p' => @p,
|
89
|
+
:'g' => @g,
|
90
|
+
:'e' => @e,
|
91
|
+
:'f' => @f,
|
92
|
+
:'k' => @shared_secret,
|
79
93
|
}
|
80
94
|
h0 = H0.encode h0_payload
|
81
|
-
|
82
95
|
h = OpenSSL::Digest.digest self.class::DIGEST, h0
|
83
|
-
|
84
|
-
h
|
85
96
|
end
|
86
97
|
|
87
98
|
def sign transport
|
88
99
|
h = hash transport
|
89
100
|
s = transport.server_host_key_algorithm.sign h
|
90
|
-
s
|
91
101
|
end
|
92
102
|
|
93
103
|
def receive_kex_dh_gex_request payload
|
94
|
-
|
95
|
-
@min = message[:'min']
|
96
|
-
@n = message[:'n']
|
97
|
-
@max = message[:'max']
|
104
|
+
Message::SSH_MSG_KEX_DH_GEX_REQUEST.decode payload
|
98
105
|
end
|
99
106
|
|
100
107
|
def send_kex_dh_gex_group transport
|
101
108
|
message = {
|
102
109
|
:'message number' => Message::SSH_MSG_KEX_DH_GEX_GROUP::VALUE,
|
103
|
-
:'p' => @
|
104
|
-
:'g' => @
|
110
|
+
:'p' => @p,
|
111
|
+
:'g' => @g,
|
105
112
|
}
|
106
113
|
payload = Message::SSH_MSG_KEX_DH_GEX_GROUP.encode message
|
107
114
|
transport.send payload
|
108
115
|
end
|
109
116
|
|
110
117
|
def receive_kex_dh_gex_init payload
|
111
|
-
|
112
|
-
set_e message[:'e']
|
118
|
+
Message::SSH_MSG_KEX_DH_GEX_INIT.decode payload
|
113
119
|
end
|
114
120
|
|
115
121
|
def send_kex_dh_gex_reply transport
|
116
122
|
message = {
|
117
123
|
:'message number' => Message::SSH_MSG_KEX_DH_GEX_REPLY::VALUE,
|
118
|
-
:'server public host key and certificates (K_S)' =>
|
119
|
-
:'f' =>
|
124
|
+
:'server public host key and certificates (K_S)' => @k_s,
|
125
|
+
:'f' => @f,
|
120
126
|
:'signature of H' => sign(transport),
|
121
127
|
}
|
122
128
|
payload = Message::SSH_MSG_KEX_DH_GEX_REPLY.encode message
|
123
129
|
transport.send payload
|
124
130
|
end
|
131
|
+
|
132
|
+
def send_kex_dh_gex_request transport
|
133
|
+
message = {
|
134
|
+
:'message number' => Message::SSH_MSG_KEX_DH_GEX_REQUEST::VALUE,
|
135
|
+
:'min' => @min,
|
136
|
+
:'n' => @n,
|
137
|
+
:'max' => @max,
|
138
|
+
}
|
139
|
+
payload = Message::SSH_MSG_KEX_DH_GEX_REQUEST.encode message
|
140
|
+
transport.send payload
|
141
|
+
end
|
142
|
+
|
143
|
+
def receive_kex_dh_gex_group payload
|
144
|
+
Message::SSH_MSG_KEX_DH_GEX_GROUP.decode payload
|
145
|
+
end
|
146
|
+
|
147
|
+
def send_kex_dh_gex_init transport
|
148
|
+
message = {
|
149
|
+
:'message number' => Message::SSH_MSG_KEX_DH_GEX_INIT::VALUE,
|
150
|
+
:'e' => @e,
|
151
|
+
}
|
152
|
+
payload = Message::SSH_MSG_KEX_DH_GEX_INIT.encode message
|
153
|
+
transport.send payload
|
154
|
+
end
|
155
|
+
|
156
|
+
def receive_kex_dh_gex_reply payload
|
157
|
+
Message::SSH_MSG_KEX_DH_GEX_REPLY.decode payload
|
158
|
+
end
|
125
159
|
end
|
126
160
|
end
|
127
161
|
end
|
@@ -16,74 +16,79 @@ module HrrRbSsh
|
|
16
16
|
@logger = Logger.new(self.class.name)
|
17
17
|
@dh = OpenSSL::PKey::EC.new(self.class::CURVE_NAME)
|
18
18
|
@dh.generate_key
|
19
|
+
@public_key = @dh.public_key.to_bn.to_i
|
19
20
|
end
|
20
21
|
|
21
|
-
def start transport
|
22
|
-
case mode
|
22
|
+
def start transport
|
23
|
+
case transport.mode
|
23
24
|
when Mode::SERVER
|
24
|
-
|
25
|
+
@k_s = transport.server_host_key_algorithm.server_public_host_key
|
26
|
+
@q_s = @public_key
|
27
|
+
message = receive_kexecdh_init transport.receive
|
28
|
+
@q_c = message[:'Q_C']
|
29
|
+
@shared_secret = OpenSSL::BN.new(@dh.dh_compute_key(OpenSSL::PKey::EC::Point.new(OpenSSL::PKey::EC.new(self.class::CURVE_NAME).group, OpenSSL::BN.new(@q_c))), 2).to_i
|
25
30
|
send_kexecdh_reply transport
|
26
|
-
|
27
|
-
|
31
|
+
when Mode::CLIENT
|
32
|
+
@q_c = @public_key
|
33
|
+
send_kexecdh_init transport
|
34
|
+
message = receive_kexecdh_reply transport.receive
|
35
|
+
@k_s = message[:'K_S']
|
36
|
+
@q_s = message[:'Q_S']
|
37
|
+
@shared_secret = OpenSSL::BN.new(@dh.dh_compute_key(OpenSSL::PKey::EC::Point.new(OpenSSL::PKey::EC.new(self.class::CURVE_NAME).group, OpenSSL::BN.new(@q_s))), 2).to_i
|
28
38
|
end
|
29
39
|
end
|
30
40
|
|
31
|
-
def set_q_c q_c
|
32
|
-
@q_c = q_c
|
33
|
-
end
|
34
|
-
|
35
41
|
def shared_secret
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
def public_key
|
40
|
-
f = @dh.public_key.to_bn.to_i
|
42
|
+
@shared_secret
|
41
43
|
end
|
42
44
|
|
43
45
|
def hash transport
|
44
|
-
q_c = @q_c
|
45
|
-
q_s = public_key
|
46
|
-
k = shared_secret
|
47
|
-
|
48
46
|
h0_payload = {
|
49
47
|
:'V_C' => transport.v_c,
|
50
48
|
:'V_S' => transport.v_s,
|
51
49
|
:'I_C' => transport.i_c,
|
52
50
|
:'I_S' => transport.i_s,
|
53
|
-
:'K_S' =>
|
54
|
-
:'Q_C' => q_c,
|
55
|
-
:'Q_S' => q_s,
|
56
|
-
:'K' =>
|
51
|
+
:'K_S' => @k_s,
|
52
|
+
:'Q_C' => @q_c,
|
53
|
+
:'Q_S' => @q_s,
|
54
|
+
:'K' => @shared_secret,
|
57
55
|
}
|
58
56
|
h0 = H0.encode h0_payload
|
59
|
-
|
60
|
-
h = OpenSSL::Digest.digest self.class::DIGEST, h0
|
61
|
-
|
62
|
-
h
|
57
|
+
h = OpenSSL::Digest.digest self.class::DIGEST, h0
|
63
58
|
end
|
64
59
|
|
65
60
|
def sign transport
|
66
61
|
h = hash transport
|
67
62
|
s = transport.server_host_key_algorithm.sign h
|
68
|
-
|
69
|
-
s
|
70
63
|
end
|
71
64
|
|
72
65
|
def receive_kexecdh_init payload
|
73
|
-
|
74
|
-
set_q_c message[:'Q_C']
|
66
|
+
Message::SSH_MSG_KEXECDH_INIT.decode payload
|
75
67
|
end
|
76
68
|
|
77
69
|
def send_kexecdh_reply transport
|
78
70
|
message = {
|
79
71
|
:'message number' => Message::SSH_MSG_KEXECDH_REPLY::VALUE,
|
80
|
-
:'K_S' =>
|
81
|
-
:'Q_S' =>
|
72
|
+
:'K_S' => @k_s,
|
73
|
+
:'Q_S' => @q_s,
|
82
74
|
:'signature of H' => sign(transport),
|
83
75
|
}
|
84
76
|
payload = Message::SSH_MSG_KEXECDH_REPLY.encode message
|
85
77
|
transport.send payload
|
86
78
|
end
|
79
|
+
|
80
|
+
def send_kexecdh_init transport
|
81
|
+
message = {
|
82
|
+
:'message number' => Message::SSH_MSG_KEXECDH_INIT::VALUE,
|
83
|
+
:'Q_C' => @q_c,
|
84
|
+
}
|
85
|
+
payload = Message::SSH_MSG_KEXECDH_INIT.encode message
|
86
|
+
transport.send payload
|
87
|
+
end
|
88
|
+
|
89
|
+
def receive_kexecdh_reply payload
|
90
|
+
Message::SSH_MSG_KEXECDH_REPLY.decode payload
|
91
|
+
end
|
87
92
|
end
|
88
93
|
end
|
89
94
|
end
|