]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
runtests: allow configuring SSH host/client key algorithm
authorViktor Szakats <commit@vsz.me>
Wed, 1 Apr 2026 23:05:54 +0000 (01:05 +0200)
committerViktor Szakats <commit@vsz.me>
Sun, 5 Apr 2026 09:19:42 +0000 (11:19 +0200)
via env `CURL_TEST_SSH_KEYALGO`, `rsa` (default), `ecdsa`, `ed25519`.

To ease debugging and testing and to make these code paths more
universal.

Closes #21223

.github/workflows/windows.yml
docs/tests/FILEFORMAT.md
tests/data/test1459
tests/runtests.pl
tests/servers.pm
tests/sshhelp.pm
tests/sshserver.pl

index ebc3193252ebde2d9a627090fd4e6e0529037310..ff6560785092488fa974eec28b8d9923d05c03ec 100644 (file)
@@ -34,6 +34,7 @@ permissions: {}
 env:
   CURL_CI: github
   CURL_TEST_MIN: 1700
+  CURL_TEST_SSH_KEYALGO: ed25519
   OPENSSH_WINDOWS_VERSION: 10.0.0.0p2-Preview
   OPENSSH_WINDOWS_SHA256_ARM64: 698c6aec31c1dd0fb996206e8741f4531a97355686b5431ef347d531b07fcd42
   OPENSSH_WINDOWS_SHA256_WIN64: 23f50f3458c4c5d0b12217c6a5ddfde0137210a30fa870e98b29827f7b43aba5
index 20718a96181e2db0e2978e318a2f4030a860112d..bce3e440f21434f4eeca64754edffefe95e45b70 100644 (file)
@@ -202,6 +202,7 @@ Available substitute variables include:
 - `%SRCDIR` - Full path to the source dir
 - `%SCP_PWD` - Current directory friendly for the SSH server for the scp:// protocol
 - `%SFTP_PWD` - Current directory friendly for the SSH server for the sftp:// protocol
+- `%SSHKEYALGO` - SSH host and client key algorithm, e.g. `ssh-rsa` or `ssh-ed25519`
 - `%SSHPORT` - Port number of the SCP/SFTP server
 - `%SSHSRVMD5` - MD5 of SSH server's public key
 - `%SSHSRVSHA256` - SHA256 of SSH server's public key
index 9285177c9a45a36b489a348e7f1a137e546e1855..ee7e3e5ffee5608ec6a2bfed33ab9cf19ee90c14 100644 (file)
@@ -23,7 +23,7 @@ SFTP with corrupted known_hosts
 -u : sftp://%HOSTIP:%SSHPORT/ -l --knownhosts %LOGDIR/known%TESTNUMBER
 </command>
 <file name="%LOGDIR/known%TESTNUMBER">
-|1|qy29Y1x/+/F39AzdG5515YSSw+c=|iB2WX5jrU3ZTWc+ZfGau7HHEvBc= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAynDN8cDJ3xNzRjTNNGciSHSxpubxhZ6YnkLdp1TkrGW8n\
+|1|qy29Y1x/+/F39AzdG5515YSSw+c=|iB2WX5jrU3ZTWc+ZfGau7HHEvBc= %SSHKEYALGO AAAAB3NzaC1yc2EAAAABIwAAAIEAynDN8cDJ3xNzRjTNNGciSHSxpubxhZ6YnkLdp1TkrGW8n\
 R93Ey5VtBeBblYTRlFXBWJgKFcTKBRJ/O4qBZwbUgt10AHj31i6h8NehfT19tR8wG/YCmj3KtYLHmwdzmW1edEL9G2NdX2KiKYv7/zuly3QvmP0QA0NhWkAz0KdWNM=
 </file>
 </client>
