import Foundation import Security public protocol CredentialStore: Sendable { func get(instanceID: UUID) throws -> String? func set(instanceID: UUID, password: String) throws func remove(instanceID: UUID) throws } public struct KeychainCredentialStore: CredentialStore { private let service = "fun.chick.multi-simadmin.instance-password" public init() {} public func get(instanceID: UUID) throws -> String? { var query = baseQuery(instanceID) query[kSecReturnData as String] = true var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) guard status == errSecSuccess else { if status == errSecItemNotFound { return nil } throw CredentialStoreError.keychainFailed } guard let data = item as? Data else { return nil } return String(data: data, encoding: .utf8) } public func set(instanceID: UUID, password: String) throws { try self.remove(instanceID: instanceID) let attributes: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: instanceID.uuidString, kSecValueData as String: Data(password.utf8), kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly ] let status = SecItemAdd(attributes as CFDictionary, nil) guard status == errSecSuccess else { throw CredentialStoreError.keychainFailed } } public func remove(instanceID: UUID) throws { let status = SecItemDelete(baseQuery(instanceID) as CFDictionary) guard status == errSecSuccess || status == errSecItemNotFound else { throw CredentialStoreError.keychainFailed } } private func baseQuery(_ instanceID: UUID) -> [String: Any] { [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: instanceID.uuidString ] } } public enum CredentialStoreError: Error, Equatable { case keychainFailed }