Added a lot of functionality. Bugfixes and changes
This commit is contained in:
114
oAI/Services/EncryptionService.swift
Normal file
114
oAI/Services/EncryptionService.swift
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user