updated keypair data structure to have an name, passphrase and type

added generateKy to generate a key, currently only rsa
update generateRSA to return the Data for pub and priv, and take a custom size
bell animation better, slightly longer in total and easeinout
added new generate button hardcoded to a rsa 4096
added keytype to define key types (duh)
update getKeys to use the new keypair structure
This commit is contained in:
neon443
2025-06-30 16:28:58 +01:00
parent 94ad2fa661
commit 80b83644b4
8 changed files with 86 additions and 24 deletions

View File

@@ -48,6 +48,7 @@
A96C6B002E0C45FE00F377FE /* KeyDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C6AFF2E0C45FE00F377FE /* KeyDetailView.swift */; };
A96C6B022E0C49E800F377FE /* CenteredLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C6B012E0C49E800F377FE /* CenteredLabel.swift */; };
A96C90A12E12B87A00724253 /* TextBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C90A02E12B87900724253 /* TextBox.swift */; };
A96C90A32E12D53B00724253 /* KeyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C90A22E12D53900724253 /* KeyType.swift */; };
A98554552E05535F009051BD /* KeyManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98554542E05535F009051BD /* KeyManagerView.swift */; };
A98554592E0553AA009051BD /* KeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98554582E0553AA009051BD /* KeyManager.swift */; };
A985545D2E055D4D009051BD /* ConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A985545C2E055D4D009051BD /* ConnectionView.swift */; };
@@ -145,6 +146,7 @@
A96C6AFF2E0C45FE00F377FE /* KeyDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyDetailView.swift; sourceTree = "<group>"; };
A96C6B012E0C49E800F377FE /* CenteredLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenteredLabel.swift; sourceTree = "<group>"; };
A96C90A02E12B87900724253 /* TextBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBox.swift; sourceTree = "<group>"; };
A96C90A22E12D53900724253 /* KeyType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyType.swift; sourceTree = "<group>"; };
A98554542E05535F009051BD /* KeyManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyManagerView.swift; sourceTree = "<group>"; };
A98554582E0553AA009051BD /* KeyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyManager.swift; sourceTree = "<group>"; };
A985545C2E055D4D009051BD /* ConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionView.swift; sourceTree = "<group>"; };
@@ -374,6 +376,7 @@
A98554572E055398009051BD /* Keys */ = {
isa = PBXGroup;
children = (
A96C90A22E12D53900724253 /* KeyType.swift */,
A98554582E0553AA009051BD /* KeyManager.swift */,
A985545E2E056EDD009051BD /* KeychainLayer.swift */,
A96C6AFD2E0C43B600F377FE /* Keypair.swift */,
@@ -587,6 +590,7 @@
A9DA97712E0D30ED00142DDC /* HostSymbol.swift in Sources */,
A96C90A12E12B87A00724253 /* TextBox.swift in Sources */,
A96BE6A82E116E2B00C0FEE9 /* SessionsListView.swift in Sources */,
A96C90A32E12D53B00724253 /* KeyType.swift in Sources */,
A98554612E058433009051BD /* HostsManager.swift in Sources */,
A985545D2E055D4D009051BD /* ConnectionView.swift in Sources */,
A98554592E0553AA009051BD /* KeyManager.swift in Sources */,

View File

@@ -185,8 +185,11 @@ class HostsManager: ObservableObject, @unchecked Sendable {
func getKeys() -> [Keypair] {
var result: [Keypair] = []
for host in hosts {
guard host.privateKey != nil && host.publicKey != nil else { continue }
let keypair = Keypair(publicKey: host.publicKey, privateKey: host.privateKey)
guard let publicKey = host.publicKey else { continue }
guard let privateKey = host.privateKey else { continue }
guard let privKeyStr = String(data: privateKey, encoding: .utf8),
let pubKeyStr = String(data: publicKey, encoding: .utf8) else { continue }
let keypair = Keypair(type: .rsa(1), name: UUID().uuidString, publicKey: pubKeyStr, privateKey: privKeyStr)
if !result.contains(keypair) {
result.append(keypair)
}

View File

@@ -38,6 +38,22 @@ class KeyManager: ObservableObject {
}
//MARK: generate keys
func generateKey(type: KeyType, SEPKeyTag: String, comment: String, passphrase: String) -> Keypair? {
switch type {
case .ecdsa(let inSEP):
fatalError()
case .rsa(let rsaSize):
guard let keyData = try? generateRSA(size: rsaSize) else { return nil }
return Keypair(
type: .rsa(rsaSize),
name: comment,
publicKey: String(data: keyData.pub, encoding: .utf8) ?? "",
privateKey: String(data: keyData.priv, encoding: .utf8) ?? "",
passphrase: ""
)
}
}
func generateEd25519() {
let privateKey = Curve25519.Signing.PrivateKey()
let publicKeyData = privateKey.publicKey
@@ -45,13 +61,13 @@ class KeyManager: ObservableObject {
print(publicKeyData.rawRepresentation)
}
func generateRSA() throws {
func generateRSA(size: Int) throws -> (priv: Data, pub: Data) {
let type = kSecAttrKeyTypeRSA
let label = Date().ISO8601Format()
let tag = label.data(using: .utf8)!
let attributes: [String: Any] =
[kSecAttrKeyType as String: type,
kSecAttrKeySizeInBits as String: 4096,
kSecAttrKeySizeInBits as String: size,
kSecPrivateKeyAttrs as String:
[kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: tag]
@@ -61,15 +77,23 @@ class KeyManager: ObservableObject {
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
guard let pubkey = getPubkey(privateKey) else {
throw error!.takeRetainedValue() as Error
}
print(privateKey)
print(SecKeyCopyPublicKey(privateKey) ?? "")
print(SecKeyCopyExternalRepresentation(privateKey, nil) as Any)
// do {
// try storeKey(privateKey, label: label)
// } catch {
// print(error.localizedDescription)
// }
guard let privKeyData = SecKeyCopyExternalRepresentation(privateKey, &error) else {
throw error!.takeRetainedValue() as Error
}
guard let pubKeyData = SecKeyCopyExternalRepresentation(pubkey, &error) else {
throw error!.takeRetainedValue() as Error
}
return (privKeyData as Data, pubKeyData as Data)
}
func getPubkey(_ privateKey: SecKey) -> SecKey? {

View File

@@ -0,0 +1,22 @@
//
// KeyType.swift
// ShhShell
//
// Created by neon443 on 30/06/2025.
//
import Foundation
enum KeyType: Codable, Equatable, Hashable, CustomStringConvertible {
var description: String {
switch self {
case .ecdsa(let inSEP):
return inSEP ? "ECDSA Secure Enclave" : "ECDSA"
case .rsa(let bits):
return "RSA \(bits)"
}
}
case ecdsa(inSEP: Bool)
case rsa(Int)
}

View File

@@ -8,21 +8,36 @@
import Foundation
protocol KeypairProtocol: Identifiable, Equatable, Codable, Hashable {
var id: UUID { get }
var type: KeyType { get set }
var name: String { get set }
var publicKey: Data? { get set }
var privateKey: Data? { get set }
var passphrase: String { get set }
}
struct Keypair: KeypairProtocol {
var id = UUID()
var type: KeyType = .rsa(4096)
var name: String = ""
var publicKey: Data?
var privateKey: Data?
var passphrase: String = ""
init(
publicKey: Data?,
privateKey: Data?
id: UUID = UUID(),
type: KeyType,
name: String,
publicKey: String,
privateKey: String,
passphrase: String = ""
) {
self.publicKey = publicKey
self.privateKey = privateKey
self.id = id
self.type = type
self.name = name
self.publicKey = publicKey.data(using: .utf8)
self.privateKey = privateKey.data(using: .utf8)
self.passphrase = passphrase
}
static func ==(lhs: Keypair, rhs: Keypair) -> Bool {

View File

@@ -184,9 +184,9 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
func ring() {
Task { @MainActor in
withAnimation { self.bell = true }
try? await Task.sleep(nanoseconds: 250_000_000) // 250ms
withAnimation { self.bell = false }
withAnimation(.easeIn(duration: 0.1)) { self.bell = true }
try? await Task.sleep(nanoseconds: 300_000_000) // 250ms
withAnimation(.easeOut(duration: 0.1)) { self.bell = false }
}
}

View File

@@ -85,7 +85,9 @@ struct KeyDetailView: View {
KeyDetailView(
hostsManager: HostsManager(),
keypair: Keypair(
publicKey: "ssh-ed25519 dskjhfajkdhfjkdashfgjkhadsjkgfbhalkjhfjkhdask user@mac".data(using: .utf8),
type: .rsa(4096),
name: "previewKey",
publicKey: "ssh-ed25519 dskjhfajkdhfjkdashfgjkhadsjkgfbhalkjhfjkhdask user@mac",
privateKey: """
-----BEGIN OPENSSH PRIVATE KEY-----
Lorem ipsum dolor sit amet, consectetur adipiscing elit
@@ -94,7 +96,6 @@ struct KeyDetailView: View {
nisi ut aliquip ex ea commodo consequat
-----END OPENSSH PRIVATE KEY-----
"""
.data(using: .utf8)
)
)
}

View File

@@ -29,15 +29,8 @@ struct KeyManagerView: View {
}
}
Button("ed25519") {
keyManager.generateEd25519()
}
Button("rsa") {
do {
try keyManager.generateRSA()
} catch {
print(error.localizedDescription)
}
Button("genereate rsa") {
keyManager.generateKey(type: .rsa(4096), SEPKeyTag: "", comment: "jaklsd", passphrase: "")
}
}
.scrollContentBackground(.hidden)