hrr_rb_ssh 0.3.0.pre3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|