Größte offene Härtungslücke geschlossen: SSHTransport nutzte bisher .acceptAnything() für Host-Key-Validierung, akzeptierte also jeden Schlüssel ohne Prüfung -- ein Man-in-the-Middle im lokalen Netz wäre unbemerkt geblieben. Jetzt Trust-on-first-use wie beim REST-Zertifikat: - SSHHostKeyFingerprint: SHA256 über NIOSSHPublicKey.write(to:) (die SSH-Wire-Format-Bytes des Schlüssels) -- exakt die Bytes, die auch OpenSSH für seine SHA256:-Fingerabdrücke hasht. Per Unit-Test gegen einen echten ssh-keygen-erzeugten Testschlüssel kreuzgeprüft (SHA256:Hllxv6LLoHl2XTIXGGjUYJHbPFoH2F7iMrR74C5J95g), nicht geraten. - SSHHostKeyTrustStore: UserDefaults-Persistenz pro Host, Pendant zu CertificateTrustStore. - SSHTransport conformt jetzt selbst zu NIOSSHClientServerAuthentication- Delegate (wie RestTransport zu URLSessionDelegate) und übergibt sich selbst als .custom(self) Host-Key-Validator. - ConnectionService: neuer State .needsSSHHostKeyConfirmation, eigener Bestätigungs-Retry-Pfad (trustCurrentSSHHostKeyAndRetry), analog zum bestehenden Zertifikat-Flow. - ConnectView: zweiter Bestätigungsdialog mit Warnhinweis, dass ein geänderter Fingerabdruck bei zuvor schon verbundenen Routern auf ein manipuliertes Netzwerk hindeuten könnte. BackupService/FactoryResetService bekommen die TOFU-Prüfung automatisch mit (SSHTransport-Default-Parameter, gleicher UserDefaults-Speicher), ohne eigene Bestätigungs-UI -- in der Praxis unkritisch, da der Verbinden-Tab das Vertrauen immer zuerst herstellt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HReLXMbmPvtQ23p1iWiJNW
45 lines
1.3 KiB
Swift
45 lines
1.3 KiB
Swift
import Foundation
|
|
|
|
@MainActor
|
|
final class ConnectViewModel: ObservableObject {
|
|
@Published var host: String = "192.168.88.1"
|
|
@Published var username: String = "admin"
|
|
@Published var password: String = ""
|
|
@Published var rememberPassword: Bool = true
|
|
|
|
let connectionService: ConnectionService
|
|
private let keychain = KeychainService()
|
|
|
|
init(connectionService: ConnectionService) {
|
|
self.connectionService = connectionService
|
|
}
|
|
|
|
func onAppear() {
|
|
if let saved = keychain.loadPassword(forHost: host, username: username) {
|
|
password = saved
|
|
}
|
|
}
|
|
|
|
func connect() {
|
|
let credentials = RouterOSCredentials(host: host, username: username, password: password)
|
|
if rememberPassword {
|
|
keychain.save(password: password, forHost: host, username: username)
|
|
}
|
|
Task {
|
|
await connectionService.connect(with: credentials)
|
|
}
|
|
}
|
|
|
|
func trustAndRetry(fingerprint: String) {
|
|
Task {
|
|
await connectionService.trustCurrentCertificateAndRetry(fingerprint: fingerprint)
|
|
}
|
|
}
|
|
|
|
func trustSSHHostKeyAndRetry(fingerprint: String) {
|
|
Task {
|
|
await connectionService.trustCurrentSSHHostKeyAndRetry(fingerprint: fingerprint)
|
|
}
|
|
}
|
|
}
|