From e09f8db75da590061573fa8d78faa1598725c9a2 Mon Sep 17 00:00:00 2001 From: Lubos Petrovic Date: Mon, 27 Jul 2026 15:16:47 +0200 Subject: [PATCH] Support the RFC 8731 name curve25519-sha256 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kex is offered under two names: the original curve25519-sha256@libssh.org and the RFC 8731 name curve25519-sha256. Only the former was advertised, so a server hardened down to a single kex (KexAlgorithms curve25519-sha256, a common hardening recipe) shares no name with us and the handshake fails with "no matching key exchange method found" — even though both sides implement the very same X25519 exchange. Add SSHKexType.x25519Rfc with the RFC name, offer it right after the libssh.org spelling, and route it to the same SSHKexX25519 in the transport switch. No wire-format change: same digest, same exchange. --- lib/src/algorithm/ssh_kex_type.dart | 9 +++++++++ lib/src/ssh_algorithm.dart | 1 + lib/src/ssh_transport.dart | 1 + test/src/algorithm/ssh_cipher_type_test.dart | 1 + 4 files changed, 12 insertions(+) diff --git a/lib/src/algorithm/ssh_kex_type.dart b/lib/src/algorithm/ssh_kex_type.dart index 28663197..420a7496 100644 --- a/lib/src/algorithm/ssh_kex_type.dart +++ b/lib/src/algorithm/ssh_kex_type.dart @@ -7,6 +7,15 @@ class SSHKexType extends SSHAlgorithm { digestFactory: digestSha256, ); + /// RFC 8731 name for the same algorithm as [x25519]. Servers hardened to a + /// single kex commonly offer only this spelling, and OpenSSH matches names + /// literally — without it the handshake dies with "no matching key exchange + /// method found". + static const x25519Rfc = SSHKexType._( + name: 'curve25519-sha256', + digestFactory: digestSha256, + ); + static const nistp256 = SSHKexType._( name: 'ecdh-sha2-nistp256', digestFactory: digestSha256, diff --git a/lib/src/ssh_algorithm.dart b/lib/src/ssh_algorithm.dart index fc57995c..1107647d 100644 --- a/lib/src/ssh_algorithm.dart +++ b/lib/src/ssh_algorithm.dart @@ -46,6 +46,7 @@ class SSHAlgorithms { const SSHAlgorithms({ this.kex = const [ SSHKexType.x25519, + SSHKexType.x25519Rfc, SSHKexType.nistp521, SSHKexType.nistp384, SSHKexType.nistp256, diff --git a/lib/src/ssh_transport.dart b/lib/src/ssh_transport.dart index ff46a42d..c61ce10b 100644 --- a/lib/src/ssh_transport.dart +++ b/lib/src/ssh_transport.dart @@ -1196,6 +1196,7 @@ class SSHTransport { switch (_kexType) { case SSHKexType.x25519: + case SSHKexType.x25519Rfc: _kex = await SSHKexX25519.createAsync(); break; case SSHKexType.nistp256: diff --git a/test/src/algorithm/ssh_cipher_type_test.dart b/test/src/algorithm/ssh_cipher_type_test.dart index 132b2263..a13b5751 100644 --- a/test/src/algorithm/ssh_cipher_type_test.dart +++ b/test/src/algorithm/ssh_cipher_type_test.dart @@ -106,6 +106,7 @@ void main() { algorithms.kex, equals([ SSHKexType.x25519, + SSHKexType.x25519Rfc, SSHKexType.nistp521, SSHKexType.nistp384, SSHKexType.nistp256,