diff --git a/ShhShell/Keys/KeyManager.swift b/ShhShell/Keys/KeyManager.swift index a86cef6..d0d8093 100644 --- a/ShhShell/Keys/KeyManager.swift +++ b/ShhShell/Keys/KeyManager.swift @@ -16,9 +16,11 @@ class KeyManager: ObservableObject { private let passwordStore = GenericPasswordStore() @Published var keypairs: [Keypair] = [] - - @Published var keyTypes: [UUID: KeyType] = [:] - @Published var keyNames: [UUID: String] = [:] + var keyIDs: [UUID] { + keypairs.map { $0.id } + } +// @Published var keyTypes: [UUID: KeyType] = [:] +// @Published var keyNames: [UUID: String] = [:] private let baseTag = "com.neon443.ShhShell.keys".data(using: .utf8)! init() { @@ -26,50 +28,28 @@ class KeyManager: ObservableObject { } func loadKeypairs() { - loadKeyIDs() - keypairs = [] - for id in keyTypes.keys { - guard let keypair = getFromKeychain(keyID: id) else { continue } - keypairs.append(keypair) + userdefaults.synchronize() + guard let data = userdefaults.data(forKey: "keypairs") else { return } + guard let decoded = try? JSONDecoder().decode([Keypair].self, from: data) else { return } + withAnimation { self.keypairs = decoded } + for kp in keypairs { + guard let KeychainKeypair = getFromKeychain(kp) else { continue } + guard let index = keypairs.firstIndex(where: { $0.id == kp.id }) else { continue } + withAnimation { keypairs[index] = KeychainKeypair } } } func saveKeypairs() { + guard let coded = try? JSONEncoder().encode(keypairs) else { return } + userdefaults.set(coded, forKey: "keypairs") + userdefaults.synchronize() for keypair in keypairs { - keyTypes.updateValue(keypair.type, forKey: keypair.id) - keyNames.updateValue(keypair.name, forKey: keypair.id) saveToKeychain(keypair) } - saveKeyIDs() - loadKeypairs() - } - - func loadKeyIDs() { - userdefaults.synchronize() - let decoder = JSONDecoder() - guard let data = userdefaults.data(forKey: "keyIDs") else { return } - guard let decoded = try? decoder.decode([UUID:KeyType].self, from: data) else { return } - keyTypes = decoded - - guard let dataNames = userdefaults.data(forKey: "keyNames") else { return } - guard let decodedNames = try? decoder.decode([UUID:String].self, from: dataNames) else { return } - keyNames = decodedNames - } - - func saveKeyIDs() { - let encoder = JSONEncoder() - guard let encoded = try? encoder.encode(keyTypes) else { return } - userdefaults.set(encoded, forKey: "keyIDs") - - guard let encodedNames = try? encoder.encode(keyNames) else { return } - userdefaults.set(encodedNames, forKey: "keyNames") - userdefaults.synchronize() loadKeypairs() } func saveToKeychain(_ keypair: Keypair) { - keyTypes.updateValue(keypair.type, forKey: keypair.id) - keyNames.updateValue(keypair.name, forKey: keypair.id) if keypair.type == .ed25519 { let curve25519 = try! Curve25519.Signing.PrivateKey(rawRepresentation: keypair.privateKey) let readKey: Curve25519.Signing.PrivateKey? @@ -83,16 +63,14 @@ class KeyManager: ObservableObject { } } - func getFromKeychain(keyID: UUID) -> Keypair? { - guard let keyType = keyTypes[keyID] else { return nil } - guard let keyName = keyNames[keyID] else { return nil } - if keyType == .ed25519 { + func getFromKeychain(_ keypair: Keypair) -> Keypair? { + if keypair.type == .ed25519 { var key: Curve25519.Signing.PrivateKey? - key = try? passwordStore.readKey(account: keyID.uuidString) + key = try? passwordStore.readKey(account: keypair.id.uuidString) guard let key else { return nil } - return Keypair(id: keyID, type: keyType, name: keyName, privateKey: key.rawRepresentation) + return Keypair(id: keypair.id, type: keypair.type, name: keypair.name, privateKey: key.rawRepresentation) } else { - let tag = baseTag+keyID.uuidString.data(using: .utf8)! + let tag = baseTag+keypair.id.uuidString.data(using: .utf8)! let getQuery: [String: Any] = [kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: tag, kSecAttrKeyType as String: kSecAttrKeyTypeEC, @@ -101,8 +79,8 @@ class KeyManager: ObservableObject { let status = SecItemCopyMatching(getQuery as CFDictionary, &item) guard status == errSecSuccess else { fatalError() } return Keypair( - type: keyType, - name: keyName, + type: keypair.type, + name: keypair.name, privateKey: item as! Data ) } @@ -116,9 +94,7 @@ class KeyManager: ObservableObject { fatalError() } } - keyNames.removeValue(forKey: keypair.id) - keyTypes.removeValue(forKey: keypair.id) - saveKeyIDs() + saveKeypairs() } func renameKey(keypair: Keypair, newName: String) { @@ -278,10 +254,6 @@ class KeyManager: ObservableObject { return content } - func getPubkey(_ privateKey: SecKey) -> SecKey? { - return SecKeyCopyPublicKey(privateKey) - } - static func encode(str: String) -> Data { guard let utf8 = str.data(using: .utf8) else { return Data() diff --git a/ShhShell/Keys/Keypair.swift b/ShhShell/Keys/Keypair.swift index 07b1194..94a468e 100644 --- a/ShhShell/Keys/Keypair.swift +++ b/ShhShell/Keys/Keypair.swift @@ -26,7 +26,8 @@ struct Keypair: KeypairProtocol { var name: String = "" var publicKey: Data { if privateKey.isEmpty { - return Data() + print("not a valid ed25519 key") + fatalError() } else { return (try? Curve25519.Signing.PrivateKey(rawRepresentation: privateKey).publicKey.rawRepresentation) ?? Data() } @@ -34,6 +35,15 @@ struct Keypair: KeypairProtocol { var privateKey: Data var passphrase: String = "" + enum CodingKeys: String, CodingKey { + case id = "id" + case type = "type" + case name = "name" + +// case privateKey = "privateKey" +// case passphrase = "passphrase" + } + var label: String { if name.isEmpty { return openSshPubkey @@ -82,6 +92,17 @@ struct Keypair: KeypairProtocol { self.passphrase = passphrase } + init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decode(UUID.self, forKey: .id) + self.type = try container.decode(KeyType.self, forKey: .type) + self.name = try container.decode(String.self, forKey: .name) +// self.privateKey = try container.decode(Data.self, forKey: .privateKey) +// self.passphrase = try container.decode(String.self, forKey: .passphrase) + self.privateKey = Data() + self.passphrase = "" + } + static func ==(lhs: Keypair, rhs: Keypair) -> Bool { if lhs.publicKey.base64EncodedString() == rhs.publicKey.base64EncodedString() && lhs.privateKey.base64EncodedString() == rhs.privateKey.base64EncodedString() {