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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/FUNDING.yml +1 -0
  5. data/.github/config/rubocop_linter_action.yml +4 -0
  6. data/.github/workflows/ci-with-docker.yml +44 -0
  7. data/.github/workflows/ci.yml +94 -0
  8. data/.github/workflows/rubocop.yml +16 -0
  9. data/.gitignore +4 -0
  10. data/.rubocop.yml +12 -1
  11. data/.rubocop_todo.yml +475 -376
  12. data/CHANGES.txt +64 -3
  13. data/DEVELOPMENT.md +23 -0
  14. data/Dockerfile +29 -0
  15. data/Dockerfile.openssl3 +17 -0
  16. data/Gemfile +2 -0
  17. data/Gemfile.noed25519 +2 -0
  18. data/Gemfile.norbnacl +12 -0
  19. data/README.md +38 -22
  20. data/Rakefile +92 -0
  21. data/SECURITY.md +4 -0
  22. data/docker-compose.yml +25 -0
  23. data/lib/net/ssh/authentication/agent.rb +29 -13
  24. data/lib/net/ssh/authentication/certificate.rb +14 -11
  25. data/lib/net/ssh/authentication/constants.rb +0 -1
  26. data/lib/net/ssh/authentication/ed25519.rb +14 -11
  27. data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
  28. data/lib/net/ssh/authentication/key_manager.rb +65 -36
  29. data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
  30. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
  31. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
  32. data/lib/net/ssh/authentication/methods/none.rb +6 -9
  33. data/lib/net/ssh/authentication/methods/password.rb +2 -3
  34. data/lib/net/ssh/authentication/methods/publickey.rb +57 -17
  35. data/lib/net/ssh/authentication/pageant.rb +97 -97
  36. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +3 -3
  37. data/lib/net/ssh/authentication/session.rb +25 -17
  38. data/lib/net/ssh/buffer.rb +71 -51
  39. data/lib/net/ssh/buffered_io.rb +25 -26
  40. data/lib/net/ssh/config.rb +33 -20
  41. data/lib/net/ssh/connection/channel.rb +84 -82
  42. data/lib/net/ssh/connection/constants.rb +0 -4
  43. data/lib/net/ssh/connection/event_loop.rb +30 -24
  44. data/lib/net/ssh/connection/keepalive.rb +12 -12
  45. data/lib/net/ssh/connection/session.rb +109 -108
  46. data/lib/net/ssh/connection/term.rb +56 -58
  47. data/lib/net/ssh/errors.rb +12 -12
  48. data/lib/net/ssh/key_factory.rb +7 -8
  49. data/lib/net/ssh/known_hosts.rb +86 -18
  50. data/lib/net/ssh/loggable.rb +8 -9
  51. data/lib/net/ssh/packet.rb +1 -1
  52. data/lib/net/ssh/prompt.rb +9 -11
  53. data/lib/net/ssh/proxy/command.rb +1 -1
  54. data/lib/net/ssh/proxy/errors.rb +2 -4
  55. data/lib/net/ssh/proxy/http.rb +18 -20
  56. data/lib/net/ssh/proxy/https.rb +8 -10
  57. data/lib/net/ssh/proxy/jump.rb +8 -10
  58. data/lib/net/ssh/proxy/socks4.rb +2 -4
  59. data/lib/net/ssh/proxy/socks5.rb +3 -5
  60. data/lib/net/ssh/service/forward.rb +7 -7
  61. data/lib/net/ssh/test/channel.rb +24 -26
  62. data/lib/net/ssh/test/extensions.rb +35 -35
  63. data/lib/net/ssh/test/kex.rb +6 -8
  64. data/lib/net/ssh/test/local_packet.rb +0 -2
  65. data/lib/net/ssh/test/packet.rb +3 -3
  66. data/lib/net/ssh/test/remote_packet.rb +6 -8
  67. data/lib/net/ssh/test/script.rb +25 -27
  68. data/lib/net/ssh/test/socket.rb +12 -15
  69. data/lib/net/ssh/test.rb +4 -5
  70. data/lib/net/ssh/transport/aes128_gcm.rb +40 -0
  71. data/lib/net/ssh/transport/aes256_gcm.rb +40 -0
  72. data/lib/net/ssh/transport/algorithms.rb +51 -19
  73. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  74. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  75. data/lib/net/ssh/transport/cipher_factory.rb +56 -29
  76. data/lib/net/ssh/transport/constants.rb +3 -3
  77. data/lib/net/ssh/transport/ctr.rb +7 -7
  78. data/lib/net/ssh/transport/gcm_cipher.rb +207 -0
  79. data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
  80. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  81. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  82. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  83. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  84. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  85. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  86. data/lib/net/ssh/transport/hmac.rb +12 -12
  87. data/lib/net/ssh/transport/identity_cipher.rb +19 -13
  88. data/lib/net/ssh/transport/kex/abstract.rb +12 -5
  89. data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
  90. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
  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 +21 -21
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
  95. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
  96. data/lib/net/ssh/transport/kex.rb +8 -6
  97. data/lib/net/ssh/transport/key_expander.rb +7 -8
  98. data/lib/net/ssh/transport/openssl.rb +51 -26
  99. data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  100. data/lib/net/ssh/transport/packet_stream.rb +46 -26
  101. data/lib/net/ssh/transport/server_version.rb +17 -16
  102. data/lib/net/ssh/transport/session.rb +9 -7
  103. data/lib/net/ssh/transport/state.rb +44 -44
  104. data/lib/net/ssh/verifiers/accept_new.rb +0 -2
  105. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  106. data/lib/net/ssh/verifiers/always.rb +6 -4
  107. data/lib/net/ssh/verifiers/never.rb +0 -2
  108. data/lib/net/ssh/version.rb +2 -2
  109. data/lib/net/ssh.rb +15 -8
  110. data/net-ssh-public_cert.pem +19 -18
  111. data/net-ssh.gemspec +7 -4
  112. data/support/ssh_tunnel_bug.rb +3 -3
  113. data.tar.gz.sig +0 -0
  114. metadata +76 -29
  115. metadata.gz.sig +0 -0
  116. 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,12 +1,10 @@
