Files
RouterOS/RouterOSAssistant/Core/Models/RouterOSModels.swift
T
KayandClaude Sonnet 5 13344c265e M7: SSH-Hostkey-TOFU (ersetzt .acceptAnything())
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
2026-09-13 00:40:26 +02:00

52 lines
1.5 KiB
Swift

import Foundation
struct RouterOSCredentials: Equatable {
var host: String
var username: String
var password: String
var httpsPort: Int = 443
var sshPort: Int = 22
}
struct RouterDeviceInfo: Equatable {
var boardName: String
var routerOSVersion: String
var architecture: String
var uptime: String
}
struct NetworkInterface: Equatable, Identifiable {
var id: String { name }
var name: String
var type: String
var running: Bool
var disabled: Bool
var macAddress: String?
}
enum RouterOSError: LocalizedError, Equatable {
case notConnected
case invalidResponse(String)
case authenticationFailed
case untrustedCertificate(fingerprint: String)
case untrustedSSHHostKey(fingerprint: String)
case transportUnavailable(String)
var errorDescription: String? {
switch self {
case .notConnected:
return "Keine Verbindung zum Router."
case .invalidResponse(let detail):
return "Unerwartete Antwort vom Router: \(detail)"
case .authenticationFailed:
return "Anmeldung fehlgeschlagen. Bitte Zugangsdaten prüfen."
case .untrustedCertificate(let fingerprint):
return "Unbekanntes Zertifikat (Fingerabdruck \(fingerprint)). Bitte bestätigen."
case .untrustedSSHHostKey(let fingerprint):
return "Unbekannter SSH-Schlüssel (Fingerabdruck \(fingerprint)). Bitte bestätigen."
case .transportUnavailable(let detail):
return detail
}
}
}