From: Miklos Fazekas <mfazekas@szemafor.com>
Date: Wed, 10 Jun 2020 08:17:44 +0200
Subject: Implemented rsa-sha2-512, rsa-sha2-256 host_key algs

This patch is a combination of the 4 commits in the Pull Request
mentioned below.

Source: https://github.com/net-ssh/net-ssh/pull/771
Backported-By: Antonio Terceiro <terceiro@debian.org>
---
 .gitignore                                    |  2 ++
 lib/net/ssh/authentication/certificate.rb     |  4 ++--
 lib/net/ssh/authentication/ed25519.rb         |  2 +-
 lib/net/ssh/transport/algorithms.rb           |  4 +++-
 lib/net/ssh/transport/kex/abstract.rb         | 11 +++++++++--
 lib/net/ssh/transport/openssl.rb              | 17 +++++++++++++----
 test/integration/Vagrantfile                  |  5 +++--
 test/integration/common.rb                    | 10 +++++++++-
 test/integration/playbook.yml                 | 11 +++++++++--
 test/transport/kex/test_ecdh_sha2_nistp256.rb |  2 +-
 test/transport/test_algorithms.rb             | 14 +++++++-------
 11 files changed, 59 insertions(+), 23 deletions(-)

diff --git a/.gitignore b/.gitignore
index b02057e..acb2b39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,5 @@ test/integration/.vagrant
 test/integration/playbook.retry
 
 .byebug_history
+
+tryout
diff --git a/lib/net/ssh/authentication/certificate.rb b/lib/net/ssh/authentication/certificate.rb
index 95b01ff..ff22eb4 100644
--- a/lib/net/ssh/authentication/certificate.rb
+++ b/lib/net/ssh/authentication/certificate.rb
@@ -69,8 +69,8 @@ module Net
           key.ssh_do_sign(data, sig_alg)
         end
 
-        def ssh_do_verify(sig, data)
-          key.ssh_do_verify(sig, data)
+        def ssh_do_verify(sig, data, options = {})
+          key.ssh_do_verify(sig, data, options)
         end
 
         def to_pem
diff --git a/lib/net/ssh/authentication/ed25519.rb b/lib/net/ssh/authentication/ed25519.rb
index 1989d1f..5f3b83b 100644
--- a/lib/net/ssh/authentication/ed25519.rb
+++ b/lib/net/ssh/authentication/ed25519.rb
@@ -123,7 +123,7 @@ module Net
             ssh_type
           end
 
-          def ssh_do_verify(sig,data)
+          def ssh_do_verify(sig, data, options = {})
             @verify_key.verify(sig,data)
           end
 
diff --git a/lib/net/ssh/transport/algorithms.rb b/lib/net/ssh/transport/algorithms.rb
index 8126aa6..9ab87b6 100644
--- a/lib/net/ssh/transport/algorithms.rb
+++ b/lib/net/ssh/transport/algorithms.rb
@@ -33,7 +33,9 @@ module Net
                        ecdsa-sha2-nistp256
                        ssh-rsa-cert-v01@openssh.com
                        ssh-rsa-cert-v00@openssh.com
