net-ssh 6.1.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.dockerignore +6 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +94 -0
- data/.github/workflows/rubocop.yml +16 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +12 -1
- data/.rubocop_todo.yml +475 -376
- data/CHANGES.txt +64 -3
- data/DEVELOPMENT.md +23 -0
- data/Dockerfile +29 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/Gemfile.norbnacl +12 -0
- data/README.md +38 -22
- data/Rakefile +92 -0
- data/SECURITY.md +4 -0
- data/docker-compose.yml +25 -0
- data/lib/net/ssh/authentication/agent.rb +29 -13
- data/lib/net/ssh/authentication/certificate.rb +14 -11
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +14 -11
- data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
- data/lib/net/ssh/authentication/key_manager.rb +65 -36
- data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
- data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
- data/lib/net/ssh/authentication/methods/none.rb +6 -9
- data/lib/net/ssh/authentication/methods/password.rb +2 -3
- data/lib/net/ssh/authentication/methods/publickey.rb +57 -17
- data/lib/net/ssh/authentication/pageant.rb +97 -97
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +3 -3
- data/lib/net/ssh/authentication/session.rb +25 -17
- data/lib/net/ssh/buffer.rb +71 -51
- data/lib/net/ssh/buffered_io.rb +25 -26
- data/lib/net/ssh/config.rb +33 -20
- data/lib/net/ssh/connection/channel.rb +84 -82
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -24
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +109 -108
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +7 -8
- data/lib/net/ssh/known_hosts.rb +86 -18
- data/lib/net/ssh/loggable.rb +8 -9
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -1
- data/lib/net/ssh/proxy/errors.rb +2 -4
- data/lib/net/ssh/proxy/http.rb +18 -20
- data/lib/net/ssh/proxy/https.rb +8 -10
- data/lib/net/ssh/proxy/jump.rb +8 -10
- data/lib/net/ssh/proxy/socks4.rb +2 -4
- data/lib/net/ssh/proxy/socks5.rb +3 -5
- data/lib/net/ssh/service/forward.rb +7 -7
- data/lib/net/ssh/test/channel.rb +24 -26
- data/lib/net/ssh/test/extensions.rb +35 -35
- data/lib/net/ssh/test/kex.rb +6 -8
- data/lib/net/ssh/test/local_packet.rb +0 -2
- data/lib/net/ssh/test/packet.rb +3 -3
- data/lib/net/ssh/test/remote_packet.rb +6 -8
- data/lib/net/ssh/test/script.rb +25 -27
- data/lib/net/ssh/test/socket.rb +12 -15
- data/lib/net/ssh/test.rb +4 -5
- data/lib/net/ssh/transport/aes128_gcm.rb +40 -0
- data/lib/net/ssh/transport/aes256_gcm.rb +40 -0
- data/lib/net/ssh/transport/algorithms.rb +51 -19
- data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
- data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
- data/lib/net/ssh/transport/cipher_factory.rb +56 -29
- data/lib/net/ssh/transport/constants.rb +3 -3
- data/lib/net/ssh/transport/ctr.rb +7 -7
- data/lib/net/ssh/transport/gcm_cipher.rb +207 -0
- data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
- data/lib/net/ssh/transport/hmac/md5.rb +0 -2
- data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/none.rb +0 -2
- data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
- data/lib/net/ssh/transport/hmac.rb +12 -12
- data/lib/net/ssh/transport/identity_cipher.rb +19 -13
- data/lib/net/ssh/transport/kex/abstract.rb +12 -5
- data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
- data/lib/net/ssh/transport/kex.rb +8 -6
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +51 -26
- data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
- data/lib/net/ssh/transport/packet_stream.rb +46 -26
- data/lib/net/ssh/transport/server_version.rb +17 -16
- data/lib/net/ssh/transport/session.rb +9 -7
- data/lib/net/ssh/transport/state.rb +44 -44
- data/lib/net/ssh/verifiers/accept_new.rb +0 -2
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
- data/lib/net/ssh/verifiers/always.rb +6 -4
- data/lib/net/ssh/verifiers/never.rb +0 -2
- data/lib/net/ssh/version.rb +2 -2
- data/lib/net/ssh.rb +15 -8
- data/net-ssh-public_cert.pem +19 -18
- data/net-ssh.gemspec +7 -4
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +76 -29
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -52
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
require 'net/ssh/loggable'
|
|
2
|
+
|
|
3
|
+
module Net
|
|
4
|
+
module SSH
|
|
5
|
+
module Transport
|
|
6
|
+
## Extension module for aes(128|256)gcm ciphers
|
|
7
|
+
module GCMCipher
|
|
8
|
+
# rubocop:disable Metrics/AbcSize
|
|
9
|
+
def self.extended(orig)
|
|
10
|
+
# rubocop:disable Metrics/BlockLength
|
|
11
|
+
orig.class_eval do
|
|
12
|
+
include Net::SSH::Loggable
|
|
13
|
+
|
|
14
|
+
attr_reader :cipher
|
|
15
|
+
attr_reader :key
|
|
16
|
+
attr_accessor :nonce
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# Semantically gcm cipher supplies the OpenSSL iv interface with a nonce
|
|
20
|
+
# as it is not randomly generated due to being supplied from a counter.
|
|
21
|
+
# The RFC's use IV and nonce interchangeably.
|
|
22
|
+
#
|
|
23
|
+
def initialize(encrypt:, key:)
|
|
24
|
+
@cipher = OpenSSL::Cipher.new(algo_name)
|
|
25
|
+
@key = key
|
|
26
|
+
key_len = @cipher.key_len
|
|
27
|
+
if key.size != key_len
|
|
28
|
+
error_message = "#{cipher_name}: keylength does not match"
|
|
29
|
+
error { error_message }
|
|
30
|
+
raise error_message
|
|
31
|
+
end
|
|
32
|
+
encrypt ? @cipher.encrypt : @cipher.decrypt
|
|
33
|
+
@cipher.key = key
|
|
34
|
+
|
|
35
|
+
@nonce = {
|
|
36
|
+
fixed: nil,
|
|
37
|
+
invocation_counter: 0
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def update_cipher_mac(payload, _sequence_number)
|
|
42
|
+
#
|
|
43
|
+
# --- RFC 5647 7.3 ---
|
|
44
|
+
# When using AES-GCM with secure shell, the packet_length field is to
|
|
45
|
+
# be treated as additional authenticated data, not as plaintext.
|
|
46
|
+
#
|
|
47
|
+
length_data = [payload.bytesize].pack('N')
|
|
48
|
+
|
|
49
|
+
cipher.auth_data = length_data
|
|
50
|
+
|
|
51
|
+
encrypted_data = cipher.update(payload) << cipher.final
|
|
52
|
+
|
|
53
|
+
mac = cipher.auth_tag
|
|
54
|
+
|
|
55
|
+
incr_nonce
|
|
56
|
+
length_data + encrypted_data + mac
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# --- RFC 5647 ---
|
|
61
|
+
# uint32 packet_length; // 0 <= packet_length < 2^32
|
|
62
|
+
#
|
|
63
|
+
def read_length(data, _sequence_number)
|
|
64
|
+
data.unpack1('N')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
#
|
|
68
|
+
# --- RFC 5647 ---
|
|
69
|
+
# In AES-GCM secure shell, the inputs to the authenticated encryption
|
|
70
|
+
# are:
|
|
71
|
+
# PT (Plain Text)
|
|
72
|
+
# byte padding_length; // 4 <= padding_length < 256
|
|
73
|
+
# byte[n1] payload; // n1 = packet_length-padding_length-1
|
|
74
|
+
# byte[n2] random_padding; // n2 = padding_length
|
|
75
|
+
# AAD (Additional Authenticated Data)
|
|
76
|
+
# uint32 packet_length; // 0 <= packet_length < 2^32
|
|
77
|
+
# IV (Initialization Vector)
|
|
78
|
+
# As described in section 7.1.
|
|
79
|
+
# BK (Block Cipher Key)
|
|
80
|
+
# The appropriate Encryption Key formed during the Key Exchange.
|
|
81
|
+
#
|
|
82
|
+
def read_and_mac(data, mac, _sequence_number)
|
|
83
|
+
# The authentication tag will be placed in the MAC field at the end of the packet
|
|
84
|
+
|
|
85
|
+
# OpenSSL does not verify auth tag length
|
|
86
|
+
# GCM mode allows arbitrary sizes for the auth_tag up to 128 bytes and a single
|
|
87
|
+
# byte allows authentication to pass. If single byte auth tags are possible
|
|
88
|
+
# an attacker would require no more than 256 attempts to forge a valid tag.
|
|
89
|
+
#
|
|
90
|
+
raise 'incorrect auth_tag length' unless mac.to_s.length == mac_length
|
|
91
|
+
|
|
92
|
+
packet_length = data.unpack1('N')
|
|
93
|
+
|
|
94
|
+
cipher.auth_tag = mac.to_s
|
|
95
|
+
cipher.auth_data = [packet_length].pack('N')
|
|
96
|
+
|
|
97
|
+
result = cipher.update(data[4...]) << cipher.final
|
|
98
|
+
incr_nonce
|
|
99
|
+
result
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def mac_length
|
|
103
|
+
16
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def block_size
|
|
107
|
+
16
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.block_size
|
|
111
|
+
16
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
#
|
|
115
|
+
# --- RFC 5647 ---
|
|
116
|
+
# N_MIN minimum nonce (IV) length 12 octets
|
|
117
|
+
# N_MAX maximum nonce (IV) length 12 octets
|
|
118
|
+
#
|
|
119
|
+
def iv_len
|
|
120
|
+
12
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#
|
|
124
|
+
# --- RFC 5288 ---
|
|
125
|
+
# Each value of the nonce_explicit MUST be distinct for each distinct
|
|
126
|
+
# invocation of the GCM encrypt function for any fixed key. Failure to
|
|
127
|
+
# meet this uniqueness requirement can significantly degrade security.
|
|
128
|
+
# The nonce_explicit MAY be the 64-bit sequence number.
|
|
129
|
+
#
|
|
130
|
+
# --- RFC 5116 ---
|
|
131
|
+
# (2.1) Applications that can generate distinct nonces SHOULD use the nonce
|
|
132
|
+
# formation method defined in Section 3.2, and MAY use any
|
|
133
|
+
# other method that meets the uniqueness requirement.
|
|
134
|
+
#
|
|
135
|
+
# (3.2) The following method to construct nonces is RECOMMENDED.
|
|
136
|
+
#
|
|
137
|
+
# <- variable -> <- variable ->
|
|
138
|
+
# - - - - - - - - - - - - - -
|
|
139
|
+
# | fixed | counter |
|
|
140
|
+
#
|
|
141
|
+
# Initial octets consist of a fixed field and final octets consist of a
|
|
142
|
+
# Counter field. Implementations SHOULD support 12-octet nonces in which
|
|
143
|
+
# the Counter field is four octets long.
|
|
144
|
+
# The Counter fields of successive nonces form a monotonically increasing
|
|
145
|
+
# sequence, when those fields are regarded as unsignd integers in network
|
|
146
|
+
# byte order.
|
|
147
|
+
# The Counter part SHOULD be equal to zero for the first nonce and increment
|
|
148
|
+
# by one for each successive nonce that is generated.
|
|
149
|
+
# The Fixed field MUST remain constant for all nonces that are generated for
|
|
150
|
+
# a given encryption device.
|
|
151
|
+
#
|
|
152
|
+
# --- RFC 5647 ---
|
|
153
|
+
# The invocation field is treated as a 64-bit integer and is increment after
|
|
154
|
+
# each invocation of AES-GCM to process a binary packet.
|
|
155
|
+
# AES-GCM produces a keystream in blocks of 16-octets that is used to
|
|
156
|
+
# encrypt the plaintext. This keystream is produced by encrypting the
|
|
157
|
+
# following 16-octet data structure:
|
|
158
|
+
#
|
|
159
|
+
# uint32 fixed; // 4 octets
|
|
160
|
+
# uint64 invocation_counter; // 8 octets
|
|
161
|
+
# unit32 block_counter; // 4 octets
|
|
162
|
+
#
|
|
163
|
+
# The block_counter is initially set to one (1) and increment as each block
|
|
164
|
+
# of key is produced.
|
|
165
|
+
#
|
|
166
|
+
# The reader is reminded that SSH requires that the data to be encrypted
|
|
167
|
+
# MUST be padded out to a multiple of the block size (16-octets for AES-GCM).
|
|
168
|
+
#
|
|
169
|
+
def incr_nonce
|
|
170
|
+
return if nonce[:fixed].nil?
|
|
171
|
+
|
|
172
|
+
nonce[:invocation_counter] = [nonce[:invocation_counter].to_s.unpack1('B*').to_i(2) + 1].pack('Q>*')
|
|
173
|
+
|
|
174
|
+
apply_nonce
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def nonce=(iv_s)
|
|
178
|
+
return if nonce[:fixed]
|
|
179
|
+
|
|
180
|
+
nonce[:fixed] = iv_s[0...4]
|
|
181
|
+
nonce[:invocation_counter] = iv_s[4...12]
|
|
182
|
+
|
|
183
|
+
apply_nonce
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def apply_nonce
|
|
187
|
+
cipher.iv = "#{nonce[:fixed]}#{nonce[:invocation_counter]}"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
#
|
|
191
|
+
# --- RFC 5647 ---
|
|
192
|
+
# If AES-GCM is selected as the encryption algorithm for a given
|
|
193
|
+
# tunnel, AES-GCM MUST also be selected as the Message Authentication
|
|
194
|
+
# Code (MAC) algorithm. Conversely, if AES-GCM is selected as the MAC
|
|
195
|
+
# algorithm, it MUST also be selected as the encryption algorithm.
|
|
196
|
+
#
|
|
197
|
+
def implicit_mac?
|
|
198
|
+
true
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
# rubocop:enable Metrics/BlockLength
|
|
203
|
+
end
|
|
204
|
+
# rubocop:enable Metrics/AbcSize
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
@@ -5,10 +5,21 @@ module Net
|
|
|
5
5
|
module SSH
|
|
6
6
|
module Transport
|
|
7
7
|
module HMAC
|
|
8
|
-
|
|
9
8
|
# The base class of all OpenSSL-based HMAC algorithm wrappers.
|
|
10
9
|
class Abstract
|
|
11
|
-
class <<self
|
|
10
|
+
class << self
|
|
11
|
+
def aead(*v)
|
|
12
|
+
@aead = false if !defined?(@aead)
|
|
13
|
+
if v.empty?
|
|
14
|
+
@aead = superclass.aead if @aead.nil? && superclass.respond_to?(:aead)
|
|
15
|
+
return @aead
|
|
16
|
+
elsif v.length == 1
|
|
17
|
+
@aead = v.first
|
|
18
|
+
else
|
|
19
|
+
raise ArgumentError, "wrong number of arguments (#{v.length} for 1)"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
12
23
|
def etm(*v)
|
|
13
24
|
@etm = false if !defined?(@etm)
|
|
14
25
|
if v.empty?
|
|
@@ -58,6 +69,10 @@ module Net
|
|
|
58
69
|
end
|
|
59
70
|
end
|
|
60
71
|
|
|
72
|
+
def aead
|
|
73
|
+
self.class.aead
|
|
74
|
+
end
|
|
75
|
+
|
|
61
76
|
def etm
|
|
62
77
|
self.class.etm
|
|
63
78
|
end
|
|
@@ -77,19 +92,19 @@ module Net
|
|
|
77
92
|
# The key in use for this instance.
|
|
78
93
|
attr_reader :key
|
|
79
94
|
|
|
80
|
-
def initialize(key=nil)
|
|
95
|
+
def initialize(key = nil)
|
|
81
96
|
self.key = key
|
|
82
97
|
end
|
|
83
98
|
|
|
84
99
|
# Sets the key to the given value, truncating it so that it is the correct
|
|
85
100
|
# length.
|
|
86
101
|
def key=(value)
|
|
87
|
-
@key = value ? value.to_s[0,key_length] : nil
|
|
102
|
+
@key = value ? value.to_s[0, key_length] : nil
|
|
88
103
|
end
|
|
89
104
|
|
|
90
105
|
# Compute the HMAC digest for the given data string.
|
|
91
106
|
def digest(data)
|
|
92
|
-
OpenSSL::HMAC.digest(digest_class.new, key, data)[0,mac_length]
|
|
107
|
+
OpenSSL::HMAC.digest(digest_class.new, key, data)[0, mac_length]
|
|
93
108
|
end
|
|
94
109
|
end
|
|
95
110
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'net/ssh/transport/hmac/abstract'
|
|
2
2
|
|
|
3
3
|
module Net::SSH::Transport::HMAC
|
|
4
|
-
|
|
5
4
|
# The "none" algorithm. This has a key and mac length of 0.
|
|
6
5
|
class None < Abstract
|
|
7
6
|
key_length 0
|
|
@@ -11,5 +10,4 @@ module Net::SSH::Transport::HMAC
|
|
|
11
10
|
""
|
|
12
11
|
end
|
|
13
12
|
end
|
|
14
|
-
|
|
15
13
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'net/ssh/transport/hmac/abstract'
|
|
2
2
|
|
|
3
3
|
module Net::SSH::Transport::HMAC
|
|
4
|
-
|
|
5
4
|
# The RIPEMD-160 HMAC algorithm. This has a mac and key length of 20, and
|
|
6
5
|
# uses the RIPEMD-160 digest algorithm.
|
|
7
6
|
class RIPEMD160 < Abstract
|
|
@@ -9,5 +8,4 @@ module Net::SSH::Transport::HMAC
|
|
|
9
8
|
key_length 20
|
|
10
9
|
digest_class OpenSSL::Digest::RIPEMD160
|
|
11
10
|
end
|
|
12
|
-
|
|
13
11
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'net/ssh/transport/hmac/abstract'
|
|
2
2
|
|
|
3
3
|
module Net::SSH::Transport::HMAC
|
|
4
|
-
|
|
5
4
|
# The SHA1 HMAC algorithm. This has a mac and key length of 20, and
|
|
6
5
|
# uses the SHA1 digest algorithm.
|
|
7
6
|
class SHA1 < Abstract
|
|
@@ -9,5 +8,4 @@ module Net::SSH::Transport::HMAC
|
|
|
9
8
|
key_length 20
|
|
10
9
|
digest_class OpenSSL::Digest::SHA1
|
|
11
10
|
end
|
|
12
|
-
|
|
13
11
|
end
|
|
@@ -17,24 +17,24 @@ require 'net/ssh/transport/hmac/none'
|
|
|
17
17
|
module Net::SSH::Transport::HMAC
|
|
18
18
|
# The mapping of SSH hmac algorithms to their implementations
|
|
19
19
|
MAP = {
|
|
20
|
-
'hmac-md5'
|
|
21
|
-
'hmac-md5-96'
|
|
22
|
-
'hmac-sha1'
|
|
23
|
-
'hmac-sha1-96'
|
|
24
|
-
'hmac-sha2-256'
|
|
25
|
-
'hmac-sha2-256-96'
|
|
26
|
-
'hmac-sha2-512'
|
|
27
|
-
'hmac-sha2-512-96'
|
|
20
|
+
'hmac-md5' => MD5,
|
|
21
|
+
'hmac-md5-96' => MD5_96,
|
|
22
|
+
'hmac-sha1' => SHA1,
|
|
23
|
+
'hmac-sha1-96' => SHA1_96,
|
|
24
|
+
'hmac-sha2-256' => SHA2_256,
|
|
25
|
+
'hmac-sha2-256-96' => SHA2_256_96,
|
|
26
|
+
'hmac-sha2-512' => SHA2_512,
|
|
27
|
+
'hmac-sha2-512-96' => SHA2_512_96,
|
|
28
28
|
'hmac-sha2-256-etm@openssh.com' => SHA2_256_Etm,
|
|
29
29
|
'hmac-sha2-512-etm@openssh.com' => SHA2_512_Etm,
|
|
30
|
-
'hmac-ripemd160'
|
|
31
|
-
'hmac-ripemd160@openssh.com'
|
|
32
|
-
'none'
|
|
30
|
+
'hmac-ripemd160' => RIPEMD160,
|
|
31
|
+
'hmac-ripemd160@openssh.com' => RIPEMD160,
|
|
32
|
+
'none' => None
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
# Retrieves a new hmac instance of the given SSH type (+name+). If +key+ is
|
|
36
36
|
# given, the new instance will be initialized with that key.
|
|
37
|
-
def self.get(name, key="", parameters = {})
|
|
37
|
+
def self.get(name, key = "", parameters = {})
|
|
38
38
|
impl = MAP[name] or raise ArgumentError, "hmac not found: #{name.inspect}"
|
|
39
39
|
impl.new(Net::SSH::Transport::KeyExpander.expand_key(impl.key_length, key, parameters))
|
|
40
40
|
end
|
|
@@ -1,59 +1,65 @@
|
|
|
1
|
-
module Net
|
|
2
|
-
module SSH
|
|
1
|
+
module Net
|
|
2
|
+
module SSH
|
|
3
3
|
module Transport
|
|
4
|
-
|
|
5
4
|
# A cipher that does nothing but pass the data through, unchanged. This
|
|
6
5
|
# keeps things in the code nice and clean when a cipher has not yet been
|
|
7
6
|
# determined (i.e., during key exchange).
|
|
8
7
|
class IdentityCipher
|
|
9
|
-
class <<self
|
|
8
|
+
class << self
|
|
10
9
|
# A default block size of 8 is required by the SSH2 protocol.
|
|
11
10
|
def block_size
|
|
12
11
|
8
|
|
13
12
|
end
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
def key_length
|
|
15
|
+
0
|
|
16
|
+
end
|
|
17
|
+
|
|
15
18
|
# Returns an arbitrary integer.
|
|
16
19
|
def iv_len
|
|
17
20
|
4
|
|
18
21
|
end
|
|
19
|
-
|
|
22
|
+
|
|
20
23
|
# Does nothing. Returns self.
|
|
21
24
|
def encrypt
|
|
22
25
|
self
|
|
23
26
|
end
|
|
24
|
-
|
|
27
|
+
|
|
25
28
|
# Does nothing. Returns self.
|
|
26
29
|
def decrypt
|
|
27
30
|
self
|
|
28
31
|
end
|
|
29
|
-
|
|
32
|
+
|
|
30
33
|
# Passes its single argument through unchanged.
|
|
31
34
|
def update(text)
|
|
32
35
|
text
|
|
33
36
|
end
|
|
34
|
-
|
|
37
|
+
|
|
35
38
|
# Returns the empty string.
|
|
36
39
|
def final
|
|
37
40
|
""
|
|
38
41
|
end
|
|
39
|
-
|
|
42
|
+
|
|
40
43
|
# The name of this cipher, which is "identity".
|
|
41
44
|
def name
|
|
42
45
|
"identity"
|
|
43
46
|
end
|
|
44
|
-
|
|
47
|
+
|
|
45
48
|
# Does nothing. Returns nil.
|
|
46
49
|
def iv=(v)
|
|
47
50
|
nil
|
|
48
51
|
end
|
|
49
|
-
|
|
52
|
+
|
|
50
53
|
# Does nothing. Returns self.
|
|
51
54
|
def reset
|
|
52
55
|
self
|
|
53
56
|
end
|
|
57
|
+
|
|
58
|
+
def implicit_mac?
|
|
59
|
+
false
|
|
60
|
+
end
|
|
54
61
|
end
|
|
55
62
|
end
|
|
56
|
-
|
|
57
63
|
end
|
|
58
64
|
end
|
|
59
65
|
end
|
|
@@ -64,11 +64,16 @@ module Net
|
|
|
64
64
|
|
|
65
65
|
private
|
|
66
66
|
|
|
67
|
+
def matching?(key_ssh_type, host_key_alg)
|
|
68
|
+
return true if key_ssh_type == host_key_alg
|
|
69
|
+
return true if key_ssh_type == 'ssh-rsa' && ['rsa-sha2-512', 'rsa-sha2-256'].include?(host_key_alg)
|
|
70
|
+
end
|
|
71
|
+
|
|
67
72
|
# Verify that the given key is of the expected type, and that it
|
|
68
73
|
# really is the key for the session's host. Raise Net::SSH::Exception
|
|
69
74
|
# if it is not.
|
|
70
|
-
def verify_server_key(key)
|
|
71
|
-
|
|
75
|
+
def verify_server_key(key) # :nodoc:
|
|
76
|
+
unless matching?(key.ssh_type, algorithms.host_key)
|
|
72
77
|
raise Net::SSH::Exception, "host key algorithm mismatch '#{key.ssh_type}' != '#{algorithms.host_key}'"
|
|
73
78
|
end
|
|
74
79
|
|
|
@@ -92,12 +97,14 @@ module Net
|
|
|
92
97
|
# Verify the signature that was received. Raise Net::SSH::Exception
|
|
93
98
|
# if the signature could not be verified. Otherwise, return the new
|
|
94
99
|
# session-id.
|
|
95
|
-
def verify_signature(result)
|
|
100
|
+
def verify_signature(result) # :nodoc:
|
|
96
101
|
response = build_signature_buffer(result)
|
|
97
102
|
|
|
98
103
|
hash = digester.digest(response.to_s)
|
|
99
104
|
|
|
100
|
-
|
|
105
|
+
server_key = result[:server_key]
|
|
106
|
+
server_sig = result[:server_sig]
|
|
107
|
+
unless connection.host_key_verifier.verify_signature { server_key.ssh_do_verify(server_sig, hash, host_key: algorithms.host_key) }
|
|
101
108
|
raise Net::SSH::Exception, 'could not verify server signature'
|
|
102
109
|
end
|
|
103
110
|
|
|
@@ -106,7 +113,7 @@ module Net
|
|
|
106
113
|
|
|
107
114
|
# Send the NEWKEYS message, and expect the NEWKEYS message in
|
|
108
115
|
# reply.
|
|
109
|
-
def confirm_newkeys
|
|
116
|
+
def confirm_newkeys # :nodoc:
|
|
110
117
|
# send own NEWKEYS message first (the wodSSHServer won't send first)
|
|
111
118
|
response = Net::SSH::Buffer.new
|
|
112
119
|
response.write_byte(NEWKEYS)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
gem 'x25519' # raise if the gem x25519 is not installed
|
|
2
2
|
|
|
3
3
|
require 'x25519'
|
|
4
|
+
|
|
4
5
|
require 'net/ssh/transport/constants'
|
|
5
6
|
require 'net/ssh/transport/kex/abstract5656'
|
|
6
7
|
|
|
@@ -17,7 +18,7 @@ module Net
|
|
|
17
18
|
|
|
18
19
|
private
|
|
19
20
|
|
|
20
|
-
def generate_key
|
|
21
|
+
def generate_key # :nodoc:
|
|
21
22
|
::X25519::Scalar.generate
|
|
22
23
|
end
|
|
23
24
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require 'net/ssh/transport/kex/diffie_hellman_group1_sha1'
|
|
2
2
|
|
|
3
|
-
module Net
|
|
4
|
-
module SSH
|
|
5
|
-
module Transport
|
|
3
|
+
module Net
|
|
4
|
+
module SSH
|
|
5
|
+
module Transport
|
|
6
6
|
module Kex
|
|
7
7
|
# A key-exchange service implementing the "diffie-hellman-group14-sha1"
|
|
8
8
|
# key-exchange algorithm. (defined in RFC 4253)
|
|
@@ -24,7 +24,7 @@ module Net
|
|
|
24
24
|
"B5C55DF0" "6F4C52C9" "DE2BCBF6" "95581718" +
|
|
25
25
|
"3995497C" "EA956AE5" "15D22618" "98FA0510" +
|
|
26
26
|
"15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF"
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
# The radix in which P_s represents the value of P
|
|
29
29
|
P_r = 16
|
|
30
30
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'net/ssh/transport/kex/diffie_hellman_group14_sha1'
|
|
2
|
+
|
|
3
|
+
module Net::SSH::Transport::Kex
|
|
4
|
+
# A key-exchange service implementing the "diffie-hellman-group14-sha256"
|
|
5
|
+
# key-exchange algorithm.
|
|
6
|
+
class DiffieHellmanGroup14SHA256 < DiffieHellmanGroup14SHA1
|
|
7
|
+
def digester
|
|
8
|
+
OpenSSL::Digest::SHA256
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -59,26 +59,26 @@ module Net
|
|
|
59
59
|
|
|
60
60
|
# Generate a DH key with a private key consisting of the given
|
|
61
61
|
# number of bytes.
|
|
62
|
-
def generate_key
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
def generate_key # :nodoc:
|
|
63
|
+
p, g = get_parameters
|
|
64
|
+
|
|
65
|
+
asn1 = OpenSSL::ASN1::Sequence(
|
|
66
|
+
[
|
|
67
|
+
OpenSSL::ASN1::Integer(p),
|
|
68
|
+
OpenSSL::ASN1::Integer(g)
|
|
69
|
+
]
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
dh_params = OpenSSL::PKey::DH.new(asn1.to_der)
|
|
73
|
+
# XXX No private key size check! In theory the latter call should work but fails on OpenSSL 3.0 as
|
|
74
|
+
# dh_paramgen_subprime_len is now reserved for DHX algorithm
|
|
75
|
+
# key = OpenSSL::PKey.generate_key(dh_params, "dh_paramgen_subprime_len" => data[:need_bytes]/8)
|
|
76
|
+
if OpenSSL::PKey.respond_to?(:generate_key)
|
|
77
|
+
OpenSSL::PKey.generate_key(dh_params)
|
|
68
78
|
else
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
dh.generate_key!
|
|
73
|
-
until dh.valid? && dh.priv_key.num_bytes == data[:need_bytes]
|
|
74
|
-
if dh.respond_to?(:set_key)
|
|
75
|
-
dh.set_key(nil, OpenSSL::BN.rand(data[:need_bytes] * 8))
|
|
76
|
-
else
|
|
77
|
-
dh.priv_key = OpenSSL::BN.rand(data[:need_bytes] * 8)
|
|
78
|
-
end
|
|
79
|
-
dh.generate_key!
|
|
79
|
+
dh_params.generate_key!
|
|
80
|
+
dh_params
|
|
80
81
|
end
|
|
81
|
-
dh
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
# Send the KEXDH_INIT message, and expect the KEXDH_REPLY. Return the
|
|
@@ -86,7 +86,7 @@ module Net
|
|
|
86
86
|
#
|
|
87
87
|
# Parse the buffer from a KEXDH_REPLY message, returning a hash of
|
|
88
88
|
# the extracted values.
|
|
89
|
-
def send_kexinit
|
|
89
|
+
def send_kexinit # :nodoc:
|
|
90
90
|
init, reply = get_message_types
|
|
91
91
|
|
|
92
92
|
# send the KEXDH_INIT message
|
|
@@ -108,8 +108,8 @@ module Net
|
|
|
108
108
|
sig_type = sig_buffer.read_string
|
|
109
109
|
if sig_type != algorithms.host_key_format
|
|
110
110
|
raise Net::SSH::Exception,
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
"host key algorithm mismatch for signature " +
|
|
112
|
+
"'#{sig_type}' != '#{algorithms.host_key_format}'"
|
|
113
113
|
end
|
|
114
114
|
result[:server_sig] = sig_buffer.read_string
|
|
115
115
|
|
|
@@ -34,7 +34,7 @@ module Net::SSH::Transport::Kex
|
|
|
34
34
|
|
|
35
35
|
# request the DH key parameters for the given number of bits.
|
|
36
36
|
buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, data[:minimum_dh_bits],
|
|
37
|
-
|
|
37
|
+
:long, data[:need_bits], :long, MAXIMUM_BITS)
|
|
38
38
|
connection.send_message(buffer)
|
|
39
39
|
|
|
40
40
|
buffer = connection.next_message
|
|
@@ -69,5 +69,4 @@ module Net::SSH::Transport::Kex
|
|
|
69
69
|
response
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
|
-
|
|
73
72
|
end
|
|
@@ -17,8 +17,8 @@ module Net
|
|
|
17
17
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
|
-
def generate_key
|
|
21
|
-
OpenSSL::PKey::EC.
|
|
20
|
+
def generate_key # :nodoc:
|
|
21
|
+
OpenSSL::PKey::EC.generate(curve_name)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# compute shared secret from server's public key and client's private key
|