From 7a28b85fe4cec5b90098384392078513ba1b1208 Mon Sep 17 00:00:00 2001 From: Johannes Thyssen Tishman <131173056+thyssentishman@users.noreply.github.com> Date: Mon, 18 May 2026 09:56:22 +0000 Subject: [PATCH 1/3] Use SHA256 instead of MD5 for fingerprints (cherry picked from commit 67cbe8ab2f54c24ccf79d0d44abf92849cf6e5f3) --- lib/src/ssh_transport.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/ssh_transport.dart b/lib/src/ssh_transport.dart index d66a245..0ce524f 100644 --- a/lib/src/ssh_transport.dart +++ b/lib/src/ssh_transport.dart @@ -31,7 +31,7 @@ typedef SSHPrintHandler = void Function(String?); /// Function called when host key is received. /// [type] is the type of the host key, For example 'ssh-rsa', -/// [fingerprint] md5 fingerprint of the host key. +/// [fingerprint] SHA256 fingerprint of the host key. typedef SSHHostkeyVerifyHandler = FutureOr Function( String type, Uint8List fingerprint, @@ -1180,7 +1180,7 @@ class SSHTransport { _sessionId ??= exchangeHash; _sharedSecret = sharedSecret; - final fingerprint = MD5Digest().process(hostkey); + final fingerprint = SHA256Digest().process(hostkey); if (_hostkeyVerified) { _sendNewKeys(); From 6d57773ef8220b878425de0c3cc652b898fa5177 Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Tue, 19 May 2026 08:24:37 +0200 Subject: [PATCH 2/3] Revert "Use SHA256 instead of MD5 for fingerprints" This reverts commit 7a28b85fe4cec5b90098384392078513ba1b1208. --- lib/src/ssh_transport.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/ssh_transport.dart b/lib/src/ssh_transport.dart index 0ce524f..d66a245 100644 --- a/lib/src/ssh_transport.dart +++ b/lib/src/ssh_transport.dart @@ -31,7 +31,7 @@ typedef SSHPrintHandler = void Function(String?); /// Function called when host key is received. /// [type] is the type of the host key, For example 'ssh-rsa', -/// [fingerprint] SHA256 fingerprint of the host key. +/// [fingerprint] md5 fingerprint of the host key. typedef SSHHostkeyVerifyHandler = FutureOr Function( String type, Uint8List fingerprint, @@ -1180,7 +1180,7 @@ class SSHTransport { _sessionId ??= exchangeHash; _sharedSecret = sharedSecret; - final fingerprint = SHA256Digest().process(hostkey); + final fingerprint = MD5Digest().process(hostkey); if (_hostkeyVerified) { _sendNewKeys(); From ea341d586270f30b020ca03167d6eae8c8290b73 Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Tue, 19 May 2026 08:26:46 +0200 Subject: [PATCH 3/3] feat: update SSHHostkeyVerifyHandler to use OpenSSH-style SHA256 fingerprints and add tests --- CHANGELOG.md | 3 ++ lib/src/ssh_transport.dart | 11 ++++++-- pubspec.yaml | 2 +- test/src/ssh_transport_fingerprint_test.dart | 29 ++++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 test/src/ssh_transport_fingerprint_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index f7d0b7f..772ed6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## [2.18.0] - 2026-05-18 +- **BREAKING**: `SSHHostkeyVerifyHandler` now receives an OpenSSH-style `SHA256:` host key fingerprint instead of the previous raw MD5 digest, so host key pinning code must be updated accordingly [#162]. Thanks [@thyssentishman]. + ## [2.17.1] - 2026-04-12 - Made `SSHPem.decode` accept CRLF (`\r\n`) line endings in addition to LF when parsing PEM content [#157]. Thanks [@gkc]. diff --git a/lib/src/ssh_transport.dart b/lib/src/ssh_transport.dart index d66a245..815885e 100644 --- a/lib/src/ssh_transport.dart +++ b/lib/src/ssh_transport.dart @@ -31,12 +31,19 @@ typedef SSHPrintHandler = void Function(String?); /// Function called when host key is received. /// [type] is the type of the host key, For example 'ssh-rsa', -/// [fingerprint] md5 fingerprint of the host key. +/// [fingerprint] OpenSSH-style SHA256 fingerprint of the host key, +/// UTF-8 encoded as `SHA256:`. typedef SSHHostkeyVerifyHandler = FutureOr Function( String type, Uint8List fingerprint, ); +Uint8List _hostkeyFingerprint(Uint8List hostkey) { + final fingerprint = SHA256Digest().process(hostkey); + final encoded = base64.encode(fingerprint).replaceAll('=', ''); + return Uint8List.fromList(utf8.encode('SHA256:$encoded')); +} + typedef SSHTransportReadyHandler = void Function(); typedef SSHPacketHandler = void Function(Uint8List payload); @@ -1180,7 +1187,7 @@ class SSHTransport { _sessionId ??= exchangeHash; _sharedSecret = sharedSecret; - final fingerprint = MD5Digest().process(hostkey); + final fingerprint = _hostkeyFingerprint(hostkey); if (_hostkeyVerified) { _sendNewKeys(); diff --git a/pubspec.yaml b/pubspec.yaml index b48b3a4..8b15ed9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dartssh2 -version: 2.17.1 +version: 2.18.0 description: SSH and SFTP client written in pure Dart, aiming to be feature-rich as well as easy to use. homepage: https://github.com/TerminalStudio/dartssh2 diff --git a/test/src/ssh_transport_fingerprint_test.dart b/test/src/ssh_transport_fingerprint_test.dart new file mode 100644 index 0000000..9415010 --- /dev/null +++ b/test/src/ssh_transport_fingerprint_test.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; +import 'dart:mirrors'; +import 'dart:typed_data'; + +import 'package:dartssh2/dartssh2.dart'; +import 'package:pointycastle/export.dart'; +import 'package:test/test.dart'; + +void main() { + final transportLibrary = reflectClass(SSHTransport).owner as LibraryMirror; + + Uint8List invokeFingerprint(Uint8List hostkey) { + final symbol = + MirrorSystem.getSymbol('_hostkeyFingerprint', transportLibrary); + return transportLibrary.invoke(symbol, [hostkey]).reflectee as Uint8List; + } + + test('formats host key fingerprints using OpenSSH SHA256 style', () { + final hostkey = + Uint8List.fromList(List.generate(32, (index) => index)); + + final fingerprint = utf8.decode(invokeFingerprint(hostkey)); + final expectedDigest = SHA256Digest().process(hostkey); + final expected = + 'SHA256:${base64.encode(expectedDigest).replaceAll('=', '')}'; + + expect(fingerprint, equals(expected)); + }); +}