// // EncryptionService.swift // oAI // // Secure encryption for sensitive data (API keys) // Uses CryptoKit with machine-specific key derivation // import Foundation import CryptoKit import IOKit class EncryptionService { static let shared = EncryptionService() private let salt = "oAI-secure-storage-v1" // App-specific salt private lazy var encryptionKey: SymmetricKey = { deriveEncryptionKey() }() private init() {} // MARK: - Public Interface /// Encrypt a string value func encrypt(_ value: String) throws -> String { guard let data = value.data(using: .utf8) else { throw EncryptionError.invalidInput } let sealedBox = try AES.GCM.seal(data, using: encryptionKey) guard let combined = sealedBox.combined else { throw EncryptionError.encryptionFailed } return combined.base64EncodedString() } /// Decrypt a string value func decrypt(_ encryptedValue: String) throws -> String { guard let data = Data(base64Encoded: encryptedValue) else { throw EncryptionError.invalidInput } let sealedBox = try AES.GCM.SealedBox(combined: data) let decryptedData = try AES.GCM.open(sealedBox, using: encryptionKey) guard let decryptedString = String(data: decryptedData, encoding: .utf8) else { throw EncryptionError.decryptionFailed } return decryptedString } // MARK: - Key Derivation /// Derive encryption key from machine-specific data private func deriveEncryptionKey() -> SymmetricKey { // Combine machine UUID + bundle ID + salt for key material let machineUUID = getMachineUUID() let bundleID = Bundle.main.bundleIdentifier ?? "com.oai.oAI" let keyMaterial = "\(machineUUID)-\(bundleID)-\(salt)" // Hash to create consistent 256-bit key let hash = SHA256.hash(data: Data(keyMaterial.utf8)) return SymmetricKey(data: hash) } /// Get machine-specific UUID (IOPlatformUUID) private func getMachineUUID() -> String { // Get IOPlatformUUID from IOKit let platformExpert = IOServiceGetMatchingService( kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice") ) guard platformExpert != 0 else { // Fallback to a stable identifier if IOKit unavailable return "oai-fallback-uuid" } defer { IOObjectRelease(platformExpert) } guard let uuidData = IORegistryEntryCreateCFProperty( platformExpert, "IOPlatformUUID" as CFString, kCFAllocatorDefault, 0 ) else { return "oai-fallback-uuid" } return (uuidData.takeRetainedValue() as? String) ?? "oai-fallback-uuid" } // MARK: - Errors enum EncryptionError: LocalizedError { case invalidInput case encryptionFailed case decryptionFailed var errorDescription: String? { switch self { case .invalidInput: return "Invalid input data for encryption/decryption" case .encryptionFailed: return "Failed to encrypt data" case .decryptionFailed: return "Failed to decrypt data" } } } }