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,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
|
|
@@ -33,21 +33,33 @@ module Net
|
|
|
33
33
|
ecdsa-sha2-nistp256
|
|
34
34
|
ssh-rsa-cert-v01@openssh.com
|
|
35
35
|
ssh-rsa-cert-v00@openssh.com
|
|
36
|
-
ssh-rsa
|
|
36
|
+
ssh-rsa
|
|
37
|
+
rsa-sha2-256
|
|
38
|
+
rsa-sha2-512],
|
|
37
39
|
|
|
38
40
|
kex: %w[ecdh-sha2-nistp521
|
|
39
41
|
ecdh-sha2-nistp384
|
|
40
42
|
ecdh-sha2-nistp256
|
|
41
43
|
diffie-hellman-group-exchange-sha256
|
|
44
|
+
diffie-hellman-group14-sha256
|
|
42
45
|
diffie-hellman-group14-sha1],
|
|
43
46
|
|
|
44
|
-
encryption: %w[aes256-ctr
|
|
47
|
+
encryption: %w[aes256-ctr
|
|
48
|
+
aes192-ctr
|
|
49
|
+
aes128-ctr
|
|
50
|
+
aes256-gcm@openssh.com
|
|
51
|
+
aes128-gcm@openssh.com],
|
|
45
52
|
|
|
46
53
|
hmac: %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
|
|
47
54
|
hmac-sha2-512 hmac-sha2-256
|
|
48
55
|
hmac-sha1]
|
|
49
56
|
}.freeze
|
|
50
57
|
|
|
58
|
+
if Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
|
|
59
|
+
DEFAULT_ALGORITHMS[:encryption].unshift(
|
|
60
|
+
'chacha20-poly1305@openssh.com'
|
|
61
|
+
)
|
|
62
|
+
end
|
|
51
63
|
if Net::SSH::Authentication::ED25519Loader::LOADED
|
|
52
64
|
DEFAULT_ALGORITHMS[:host_key].unshift(
|
|
53
65
|
'ssh-ed25519-cert-v01@openssh.com',
|
|
@@ -143,7 +155,7 @@ module Net
|
|
|
143
155
|
|
|
144
156
|
# Instantiates a new Algorithms object, and prepares the hash of preferred
|
|
145
157
|
# algorithms based on the options parameter and the ALGORITHMS constant.
|
|
146
|
-
def initialize(session, options={})
|
|
158
|
+
def initialize(session, options = {})
|
|
147
159
|
@session = session
|
|
148
160
|
@logger = session.logger
|
|
149
161
|
@options = options
|
|
@@ -275,7 +287,7 @@ module Net
|
|
|
275
287
|
# existing known key for the host has preference.
|
|
276
288
|
|
|
277
289
|
existing_keys = session.host_keys
|
|
278
|
-
host_keys = existing_keys.
|
|
290
|
+
host_keys = existing_keys.flat_map { |key| key.respond_to?(:ssh_types) ? key.ssh_types : [key.ssh_type] }.uniq
|
|
279
291
|
algorithms[:host_key].each do |name|
|
|
280
292
|
host_keys << name unless host_keys.include?(name)
|
|
281
293
|
end
|
|
@@ -366,10 +378,10 @@ module Net
|
|
|
366
378
|
language = algorithms[:language].join(",")
|
|
367
379
|
|
|
368
380
|
Net::SSH::Buffer.from(:byte, KEXINIT,
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
381
|
+
:long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)],
|
|
382
|
+
:mstring, [kex, host_key, encryption, encryption, hmac, hmac],
|
|
383
|
+
:mstring, [compression, compression, language, language],
|
|
384
|
+
:bool, false, :long, 0)
|
|
373
385
|
end
|
|
374
386
|
|
|
375
387
|
# Given the parsed server KEX packet, and the client's preferred algorithm
|
|
@@ -434,14 +446,15 @@ module Net
|
|
|
434
446
|
def exchange_keys
|
|
435
447
|
debug { "exchanging keys" }
|
|
436
448
|
|
|
449
|
+
need_bytes = kex_byte_requirement
|
|
437
450
|
algorithm = Kex::MAP[kex].new(self, session,
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
451
|
+
client_version_string: Net::SSH::Transport::ServerVersion::PROTO_VERSION,
|
|
452
|
+
server_version_string: session.server_version.version,
|
|
453
|
+
server_algorithm_packet: @server_packet,
|
|
454
|
+
client_algorithm_packet: @client_packet,
|
|
455
|
+
need_bytes: need_bytes,
|
|
456
|
+
minimum_dh_bits: options[:minimum_dh_bits],
|
|
457
|
+
logger: logger)
|
|
445
458
|
result = algorithm.exchange_keys
|
|
446
459
|
|
|
447
460
|
secret = result[:shared_secret].to_ssh
|
|
@@ -461,11 +474,30 @@ module Net
|
|
|
461
474
|
|
|
462
475
|
parameters = { shared: secret, hash: hash, digester: digester }
|
|
463
476
|
|
|
464
|
-
cipher_client = CipherFactory.get(
|
|
465
|
-
|
|
477
|
+
cipher_client = CipherFactory.get(
|
|
478
|
+
encryption_client,
|
|
479
|
+
parameters.merge(iv: iv_client, key: key_client, encrypt: true)
|
|
480
|
+
)
|
|
481
|
+
cipher_server = CipherFactory.get(
|
|
482
|
+
encryption_server,
|
|
483
|
+
parameters.merge(iv: iv_server, key: key_server, decrypt: true)
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
mac_client =
|
|
487
|
+
if cipher_client.implicit_mac?
|
|
488
|
+
cipher_client.implicit_mac
|
|
489
|
+
else
|
|
490
|
+
HMAC.get(hmac_client, mac_key_client, parameters)
|
|
491
|
+
end
|
|
492
|
+
mac_server =
|
|
493
|
+
if cipher_server.implicit_mac?
|
|
494
|
+
cipher_server.implicit_mac
|
|
495
|
+
else
|
|
496
|
+
HMAC.get(hmac_server, mac_key_server, parameters)
|
|
497
|
+
end
|
|
466
498
|
|
|
467
|
-
|
|
468
|
-
|
|
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
|
|
469
501
|
|
|
470
502
|
session.configure_client cipher: cipher_client, hmac: mac_client,
|
|
471
503
|
compression: normalize_compression_name(compression_client),
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require 'rbnacl'
|
|
2
|
+
require 'net/ssh/loggable'
|
|
3
|
+
|
|
4
|
+
module Net
|
|
5
|
+
module SSH
|
|
6
|
+
module Transport
|
|
7
|
+
## Implements the chacha20-poly1305@openssh cipher
|
|
8
|
+
class ChaCha20Poly1305Cipher
|
|
9
|
+
include Net::SSH::Loggable
|
|
10
|
+
|
|
11
|
+
# Implicit HMAC, no need to do anything
|
|
12
|
+
class ImplicitHMac
|
|
13
|
+
def etm
|
|
14
|
+
# TODO: ideally this shouln't be called
|
|
15
|
+
true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def key_length
|
|
19
|
+
64
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(encrypt:, key:)
|
|
24
|
+
@chacha_hdr = OpenSSL::Cipher.new("chacha20")
|
|
25
|
+
key_len = @chacha_hdr.key_len
|
|
26
|
+
@chacha_main = OpenSSL::Cipher.new("chacha20")
|
|
27
|
+
@poly = RbNaCl::OneTimeAuths::Poly1305
|
|
28
|
+
if key.size < key_len * 2
|
|
29
|
+
error { "chacha20_poly1305: keylength doesn't match" }
|
|
30
|
+
raise "chacha20_poly1305: keylength doesn't match"
|
|
31
|
+
end
|
|
32
|
+
if encrypt
|
|
33
|
+
@chacha_hdr.encrypt
|
|
34
|
+
@chacha_main.encrypt
|
|
35
|
+
else
|
|
36
|
+
@chacha_hdr.decrypt
|
|
37
|
+
@chacha_main.decrypt
|
|
38
|
+
end
|
|
39
|
+
main_key = key[0...key_len]
|
|
40
|
+
@chacha_main.key = main_key
|
|
41
|
+
hdr_key = key[key_len...(2 * key_len)]
|
|
42
|
+
@chacha_hdr.key = hdr_key
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def update_cipher_mac(payload, sequence_number)
|
|
46
|
+
iv_data = [0, 0, 0, sequence_number].pack("NNNN")
|
|
47
|
+
@chacha_main.iv = iv_data
|
|
48
|
+
poly_key = @chacha_main.update(([0] * 32).pack('C32'))
|
|
49
|
+
|
|
50
|
+
packet_length = payload.size
|
|
51
|
+
length_data = [packet_length].pack("N")
|
|
52
|
+
@chacha_hdr.iv = iv_data
|
|
53
|
+
packet = @chacha_hdr.update(length_data)
|
|
54
|
+
|
|
55
|
+
iv_data[0] = 1.chr
|
|
56
|
+
@chacha_main.iv = iv_data
|
|
57
|
+
unencrypted_data = payload
|
|
58
|
+
packet += @chacha_main.update(unencrypted_data)
|
|
59
|
+
|
|
60
|
+
packet += @poly.auth(poly_key, packet)
|
|
61
|
+
return packet
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def read_length(data, sequence_number)
|
|
65
|
+
iv_data = [0, 0, 0, sequence_number].pack("NNNN")
|
|
66
|
+
@chacha_hdr.iv = iv_data
|
|
67
|
+
@chacha_hdr.update(data).unpack1("N")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def read_and_mac(data, mac, sequence_number)
|
|
71
|
+
iv_data = [0, 0, 0, sequence_number].pack("NNNN")
|
|
72
|
+
@chacha_main.iv = iv_data
|
|
73
|
+
poly_key = @chacha_main.update(([0] * 32).pack('C32'))
|
|
74
|
+
|
|
75
|
+
iv_data[0] = 1.chr
|
|
76
|
+
@chacha_main.iv = iv_data
|
|
77
|
+
unencrypted_data = @chacha_main.update(data[4..])
|
|
78
|
+
begin
|
|
79
|
+
ok = @poly.verify(poly_key, mac, data[0..])
|
|
80
|
+
raise Net::SSH::Exception, "corrupted hmac detected #{name}" unless ok
|
|
81
|
+
rescue RbNaCl::BadAuthenticatorError
|
|
82
|
+
raise Net::SSH::Exception, "corrupted hmac detected #{name}"
|
|
83
|
+
end
|
|
84
|
+
return unencrypted_data
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def mac_length
|
|
88
|
+
16
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def block_size
|
|
92
|
+
8
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def name
|
|
96
|
+
"chacha20-poly1305@openssh.com"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def implicit_mac?
|
|
100
|
+
true
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def implicit_mac
|
|
104
|
+
return ImplicitHMac.new
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def self.block_size
|
|
108
|
+
8
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def self.key_length
|
|
112
|
+
64
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Net
|
|
2
|
+
module SSH
|
|
3
|
+
module Transport
|
|
4
|
+
# Loads chacha20 poly1305 support which requires optinal dependency rbnacl
|
|
5
|
+
module ChaCha20Poly1305CipherLoader
|
|
6
|
+
begin
|
|
7
|
+
require 'net/ssh/transport/chacha20_poly1305_cipher'
|
|
8
|
+
LOADED = true
|
|
9
|
+
ERROR = nil
|
|
10
|
+
rescue LoadError => e
|
|
11
|
+
ERROR = e
|
|
12
|
+
LOADED = false
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -1,57 +1,82 @@
|
|
|
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'
|
|
7
|
+
require 'net/ssh/transport/chacha20_poly1305_cipher_loader'
|
|
8
|
+
require 'net/ssh/transport/openssl_cipher_extensions'
|
|
5
9
|
|
|
6
|
-
module Net
|
|
7
|
-
module SSH
|
|
10
|
+
module Net
|
|
11
|
+
module SSH
|
|
8
12
|
module Transport
|
|
9
|
-
|
|
10
13
|
# Implements a factory of OpenSSL cipher algorithms.
|
|
11
14
|
class CipherFactory
|
|
12
15
|
# Maps the SSH name of a cipher to it's corresponding OpenSSL name
|
|
13
16
|
SSH_TO_OSSL = {
|
|
14
|
-
"3des-cbc"
|
|
15
|
-
"blowfish-cbc"
|
|
16
|
-
"aes256-cbc"
|
|
17
|
-
"aes192-cbc"
|
|
18
|
-
"aes128-cbc"
|
|
19
|
-
"idea-cbc"
|
|
20
|
-
"cast128-cbc"
|
|
17
|
+
"3des-cbc" => "des-ede3-cbc",
|
|
18
|
+
"blowfish-cbc" => "bf-cbc",
|
|
19
|
+
"aes256-cbc" => "aes-256-cbc",
|
|
20
|
+
"aes192-cbc" => "aes-192-cbc",
|
|
21
|
+
"aes128-cbc" => "aes-128-cbc",
|
|
22
|
+
"idea-cbc" => "idea-cbc",
|
|
23
|
+
"cast128-cbc" => "cast-cbc",
|
|
21
24
|
"rijndael-cbc@lysator.liu.se" => "aes-256-cbc",
|
|
22
|
-
"3des-ctr"
|
|
23
|
-
"blowfish-ctr"
|
|
25
|
+
"3des-ctr" => "des-ede3",
|
|
26
|
+
"blowfish-ctr" => "bf-ecb",
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
'cast128-ctr'
|
|
28
|
+
"aes256-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-256-ctr") ? "aes-256-ctr" : "aes-256-ecb",
|
|
29
|
+
"aes192-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-192-ctr") ? "aes-192-ctr" : "aes-192-ecb",
|
|
30
|
+
"aes128-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-128-ctr") ? "aes-128-ctr" : "aes-128-ecb",
|
|
31
|
+
'cast128-ctr' => 'cast5-ecb',
|
|
29
32
|
|
|
30
|
-
'none'
|
|
33
|
+
'none' => 'none'
|
|
31
34
|
}
|
|
32
35
|
|
|
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|
|
|
40
|
+
if Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
|
|
41
|
+
hash['chacha20-poly1305@openssh.com'] =
|
|
42
|
+
Net::SSH::Transport::ChaCha20Poly1305Cipher
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
33
46
|
# Returns true if the underlying OpenSSL library supports the given cipher,
|
|
34
47
|
# and false otherwise.
|
|
35
48
|
def self.supported?(name)
|
|
49
|
+
return true if SSH_TO_CLASS.key?(name)
|
|
50
|
+
|
|
36
51
|
ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
|
|
37
52
|
return true if ossl_name == "none"
|
|
38
|
-
|
|
53
|
+
|
|
54
|
+
return SSH_TO_CLASS.key?(name) || OpenSSL::Cipher.ciphers.include?(ossl_name)
|
|
39
55
|
end
|
|
40
|
-
|
|
56
|
+
|
|
41
57
|
# Retrieves a new instance of the named algorithm. The new instance
|
|
42
58
|
# will be initialized using an iv and key generated from the given
|
|
43
59
|
# iv, key, shared, hash and digester values. Additionally, the
|
|
44
60
|
# cipher will be put into encryption or decryption mode, based on the
|
|
45
61
|
# value of the +encrypt+ parameter.
|
|
46
|
-
def self.get(name, options={})
|
|
62
|
+
def self.get(name, options = {})
|
|
63
|
+
klass = SSH_TO_CLASS[name]
|
|
64
|
+
unless klass.nil?
|
|
65
|
+
key_len = klass.key_length
|
|
66
|
+
key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options)
|
|
67
|
+
return klass.new(encrypt: options[:encrypt], key: key)
|
|
68
|
+
end
|
|
69
|
+
|
|
47
70
|
ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
|
|
48
71
|
return IdentityCipher if ossl_name == "none"
|
|
72
|
+
|
|
49
73
|
cipher = OpenSSL::Cipher.new(ossl_name)
|
|
50
|
-
|
|
74
|
+
|
|
51
75
|
cipher.send(options[:encrypt] ? :encrypt : :decrypt)
|
|
52
|
-
|
|
76
|
+
|
|
53
77
|
cipher.padding = 0
|
|
54
|
-
|
|
78
|
+
|
|
79
|
+
cipher.extend(Net::SSH::Transport::OpenSSLCipherExtensions)
|
|
55
80
|
if name =~ /-ctr(@openssh.org)?$/
|
|
56
81
|
if ossl_name !~ /-ctr/
|
|
57
82
|
cipher.extend(Net::SSH::Transport::CTR)
|
|
@@ -60,20 +85,23 @@ module Net
|
|
|
60
85
|
end
|
|
61
86
|
end
|
|
62
87
|
cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options)
|
|
63
|
-
|
|
88
|
+
|
|
64
89
|
key_len = cipher.key_len
|
|
65
90
|
cipher.key_len = key_len
|
|
66
91
|
cipher.key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options)
|
|
67
|
-
|
|
92
|
+
|
|
68
93
|
return cipher
|
|
69
94
|
end
|
|
70
|
-
|
|
95
|
+
|
|
71
96
|
# Returns a two-element array containing the [ key-length,
|
|
72
97
|
# block-size ] for the named cipher algorithm. If the cipher
|
|
73
98
|
# algorithm is unknown, or is "none", 0 is returned for both elements
|
|
74
99
|
# of the tuple.
|
|
75
100
|
# if :iv_len option is supplied the third return value will be ivlen
|
|
76
101
|
def self.get_lengths(name, options = {})
|
|
102
|
+
klass = SSH_TO_CLASS[name]
|
|
103
|
+
return [klass.key_length, klass.block_size] unless klass.nil?
|
|
104
|
+
|
|
77
105
|
ossl_name = SSH_TO_OSSL[name]
|
|
78
106
|
if ossl_name.nil? || ossl_name == "none"
|
|
79
107
|
result = [0, 0]
|
|
@@ -82,7 +110,7 @@ module Net
|
|
|
82
110
|
cipher = OpenSSL::Cipher.new(ossl_name)
|
|
83
111
|
key_len = cipher.key_len
|
|
84
112
|
cipher.key_len = key_len
|
|
85
|
-
|
|
113
|
+
|
|
86
114
|
block_size =
|
|
87
115
|
case ossl_name
|
|
88
116
|
when /\-ctr/
|
|
@@ -90,14 +118,13 @@ module Net
|
|
|
90
118
|
else
|
|
91
119
|
cipher.block_size
|
|
92
120
|
end
|
|
93
|
-
|
|
121
|
+
|
|
94
122
|
result = [key_len, block_size]
|
|
95
123
|
result << cipher.iv_len if options[:iv_len]
|
|
96
124
|
end
|
|
97
125
|
result
|
|
98
126
|
end
|
|
99
127
|
end
|
|
100
|
-
|
|
101
128
|
end
|
|
102
129
|
end
|
|
103
130
|
end
|
|
@@ -2,7 +2,7 @@ require 'openssl'
|
|
|
2
2
|
require 'delegate'
|
|
3
3
|
|
|
4
4
|
module Net::SSH::Transport
|
|
5
|
-
|
|
5
|
+
# :nodoc:
|
|
6
6
|
class OpenSSLAESCTR < SimpleDelegator
|
|
7
7
|
def initialize(original)
|
|
8
8
|
super
|
|
@@ -26,13 +26,13 @@ module Net::SSH::Transport
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# :nodoc:
|
|
30
30
|
# Pure-Ruby implementation of Stateful Decryption Counter(SDCTR) Mode
|
|
31
31
|
# for Block Ciphers. See RFC4344 for detail.
|
|
32
32
|
module CTR
|
|
33
33
|
def self.extended(orig)
|
|
34
34
|
orig.instance_eval {
|
|
35
|
-
@remaining =
|
|
35
|
+
@remaining = String.new
|
|
36
36
|
@counter = nil
|
|
37
37
|
@counter_len = orig.block_size
|
|
38
38
|
orig.encrypt
|
|
@@ -67,13 +67,13 @@ module Net::SSH::Transport
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def reset
|
|
70
|
-
@remaining =
|
|
70
|
+
@remaining = String.new
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def update(data)
|
|
74
74
|
@remaining += data
|
|
75
75
|
|
|
76
|
-
encrypted =
|
|
76
|
+
encrypted = String.new
|
|
77
77
|
|
|
78
78
|
offset = 0
|
|
79
79
|
while (@remaining.bytesize - offset) >= block_size
|
|
@@ -89,13 +89,13 @@ module Net::SSH::Transport
|
|
|
89
89
|
|
|
90
90
|
def final
|
|
91
91
|
s = @remaining.empty? ? '' : xor!(@remaining, _update(@counter))
|
|
92
|
-
@remaining =
|
|
92
|
+
@remaining = String.new
|
|
93
93
|
s
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
def xor!(s1, s2)
|
|
97
97
|
s = []
|
|
98
|
-
s1.unpack('Q*').zip(s2.unpack('Q*')) {|a,b| s.push(a ^ b) }
|
|
98
|
+
s1.unpack('Q*').zip(s2.unpack('Q*')) {|a, b| s.push(a ^ b) }
|
|
99
99
|
s.pack('Q*')
|
|
100
100
|
end
|
|
101
101
|
singleton_class.send(:private, :xor!)
|