Skip to content
Snippets Groups Projects
Commit 5f75268c authored by Unit 193's avatar Unit 193
Browse files

New upstream version 7.3.0.

parent 7eb4de10
No related branches found
No related tags found
No related merge requests found
Showing
with 453 additions and 32 deletions
......@@ -235,7 +235,7 @@ Lint/UselessTimes:
# Offense count: 205
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 74
Max: 75
# Offense count: 16
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
......
=== 7.3.0 rc0
* aes(128|256)gcm [#946]
=== 7.2.2
* ruby 3.3.0: base64 fix
......
......@@ -274,6 +274,9 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
[![Sponsor](https://opencollective.com/net-ssh/sponsor/0/avatar.svg)](https://opencollective.com/net-ssh/sponsor/0/website)
[<img src="https://github.com/net-ssh/net-ssh/assets/52435/9690bf3e-34ea-4c52-8aea-1cc4cb5bcb6d" width="320">](https://ubicloud.com)
## LICENSE:
(The MIT License)
......
require 'net/ssh/transport/hmac/abstract'
require 'net/ssh/transport/gcm_cipher'
module Net::SSH::Transport
## Implements the aes128-gcm@openssh cipher
class AES128_GCM
extend ::Net::SSH::Transport::GCMCipher
## Implicit HMAC, do need to do anything
class ImplicitHMac < ::Net::SSH::Transport::HMAC::Abstract
def aead
true
end
def key_length
16
end
end
def implicit_mac
ImplicitHMac.new
end
def algo_name
'aes-128-gcm'
end
def name
'aes128-gcm@openssh.com'
end
#
# --- RFC 5647 ---
# K_LEN AES key length 16 octets
#
def self.key_length
16
end
end
end
require 'net/ssh/transport/hmac/abstract'
require 'net/ssh/transport/gcm_cipher'
module Net::SSH::Transport
## Implements the aes256-gcm@openssh cipher
class AES256_GCM
extend ::Net::SSH::Transport::GCMCipher
## Implicit HMAC, do need to do anything
class ImplicitHMac < ::Net::SSH::Transport::HMAC::Abstract
def aead
true
end
def key_length
32
end
end
def implicit_mac
ImplicitHMac.new
end
def algo_name
'aes-256-gcm'
end
def name
'aes256-gcm@openssh.com'
end
#
# --- RFC 5647 ---
# K_LEN AES key length 32 octets
#
def self.key_length
32
end
end
end
......@@ -44,7 +44,11 @@ module Net
diffie-hellman-group14-sha256
diffie-hellman-group14-sha1],
encryption: %w[aes256-ctr aes192-ctr aes128-ctr],
encryption: %w[aes256-ctr
aes192-ctr
aes128-ctr
aes256-gcm@openssh.com
aes128-gcm@openssh.com],
hmac: %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
hmac-sha2-512 hmac-sha2-256
......@@ -492,6 +496,9 @@ module Net
HMAC.get(hmac_server, mac_key_server, parameters)
end
cipher_client.nonce = iv_client if mac_client.respond_to?(:aead) && mac_client.aead
cipher_server.nonce = iv_server if mac_server.respond_to?(:aead) && mac_client.aead
session.configure_client cipher: cipher_client, hmac: mac_client,
compression: normalize_compression_name(compression_client),
compression_level: options[:compression_level],
......
require 'openssl'
require 'net/ssh/transport/ctr.rb'
require 'net/ssh/transport/aes128_gcm'
require 'net/ssh/transport/aes256_gcm'
require 'net/ssh/transport/key_expander'
require 'net/ssh/transport/identity_cipher'
require 'net/ssh/transport/chacha20_poly1305_cipher_loader'
......@@ -31,15 +33,15 @@ module Net
'none' => 'none'
}
SSH_TO_CLASS =
SSH_TO_CLASS = {
'aes256-gcm@openssh.com' => Net::SSH::Transport::AES256_GCM,
'aes128-gcm@openssh.com' => Net::SSH::Transport::AES128_GCM
}.tap do |hash|
if Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
{
'chacha20-poly1305@openssh.com' => Net::SSH::Transport::ChaCha20Poly1305Cipher
}
else
{
}
hash['chacha20-poly1305@openssh.com'] =
Net::SSH::Transport::ChaCha20Poly1305Cipher
end
end
# Returns true if the underlying OpenSSL library supports the given cipher,
# and false otherwise.
......
require 'net/ssh/loggable'
module Net
module SSH
module Transport
## Extension module for aes(128|256)gcm ciphers
module GCMCipher
# rubocop:disable Metrics/AbcSize
def self.extended(orig)
# rubocop:disable Metrics/BlockLength
orig.class_eval do
include Net::SSH::Loggable
attr_reader :cipher
attr_reader :key
attr_accessor :nonce
#
# Semantically gcm cipher supplies the OpenSSL iv interface with a nonce
# as it is not randomly generated due to being supplied from a counter.
# The RFC's use IV and nonce interchangeably.
#
def initialize(encrypt:, key:)
@cipher = OpenSSL::Cipher.new(algo_name)
@key = key
key_len = @cipher.key_len
if key.size != key_len
error_message = "#{cipher_name}: keylength does not match"
error { error_message }
raise error_message
end
encrypt ? @cipher.encrypt : @cipher.decrypt
@cipher.key = key
@nonce = {
fixed: nil,
invocation_counter: 0
}
end
def update_cipher_mac(payload, _sequence_number)
#
# --- RFC 5647 7.3 ---
# When using AES-GCM with secure shell, the packet_length field is to
# be treated as additional authenticated data, not as plaintext.
#
length_data = [payload.bytesize].pack('N')
cipher.auth_data = length_data
encrypted_data = cipher.update(payload) << cipher.final
mac = cipher.auth_tag
incr_nonce
length_data + encrypted_data + mac
end
#
# --- RFC 5647 ---
# uint32 packet_length; // 0 <= packet_length < 2^32
#
def read_length(data, _sequence_number)
data.unpack1('N')
end
#
# --- RFC 5647 ---
# In AES-GCM secure shell, the inputs to the authenticated encryption
# are:
# PT (Plain Text)
# byte padding_length; // 4 <= padding_length < 256
# byte[n1] payload; // n1 = packet_length-padding_length-1
# byte[n2] random_padding; // n2 = padding_length
# AAD (Additional Authenticated Data)
# uint32 packet_length; // 0 <= packet_length < 2^32
# IV (Initialization Vector)
# As described in section 7.1.
# BK (Block Cipher Key)
# The appropriate Encryption Key formed during the Key Exchange.
#
def read_and_mac(data, mac, _sequence_number)
# The authentication tag will be placed in the MAC field at the end of the packet
# OpenSSL does not verify auth tag length
# GCM mode allows arbitrary sizes for the auth_tag up to 128 bytes and a single
# byte allows authentication to pass. If single byte auth tags are possible
# an attacker would require no more than 256 attempts to forge a valid tag.
#
raise 'incorrect auth_tag length' unless mac.to_s.length == mac_length
packet_length = data.unpack1('N')
cipher.auth_tag = mac.to_s
cipher.auth_data = [packet_length].pack('N')
result = cipher.update(data[4...]) << cipher.final
incr_nonce
result
end
def mac_length
16
end
def block_size
16
end
def self.block_size
16
end
#
# --- RFC 5647 ---
# N_MIN minimum nonce (IV) length 12 octets
# N_MAX maximum nonce (IV) length 12 octets
#
def iv_len
12
end
#
# --- RFC 5288 ---
# Each value of the nonce_explicit MUST be distinct for each distinct
# invocation of the GCM encrypt function for any fixed key. Failure to
# meet this uniqueness requirement can significantly degrade security.
# The nonce_explicit MAY be the 64-bit sequence number.
#
# --- RFC 5116 ---
# (2.1) Applications that can generate distinct nonces SHOULD use the nonce
# formation method defined in Section 3.2, and MAY use any
# other method that meets the uniqueness requirement.
#
# (3.2) The following method to construct nonces is RECOMMENDED.
#
# <- variable -> <- variable ->
# - - - - - - - - - - - - - -
# | fixed | counter |
#
# Initial octets consist of a fixed field and final octets consist of a
# Counter field. Implementations SHOULD support 12-octet nonces in which
# the Counter field is four octets long.
# The Counter fields of successive nonces form a monotonically increasing
# sequence, when those fields are regarded as unsignd integers in network
# byte order.
# The Counter part SHOULD be equal to zero for the first nonce and increment
# by one for each successive nonce that is generated.
# The Fixed field MUST remain constant for all nonces that are generated for
# a given encryption device.
#
# --- RFC 5647 ---
# The invocation field is treated as a 64-bit integer and is increment after
# each invocation of AES-GCM to process a binary packet.
# AES-GCM produces a keystream in blocks of 16-octets that is used to
# encrypt the plaintext. This keystream is produced by encrypting the
# following 16-octet data structure:
#
# uint32 fixed; // 4 octets
# uint64 invocation_counter; // 8 octets
# unit32 block_counter; // 4 octets
#
# The block_counter is initially set to one (1) and increment as each block
# of key is produced.
#
# The reader is reminded that SSH requires that the data to be encrypted
# MUST be padded out to a multiple of the block size (16-octets for AES-GCM).
#
def incr_nonce
return if nonce[:fixed].nil?
nonce[:invocation_counter] = [nonce[:invocation_counter].to_s.unpack1('B*').to_i(2) + 1].pack('Q>*')
apply_nonce
end
def nonce=(iv_s)
return if nonce[:fixed]
nonce[:fixed] = iv_s[0...4]
nonce[:invocation_counter] = iv_s[4...12]
apply_nonce
end
def apply_nonce
cipher.iv = "#{nonce[:fixed]}#{nonce[:invocation_counter]}"
end
#
# --- RFC 5647 ---
# If AES-GCM is selected as the encryption algorithm for a given
# tunnel, AES-GCM MUST also be selected as the Message Authentication
# Code (MAC) algorithm. Conversely, if AES-GCM is selected as the MAC
# algorithm, it MUST also be selected as the encryption algorithm.
#
def implicit_mac?
true
end
end
end
# rubocop:enable Metrics/BlockLength
end
# rubocop:enable Metrics/AbcSize
end
end
end
......@@ -8,6 +8,18 @@ module Net
# The base class of all OpenSSL-based HMAC algorithm wrappers.
class Abstract
class << self
def aead(*v)
@aead = false if !defined?(@aead)
if v.empty?
@aead = superclass.aead if @aead.nil? && superclass.respond_to?(:aead)
return @aead
elsif v.length == 1
@aead = v.first
else
raise ArgumentError, "wrong number of arguments (#{v.length} for 1)"
end
end
def etm(*v)
@etm = false if !defined?(@etm)
if v.empty?
......@@ -57,6 +69,10 @@ module Net
end
end
def aead
self.class.aead
end
def etm
self.class.etm
end
......
......@@ -128,7 +128,7 @@ module Net
payload = client.compress(payload)
# the length of the packet, minus the padding
actual_length = (client.hmac.etm ? 0 : 4) + payload.bytesize + 1
actual_length = (client.hmac.etm || client.hmac.aead ? 0 : 4) + payload.bytesize + 1
# compute the padding length
padding_length = client.block_size - (actual_length % client.block_size)
......@@ -151,7 +151,7 @@ module Net
debug { "using encrypt-then-mac" }
# Encrypt padding_length, payload, and padding. Take MAC
# from the unencrypted packet_lenght and the encrypted
# from the unencrypted packet_length and the encrypted
# data.
length_data = [packet_length].pack("N")
......@@ -219,7 +219,7 @@ module Net
# new Packet object.
# rubocop:disable Metrics/AbcSize
def poll_next_packet
aad_length = server.hmac.etm ? 4 : 0
aad_length = server.hmac.etm || server.hmac.aead ? 4 : 0
if @packet.nil?
minimum = server.block_size < 4 ? 4 : server.block_size
......
......@@ -125,7 +125,7 @@ module Net
compressor.deflate(data, Zlib::SYNC_FLUSH)
end
# Deompresses the data. If no compression is in effect, this will just return
# Decompresses the data. If no compression is in effect, this will just return
# the data unmodified, otherwise it uses #decompressor to decompress the data.
def decompress(data)
data = data.to_s
......
......@@ -49,10 +49,10 @@ module Net
MAJOR = 7
# The minor component of this version of the Net::SSH library
MINOR = 2
MINOR = 3
# The tiny component of this version of the Net::SSH library
TINY = 3
TINY = 0
# The prerelease component of this version of the Net::SSH library
# nil allowed
......
......@@ -10,7 +10,7 @@ Setup:
ansible-galaxy install rvm.ruby
vagrant up ; vagrant ssh
rvmsudo_secure_path=1 rvmsudo rvm all do gem install bundler
rvm all do sh -c 'rm Gemfile.lock; bundle'
rvmsudo_secure_path=1 rvmsudo rvm all do sh -c 'rm Gemfile.lock; bundle'
rvm all do rake test
# Debugging
......
......@@ -30,6 +30,8 @@
register: openssl_version_query
- name: Install openssl-1.1.1g
block:
- name: "Install make gcc"
command: sh -c "sudo apt-get update && sudo apt install -y make gcc"
- name: "Download openssl-1.1.1g sources"
unarchive:
src: https://www.openssl.org/source/openssl-1.1.1g.tar.gz
......
require_relative 'common'
require 'fileutils'
require 'tmpdir'
require 'net/ssh'
require 'timeout'
# bundle exec ruby -Ilib:test ./test/integration/test_gcm_cipher.rb
# see Vagrantfile,playbook for env.
# we're running as net_ssh_1 user password foo
# and usually connecting to net_ssh_2 user password foo2pwd
class TestGcmCipher < NetSSHTest
include IntegrationTestHelpers
def run_with_only_cipher(cipher)
config_lines = File.read('/etc/ssh/sshd_config').split("\n")
config_lines = config_lines.map do |line|
if line =~ /^Ciphers/
"##{line}"
else
line
end
end
config_lines.push("Ciphers #{cipher}")
Tempfile.open('empty_kh') do |f|
f.close
start_sshd_7_or_later(config: config_lines, debug: true) do |_pid, port|
Timeout.timeout(4) do
# We have our own sshd, give it a chance to come up before
# listening.
ret = Net::SSH.start("localhost", "net_ssh_1", encryption: cipher, password: 'foopwd', port: port, user_known_hosts_file: [f.path], verbose: :debug) do |ssh|
ssh.exec! "echo 'foo'"
end
assert_equal "foo\n", ret
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH
sleep 0.25
retry
end
end
end
end
def test_aes128_gcm
run_with_only_cipher('aes128-gcm@openssh.com')
end
def test_aes256_gcm
run_with_only_cipher('aes256-gcm@openssh.com')
end
end
......@@ -19,7 +19,7 @@ module Transport
def test_constructor_should_build_default_list_of_preferred_algorithms
assert_equal ed_ec_host_keys + %w[ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-rsa rsa-sha2-256 rsa-sha2-512], algorithms[:host_key]
assert_equal x25519_kex + ec_kex + %w[diffie-hellman-group-exchange-sha256 diffie-hellman-group14-sha256 diffie-hellman-group14-sha1], algorithms[:kex]
assert_equal chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr], algorithms[:encryption]
assert_equal chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com], algorithms[:encryption]
assert_equal %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com hmac-sha2-512 hmac-sha2-256 hmac-sha1], algorithms[:hmac]
assert_equal %w[none zlib@openssh.com zlib], algorithms[:compression]
assert_equal %w[], algorithms[:language]
......@@ -28,7 +28,7 @@ module Transport
def test_constructor_should_build_complete_list_of_algorithms_with_append_all_supported_algorithms
assert_equal ed_ec_host_keys + %w[ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-rsa rsa-sha2-256 rsa-sha2-512 ssh-dss], algorithms(append_all_supported_algorithms: true)[:host_key]
assert_equal x25519_kex + ec_kex + %w[diffie-hellman-group-exchange-sha256 diffie-hellman-group14-sha256 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha1 diffie-hellman-group1-sha1], algorithms(append_all_supported_algorithms: true)[:kex]
assert_equal chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes256-cbc aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr 3des-cbc idea-cbc none], algorithms(append_all_supported_algorithms: true)[:encryption]
assert_equal chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com aes256-cbc aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr 3des-cbc idea-cbc none], algorithms(append_all_supported_algorithms: true)[:encryption]
assert_equal %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com hmac-sha2-512 hmac-sha2-256 hmac-sha1 hmac-sha2-512-96 hmac-sha2-256-96 hmac-sha1-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-md5 hmac-md5-96 none], algorithms(append_all_supported_algorithms: true)[:hmac]
assert_equal %w[none zlib@openssh.com zlib], algorithms(append_all_supported_algorithms: true)[:compression]
assert_equal %w[], algorithms[:language]
......@@ -128,27 +128,27 @@ module Transport
end
def test_constructor_with_preferred_encryption_should_put_preferred_encryption_first
assert_equal %w[aes256-cbc] + chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr 3des-cbc idea-cbc none], algorithms(encryption: "aes256-cbc", append_all_supported_algorithms: true)[:encryption]
assert_equal %w[aes256-cbc] + chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr 3des-cbc idea-cbc none], algorithms(encryption: "aes256-cbc", append_all_supported_algorithms: true)[:encryption]
end
def test_constructor_with_multiple_preferred_encryption_should_put_all_preferred_encryption_first
assert_equal %w[aes256-cbc 3des-cbc idea-cbc] + chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr none], algorithms(encryption: %w[aes256-cbc 3des-cbc idea-cbc], append_all_supported_algorithms: true)[:encryption]
assert_equal %w[aes256-cbc 3des-cbc idea-cbc] + chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr none], algorithms(encryption: %w[aes256-cbc 3des-cbc idea-cbc], append_all_supported_algorithms: true)[:encryption]
end
def test_constructor_with_unrecognized_encryption_should_keep_whats_supported
assert_equal %w[aes256-cbc] + chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr 3des-cbc idea-cbc none], algorithms(encryption: %w[bogus aes256-cbc], append_all_supported_algorithms: true)[:encryption]
assert_equal %w[aes256-cbc] + chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr 3des-cbc idea-cbc none], algorithms(encryption: %w[bogus aes256-cbc], append_all_supported_algorithms: true)[:encryption]
end
def test_constructor_with_preferred_encryption_supports_additions
# There's nothing we can really append to the set since the default algos
# are frozen so this is really just testing that it doesn't do anything
# unexpected.
assert_equal chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes256-cbc aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr 3des-cbc idea-cbc none],
assert_equal chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com aes256-cbc aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc 3des-ctr 3des-cbc idea-cbc none],
algorithms(encryption: %w[+3des-cbc])[:encryption]
end
def test_constructor_with_preferred_encryption_supports_removals_with_wildcard
assert_equal chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr cast128-ctr],
assert_equal chacha_poly_cipher + %w[aes256-ctr aes192-ctr aes128-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com cast128-ctr],
algorithms(encryption: %w[-rijndael-cbc@lysator.liu.se -blowfish-* -3des-* -*-cbc -none])[:encryption]
end
......@@ -432,8 +432,8 @@ module Transport
assert_equal 16, buffer.read(16).length
assert_equal options[:kex] || (x25519_kex + ec_kex + %w[diffie-hellman-group-exchange-sha256 diffie-hellman-group14-sha256 diffie-hellman-group14-sha1]).join(','), buffer.read_string
assert_equal options[:host_key] || (ed_ec_host_keys + %w[ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-rsa rsa-sha2-256 rsa-sha2-512]).join(','), buffer.read_string
assert_equal options[:encryption_client] || "#{chacha_poly_cipher_str}aes256-ctr,aes192-ctr,aes128-ctr", buffer.read_string
assert_equal options[:encryption_server] || "#{chacha_poly_cipher_str}aes256-ctr,aes192-ctr,aes128-ctr", buffer.read_string
assert_equal options[:encryption_client] || "#{chacha_poly_cipher_str}aes256-ctr,aes192-ctr,aes128-ctr,aes256-gcm@openssh.com,aes128-gcm@openssh.com", buffer.read_string
assert_equal options[:encryption_server] || "#{chacha_poly_cipher_str}aes256-ctr,aes192-ctr,aes128-ctr,aes256-gcm@openssh.com,aes128-gcm@openssh.com", buffer.read_string
assert_equal options[:hmac_client] || 'hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-sha1', buffer.read_string
assert_equal options[:hmac_server] || 'hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-sha1', buffer.read_string
assert_equal options[:compression_client] || 'none,zlib@openssh.com,zlib', buffer.read_string
......
......@@ -246,8 +246,21 @@ module Transport
assert_equal TEXT, decrypt("none", TEXT)
end
AES128_GCM = ["00000040b9a5166a6b4382e1989b55da47618b097b3b8cfdaa7f6d5b483e57ae60d542acb0525bd5fdee2bf127f8ae8293934b8da69f2afac6005818490df2ab87c24bcdeb3cc42d4ff26900bb97b1ac471067bb00000019a49b2768fb31ca36032e0431b342546144e12127f2fa142638a0a7f85c338576b2f47306da2fef6785"].pack('H*')
AES256_GCM = ["000000408a29a280b1d60b55d772d822ac890b565b96592d0eca0f0e70d530a17a91b74802577aab7ebabbd877dc86216f4ec8bd7b6220b139032f884bce1346164b7ab06718d6e08be7064609e771dfc50c25800000001984d432a5699ad5f9c6c6588101d0e5af507a522732077c9f9db85532741102669b63b63026a85e2a54"].pack('H*')
CHACHA20POLY1305 = ["73cbe4dd0d6495d2048bb1ba5f01f055f6271efaa5e56b9de3586bd116ededab481dc7833d5b6aaa7f7827d49c82185a02f62262d9efae8e6973a4e98251dcbbf2beebfc29c40a75192604729b6f7d412add8fe1a730e4b21c8aa1c73786090bd46bb66121c888b3e628c3f5a9a6a0738f8fcc2a611ef97bd0a665eb565ba0247d"].pack('H*')
OPTIONS_AES128_GCM = {
key: ["f4089170b3ae562c23cfcaebd73d3052"].pack("H*"),
aead: true
}
OPTIONS_AES256_GCM = {
key: ["f4089170b3ae562c23cfcaebd73d30521b6812c0fd7e93346c8144d4de04e17b"].pack("H*"),
aead: true
}
OPTIONS_CHACHAPOLY = {
iv: "ABC",
key: "abcd" * 16,
......@@ -256,16 +269,18 @@ module Transport
hash: '!@#$%#$^%$&^&%#$@$'
}
def encrypt_cha_cha_poly(type)
cipher = factory.get(type, OPTIONS_CHACHAPOLY.merge(encrypt: true))
def encrypt_implicit(type, options)
cipher = factory.get(type, options.merge(encrypt: true))
cipher.nonce = ["000000000000000000000032"].pack('H*') if options[:aead]
sequence_number = 1
result = cipher.update_cipher_mac(TEXT.dup[0...64], sequence_number)
result << cipher.update_cipher_mac(TEXT.dup[64...], sequence_number + 1)
result
end
def decrypt_chacha_poly(type, data)
cipher = factory.get(type, OPTIONS_CHACHAPOLY.merge(decrypt: true))
def decrypt_implicit(type, data, options)
cipher = factory.get(type, options.merge(encrypt: false))
cipher.nonce = ["000000000000000000000032"].pack('H*') if options[:aead]
result = ""
sequence_number = 1
pos = 0
......@@ -285,14 +300,30 @@ module Transport
def test_chacha20_poly1305_for_encryption
skip "TODO: chacha20-poly1305 not loaded" unless Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
ret = encrypt_cha_cha_poly("chacha20-poly1305@openssh.com")
ret = encrypt_implicit("chacha20-poly1305@openssh.com", OPTIONS_CHACHAPOLY)
assert_equal CHACHA20POLY1305, ret
end
def test_chacha20_poly1305_for_decryption
skip "TODO: chacha20-poly1305 not loaded" unless Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
assert_equal TEXT, decrypt_chacha_poly("chacha20-poly1305@openssh.com", CHACHA20POLY1305)
assert_equal TEXT, decrypt_implicit("chacha20-poly1305@openssh.com", CHACHA20POLY1305, OPTIONS_CHACHAPOLY)
end
def test_aes128_gcm_for_encryption
assert_equal AES128_GCM, encrypt_implicit("aes128-gcm@openssh.com", OPTIONS_AES128_GCM)
end
def test_aes128_gcm_to_decryption
assert_equal TEXT, decrypt_implicit("aes128-gcm@openssh.com", AES128_GCM, OPTIONS_AES128_GCM)
end
def test_aes256_gcm_for_encryption
assert_equal AES256_GCM, encrypt_implicit("aes256-gcm@openssh.com", OPTIONS_AES256_GCM)
end
def test_aes256_gcm_for_decryption
assert_equal TEXT, decrypt_implicit("aes256-gcm@openssh.com", AES256_GCM, OPTIONS_AES256_GCM)
end
private
......
......@@ -209,6 +209,18 @@ module Transport
:standard => ["5aa01d263f83e1451c7d981526aa8e03b3ec44857a5dde471d76ba92fd92c9a77911c43ca96a13e37a5b1a346508016793f4a57a"].pack('H*')
}
},
'aes128-gcm@openssh.com' => {
'implicit' => {
false => ["00000020462f5dc27e3ba9da491bbfa70deb5183ffc808c9178e505374ed437b46eb2474c470d68dc015cc677c91794a0d5603a8"].pack('H*'),
:standard => ["000000204f53c0a01f5fc0decc35838d40cf702b33830fbb07961334235d0cf9001c211636ef2046feff51f3e1d5e7375308896d"].pack('H*')
}
},
'aes256-gcm@openssh.com' => {
'implicit' => {
false => ["00000020dfd5571fd6a781e395dcf18552a2b93628bf6a2f3ce456bd08dd3a457094b967d47977a309126e7d02dfc8d5f91a5588"].pack('H*'),
:standard => ["00000020d6a9ca7db7c3e8e710f2cdaf1f86989ee4f46d5d2cfc15da5f6d75c73663bc056b6644958591f155d06816aa87adbaff"].pack('H*')
}
},
"3des-cbc" => {
"hmac-md5" => {
false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, \000\032w\312\t\306\374\271\345p\215\224\373\363\v\261",
......@@ -1059,7 +1071,7 @@ module Transport
ciphers = Net::SSH::Transport::CipherFactory::SSH_TO_OSSL.keys + Net::SSH::Transport::CipherFactory::SSH_TO_CLASS.keys
hmacs = Net::SSH::Transport::HMAC::MAP.keys + ["implicit"]
implicit_ciphers = ["chacha20-poly1305@openssh.com"]
implicit_ciphers = %w[chacha20-poly1305@openssh.com aes256-gcm@openssh.com aes128-gcm@openssh.com]
ciphers.each do |cipher_name|
unless Net::SSH::Transport::CipherFactory.supported?(cipher_name) && PACKETS.key?(cipher_name)
......@@ -1093,6 +1105,8 @@ module Transport
Net::SSH::Transport::HMAC.get(hmac_name, "{}|", opts)
end
cipher.nonce = ["000000000000000000000031"].pack('H*') if hmac.respond_to?(:aead) && hmac.aead
stream.server.set cipher: cipher, hmac: hmac, compression: compress
stream.stubs(:recv).returns(PACKETS[cipher_name][hmac_name][compress])
IO.stubs(:select).returns([[stream]])
......@@ -1116,6 +1130,8 @@ module Transport
Net::SSH::Transport::HMAC.get(hmac_name, "{}|", opts)
end
cipher.nonce = ["000000000000000000000031"].pack('H*') if hmac.respond_to?(:aead) && hmac.aead
srand(100)
stream.client.set cipher: cipher, hmac: hmac, compression: compress
stream.enqueue_packet(ssh_packet)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment