added keydetail view

added faceid authentication to view private keys
added authwithbiometrics for secure displaying of sensitive data
fix getkeys returns nothing
added centredlabel (hstack with two spacers)
made keypair identifieable
fix alignment of text
This commit is contained in:
neon443
2025-06-25 16:46:41 +01:00
parent d4b9a4e2ae
commit 2548dde85b
6 changed files with 140 additions and 9 deletions

View File

@@ -6,8 +6,9 @@
//
import Foundation
import LocalAuthentication
class HostsManager: ObservableObject {
class HostsManager: ObservableObject, @unchecked Sendable {
private let userDefaults = NSUbiquitousKeyValueStore.default
@Published var savedHosts: [Host] = []
@@ -61,7 +62,7 @@ class HostsManager: ObservableObject {
func getKeys() -> [Keypair] {
var result: [Keypair] = []
for host in savedHosts {
if !result.contains(where: { $0 == Keypair(publicKey: host.publicKey, privateKey: host.privateKey)}) {
if result.contains(where: { $0 == Keypair(publicKey: host.publicKey, privateKey: host.privateKey)}) {
} else {
result.append(Keypair(publicKey: host.publicKey, privateKey: host.privateKey))
@@ -69,4 +70,19 @@ class HostsManager: ObservableObject {
}
return result
}
func authWithBiometrics() async -> Bool {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
return false
}
let reason = "Authenticate yourself with Face ID to view private keys"
return await withCheckedContinuation { continuation in
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, _ in
continuation.resume(returning: success)
}
}
}
}

View File

@@ -0,0 +1,25 @@
//
// CenteredLabel.swift
// ShhShell
//
// Created by neon443 on 25/06/2025.
//
import SwiftUI
struct CenteredLabel: View {
@State var title: String
@State var systemName: String
var body: some View {
HStack {
Spacer()
Image(systemName: systemName)
Text(title)
Spacer()
}
}
}
#Preview {
CenteredLabel(title: "Button Text Labek", systemName: "pencil.tip.crop.circle")
}

View File

@@ -0,0 +1,77 @@
//
// KeyDetailView.swift
// ShhShell
//
// Created by neon443 on 25/06/2025.
//
import SwiftUI
struct KeyDetailView: View {
@ObservedObject var hostsManager: HostsManager
@State var keypair: Keypair
@State private var reveal: Bool = false
var body: some View {
List {
VStack(alignment: .leading) {
Text("Public key")
.bold()
Text(String(data: keypair.publicKey!, encoding: .utf8) ?? "nil")
}
VStack(alignment: .leading) {
Text("Private key")
.bold()
ZStack {
Text(String(data: keypair.privateKey!, encoding: .utf8) ?? "nil")
.blur(radius: reveal ? 0 : 5)
VStack {
Image(systemName: "eye.slash.fill")
.resizable().scaledToFit()
.frame(width: 50)
Text("Tap to reveal")
}
.opacity(reveal ? 0 : 1)
}
.onTapGesture {
Task {
if !reveal {
guard await hostsManager.authWithBiometrics() else { return }
}
withAnimation(.spring) { reveal.toggle() }
}
}
}
Button {
Task {
guard await hostsManager.authWithBiometrics() else { return }
if let privateKey = keypair.privateKey {
UIPasteboard.general.string = String(data: privateKey, encoding: .utf8)
}
}
} label: {
CenteredLabel(title: "Copy private key", systemName: "document.on.document")
}
.listRowSeparator(.hidden)
}
}
}
#Preview {
KeyDetailView(
hostsManager: HostsManager(),
keypair: Keypair(
publicKey: "ssh-ed25519 dskjhfajkdhfjkdashfgjkhadsjkgfbhalkjhfjkhdask user@mac".data(using: .utf8),
privateKey: """
-----BEGIN OPENSSH PRIVATE KEY-----
Lorem ipsum dolor sit amet, consectetur adipiscing elit
sed do eiusmod tempor incididunt ut labore et dolore magna aliqu
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat
-----END OPENSSH PRIVATE KEY-----
"""
.data(using: .utf8)
)
)
}

View File

@@ -15,11 +15,11 @@ struct KeyManagerView: View {
NavigationStack {
List {
Section {
ForEach(hostsManager.savedHosts) { host in
ForEach(hostsManager.getKeys()) { keypair in
NavigationLink {
KeyDetailView(hostsManager: hostsManager, keypair: keypair)
} label: {
Text(String(data: keypair.publicKey!, encoding: .utf8) ?? "nil")
}
}
}
@@ -27,9 +27,11 @@ struct KeyManagerView: View {
NavigationLink {
List {
ForEach(hostsManager.savedHosts) { host in
Text(host.address)
.bold()
Text(host.key ?? "nil")
VStack(alignment: .leading) {
Text(host.address)
.bold()
Text(host.key ?? "nil")
}
}
}
} label: {

View File

@@ -7,12 +7,13 @@
import Foundation
protocol KeypairProtocol: Equatable, Codable, Hashable {
protocol KeypairProtocol: Identifiable, Equatable, Codable, Hashable {
var publicKey: Data? { get set }
var privateKey: Data? { get set }
}
struct Keypair: KeypairProtocol {
var id = UUID()
var publicKey: Data?
var privateKey: Data?