Add UI, Model, and BitcoinService support to use insecure SSL. Dependent on BDK-FFI that exposes validate_domain parameter in the electrum client contstructor
This commit is contained in:
parent
bd847a9a57
commit
5ec22bb4fc
@ -17,6 +17,7 @@ final class WalletProfile {
|
||||
var electrumPort: Int
|
||||
var electrumSSL: Int // 0 = network default, 1 = TCP, 2 = SSL
|
||||
var blockExplorerHost: String // empty = mempool.space
|
||||
var electrumAllowInsecureSSL: Bool = false
|
||||
var privacyMode: Bool = false
|
||||
|
||||
@Relationship(deleteRule: .cascade, inverse: \CosignerInfo.wallet)
|
||||
@ -35,6 +36,7 @@ final class WalletProfile {
|
||||
electrumHost: String = "",
|
||||
electrumPort: Int = 0,
|
||||
electrumSSL: Int = 0,
|
||||
electrumAllowInsecureSSL: Bool = false,
|
||||
blockExplorerHost: String = "",
|
||||
privacyMode: Bool = false
|
||||
) {
|
||||
@ -51,6 +53,7 @@ final class WalletProfile {
|
||||
self.electrumHost = electrumHost
|
||||
self.electrumPort = electrumPort
|
||||
self.electrumSSL = electrumSSL
|
||||
self.electrumAllowInsecureSSL = electrumAllowInsecureSSL
|
||||
self.blockExplorerHost = blockExplorerHost
|
||||
self.privacyMode = privacyMode
|
||||
cosigners = []
|
||||
@ -69,7 +72,7 @@ final class WalletProfile {
|
||||
case 2: true // SSL
|
||||
default: net.usesSSL // 0 = network default
|
||||
}
|
||||
return ElectrumConfig(host: host, port: port, useSSL: ssl)
|
||||
return ElectrumConfig(host: host, port: port, useSSL: ssl, allowInsecureSSL: electrumAllowInsecureSSL)
|
||||
}
|
||||
|
||||
var multisigDescription: String {
|
||||
|
||||
@ -264,7 +264,8 @@ final class BitcoinService {
|
||||
addToLog("Connecting to Electrum: \(config.url)")
|
||||
do {
|
||||
let url = config.url
|
||||
electrumClient = try await Task.detached { try ElectrumClient(url: url) }.value
|
||||
let validateDomain = !config.allowInsecureSSL
|
||||
electrumClient = try await Task.detached { try ElectrumClient(url: url, validateDomain: validateDomain) }.value
|
||||
electrumConnectionError = nil
|
||||
addToLog("Electrum client initialized")
|
||||
} catch {
|
||||
@ -301,7 +302,8 @@ final class BitcoinService {
|
||||
let config = profile.electrumConfig
|
||||
addToLog("Re-initializing Electrum client: \(config.url)")
|
||||
let reconnectURL = config.url
|
||||
electrumClient = try await Task.detached { try ElectrumClient(url: reconnectURL) }.value
|
||||
let validateDomain = !config.allowInsecureSSL
|
||||
electrumClient = try await Task.detached { try ElectrumClient(url: reconnectURL, validateDomain: validateDomain) }.value
|
||||
electrumConnectionError = nil
|
||||
}
|
||||
|
||||
@ -453,13 +455,14 @@ final class BitcoinService {
|
||||
@discardableResult
|
||||
func testElectrumConnection(config: ElectrumConfig) async throws -> UInt32 {
|
||||
// Pre-check SSL certificate before handing off to BDK
|
||||
if config.useSSL {
|
||||
if config.useSSL, !config.allowInsecureSSL {
|
||||
try await Self.validateTLSCertificate(host: config.host, port: config.port)
|
||||
}
|
||||
|
||||
let url = config.url
|
||||
let validateDomain = !config.allowInsecureSSL
|
||||
let header = try await Task.detached {
|
||||
let client = try ElectrumClient(url: url)
|
||||
let client = try ElectrumClient(url: url, validateDomain: validateDomain)
|
||||
return try client.blockHeadersSubscribe()
|
||||
}.value
|
||||
return UInt32(header.height)
|
||||
|
||||
@ -4,21 +4,24 @@ struct ElectrumConfig: Equatable {
|
||||
var host: String
|
||||
var port: UInt16
|
||||
var useSSL: Bool
|
||||
var allowInsecureSSL: Bool
|
||||
|
||||
var url: String {
|
||||
let proto = useSSL ? "ssl" : "tcp"
|
||||
return "\(proto)://\(host):\(port)"
|
||||
}
|
||||
|
||||
init(host: String, port: UInt16, useSSL: Bool) {
|
||||
init(host: String, port: UInt16, useSSL: Bool, allowInsecureSSL: Bool = false) {
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.useSSL = useSSL
|
||||
self.allowInsecureSSL = allowInsecureSSL
|
||||
}
|
||||
|
||||
init(network: BitcoinNetwork) {
|
||||
host = network.defaultElectrumHost ?? ""
|
||||
port = network.defaultElectrumPort
|
||||
useSSL = network.usesSSL
|
||||
allowInsecureSSL = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +50,7 @@ final class SetupWizardViewModel {
|
||||
var electrumHost: String = ""
|
||||
var electrumPort: String = ""
|
||||
var electrumSSL: Int = 0 // 0 = network default, 1 = TCP, 2 = SSL
|
||||
var electrumAllowInsecureSSL: Bool = false
|
||||
|
||||
/// Returns an error message if the descriptor contains keys that don't match the selected network, nil otherwise.
|
||||
var descriptorNetworkMismatchError: String? {
|
||||
@ -449,6 +450,7 @@ final class SetupWizardViewModel {
|
||||
electrumHost: electrumHost.trimmingCharacters(in: .whitespaces),
|
||||
electrumPort: Int(electrumPort) ?? 0,
|
||||
electrumSSL: electrumSSL,
|
||||
electrumAllowInsecureSSL: electrumAllowInsecureSSL,
|
||||
blockExplorerHost: blockExplorerHost.trimmingCharacters(in: .whitespaces)
|
||||
)
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ struct WalletInfoView: View {
|
||||
@State private var connectionTestResult: String?
|
||||
@State private var blockExplorerText: String = ""
|
||||
@State private var initialElectrumConfig: ElectrumConfig?
|
||||
@State private var showInsecureSSLAlert = false
|
||||
@State private var showDescriptorQR = false
|
||||
@State private var showDeleteConfirmation = false
|
||||
@State private var showDescriptorPDF = false
|
||||
@ -200,7 +201,12 @@ struct WalletInfoView: View {
|
||||
default: wallet.bitcoinNetwork.usesSSL ? 2 : 1
|
||||
}
|
||||
},
|
||||
set: { wallet.electrumSSL = $0 }
|
||||
set: {
|
||||
wallet.electrumSSL = $0
|
||||
if $0 != 2 {
|
||||
wallet.electrumAllowInsecureSSL = false
|
||||
}
|
||||
}
|
||||
)) {
|
||||
Text("TCP").tag(1)
|
||||
Text("SSL").tag(2)
|
||||
@ -208,6 +214,24 @@ struct WalletInfoView: View {
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
|
||||
if wallet.electrumSSL == 2 || (wallet.electrumSSL == 0 && wallet.bitcoinNetwork.usesSSL) {
|
||||
Toggle(isOn: Binding(
|
||||
get: { wallet.electrumAllowInsecureSSL },
|
||||
set: { newValue in
|
||||
if newValue {
|
||||
showInsecureSSLAlert = true
|
||||
} else {
|
||||
wallet.electrumAllowInsecureSSL = false
|
||||
}
|
||||
}
|
||||
)) {
|
||||
Text("Allow insecure SSL")
|
||||
.font(.hbBody(13))
|
||||
.foregroundStyle(Color.hbTextPrimary)
|
||||
}
|
||||
.tint(Color.hbBitcoinOrange)
|
||||
}
|
||||
|
||||
if let result = connectionTestResult {
|
||||
Text(result)
|
||||
.font(.hbBody(13))
|
||||
@ -426,6 +450,14 @@ struct WalletInfoView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert("Allow Insecure SSL?", isPresented: $showInsecureSSLAlert) {
|
||||
Button("Cancel", role: .cancel) {}
|
||||
Button("Allow", role: .destructive) {
|
||||
wallet.electrumAllowInsecureSSL = true
|
||||
}
|
||||
} message: {
|
||||
Text("This removes the requirement to verify that the server is who it claims to be. The connection will still be encrypted, but self-signed, expired, or invalid certificates will be accepted.")
|
||||
}
|
||||
.sheet(isPresented: $showEditCosigners) {
|
||||
EditCosignersView(wallet: wallet)
|
||||
}
|
||||
@ -460,6 +492,7 @@ struct WalletInfoView: View {
|
||||
wallet.electrumHost = ""
|
||||
wallet.electrumPort = 0
|
||||
wallet.electrumSSL = 0
|
||||
wallet.electrumAllowInsecureSSL = false
|
||||
electrumHostText = ""
|
||||
electrumPortText = ""
|
||||
connectionTestResult = nil
|
||||
|
||||
@ -2,6 +2,15 @@ import SwiftUI
|
||||
|
||||
struct ElectrumServerSetupSection: View {
|
||||
@Bindable var viewModel: SetupWizardViewModel
|
||||
@State private var showInsecureSSLAlert = false
|
||||
|
||||
private var isSSLSelected: Bool {
|
||||
switch viewModel.electrumSSL {
|
||||
case 1: false
|
||||
case 2: true
|
||||
default: viewModel.network.usesSSL
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 12) {
|
||||
@ -50,7 +59,12 @@ struct ElectrumServerSetupSection: View {
|
||||
default: viewModel.network.usesSSL ? 2 : 1
|
||||
}
|
||||
},
|
||||
set: { viewModel.electrumSSL = $0 }
|
||||
set: {
|
||||
viewModel.electrumSSL = $0
|
||||
if $0 != 2 {
|
||||
viewModel.electrumAllowInsecureSSL = false
|
||||
}
|
||||
}
|
||||
)) {
|
||||
Text("TCP").tag(1)
|
||||
Text("SSL").tag(2)
|
||||
@ -59,6 +73,24 @@ struct ElectrumServerSetupSection: View {
|
||||
}
|
||||
}
|
||||
|
||||
if isSSLSelected {
|
||||
Toggle(isOn: Binding(
|
||||
get: { viewModel.electrumAllowInsecureSSL },
|
||||
set: { newValue in
|
||||
if newValue {
|
||||
showInsecureSSLAlert = true
|
||||
} else {
|
||||
viewModel.electrumAllowInsecureSSL = false
|
||||
}
|
||||
}
|
||||
)) {
|
||||
Text("Allow insecure SSL")
|
||||
.font(.hbBody(13))
|
||||
.foregroundStyle(Color.hbTextPrimary)
|
||||
}
|
||||
.tint(Color.hbBitcoinOrange)
|
||||
}
|
||||
|
||||
if viewModel.network.defaultElectrumHost != nil {
|
||||
Text("Leave blank to use defaults for \(viewModel.network.displayName)")
|
||||
.font(.hbBody(11))
|
||||
@ -70,6 +102,14 @@ struct ElectrumServerSetupSection: View {
|
||||
}
|
||||
}
|
||||
.hbCard()
|
||||
.alert("Allow Insecure SSL?", isPresented: $showInsecureSSLAlert) {
|
||||
Button("Cancel", role: .cancel) {}
|
||||
Button("Allow", role: .destructive) {
|
||||
viewModel.electrumAllowInsecureSSL = true
|
||||
}
|
||||
} message: {
|
||||
Text("This removes the requirement to verify that the server is who it claims to be. The connection will still be encrypted, but self-signed, expired, or invalid certificates will be accepted.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user