fix(security): require explicit trust for first-time TLS pins

This commit is contained in:
Peter Steinberger
2026-02-14 17:47:13 +01:00
parent d714ac7797
commit 054366dea4
16 changed files with 549 additions and 76 deletions
@@ -13,6 +13,7 @@ enum GatewaySettingsStore {
private static let manualPortDefaultsKey = "gateway.manual.port"
private static let manualTlsDefaultsKey = "gateway.manual.tls"
private static let discoveryDebugLogsDefaultsKey = "gateway.discovery.debugLogs"
private static let lastGatewayKindDefaultsKey = "gateway.last.kind"
private static let lastGatewayHostDefaultsKey = "gateway.last.host"
private static let lastGatewayPortDefaultsKey = "gateway.last.port"
private static let lastGatewayTlsDefaultsKey = "gateway.last.tls"
@@ -114,25 +115,73 @@ enum GatewaySettingsStore {
account: self.gatewayPasswordAccount(instanceId: instanceId))
}
static func saveLastGatewayConnection(host: String, port: Int, useTLS: Bool, stableID: String) {
enum LastGatewayConnection: Equatable {
case manual(host: String, port: Int, useTLS: Bool, stableID: String)
case discovered(stableID: String, useTLS: Bool)
var stableID: String {
switch self {
case let .manual(_, _, _, stableID):
return stableID
case let .discovered(stableID, _):
return stableID
}
}
var useTLS: Bool {
switch self {
case let .manual(_, _, useTLS, _):
return useTLS
case let .discovered(_, useTLS):
return useTLS
}
}
}
private enum LastGatewayKind: String {
case manual
case discovered
}
static func saveLastGatewayConnectionManual(host: String, port: Int, useTLS: Bool, stableID: String) {
let defaults = UserDefaults.standard
defaults.set(LastGatewayKind.manual.rawValue, forKey: self.lastGatewayKindDefaultsKey)
defaults.set(host, forKey: self.lastGatewayHostDefaultsKey)
defaults.set(port, forKey: self.lastGatewayPortDefaultsKey)
defaults.set(useTLS, forKey: self.lastGatewayTlsDefaultsKey)
defaults.set(stableID, forKey: self.lastGatewayStableIDDefaultsKey)
}
static func loadLastGatewayConnection() -> (host: String, port: Int, useTLS: Bool, stableID: String)? {
static func saveLastGatewayConnectionDiscovered(stableID: String, useTLS: Bool) {
let defaults = UserDefaults.standard
defaults.set(LastGatewayKind.discovered.rawValue, forKey: self.lastGatewayKindDefaultsKey)
defaults.removeObject(forKey: self.lastGatewayHostDefaultsKey)
defaults.removeObject(forKey: self.lastGatewayPortDefaultsKey)
defaults.set(useTLS, forKey: self.lastGatewayTlsDefaultsKey)
defaults.set(stableID, forKey: self.lastGatewayStableIDDefaultsKey)
}
static func loadLastGatewayConnection() -> LastGatewayConnection? {
let defaults = UserDefaults.standard
let stableID = defaults.string(forKey: self.lastGatewayStableIDDefaultsKey)?
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
guard !stableID.isEmpty else { return nil }
let useTLS = defaults.bool(forKey: self.lastGatewayTlsDefaultsKey)
let kindRaw = defaults.string(forKey: self.lastGatewayKindDefaultsKey)?
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
let kind = LastGatewayKind(rawValue: kindRaw) ?? .manual
if kind == .discovered {
return .discovered(stableID: stableID, useTLS: useTLS)
}
let host = defaults.string(forKey: self.lastGatewayHostDefaultsKey)?
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
let port = defaults.integer(forKey: self.lastGatewayPortDefaultsKey)
let useTLS = defaults.bool(forKey: self.lastGatewayTlsDefaultsKey)
let stableID = defaults.string(forKey: self.lastGatewayStableIDDefaultsKey)?
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
guard !host.isEmpty, port > 0, port <= 65535, !stableID.isEmpty else { return nil }
return (host: host, port: port, useTLS: useTLS, stableID: stableID)
// Back-compat: older builds persisted manual-style host/port without a kind marker.
guard !host.isEmpty, port > 0, port <= 65535 else { return nil }
return .manual(host: host, port: port, useTLS: useTLS, stableID: stableID)
}
static func loadGatewayClientIdOverride(stableID: String) -> String? {