1
1
  require 'net/ssh/transport/hmac/abstract'
2
2
 
3
3
  module Net::SSH::Transport::HMAC
4
-
5
4
  # The MD5 HMAC algorithm.
6
5
  class MD5 < Abstract
7
6
  mac_length 16
8
7
  key_length 16
9
8
  digest_class OpenSSL::Digest::MD5
10
9
  end
11
-
12
10
  end
@@ -1,11 +1,9 @@
1
1
  require 'net/ssh/transport/hmac/md5'
2
2
 
3
3
  module Net::SSH::Transport::HMAC
4
-
5
4
  # The MD5-96 HMAC algorithm. This returns only the first 12 bytes of
6
5
  # the digest.
7
6
  class MD5_96 < MD5
8
7
  mac_length 12
9
8
  end
10
-
11
9
  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
@@ -1,11 +1,9 @@
1
1
  require 'net/ssh/transport/hmac/sha1'
2
2
 
3
3
  module Net::SSH::Transport::HMAC
4
-
5
4
  # The SHA1-96 HMAC algorithm. This returns only the first 12 bytes of
6
5
  # the digest.
7
6
  class SHA1_96 < SHA1
8
7
  mac_length 12
9
8
  end
10
-
11
9
  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' => 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,
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' => RIPEMD160,
31
- 'hmac-ripemd160@openssh.com' => RIPEMD160,
32
- 'none' => 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) #:nodoc:
71
- if key.ssh_type != algorithms.host_key
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) #:nodoc:
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
- unless connection.host_key_verifier.verify_signature { result[:server_key].ssh_do_verify(result[:server_sig], hash) }
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 #:nodoc:
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)
@@ -32,7 +32,7 @@ module Net
32
32
  response
33
33
  end
34
34
 
35
- def send_kexinit #:nodoc:
35
+ def send_kexinit # :nodoc:
36
36
  init, reply = get_message_types
37
37
 
38
38
  # send the KEXECDH_INIT message
@@ -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 #:nodoc:
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 #:nodoc:
63
- dh = OpenSSL::PKey::DH.new
64
-
65
- if dh.respond_to?(:set_pqg)
66
- p, g = get_parameters
67
- dh.set_pqg(p, nil, g)
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
- dh.p, dh.g = get_parameters
70
- end
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 #:nodoc:
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
- "host key algorithm mismatch for signature " +
112
- "'#{sig_type}' != '#{algorithms.host_key_format}'"
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
- :long, data[:need_bits], :long, MAXIMUM_BITS)
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 #:nodoc:
21
- OpenSSL::PKey::EC.new(curve_name).generate_key
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