index 8a3a035d9280dcac6444a553627e9b4ad1b2b373..e9184fd47a635dbfbb504302e5d093fa7b461414 100755 (executable)
@@ -627,9 +627,13 @@ sub checksystemfeatures {
             }
             if($libcurl =~ /libssh2/i) {
                 $feature{"libssh2"} = 1;
+                $feature{"sshkeyalgo"} = ($ENV{'CURL_TEST_SSH_KEYALGO'} and
+                    $ENV{'CURL_TEST_SSH_KEYALGO'} =~ /^(?:rsa|ecdsa|ed25519)$/) ? $ENV{'CURL_TEST_SSH_KEYALGO'} : 'rsa';
             }
             if($libcurl =~ /libssh\/([0-9.]*)\//i) {
                 $feature{"libssh"} = 1;
+                $feature{"sshkeyalgo"} = ($ENV{'CURL_TEST_SSH_KEYALGO'} and
+                    $ENV{'CURL_TEST_SSH_KEYALGO'} =~ /^(?:rsa|ecdsa|ed25519)$/) ? $ENV{'CURL_TEST_SSH_KEYALGO'} : 'rsa';
                 # Detect simple cases of default libssh configuration files ending up
                 # setting `StrictHostKeyChecking no`. include files, quoted values,
                 # '=value' format not implemented.
index 8c183a53667d2ab92b74870a8c803397128221e6..fca0597ec6bd76e815a010a9602cf5018cb9eedc 100644 (file)
@@ -1784,6 +1784,17 @@ sub runrtspserver {
     return (0, $rtsppid, $pid2, $port);
 }
 
+#***************************************************************************
+# Return key algorithm string
+#
+sub sshkeyalgostr {
+    my ($algo) = @_;
+    my %algomap = (
+        ecdsa => 'ecdsa-sha2-nistp256',
+    );
+    return exists $algomap{$algo} ? $algomap{$algo} : 'ssh-' . $algo;
+}
+
 #######################################################################
 # Start the ssh (scp/sftp) server
 #
@@ -1831,6 +1842,9 @@ sub runsshserver {
     $flags .= "--id $idnum " if($idnum > 1);
     $flags .= "--ipv$ipvnum --addr \"$ip\" ";
     $flags .= "--user \"$USER\"";
+    if(defined $feature{"sshkeyalgo"}) {
+        $flags .= ' --keyalgo ' . $feature{"sshkeyalgo"};
+    }
 
     my @tports;
     my $port = getfreeport($ipvnum);
@@ -3199,6 +3213,8 @@ sub subvariables {
 
     $$thing =~ s/${prefix}SSHSRVMD5/$SSHSRVMD5/g;
     $$thing =~ s/${prefix}SSHSRVSHA256/$SSHSRVSHA256/g;
+    my $keyalgostr = sshkeyalgostr(defined $feature{"sshkeyalgo"} ? $feature{"sshkeyalgo"} : "");
+    $$thing =~ s/${prefix}SSHKEYALGO/$keyalgostr/g;
 
     # The purpose of FTPTIME2 is to provide times that can be
     # used for time-out tests and that would work on most hosts as these
index a939670cdec196f6faabb4923f77c32fc954d229..41a21662618f64e41a3764512a5c3a500d62754a 100644 (file)
@@ -91,10 +91,10 @@ our $sshlog          = undef;                    # ssh client log file
 our $sftplog         = undef;                    # sftp client log file
 our $sftpcmds        = 'curl_sftp_cmds';         # sftp client commands batch file
 our $knownhosts      = 'curl_client_knownhosts'; # ssh knownhosts file
-our $hstprvkeyf      = 'curl_host_rsa_key';      # host private key file
-our $hstpubkeyf      = 'curl_host_rsa_key.pub';  # host public key file
-our $hstpubmd5f      = 'curl_host_rsa_key.pub_md5';  # md5 hash of host public key
-our $hstpubsha256f   = 'curl_host_rsa_key.pub_sha256';  # sha256 hash of host public key
+our $hstprvkeyf      = 'curl_host_key';          # host private key file
+our $hstpubkeyf      = 'curl_host_key.pub';      # host public key file
+our $hstpubmd5f      = 'curl_host_key.pub_md5';  # md5 hash of host public key
+our $hstpubsha256f   = 'curl_host_key.pub_sha256';  # sha256 hash of host public key
 our $cliprvkeyf      = 'curl_client_key';        # client private key file
 our $clipubkeyf      = 'curl_client_key.pub';    # client public key file
 
index 282c9fbf2958eb7d1101f4717b138d7e7c4979a3..e425f92ff0f5f51ecc094511d00b397008cf127a 100755 (executable)
@@ -96,6 +96,7 @@ my $listenaddr = '127.0.0.1'; # default address on which to listen
 my $ipvnum = 4;               # default IP version of listener address
 my $idnum = 1;                # default ssh daemon instance number
 my $proto = 'ssh';            # protocol the ssh daemon speaks
+my $keyalgo = 'rsa';          # key algorithm
 my $path = getcwd();          # current working directory
 my $logdir = $path .'/log';   # directory for log files
 my $piddir;                   # directory for server config files
@@ -190,6 +191,12 @@ while(@ARGV) {
             }
         }
     }
+    elsif($ARGV[0] eq '--keyalgo') {
+        if($ARGV[1]) {
+            $keyalgo = $ARGV[1];
+            shift @ARGV;
+        }
+    }
     else {
         print STDERR "\nWarning: sshserver.pl unknown parameter: '$ARGV[0]'\n";
     }
@@ -373,6 +380,7 @@ if((($sshid =~ /OpenSSH/) && ($sshvernum < 299)) ||
 #  -N:  new passphrase   : OpenSSH 1.2.1 and later
 #  -q:  quiet keygen     : OpenSSH 1.2.1 and later
 #  -t:  key type         : OpenSSH 2.5.0 and later
+#  -m:  key format       : OpenSSH 5.6.0 and later
 #
 #  -C:  identity comment : SunSSH 1.0.0 and later
 #  -f:  key filename     : SunSSH 1.0.0 and later
@@ -404,17 +412,17 @@ if((! -e pp($hstprvkeyf)) || (! -s pp($hstprvkeyf)) ||
         # format, e.g. WinCNG.
         # Accepted values: RFC4716, PKCS8, PEM (see also 'man ssh-keygen')
         push @sshkeygenopt, '-m';
-        # Default to the most compatible RSA format for tests.
+        # Default to the most compatible format for tests.
         push @sshkeygenopt, $ENV{'CURL_TEST_SSH_KEY_FORMAT'} ? $ENV{'CURL_TEST_SSH_KEY_FORMAT'} : 'PEM';
     }
     logmsg "generating host keys...\n" if($verbose);
-    if(system($sshkeygen, ('-q', '-t', 'rsa', '-f', pp($hstprvkeyf), '-C', 'curl test server', '-N', '', @sshkeygenopt))) {
+    if(system($sshkeygen, ('-q', '-t', $keyalgo, '-f', pp($hstprvkeyf), '-C', 'curl test server', '-N', '', @sshkeygenopt))) {
         logmsg "Could not generate host key\n";
         exit 1;
     }
     display_file_top(pp($hstprvkeyf)) if($verbose);
     logmsg "generating client keys...\n" if($verbose);
-    if(system($sshkeygen, ('-q', '-t', 'rsa', '-f', pp($cliprvkeyf), '-C', 'curl test client', '-N', '', @sshkeygenopt))) {
+    if(system($sshkeygen, ('-q', '-t', $keyalgo, '-f', pp($cliprvkeyf), '-C', 'curl test client', '-N', '', @sshkeygenopt))) {
         logmsg "Could not generate client key\n";
         exit 1;
     }
@@ -604,7 +612,7 @@ if($sshdid !~ /OpenSSH-Windows/) {
     push @cfgarr, "PidFile $pidfile_config";
     push @cfgarr, '#';
 }
-if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 880)) {
+if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 880) && ($keyalgo eq 'rsa')) {
     push @cfgarr, 'HostKeyAlgorithms +ssh-rsa';
     push @cfgarr, 'PubkeyAcceptedKeyTypes +ssh-rsa';
 }
@@ -828,11 +836,12 @@ if(system("\"$sshd\" -t -f $sshdconfig_abs > $sshdlog 2>&1")) {
 if((! -e pp($knownhosts)) || (! -s pp($knownhosts))) {
     logmsg "generating ssh client known hosts file...\n" if($verbose);
     unlink(pp($knownhosts));
-    if(open(my $rsakeyfile, "<", pp($hstpubkeyf))) {
-        my @rsahostkey = do { local $/ = ' '; <$rsakeyfile> };
-        if(close($rsakeyfile)) {
+    if(open(my $keyfile, "<", pp($hstpubkeyf))) {
+        chomp(my $line = <$keyfile>);
+        if(close($keyfile)) {
             if(open(my $knownhostsh, ">", pp($knownhosts))) {
-                print $knownhostsh "$listenaddr ssh-rsa $rsahostkey[1]\n";
+                my @hostkey = split /\s+/, $line;
+                print $knownhostsh "$listenaddr $hostkey[0] $hostkey[1]\n";
                 if(!close($knownhostsh)) {
                     $error = "Error: cannot close file $knownhosts";
                 }