Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [2.18.0] - 2026-05-18
- **BREAKING**: `SSHHostkeyVerifyHandler` now receives an OpenSSH-style `SHA256:<base64>` 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].

Expand Down
11 changes: 9 additions & 2 deletions lib/src/ssh_transport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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:<base64>`.
typedef SSHHostkeyVerifyHandler = FutureOr<bool> 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);
Expand Down Expand Up @@ -1180,7 +1187,7 @@ class SSHTransport {
_sessionId ??= exchangeHash;
_sharedSecret = sharedSecret;

final fingerprint = MD5Digest().process(hostkey);
final fingerprint = _hostkeyFingerprint(hostkey);

if (_hostkeyVerified) {
_sendNewKeys();
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand Down
29 changes: 29 additions & 0 deletions test/src/ssh_transport_fingerprint_test.dart
Original file line number Diff line number Diff line change
@@ -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<int>.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));
});
}
Loading