net-ssh 7.2.3 → 7.3.0.rc1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 524dafc8f2460b2e19662e19cb1916dc6b7f57b78b9cdc6ceadf486bc4fc1945
4
- data.tar.gz: 1f400ebf2e8622ab81b348c6db7d45d88dfd3c3bd04e61bc60ea8678482f5f11
3
+ metadata.gz: 5be7fdb8fff6f5929ae3fae446bed88d9d747ba8d946f50451ff02dc3bbed090
4
+ data.tar.gz: b0fd4cb80779368e07fa6c81f96e0f6270a40a29de1e6dcd3aba5d6a2a924990
5
5
  SHA512:
6
- metadata.gz: 00c33f37023f2cb493b231ee924db6be810fb8d3505d085160c6d790bcd928856081321b5743c2829e773ba506fee8aa3e5a5e9c94ec705199fa77458ad8c17f
7
- data.tar.gz: 195425e6e604b3dbe56cfabd2567b292cd40caa8affbd3f624901a2ddbcb024bacc052093722407a62070bfd3bacdce4c60aee6693a29ac20ad140ba2683fcc3
6
+ metadata.gz: 439ed07f9029410454211a9e91ea905a3614db275a9e4c46d8c9bcdfd88865be3ddc5ea83152b6347a139b6c6bf3b814306977e2dff94bffc029b4cc48782bac
7
+ data.tar.gz: d3e626ecec7614a33f9d534753212f6dd21640d6f4bde7d8ea32401427ee646b81d282f929b7d5231aea48aee64ace38d90fe07b068114bca7962bbc12f6c5f3
checksums.yaml.gz.sig CHANGED
Binary file
data/.rubocop_todo.yml CHANGED
@@ -235,7 +235,7 @@ Lint/UselessTimes:
235
235
  # Offense count: 205
236
236
  # Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
237
237
  Metrics/AbcSize:
238
- Max: 74
238
+ Max: 75
239
239
 
240
240
  # Offense count: 16
241
241
  # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
data/README.md CHANGED
@@ -274,6 +274,9 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
274
274
 