-                       ssh-rsa],
+                       ssh-rsa
+                       rsa-sha2-256
+                       rsa-sha2-512],
 
           kex: %w[ecdh-sha2-nistp521
                   ecdh-sha2-nistp384
diff --git a/lib/net/ssh/transport/kex/abstract.rb b/lib/net/ssh/transport/kex/abstract.rb
index c70eb94..3fd8c3c 100644
--- a/lib/net/ssh/transport/kex/abstract.rb
+++ b/lib/net/ssh/transport/kex/abstract.rb
@@ -64,11 +64,16 @@ module Net
 
           private
 
+          def matching?(key_ssh_type, host_key_alg)
+            return true if key_ssh_type == host_key_alg
+            return true if key_ssh_type == 'ssh-rsa' && ['rsa-sha2-512', 'rsa-sha2-256'].include?(host_key_alg)
+          end
+
           # Verify that the given key is of the expected type, and that it
           # really is the key for the session's host. Raise Net::SSH::Exception
           # if it is not.
           def verify_server_key(key) #:nodoc:
-            if key.ssh_type != algorithms.host_key
+            unless matching?(key.ssh_type, algorithms.host_key)
               raise Net::SSH::Exception, "host key algorithm mismatch '#{key.ssh_type}' != '#{algorithms.host_key}'"
             end
 
@@ -97,7 +102,9 @@ module Net
 
             hash = digester.digest(response.to_s)
 
-            unless connection.host_key_verifier.verify_signature { result[:server_key].ssh_do_verify(result[:server_sig], hash) }
+            server_key = result[:server_key]
+            server_sig = result[:server_sig]
+            unless connection.host_key_verifier.verify_signature { server_key.ssh_do_verify(server_sig, hash, host_key: algorithms.host_key) }
               raise Net::SSH::Exception, 'could not verify server signature'
             end
 
diff --git a/lib/net/ssh/transport/openssl.rb b/lib/net/ssh/transport/openssl.rb
index 3f10b02..e995ad6 100644
--- a/lib/net/ssh/transport/openssl.rb
+++ b/lib/net/ssh/transport/openssl.rb
@@ -63,8 +63,17 @@ module OpenSSL
       end
 
       # Verifies the given signature matches the given data.
-      def ssh_do_verify(sig, data)
-        verify(OpenSSL::Digest::SHA1.new, sig, data)
+      def ssh_do_verify(sig, data, options = {})
+        digester =
+          if options[:host_key] == "rsa-sha2-512"
+            OpenSSL::Digest::SHA512.new
+          elsif options[:host_key] == "rsa-sha2-256"
+            OpenSSL::Digest::SHA256.new
+          else
+            OpenSSL::Digest::SHA1.new
+          end
+
+        verify(digester, sig, data)
       end
 
       # Returns the signature for the given data.
@@ -102,7 +111,7 @@ module OpenSSL
       end
 
       # Verifies the given signature matches the given data.
-      def ssh_do_verify(sig, data)
+      def ssh_do_verify(sig, data, options = {})
         sig_r = sig[0,20].unpack("H*")[0].to_i(16)
         sig_s = sig[20,20].unpack("H*")[0].to_i(16)
         a1sig = OpenSSL::ASN1::Sequence([
@@ -200,7 +209,7 @@ module OpenSSL
       end
 
       # Verifies the given signature matches the given data.
-      def ssh_do_verify(sig, data)
+      def ssh_do_verify(sig, data, options = {})
         digest = digester.digest(data)
         a1sig = nil
 
diff --git a/test/integration/Vagrantfile b/test/integration/Vagrantfile
index 2c42d9a..2327e5d 100644
--- a/test/integration/Vagrantfile
+++ b/test/integration/Vagrantfile
@@ -1,10 +1,11 @@
 VAGRANTFILE_API_VERSION = "2"
 
 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
-  config.vm.box = "ubuntu/trusty64"
+  config.vm.box = "ubuntu/bionic64"
   config.vm.provision "ansible" do |ansible|
     ansible.playbook = "./playbook.yml"
-    ansible.sudo = true
+    ansible.become = true
+    ansible.become_user = 'root'
     ansible.verbose = 'vvvv'
     ansible.compatibility_mode = "2.0"
   end
diff --git a/test/integration/common.rb b/test/integration/common.rb
index 6295ada..8fe0881 100644
--- a/test/integration/common.rb
+++ b/test/integration/common.rb
@@ -110,7 +110,15 @@ module IntegrationTestHelpers
     # down sshd.
     if pid
       system('sudo', 'kill', '-15', pid.to_s)
-      Process.wait(pid)
+      begin
+        Timeout.timeout(5) do
+          Process.wait(pid)
+        end
+      rescue Timeout::Error
+        warn "Failed to kill net-ssh process: #{pid}"
+        system('sudo', 'kill', '-9', pid)
+        raise
+      end
     end
   end
 
diff --git a/test/integration/playbook.yml b/test/integration/playbook.yml
index 47eba34..6d79a54 100644
--- a/test/integration/playbook.yml
+++ b/test/integration/playbook.yml
@@ -93,13 +93,20 @@
     - name: add host aliases
       lineinfile: dest='/etc/hosts' owner='root' group='root' mode=0644
         regexp='^127\.0\.0\.1\s+gateway.netssh' line='127.0.0.1  gateway.netssh'
-    - apt:
+    - name: Update APT Cache
+      apt:
+        update_cache: yes
+        force_apt_get: yes
+    - name: Wait for locfile removal
+      become: yes
+      shell:  while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 5; done;
+    - name: Install packages
+      apt:
         pkg:
           - pv
           - libgmp3-dev
           - git
         state: present
-        update_cache: yes
     - copy: content='echo "cd /net-ssh ; rake integration-test"' dest=/etc/update-motd.d/99-net-ssh-tests mode=0755
     - name: add user to rvm group so they can change gem wrappers
       user:
diff --git a/test/transport/kex/test_ecdh_sha2_nistp256.rb b/test/transport/kex/test_ecdh_sha2_nistp256.rb
index 932d8d7..3489230 100644
--- a/test/transport/kex/test_ecdh_sha2_nistp256.rb
+++ b/test/transport/kex/test_ecdh_sha2_nistp256.rb
@@ -1,7 +1,7 @@
 require 'openssl'
 require 'ostruct'
 require_relative '../../common'
-require 'transport/kex/test_diffie_hellman_group1_sha1'
+require_relative './test_diffie_hellman_group1_sha1'
 require 'net/ssh/transport/kex/ecdh_sha2_nistp256'
 
 module Transport
diff --git a/test/transport/test_algorithms.rb b/test/transport/test_algorithms.rb
index 105f3af..aac8b9a 100644
--- a/test/transport/test_algorithms.rb
+++ b/test/transport/test_algorithms.rb
@@ -18,7 +18,7 @@ module Transport
     end
 
     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], algorithms[:host_key]
+      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-sha1], algorithms[:kex]
       assert_equal %w[aes256-ctr aes192-ctr aes128-ctr], 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]
