mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +00:00
simplified logic by saving Keypairs as an array to defaults, but without the privateKey and passphrase for obvious reasons
added codingkeys, omitting the sensitive ones like privatekey and keypair added init from decoder to skip privatekey and passphrase removed keyTypes and keyNames, vastly simplifying logic
This commit is contained in:
@@ -16,9 +16,11 @@ class KeyManager: ObservableObject {
|
|||||||
private let passwordStore = GenericPasswordStore()
|
private let passwordStore = GenericPasswordStore()
|
||||||
|
|
||||||
@Published var keypairs: [Keypair] = []
|
@Published var keypairs: [Keypair] = []
|
||||||
|
var keyIDs: [UUID] {
|
||||||
@Published var keyTypes: [UUID: KeyType] = [:]
|
keypairs.map { $0.id }
|
||||||
@Published var keyNames: [UUID: String] = [:]
|
}
|
||||||
|
// @Published var keyTypes: [UUID: KeyType] = [:]
|
||||||
|
// @Published var keyNames: [UUID: String] = [:]
|
||||||
private let baseTag = "com.neon443.ShhShell.keys".data(using: .utf8)!
|
private let baseTag = "com.neon443.ShhShell.keys".data(using: .utf8)!
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -26,50 +28,28 @@ class KeyManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadKeypairs() {
|
func loadKeypairs() {
|
||||||
loadKeyIDs()
|
userdefaults.synchronize()
|
||||||
keypairs = []
|
guard let data = userdefaults.data(forKey: "keypairs") else { return }
|
||||||
for id in keyTypes.keys {
|
guard let decoded = try? JSONDecoder().decode([Keypair].self, from: data) else { return }
|
||||||
guard let keypair = getFromKeychain(keyID: id) else { continue }
|
withAnimation { self.keypairs = decoded }
|
||||||
keypairs.append(keypair)
|
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() {
|
func saveKeypairs() {
|
||||||
|
guard let coded = try? JSONEncoder().encode(keypairs) else { return }
|
||||||
|
userdefaults.set(coded, forKey: "keypairs")
|
||||||
|
userdefaults.synchronize()
|
||||||
for keypair in keypairs {
|
for keypair in keypairs {
|
||||||
keyTypes.updateValue(keypair.type, forKey: keypair.id)
|
|
||||||
keyNames.updateValue(keypair.name, forKey: keypair.id)
|
|
||||||
saveToKeychain(keypair)
|
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()
|
loadKeypairs()
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveToKeychain(_ keypair: Keypair) {
|
func saveToKeychain(_ keypair: Keypair) {
|
||||||
keyTypes.updateValue(keypair.type, forKey: keypair.id)
|
|
||||||
keyNames.updateValue(keypair.name, forKey: keypair.id)
|
|
||||||
if keypair.type == .ed25519 {
|
if keypair.type == .ed25519 {
|
||||||
let curve25519 = try! Curve25519.Signing.PrivateKey(rawRepresentation: keypair.privateKey)
|
let curve25519 = try! Curve25519.Signing.PrivateKey(rawRepresentation: keypair.privateKey)
|
||||||
let readKey: Curve25519.Signing.PrivateKey?
|
let readKey: Curve25519.Signing.PrivateKey?
|
||||||
@@ -83,16 +63,14 @@ class KeyManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFromKeychain(keyID: UUID) -> Keypair? {
|
func getFromKeychain(_ keypair: Keypair) -> Keypair? {
|
||||||
guard let keyType = keyTypes[keyID] else { return nil }
|
if keypair.type == .ed25519 {
|
||||||
guard let keyName = keyNames[keyID] else { return nil }
|
|
||||||
if keyType == .ed25519 {
|
|
||||||
var key: Curve25519.Signing.PrivateKey?
|
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 }
|
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 {
|
} 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,
|
let getQuery: [String: Any] = [kSecClass as String: kSecClassKey,
|
||||||
kSecAttrApplicationTag as String: tag,
|
kSecAttrApplicationTag as String: tag,
|
||||||
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
|
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
|
||||||
@@ -101,8 +79,8 @@ class KeyManager: ObservableObject {
|
|||||||
let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
|
let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
|
||||||
guard status == errSecSuccess else { fatalError() }
|
guard status == errSecSuccess else { fatalError() }
|
||||||
return Keypair(
|
return Keypair(
|
||||||
type: keyType,
|
type: keypair.type,
|
||||||
name: keyName,
|
name: keypair.name,
|
||||||
privateKey: item as! Data
|
privateKey: item as! Data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -116,9 +94,7 @@ class KeyManager: ObservableObject {
|
|||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keyNames.removeValue(forKey: keypair.id)
|
saveKeypairs()
|
||||||
keyTypes.removeValue(forKey: keypair.id)
|
|
||||||
saveKeyIDs()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func renameKey(keypair: Keypair, newName: String) {
|
func renameKey(keypair: Keypair, newName: String) {
|
||||||
@@ -278,10 +254,6 @@ class KeyManager: ObservableObject {
|
|||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPubkey(_ privateKey: SecKey) -> SecKey? {
|
|
||||||
return SecKeyCopyPublicKey(privateKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func encode(str: String) -> Data {
|
static func encode(str: String) -> Data {
|
||||||
guard let utf8 = str.data(using: .utf8) else {
|
guard let utf8 = str.data(using: .utf8) else {
|
||||||
return Data()
|
return Data()
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ struct Keypair: KeypairProtocol {
|
|||||||
var name: String = ""
|
var name: String = ""
|
||||||
var publicKey: Data {
|
var publicKey: Data {
|
||||||
if privateKey.isEmpty {
|
if privateKey.isEmpty {
|
||||||
return Data()
|
print("not a valid ed25519 key")
|
||||||
|
fatalError()
|
||||||
} else {
|
} else {
|
||||||
return (try? Curve25519.Signing.PrivateKey(rawRepresentation: privateKey).publicKey.rawRepresentation) ?? Data()
|
return (try? Curve25519.Signing.PrivateKey(rawRepresentation: privateKey).publicKey.rawRepresentation) ?? Data()
|
||||||
}
|
}
|
||||||
@@ -34,6 +35,15 @@ struct Keypair: KeypairProtocol {
|
|||||||
var privateKey: Data
|
var privateKey: Data
|
||||||
var passphrase: String = ""
|
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 {
|
var label: String {
|
||||||
if name.isEmpty {
|
if name.isEmpty {
|
||||||
return openSshPubkey
|
return openSshPubkey
|
||||||
@@ -82,6 +92,17 @@ struct Keypair: KeypairProtocol {
|
|||||||
self.passphrase = passphrase
|
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 {
|
static func ==(lhs: Keypair, rhs: Keypair) -> Bool {
|
||||||
if lhs.publicKey.base64EncodedString() == rhs.publicKey.base64EncodedString()
|
if lhs.publicKey.base64EncodedString() == rhs.publicKey.base64EncodedString()
|
||||||
&& lhs.privateKey.base64EncodedString() == rhs.privateKey.base64EncodedString() {
|
&& lhs.privateKey.base64EncodedString() == rhs.privateKey.base64EncodedString() {
|
||||||
|
|||||||
Reference in New Issue
Block a user