275
275
  [![Sponsor](https://opencollective.com/net-ssh/sponsor/0/avatar.svg)](https://opencollective.com/net-ssh/sponsor/0/website)
276
276
 
277
+ [<img src="https://github.com/net-ssh/net-ssh/assets/52435/9690bf3e-34ea-4c52-8aea-1cc4cb5bcb6d" width="320">](https://ubicloud.com)
278
+
279
+
277
280
  ## LICENSE:
278
281
 
279
282
  (The MIT License)
@@ -0,0 +1,40 @@
1
+ require 'net/ssh/transport/hmac/abstract'
2
+ require 'net/ssh/transport/gcm_cipher'
3
+
4
+ module Net::SSH::Transport
5
+ ## Implements the aes128-gcm@openssh cipher
6
+ class AES128_GCM
7
+ extend ::Net::SSH::Transport::GCMCipher
8
+
9
+ ## Implicit HMAC, do need to do anything
10
+ class ImplicitHMac < ::Net::SSH::Transport::HMAC::Abstract
11
+ def aead
12
+ true
13
+ end
14
+
15
+ def key_length
16
+ 16
17
+ end
18
+ end
19
+
20
+ def implicit_mac
21
+ ImplicitHMac.new
22
+ end
23
+
24
+ def algo_name
25
+ 'aes-128-gcm'
26
+ end
27
+
28
+ def name
29
+ 'aes128-gcm@openssh.com'
30
+ end
31
+
32
+ #
33
+ # --- RFC 5647 ---
34
+ # K_LEN AES key length 16 octets
35
+ #
36
+ def self.key_length
37
+ 16
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ require 'net/ssh/transport/hmac/abstract'
2
+ require 'net/ssh/transport/gcm_cipher'
3
+
4
+ module Net::SSH::Transport
5
+ ## Implements the aes256-gcm@openssh cipher
6
+ class AES256_GCM
7
+ extend ::Net::SSH::Transport::GCMCipher
8
+
9
+ ## Implicit HMAC, do need to do anything
10
+ class ImplicitHMac < ::Net::SSH::Transport::HMAC::Abstract
11
+ def aead
12
+ true
13
+ end
14
+
15
+ def key_length
16
+ 32
17
+ end
18
+ end
19
+
20
+ def implicit_mac
21
+ ImplicitHMac.new
22
+ end
23
+
24
+ def algo_name
25
+ 'aes-256-gcm'
26
+ end
27
+
28
+ def name
29
+ 'aes256-gcm@openssh.com'
30
+ end
31
+
32
+ #
33
+ # --- RFC 5647 ---
34
+ # K_LEN AES key length 32 octets
35
+ #
36
+ def self.key_length
37
+ 32
38
+ end
39
+ end
40
+ end
@@ -44,7 +44,11 @@ module Net
44
44
  diffie-hellman-group14-sha256
45
45
  diffie-hellman-group14-sha1],
46
46
 
47
- encryption: %w[aes256-ctr aes192-ctr aes128-ctr],
47
+ encryption: %w[aes256-ctr
48
+ aes192-ctr
49
+ aes128-ctr
50
+ aes256-gcm@openssh.com
51
+ aes128-gcm@openssh.com],
48
52
 
49
53
  hmac: %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
50
54
  hmac-sha2-512 hmac-sha2-256
@@ -492,6 +496,9 @@ module Net
492
496
  HMAC.get(hmac_server, mac_key_server, parameters)
493
497
  end
494
498
 
499
+ cipher_client.nonce = iv_client if mac_client.respond_to?(:aead) && mac_client.aead
500
+ cipher_server.nonce = iv_server if mac_server.respond_to?(:aead) && mac_client.aead
501
+
495
502
  session.configure_client cipher: cipher_client, hmac: mac_client,
496
503
  compression: normalize_compression_name(compression_client),
497
504
  compression_level: options[:compression_level],
@@ -1,5 +1,7 @@
1
1
  require 'openssl'
2
2
  require 'net/ssh/transport/ctr.rb'
3
+ require 'net/ssh/transport/aes128_gcm'
4
+ require 'net/ssh/transport/aes256_gcm'
3
5
  require 'net/ssh/transport/key_expander'
4
6
  require 'net/ssh/transport/identity_cipher'
5
7
  require 'net/ssh/transport/chacha20_poly1305_cipher_loader'
@@ -31,15 +33,15 @@ module Net
31
33
  'none' => 'none'
32
34
  }
33
35
 
34
- SSH_TO_CLASS =
36
+ SSH_TO_CLASS = {
37
+ 'aes256-gcm@openssh.com' => Net::SSH::Transport::AES256_GCM,
38
+ 'aes128-gcm@openssh.com' => Net::SSH::Transport::AES128_GCM
39
+ }.tap do |hash|
35
40
  if Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
36
- {
37
- 'chacha20-poly1305@openssh.com' => Net::SSH::Transport::ChaCha20Poly1305Cipher
38
- }
39
- else
40
- {
41
- }
41
+ hash['chacha20-poly1305@openssh.com'] =
42
+ Net::SSH::Transport::ChaCha20Poly1305Cipher
42
43
  end
44
+ end
43
45
 
44
46
  # Returns true if the underlying OpenSSL library supports the given cipher,
45
47
  # and false otherwise.
@@ -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
@@ -8,6 +8,18 @@ module Net
8
8
  # The base class of all OpenSSL-based HMAC algorithm wrappers.
9
9
  class Abstract
10
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
+
11
23
  def etm(*v)
12
24
  @etm = false if !defined?(@etm)
13
25
  if v.empty?
@@ -57,6 +69,10 @@ module Net
57
69
  end
58
70
  end
59
71
 
72
+ def aead
73
+ self.class.aead
74
+ end
75
+
60
76
  def etm
61
77
  self.class.etm
62
78
  end
@@ -128,7 +128,7 @@ module Net
128
128
  payload = client.compress(payload)
129
129
 
130
130
  # the length of the packet, minus the padding
131
- actual_length = (client.hmac.etm ? 0 : 4) + payload.bytesize + 1
131
+ actual_length = (client.hmac.etm || client.hmac.aead ? 0 : 4) + payload.bytesize + 1
132
132
 
133
133
  # compute the padding length
134
134
  padding_length = client.block_size - (actual_length % client.block_size)
@@ -151,7 +151,7 @@ module Net
151
151
  debug { "using encrypt-then-mac" }
152
152
 
153
153
  # Encrypt padding_length, payload, and padding. Take MAC
154
- # from the unencrypted packet_lenght and the encrypted
154
+ # from the unencrypted packet_length and the encrypted
155
155
  # data.
156
156
  length_data = [packet_length].pack("N")
157
157
 
@@ -219,7 +219,7 @@ module Net
219
219
  # new Packet object.
220
220
  # rubocop:disable Metrics/AbcSize
221
221
  def poll_next_packet
222
- aad_length = server.hmac.etm ? 4 : 0
222
+ aad_length = server.hmac.etm || server.hmac.aead ? 4 : 0
223
223
 
224
224
  if @packet.nil?
225
225
  minimum = server.block_size < 4 ? 4 : server.block_size
@@ -125,7 +125,7 @@ module Net
125
125
  compressor.deflate(data, Zlib::SYNC_FLUSH)
126
126
  end
127
127
 
128
- # Deompresses the data. If no compression is in effect, this will just return
128
+ # Decompresses the data. If no compression is in effect, this will just return
129
129
  # the data unmodified, otherwise it uses #decompressor to decompress the data.
130
130
  def decompress(data)
131
131
  data = data.to_s
@@ -49,14 +49,14 @@ module Net
49
49
  MAJOR = 7
50
50
 
51
51
  # The minor component of this version of the Net::SSH library
52
- MINOR = 2
52
+ MINOR = 3
53
53
 
54
54
  # The tiny component of this version of the Net::SSH library
55
- TINY = 3
55
+ TINY = 0
56
56
 
57
57
  # The prerelease component of this version of the Net::SSH library
58
58
  # nil allowed
59
- PRE = nil
59
+ PRE = "rc1"
60
60
 
61
61
  # The current version of the Net::SSH library as a Version instance
62
62
  CURRENT = new(*[MAJOR, MINOR, TINY, PRE].compact)
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-ssh
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.2.3
4
+ version: 7.3.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamis Buck
@@ -32,7 +32,7 @@ cert_chain:
32
32
  1uE/LmB/qWBVSNW8e9KDtJynhDDZBlpESyQHFQCZj6UapzxlnC46LaDncPoAtJPc
33
33
  MlWxJ8mKghIcyXc5y4cSyGypNG5BralqnvQUyg==
34
34
  -----END CERTIFICATE-----
35
- date: 2024-04-02 00:00:00.000000000 Z
35
+ date: 2024-06-12 00:00:00.000000000 Z
36
36
  dependencies:
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: bcrypt_pbkdf
@@ -258,12 +258,15 @@ files:
258
258
  - lib/net/ssh/test/remote_packet.rb
259
259
  - lib/net/ssh/test/script.rb
260
260
  - lib/net/ssh/test/socket.rb
261
+ - lib/net/ssh/transport/aes128_gcm.rb
262
+ - lib/net/ssh/transport/aes256_gcm.rb
261
263
  - lib/net/ssh/transport/algorithms.rb
262
264
  - lib/net/ssh/transport/chacha20_poly1305_cipher.rb
263
265
  - lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb
264
266
  - lib/net/ssh/transport/cipher_factory.rb
265
267
  - lib/net/ssh/transport/constants.rb
266
268
  - lib/net/ssh/transport/ctr.rb
269
+ - lib/net/ssh/transport/gcm_cipher.rb
267
270
  - lib/net/ssh/transport/hmac.rb
268
271
  - lib/net/ssh/transport/hmac/abstract.rb
269
272
  - lib/net/ssh/transport/hmac/md5.rb
@@ -323,9 +326,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
323
326
  version: '2.6'
324
327
  required_rubygems_version: !ruby/object:Gem::Requirement
325
328
  requirements:
326
- - - ">="
329
+ - - ">"
327
330
  - !ruby/object:Gem::Version
328
- version: '0'
331
+ version: 1.3.1
329
332
  requirements: []
330
333
  rubygems_version: 3.3.3
331
334
  signing_key:
metadata.gz.sig CHANGED
Binary file