net-ssh 7.2.3 → 7.3.0.rc1

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