net-ssh 8.0.0.rc4 → 8.0.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/.github/pull_request_template.md +1 -0
- data/.github/workflows/changelog.yml +43 -0
- data/CHANGES.txt +30 -0
- data/CONTRIBUTING.md +35 -0
- data/README.md +58 -50
- data/Rakefile +48 -0
- data/changelog.d/README.md +1 -0
- data/lib/net/ssh/authentication/agent.rb +4 -1
- data/lib/net/ssh/authentication/key_manager.rb +22 -12
- data/lib/net/ssh/authentication/security_key.rb +2 -1
- data/lib/net/ssh/buffer.rb +2 -1
- data/lib/net/ssh/config.rb +6 -0
- data/lib/net/ssh/connection/channel.rb +4 -1
- data/lib/net/ssh/connection/session.rb +7 -4
- data/lib/net/ssh/errors.rb +10 -0
- data/lib/net/ssh/known_hosts.rb +18 -8
- data/lib/net/ssh/multiplex/client.rb +162 -0
- data/lib/net/ssh/multiplex/control_path.rb +120 -0
- data/lib/net/ssh/multiplex/proxy_transport.rb +218 -0
- data/lib/net/ssh/multiplex.rb +130 -0
- data/lib/net/ssh/proxy/jump.rb +40 -2
- data/lib/net/ssh/test.rb +2 -1
- data/lib/net/ssh/transport/algorithms.rb +33 -7
- data/lib/net/ssh/transport/hmac/sha1_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac.rb +2 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +8 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +45 -15
- data/lib/net/ssh/transport/openssl.rb +2 -1
- data/lib/net/ssh/transport/session.rb +17 -44
- data/lib/net/ssh/verifiers/always.rb +0 -3
- data/lib/net/ssh/version.rb +1 -1
- data/lib/net/ssh.rb +125 -51
- data/net-ssh.gemspec +1 -1
- data/rakelib/changelog.rb +229 -0
- data.tar.gz.sig +0 -0
- metadata +24 -8
- metadata.gz.sig +1 -2
data/lib/net/ssh/test.rb
CHANGED
|
@@ -73,7 +73,8 @@ module Net
|
|
|
73
73
|
def transport(options = {})
|
|
74
74
|
@transport ||= Net::SSH::Transport::Session.new(
|
|
75
75
|
options[:host] || "localhost",
|
|
76
|
-
options.merge(kex: "test", host_key: "ssh-rsa",
|
|
76
|
+
options.merge(kex: "test", host_key: "ssh-rsa", encryption: "+none", hmac: "+none",
|
|
77
|
+
append_all_supported_algorithms: true, verify_host_key: :never, proxy: socket(options))
|
|
77
78
|
)
|
|
78
79
|
end
|
|
79
80
|
|
|
@@ -39,8 +39,7 @@ module Net
|
|
|
39
39
|
ecdh-sha2-nistp384
|
|
40
40
|
ecdh-sha2-nistp256
|
|
41
41
|
diffie-hellman-group-exchange-sha256
|
|
42
|
-
diffie-hellman-group14-sha256
|
|
43
|
-
diffie-hellman-group14-sha1],
|
|
42
|
+
diffie-hellman-group14-sha256],
|
|
44
43
|
|
|
45
44
|
encryption: %w[aes256-ctr
|
|
46
45
|
aes192-ctr
|
|
@@ -49,6 +48,7 @@ module Net
|
|
|
49
48
|
aes128-gcm@openssh.com],
|
|
50
49
|
|
|
51
50
|
hmac: %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
|
|
51
|
+
hmac-sha1-etm@openssh.com
|
|
52
52
|
hmac-sha2-512 hmac-sha2-256
|
|
53
53
|
hmac-sha1],
|
|
54
54
|
|
|
@@ -87,7 +87,8 @@ module Net
|
|
|
87
87
|
%w[ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-rsa ssh-dss],
|
|
88
88
|
|
|
89
89
|
kex: DEFAULT_ALGORITHMS[:kex] +
|
|
90
|
-
%w[diffie-hellman-
|
|
90
|
+
%w[diffie-hellman-group14-sha1
|
|
91
|
+
diffie-hellman-group-exchange-sha1
|
|
91
92
|
diffie-hellman-group1-sha1],
|
|
92
93
|
|
|
93
94
|
encryption: DEFAULT_ALGORITHMS[:encryption] +
|
|
@@ -112,6 +113,14 @@ module Net
|
|
|
112
113
|
language: %w[]
|
|
113
114
|
}.freeze
|
|
114
115
|
|
|
116
|
+
# Not added by append_all_supported_algorithms; only used when named explicitly.
|
|
117
|
+
EXPLICIT_ONLY_ALGORITHMS = {
|
|
118
|
+
encryption: %w[none],
|
|
119
|
+
hmac: %w[none]
|
|
120
|
+
}.freeze
|
|
121
|
+
|
|
122
|
+
DEPRECATED_HOST_KEY = 'ssh-rsa-cert-v00@openssh.com'.freeze
|
|
123
|
+
|
|
115
124
|
# The underlying transport layer session that supports this object
|
|
116
125
|
attr_reader :session
|
|
117
126
|
|
|
@@ -282,10 +291,13 @@ module Net
|
|
|
282
291
|
supported,
|
|
283
292
|
DEFAULT_ALGORITHMS[algorithm],
|
|
284
293
|
options[algorithm] || DEFAULT_ALGORITHMS[algorithm],
|
|
285
|
-
options[:append_all_supported_algorithms]
|
|
294
|
+
options[:append_all_supported_algorithms],
|
|
295
|
+
supported - EXPLICIT_ONLY_ALGORITHMS.fetch(algorithm, [])
|
|
286
296
|
)
|
|
287
297
|
end
|
|
288
298
|
|
|
299
|
+
warn_deprecated_host_key if host_key_explicitly_named?(DEPRECATED_HOST_KEY)
|
|
300
|
+
|
|
289
301
|
# for convention, make sure our list has the same keys as the server
|
|
290
302
|
# list
|
|
291
303
|
|
|
@@ -312,7 +324,7 @@ module Net
|
|
|
312
324
|
end
|
|
313
325
|
|
|
314
326
|
# Composes the list of algorithms by taking supported algorithms and matching with supplied options.
|
|
315
|
-
def compose_algorithm_list(supported, defaults, option, append_all_supported_algorithms = false)
|
|
327
|
+
def compose_algorithm_list(supported, defaults, option, append_all_supported_algorithms = false, appendable = supported)
|
|
316
328
|
return supported.dup unless option
|
|
317
329
|
|
|
318
330
|
list = []
|
|
@@ -330,7 +342,7 @@ module Net
|
|
|
330
342
|
end
|
|
331
343
|
|
|
332
344
|
list = ops['^'] + defaults + ops['+']
|
|
333
|
-
list.concat(
|
|
345
|
+
list.concat(appendable) if append_all_supported_algorithms
|
|
334
346
|
|
|
335
347
|
ops['-'].each do |opt|
|
|
336
348
|
if opt.include?('*')
|
|
@@ -347,7 +359,7 @@ module Net
|
|
|
347
359
|
list = option
|
|
348
360
|
|
|
349
361
|
if append_all_supported_algorithms
|
|
350
|
-
|
|
362
|
+
appendable.each { |name| list << name unless list.include?(name) }
|
|
351
363
|
end
|
|
352
364
|
end
|
|
353
365
|
|
|
@@ -363,6 +375,19 @@ module Net
|
|
|
363
375
|
list
|
|
364
376
|
end
|
|
365
377
|
|
|
378
|
+
def host_key_explicitly_named?(name)
|
|
379
|
+
algorithms[:host_key].include?(name) &&
|
|
380
|
+
Array(options[:host_key]).any? { |opt| opt.to_s.delete_prefix('+').delete_prefix('^') == name }
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def warn_deprecated_host_key
|
|
384
|
+
return if @deprecated_host_key_warned
|
|
385
|
+
|
|
386
|
+
@deprecated_host_key_warned = true
|
|
387
|
+
Kernel.warn("#{DEPRECATED_HOST_KEY} is deprecated and will be removed in a future version; " \
|
|
388
|
+
"net-ssh cannot parse v00 certificates, use ssh-rsa-cert-v01@openssh.com or a modern host key type instead")
|
|
389
|
+
end
|
|
390
|
+
|
|
366
391
|
# Parses a KEXINIT packet from the server.
|
|
367
392
|
def parse_server_algorithm_packet(packet)
|
|
368
393
|
data = { raw: packet.content }
|
|
@@ -420,6 +445,7 @@ module Net
|
|
|
420
445
|
def negotiate_algorithms
|
|
421
446
|
@kex = negotiate(:kex)
|
|
422
447
|
@host_key = negotiate(:host_key)
|
|
448
|
+
warn_deprecated_host_key if @host_key == DEPRECATED_HOST_KEY
|
|
423
449
|
@encryption_client = negotiate(:encryption_client)
|
|
424
450
|
@encryption_server = negotiate(:encryption_server)
|
|
425
451
|
@hmac_client = negotiate(:hmac_client)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'net/ssh/transport/hmac/abstract'
|
|
2
|
+
|
|
3
|
+
module Net::SSH::Transport::HMAC
|
|
4
|
+
# The SHA1 Encrypt-Then-Mac HMAC algorithm. This has a mac and
|
|
5
|
+
# key length of 20, and uses the SHA1 digest algorithm.
|
|
6
|
+
class SHA1_Etm < Abstract
|
|
7
|
+
etm true
|
|
8
|
+
mac_length 20
|
|
9
|
+
key_length 20
|
|
10
|
+
digest_class OpenSSL::Digest::SHA1
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -9,6 +9,7 @@ require 'net/ssh/transport/hmac/sha2_512'
|
|
|
9
9
|
require 'net/ssh/transport/hmac/sha2_512_96'
|
|
10
10
|
require 'net/ssh/transport/hmac/sha2_256_etm'
|
|
11
11
|
require 'net/ssh/transport/hmac/sha2_512_etm'
|
|
12
|
+
require 'net/ssh/transport/hmac/sha1_etm'
|
|
12
13
|
require 'net/ssh/transport/hmac/ripemd160'
|
|
13
14
|
require 'net/ssh/transport/hmac/none'
|
|
14
15
|
|
|
@@ -27,6 +28,7 @@ module Net::SSH::Transport::HMAC
|
|
|
27
28
|
'hmac-sha2-512-96' => SHA2_512_96,
|
|
28
29
|
'hmac-sha2-256-etm@openssh.com' => SHA2_256_Etm,
|
|
29
30
|
'hmac-sha2-512-etm@openssh.com' => SHA2_512_Etm,
|
|
31
|
+
'hmac-sha1-etm@openssh.com' => SHA1_Etm,
|
|
30
32
|
'hmac-ripemd160' => RIPEMD160,
|
|
31
33
|
'hmac-ripemd160@openssh.com' => RIPEMD160,
|
|
32
34
|
'none' => None
|
|
@@ -102,6 +102,7 @@ module Net
|
|
|
102
102
|
result[:key_blob] = buffer.read_string
|
|
103
103
|
result[:server_key] = Net::SSH::Buffer.new(result[:key_blob]).read_key
|
|
104
104
|
result[:server_dh_pubkey] = buffer.read_bignum
|
|
105
|
+
validate_server_dh_pubkey(result[:server_dh_pubkey])
|
|
105
106
|
result[:shared_secret] = OpenSSL::BN.new(dh.compute_key(result[:server_dh_pubkey]), 2)
|
|
106
107
|
|
|
107
108
|
sig_buffer = Net::SSH::Buffer.new(buffer.read_string)
|
|
@@ -115,6 +116,13 @@ module Net
|
|
|
115
116
|
|
|
116
117
|
return result
|
|
117
118
|
end
|
|
119
|
+
|
|
120
|
+
# Rejects degenerate values that would let the server force a
|
|
121
|
+
# predictable shared secret, as OpenSSH's dh_pub_is_valid.
|
|
122
|
+
def validate_server_dh_pubkey(pub)
|
|
123
|
+
valid = pub > 1 && pub < dh.p - 1 && pub.to_i.to_s(2).count("1") >= 4
|
|
124
|
+
raise Net::SSH::Exception, "server's DH public value is invalid" unless valid
|
|
125
|
+
end
|
|
118
126
|
end
|
|
119
127
|
end
|
|
120
128
|
end
|
|
@@ -6,26 +6,43 @@ module Net::SSH::Transport::Kex
|
|
|
6
6
|
# A key-exchange service implementing the
|
|
7
7
|
# "diffie-hellman-group-exchange-sha1" key-exchange algorithm.
|
|
8
8
|
class DiffieHellmanGroupExchangeSHA1 < DiffieHellmanGroup1SHA1
|
|
9
|
-
MINIMUM_BITS =
|
|
9
|
+
MINIMUM_BITS = 2048
|
|
10
10
|
MAXIMUM_BITS = 8192
|
|
11
|
+
CISCO_MAXIMUM_NEED_BITS = 4096
|
|
11
12
|
|
|
12
13
|
private
|
|
13
14
|
|
|
14
|
-
#
|
|
15
|
+
# Computes the minimum, preferred and maximum group sizes to request,
|
|
16
|
+
# like OpenSSH's kexgex_client.
|
|
15
17
|
def compute_need_bits
|
|
16
|
-
# for Compatibility: OpenSSH requires (need_bits * 2 + 1) length of parameter
|
|
17
|
-
need_bits = data[:need_bytes] * 8 * 2 + 1
|
|
18
|
-
|
|
19
18
|
data[:minimum_dh_bits] ||= MINIMUM_BITS
|
|
19
|
+
data[:maximum_dh_bits] = [MAXIMUM_BITS, data[:minimum_dh_bits]].max
|
|
20
|
+
data[:need_bits] = estimate_group_bits(data[:need_bytes] * 8).clamp(data[:minimum_dh_bits], maximum_need_bits)
|
|
21
|
+
end
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
# Some Cisco servers fail instead of returning their 4096-bit group when asked for more (OpenSSH bz#2209).
|
|
24
|
+
def maximum_need_bits
|
|
25
|
+
return data[:maximum_dh_bits] unless cisco_server?
|
|
26
|
+
|
|
27
|
+
[CISCO_MAXIMUM_NEED_BITS, data[:minimum_dh_bits]].max
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def cisco_server?
|
|
31
|
+
data[:server_version_string].to_s.match?(/\ASSH-[\d.]+-Cisco-1\./)
|
|
32
|
+
end
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
# Group size giving at least the given bits of security (NIST SP 800-57),
|
|
35
|
+
# as OpenSSH's dh_estimate.
|
|
36
|
+
def estimate_group_bits(security_bits)
|
|
37
|
+
if security_bits <= 112
|
|
38
|
+
2048
|
|
39
|
+
elsif security_bits <= 128
|
|
40
|
+
3072
|
|
41
|
+
elsif security_bits <= 192
|
|
42
|
+
7680
|
|
43
|
+
else
|
|
44
|
+
8192
|
|
45
|
+
end
|
|
29
46
|
end
|
|
30
47
|
|
|
31
48
|
# Returns the DH key parameters for the given session.
|
|
@@ -34,7 +51,7 @@ module Net::SSH::Transport::Kex
|
|
|
34
51
|
|
|
35
52
|
# request the DH key parameters for the given number of bits.
|
|
36
53
|
buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, data[:minimum_dh_bits],
|
|
37
|
-
:long, data[:need_bits], :long,
|
|
54
|
+
:long, data[:need_bits], :long, data[:maximum_dh_bits])
|
|
38
55
|
connection.send_message(buffer)
|
|
39
56
|
|
|
40
57
|
buffer = connection.next_message
|
|
@@ -43,9 +60,22 @@ module Net::SSH::Transport::Kex
|
|
|
43
60
|
p = buffer.read_bignum
|
|
44
61
|
g = buffer.read_bignum
|
|
45
62
|
|
|
63
|
+
validate_group(p, g)
|
|
64
|
+
|
|
46
65
|
[p, g]
|
|
47
66
|
end
|
|
48
67
|
|
|
68
|
+
def validate_group(prime, generator)
|
|
69
|
+
bits = prime.num_bits
|
|
70
|
+
if bits < data[:minimum_dh_bits]
|
|
71
|
+
raise Net::SSH::Exception,
|
|
72
|
+
"server's DH group is #{bits} bits, below the minimum of #{data[:minimum_dh_bits]}; " \
|
|
73
|
+
"pass minimum_dh_bits: #{bits} to allow it"
|
|
74
|
+
end
|
|
75
|
+
raise Net::SSH::Exception, "server's DH group is #{bits} bits, above the maximum of #{data[:maximum_dh_bits]}" if bits > data[:maximum_dh_bits]
|
|
76
|
+
raise Net::SSH::Exception, "server's DH group generator is invalid" unless generator > 1 && generator < prime - 1
|
|
77
|
+
end
|
|
78
|
+
|
|
49
79
|
# Returns the INIT/REPLY constants used by this algorithm.
|
|
50
80
|
def get_message_types
|
|
51
81
|
[KEXDH_GEX_INIT, KEXDH_GEX_REPLY]
|
|
@@ -60,9 +90,9 @@ module Net::SSH::Transport::Kex
|
|
|
60
90
|
data[:client_algorithm_packet],
|
|
61
91
|
data[:server_algorithm_packet],
|
|
62
92
|
result[:key_blob]
|
|
63
|
-
response.write_long
|
|
93
|
+
response.write_long data[:minimum_dh_bits],
|
|
64
94
|
data[:need_bits],
|
|
65
|
-
|
|
95
|
+
data[:maximum_dh_bits]
|
|
66
96
|
response.write_bignum dh.p, dh.g, dh.pub_key,
|
|
67
97
|
result[:server_dh_pubkey],
|
|
68
98
|
result[:shared_secret]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'openssl'
|
|
2
|
+
require 'net/ssh/errors'
|
|
2
3
|
require 'net/ssh/authentication/pub_key_fingerprint'
|
|
3
4
|
|
|
4
5
|
module OpenSSL
|
|
@@ -178,7 +179,7 @@ module OpenSSL
|
|
|
178
179
|
|
|
179
180
|
return key
|
|
180
181
|
rescue OpenSSL::PKey::ECError
|
|
181
|
-
raise
|
|
182
|
+
raise Net::SSH::UnsupportedKeyType, "unsupported key type `ecdsa-sha2-#{curve_name_in_type}'"
|
|
182
183
|
end
|
|
183
184
|
end
|
|
184
185
|
|
|
@@ -317,22 +317,12 @@ module Net
|
|
|
317
317
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
318
318
|
end
|
|
319
319
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
def verify(arguments)
|
|
329
|
-
@verifier.verify(arguments)
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
def verify_signature(&block)
|
|
333
|
-
yield
|
|
334
|
-
end
|
|
335
|
-
end
|
|
320
|
+
REMOVED_VERIFY_HOST_KEY_VALUES = {
|
|
321
|
+
false => :never,
|
|
322
|
+
true => :accept_new_or_local_tunnel,
|
|
323
|
+
very: :accept_new,
|
|
324
|
+
secure: :always
|
|
325
|
+
}.freeze
|
|
336
326
|
|
|
337
327
|
# Instantiates a new host-key verification class, based on the value of
|
|
338
328
|
# the parameter.
|
|
@@ -347,46 +337,29 @@ module Net
|
|
|
347
337
|
#
|
|
348
338
|
# If the argument happens to respond to :verify and :verify_signature,
|
|
349
339
|
# it is returned directly. Otherwise, an exception is raised.
|
|
350
|
-
#
|
|
351
|
-
# Values false, true, and :very were deprecated in
|
|
352
|
-
# [#595](https://github.com/net-ssh/net-ssh/pull/595)
|
|
353
340
|
def select_host_key_verifier(verifier)
|
|
354
341
|
case verifier
|
|
355
|
-
when false
|
|
356
|
-
Kernel.warn('verify_host_key: false is deprecated, use :never')
|
|
357
|
-
Net::SSH::Verifiers::Never.new
|
|
358
342
|
when :never then
|
|
359
343
|
Net::SSH::Verifiers::Never.new
|
|
360
|
-
when true
|
|
361
|
-
Kernel.warn('verify_host_key: true is deprecated, use :accept_new_or_local_tunnel')
|
|
362
|
-
Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
|
|
363
344
|
when :accept_new_or_local_tunnel, nil then
|
|
364
345
|
Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
|
|
365
|
-
when :very
|
|
366
|
-
Kernel.warn('verify_host_key: :very is deprecated, use :accept_new')
|
|
367
|
-
Net::SSH::Verifiers::AcceptNew.new
|
|
368
346
|
when :accept_new then
|
|
369
347
|
Net::SSH::Verifiers::AcceptNew.new
|
|
370
|
-
when :secure then
|
|
371
|
-
Kernel.warn('verify_host_key: :secure is deprecated, use :always')
|
|
372
|
-
Net::SSH::Verifiers::Always.new
|
|
373
348
|
when :always then
|
|
374
349
|
Net::SSH::Verifiers::Always.new
|
|
350
|
+
when *REMOVED_VERIFY_HOST_KEY_VALUES.keys
|
|
351
|
+
raise ArgumentError,
|
|
352
|
+
"verify_host_key: #{verifier.inspect} has been removed, use #{REMOVED_VERIFY_HOST_KEY_VALUES[verifier].inspect}"
|
|
375
353
|
else
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
end
|
|
383
|
-
else
|
|
384
|
-
raise(
|
|
385
|
-
ArgumentError,
|
|
386
|
-
"Invalid argument to :verify_host_key (or deprecated " \
|
|
387
|
-
":paranoid): #{verifier.inspect}"
|
|
388
|
-
)
|
|
354
|
+
raise ArgumentError, "Invalid argument to :verify_host_key: #{verifier.inspect}" unless verifier.respond_to?(:verify)
|
|
355
|
+
|
|
356
|
+
unless verifier.respond_to?(:verify_signature)
|
|
357
|
+
raise ArgumentError,
|
|
358
|
+
"verify_host_key verifier #{verifier.inspect} must implement verify_signature(&block): " \
|
|
359
|
+
"yield to the block, which checks the server's host key signature, and return its result"
|
|
389
360
|
end
|
|
361
|
+
|
|
362
|
+
verifier
|
|
390
363
|
end
|
|
391
364
|
end
|
|
392
365
|
end
|
|
@@ -51,9 +51,6 @@ module Net
|
|
|
51
51
|
# implement them (Net::SSH::HostKeyEntries::CertAuthority). Non-certificate
|
|
52
52
|
# entries do not respond to these predicates and are left untouched.
|
|
53
53
|
def verify_certificate!(entry, host_keys, arguments)
|
|
54
|
-
# Plain keys carry no certificate constraints, and respond_to? on this delegator prints a deprecation warning (#1026).
|
|
55
|
-
return if entry.is_a?(Net::SSH::HostKeyEntries::PubKey)
|
|
56
|
-
|
|
57
54
|
cert = arguments[:key]
|
|
58
55
|
|
|
59
56
|
if entry.respond_to?(:matches_validity?) && !entry.matches_validity?(cert)
|
data/lib/net/ssh/version.rb
CHANGED