Meshtastic-Apple/Meshtastic/Views/Settings/Config/SecurityConfig.swift

223 lines
7.8 KiB
Swift
Raw Normal View History

2024-08-07 09:43:48 -07:00
//
// Security.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 8/7/24.
//
import Foundation
import SwiftUI
import CoreData
import MeshtasticProtobufs
import OSLog
struct SecurityConfig: View {
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
2024-08-07 09:43:48 -07:00
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@Environment(\.dismiss) private var goBack
var node: NodeInfoEntity?
@State var hasChanges = false
@State var publicKey = ""
2024-08-19 21:50:28 -07:00
@State var hasValidPublicKey: Bool = false
2024-08-07 09:43:48 -07:00
@State var privateKey = ""
2024-08-19 21:50:28 -07:00
@State var hasValidPrivateKey: Bool = false
2024-08-07 09:43:48 -07:00
@State var adminKey = ""
2024-08-19 21:50:28 -07:00
@State var hasValidAdminKey: Bool = true
2024-08-08 07:33:31 -07:00
@State var isManaged = false
@State var serialEnabled = false
2024-08-08 10:39:45 -07:00
@State var debugLogApiEnabled = false
2024-08-08 07:33:31 -07:00
@State var bluetoothLoggingEnabled = false
@State var adminChannelEnabled = false
2024-08-07 09:43:48 -07:00
var body: some View {
VStack {
Form {
ConfigHeader(title: "Security", config: \.securityConfig, node: node, onAppear: setSecurityValues)
Text("Security Config Settings require a firmware version 2.5+")
.font(.title3)
2024-08-08 07:33:31 -07:00
Section(header: Text("Admin & Direct Message Keys")) {
VStack(alignment: .leading) {
Label("Public Key", systemImage: "key")
2024-08-19 21:50:28 -07:00
SecureInput("Public Key", text: $publicKey, isValid: $hasValidPublicKey)
.background(
RoundedRectangle(cornerRadius: 10.0)
.stroke(hasValidPublicKey ? Color.clear : Color.red, lineWidth: 2.0)
)
Text("Sent out to other nodes on the mesh to allow them to compute a shared secret key.")
.foregroundStyle(.secondary)
.font(idiom == .phone ? .caption : .callout)
2024-08-08 07:33:31 -07:00
}
VStack(alignment: .leading) {
Label("Private Key", systemImage: "key.fill")
2024-08-19 21:50:28 -07:00
SecureInput("Private Key", text: $privateKey, isValid: $hasValidPrivateKey)
.background(
RoundedRectangle(cornerRadius: 10.0)
.stroke(hasValidPrivateKey ? Color.clear : Color.red, lineWidth: 2.0)
)
Text("Used to create a shared key with a remote device.")
.foregroundStyle(.secondary)
.font(idiom == .phone ? .caption : .callout)
2024-08-08 07:33:31 -07:00
}
VStack(alignment: .leading) {
Label("Admin Key", systemImage: "key.viewfinder")
2024-08-19 21:50:28 -07:00
SecureInput("Admin Key", text: $adminKey, isValid: $hasValidAdminKey)
Text("The public key authorized to send admin messages to this node.")
.foregroundStyle(.secondary)
.font(idiom == .phone ? .caption : .callout)
2024-08-08 07:33:31 -07:00
}
}
Section(header: Text("Logs")) {
Toggle(isOn: $bluetoothLoggingEnabled) {
Label("Bluetooth Logs", systemImage: "dot.radiowaves.right")
Text("View and export position-redacted device logs over Bluetooth")
Link("View Logs", destination: URL(string: "meshtastic:///settings/debugLogs")!)
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
Section(header: Text("Administration")) {
if adminKey.length > 0 || adminChannelEnabled {
Toggle(isOn: $isManaged) {
Label("Managed Device", systemImage: "gearshape.arrow.triangle.2.circlepath")
Text("Device is managed by a mesh administrator.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
Toggle(isOn: $adminChannelEnabled) {
Label("Legacy Administration", systemImage: "lock.slash")
Text("Allow incoming device control over the insecure legacy admin channel.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
Section(header: Text("Developer")) {
Toggle(isOn: $serialEnabled) {
Label("Serial Console", systemImage: "terminal")
Text("Serial Console over the Stream API.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
if serialEnabled {
2024-08-08 10:39:45 -07:00
Toggle(isOn: $debugLogApiEnabled) {
2024-08-08 07:33:31 -07:00
Label("Serial Debug Logs", systemImage: "ant.fill")
Text("Output live debug logging over serial.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
}
2024-08-07 09:43:48 -07:00
}
}
.scrollDismissesKeyboard(.immediately)
2024-08-08 07:33:31 -07:00
.navigationTitle("Security Config")
2024-08-11 09:59:52 -07:00
.navigationBarItems(trailing: ZStack {
ConnectedDevice(
bluetoothOn: bleManager.isSwitchedOn,
deviceConnected: bleManager.connectedPeripheral != nil,
name: "\(bleManager.connectedPeripheral?.shortName ?? "?")"
)
2024-08-08 07:33:31 -07:00
})
2024-08-11 10:04:17 -07:00
.onChange(of: isManaged) {
if $0 != node?.securityConfig?.isManaged { hasChanges = true }
2024-08-11 09:59:52 -07:00
}
2024-08-11 10:04:17 -07:00
.onChange(of: serialEnabled) {
if $0 != node?.securityConfig?.serialEnabled { hasChanges = true }
2024-08-11 09:59:52 -07:00
}
2024-08-11 10:04:17 -07:00
.onChange(of: debugLogApiEnabled) {
if $0 != node?.securityConfig?.debugLogApiEnabled { hasChanges = true }
2024-08-10 07:05:09 -07:00
}
2024-08-11 10:04:17 -07:00
.onChange(of: bluetoothLoggingEnabled) {
if $0 != node?.securityConfig?.bluetoothLoggingEnabled { hasChanges = true }
2024-08-11 09:59:52 -07:00
}
2024-08-11 10:04:17 -07:00
.onChange(of: adminChannelEnabled) {
if $0 != node?.securityConfig?.adminChannelEnabled { hasChanges = true }
2024-08-11 09:59:52 -07:00
}
2024-08-18 11:43:42 -07:00
.onChange(of: publicKey) { _ in
2024-08-19 21:50:28 -07:00
let tempKey = Data(base64Encoded: publicKey) ?? Data()
if tempKey.count == 32 {
hasValidPublicKey = true
} else {
hasValidPublicKey = false
}
2024-08-18 11:43:42 -07:00
hasChanges = true
}
.onChange(of: privateKey) { _ in
2024-08-19 21:50:28 -07:00
let tempKey = Data(base64Encoded: privateKey) ?? Data()
if tempKey.count == 32 {
hasValidPrivateKey = true
} else {
hasValidPrivateKey = false
}
2024-08-18 11:43:42 -07:00
hasChanges = true
}
2024-08-20 07:33:29 -07:00
.onChange(of: adminKey) { key in
let tempKey = Data(base64Encoded: key) ?? Data()
if key.isEmpty {
hasValidAdminKey = true
} else if tempKey.count == 32 {
2024-08-19 21:50:28 -07:00
hasValidAdminKey = true
} else {
hasValidAdminKey = false
}
2024-08-18 10:00:15 -07:00
hasChanges = true
}
.onFirstAppear {
// Need to request a Power config from the remote node before allowing changes
if bleManager.connectedPeripheral != nil && node?.securityConfig == nil {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context)
if node != nil && connectedNode != nil {
_ = bleManager.requestSecurityConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
}
}
}
2024-08-11 09:59:52 -07:00
2024-08-10 07:05:09 -07:00
SaveConfigButton(node: node, hasChanges: $hasChanges) {
2024-08-19 21:50:28 -07:00
2024-08-20 07:40:20 -07:00
if !hasValidPublicKey || !hasValidPrivateKey || !hasValidAdminKey {
2024-08-19 21:50:28 -07:00
return
}
2024-08-10 07:05:09 -07:00
guard let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context),
let fromUser = connectedNode.user,
let toUser = node?.user else {
return
}
var config = Config.SecurityConfig()
2024-08-18 11:39:41 -07:00
config.publicKey = Data(base64Encoded: publicKey) ?? Data()
config.privateKey = Data(base64Encoded: privateKey) ?? Data()
2024-08-18 08:44:33 -07:00
config.adminKey = Data(base64Encoded: adminKey) ?? Data()
2024-08-10 07:05:09 -07:00
config.isManaged = isManaged
config.serialEnabled = serialEnabled
config.debugLogApiEnabled = debugLogApiEnabled
config.bluetoothLoggingEnabled = bluetoothLoggingEnabled
config.adminChannelEnabled = adminChannelEnabled
let adminMessageId = bleManager.saveSecurityConfig(
config: config,
fromUser: fromUser,
toUser: toUser,
adminIndex: connectedNode.myInfo?.adminIndex ?? 0
)
if adminMessageId > 0 {
// Should show a saved successfully alert once I know that to be true
// for now just disable the button after a successful save
hasChanges = false
goBack()
}
}
2024-08-07 09:43:48 -07:00
}
2024-08-10 07:05:09 -07:00
2024-08-07 09:43:48 -07:00
func setSecurityValues() {
2024-08-08 07:33:31 -07:00
self.publicKey = node?.securityConfig?.publicKey?.base64EncodedString() ?? ""
self.privateKey = node?.securityConfig?.privateKey?.base64EncodedString() ?? ""
self.adminKey = node?.securityConfig?.adminKey?.base64EncodedString() ?? ""
2024-08-07 09:43:48 -07:00
self.isManaged = node?.securityConfig?.isManaged ?? false
self.serialEnabled = node?.securityConfig?.serialEnabled ?? false
2024-08-08 10:39:45 -07:00
self.debugLogApiEnabled = node?.securityConfig?.debugLogApiEnabled ?? false
2024-08-08 07:33:31 -07:00
self.bluetoothLoggingEnabled = node?.securityConfig?.bluetoothLoggingEnabled ?? false
2024-08-07 09:43:48 -07:00
self.adminChannelEnabled = node?.securityConfig?.adminChannelEnabled ?? false
self.hasChanges = false
}
}