@@ -27,7 +27,7 @@ module Transport
     end
 
     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 ssh-dss], algorithms(append_all_supported_algorithms: true)[:host_key]
+      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-sha1 diffie-hellman-group-exchange-sha1 diffie-hellman-group1-sha1], algorithms(append_all_supported_algorithms: true)[:kex]
       assert_equal %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 %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]
@@ -43,12 +43,12 @@ module Transport
     end
 
     def test_constructor_with_preferred_host_key_type_should_put_preferred_host_key_type_first
-      assert_equal %w[ssh-dss] + ed_ec_host_keys + %w[ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-rsa], algorithms(host_key: "ssh-dss", append_all_supported_algorithms: true)[:host_key]
+      assert_equal %w[ssh-dss] + 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: "ssh-dss", append_all_supported_algorithms: true)[:host_key]
     end
 
     def test_constructor_with_known_hosts_reporting_known_host_key_should_use_that_host_key_type
       Net::SSH::KnownHosts.expects(:search_for).with("net.ssh.test,127.0.0.1", {}).returns([stub("key", ssh_type: "ssh-dss")])
-      assert_equal %w[ssh-dss] + ed_ec_host_keys + %w[ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-rsa], algorithms[:host_key]
+      assert_equal %w[ssh-dss] + 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]
     end
 
     def ed_host_keys
@@ -73,7 +73,7 @@ module Transport
     end
 
     def test_constructor_with_unrecognized_host_key_type_should_return_whats_supported
-      assert_equal ed_ec_host_keys + %w[ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-rsa ssh-dss],
+      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(host_key: "bogus ssh-rsa", append_all_supported_algorithms: true)[:host_key]
     end
 
@@ -182,7 +182,7 @@ module Transport
     end
 
     def test_constructor_with_host_key_removals_with_wildcard
-      assert_equal ed_host_keys + %w[ecdsa-sha2-nistp521-cert-v01@openssh.com ecdsa-sha2-nistp384-cert-v01@openssh.com ecdsa-sha2-nistp256-cert-v01@openssh.com ecdsa-sha2-nistp521 ecdsa-sha2-nistp384 ecdsa-sha2-nistp256], algorithms(host_key: %w[-ssh-rsa* -ssh-dss])[:host_key]
+      assert_equal ed_host_keys + %w[ecdsa-sha2-nistp521-cert-v01@openssh.com ecdsa-sha2-nistp384-cert-v01@openssh.com ecdsa-sha2-nistp256-cert-v01@openssh.com ecdsa-sha2-nistp521 ecdsa-sha2-nistp384 ecdsa-sha2-nistp256], algorithms(host_key: %w[-ssh-rsa* -ssh-dss -rsa-sha*])[:host_key]
     end
 
     def test_initial_state_should_be_neither_pending_nor_initialized
@@ -407,7 +407,7 @@ module Transport
       assert_equal KEXINIT, buffer.type
       assert_equal 16, buffer.read(16).length
       assert_equal options[:kex] || (x25519_kex + ec_kex + %w[diffie-hellman-group-exchange-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]).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] || 'aes256-ctr,aes192-ctr,aes128-ctr', buffer.read_string
       assert_equal options[:encryption_server] || 'aes256-ctr,aes192-ctr,aes128-ctr', 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
