diff --git a/Mactrix.xcodeproj/project.pbxproj b/Mactrix.xcodeproj/project.pbxproj index d354a95..591f0ef 100644 --- a/Mactrix.xcodeproj/project.pbxproj +++ b/Mactrix.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ 348E99882F40ABC3009F57A9 /* AsyncAlgorithms in Frameworks */ = {isa = PBXBuildFile; productRef = 348E99872F40ABC3009F57A9 /* AsyncAlgorithms */; }; 34913F6E2EC0F532003034CB /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 34913F6D2EC0F532003034CB /* MatrixRustSDK */; }; 34913F702EC0F59F003034CB /* Models in Frameworks */ = {isa = PBXBuildFile; productRef = 34913F6F2EC0F59F003034CB /* Models */; }; - 34F7225F2EB531F40007B2A4 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 34F7225E2EB531F40007B2A4 /* KeychainAccess */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -50,7 +49,6 @@ 34913F6E2EC0F532003034CB /* MatrixRustSDK in Frameworks */, 345E77E82ED9F309002E5B9A /* Utils in Frameworks */, 34913F702EC0F59F003034CB /* Models in Frameworks */, - 34F7225F2EB531F40007B2A4 /* KeychainAccess in Frameworks */, 348E99882F40ABC3009F57A9 /* AsyncAlgorithms in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -103,7 +101,6 @@ ); name = Mactrix; packageProductDependencies = ( - 34F7225E2EB531F40007B2A4 /* KeychainAccess */, 3409F4EB2EBFD8D4009537B4 /* UI */, 34913F6D2EC0F532003034CB /* MatrixRustSDK */, 34913F6F2EC0F59F003034CB /* Models */, @@ -140,7 +137,6 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( 342C624C2EB4DDE600E2426A /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */, - 34F7225D2EB531F40007B2A4 /* XCRemoteSwiftPackageReference "KeychainAccess" */, 3409F4E82EBFD8D4009537B4 /* XCLocalSwiftPackageReference "MactrixLibrary" */, 348E99862F40ABC3009F57A9 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */, ); @@ -450,14 +446,6 @@ minimumVersion = 1.1.2; }; }; - 34F7225D2EB531F40007B2A4 /* XCRemoteSwiftPackageReference "KeychainAccess" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess"; - requirement = { - branch = master; - kind = branch; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -483,11 +471,6 @@ isa = XCSwiftPackageProductDependency; productName = Models; }; - 34F7225E2EB531F40007B2A4 /* KeychainAccess */ = { - isa = XCSwiftPackageProductDependency; - package = 34F7225D2EB531F40007B2A4 /* XCRemoteSwiftPackageReference "KeychainAccess" */; - productName = KeychainAccess; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 3438583A2EB394590010922A /* Project object */; diff --git a/Mactrix.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mactrix.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c65f2ed..a672933 100644 --- a/Mactrix.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mactrix.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,15 +1,6 @@ { - "originHash" : "d084e0c9b1d3b8ac948908f0ca98de1cca938f342f749686b8062637737a5cf4", + "originHash" : "2e655dd159a59779ade7006421372d658386daea86b82435cbe33da36bb81efb", "pins" : [ - { - "identity" : "keychainaccess", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kishikawakatsumi/KeychainAccess", - "state" : { - "branch" : "master", - "revision" : "e0c7eebc5a4465a3c4680764f26b7a61f567cdaf" - } - }, { "identity" : "matrix-rust-components-swift", "kind" : "remoteSourceControl", diff --git a/Mactrix/Models/AppKeychain.swift b/Mactrix/Models/AppKeychain.swift new file mode 100644 index 0000000..53b9bdb --- /dev/null +++ b/Mactrix/Models/AppKeychain.swift @@ -0,0 +1,68 @@ +import Foundation +import Security + +enum KeychainError: Error { + case unexpectedStatus(OSStatus) +} + +struct AppKeychain { + private let service: String + + init(service: String = Bundle.main.bundleIdentifier!) { + self.service = service + } + + private var baseQuery: [CFString: Any] { + [ + kSecClass: kSecClassGenericPassword, + kSecAttrService: service, + /// change to true for Data Protection keychain mode + kSecUseDataProtectionKeychain: false, + ] + } + + /// Saves data for the given key, creating or updating the keychain item. + func save(_ data: Data, forKey key: String) throws { + var query = baseQuery + query[kSecAttrAccount] = key + + let updateStatus = SecItemUpdate(query as CFDictionary, [kSecValueData: data] as CFDictionary) + if updateStatus == errSecItemNotFound { + query[kSecValueData] = data + query[kSecAttrAccessible] = kSecAttrAccessibleWhenUnlocked + let addStatus = SecItemAdd(query as CFDictionary, nil) + guard addStatus == errSecSuccess else { + throw KeychainError.unexpectedStatus(addStatus) + } + } else if updateStatus != errSecSuccess { + /// with kSecUseDataProtectionKeychain set to true, this will throw with error -34018, which according to + /// https://github.com/apple-oss-distributions/Security/blob/09becc8fb155462f0853f99cd09c26b10a38e2f4/sec/Security/SecBasePriv.h#L113 + /// is the error "Internal error when a required entitlement isn't present" + throw KeychainError.unexpectedStatus(updateStatus) + } + } + + /// Returns the data stored for the given key, or `nil` if no item exists. + func load(forKey key: String) throws -> Data? { + var query = baseQuery + query[kSecAttrAccount] = key + query[kSecReturnData] = true + query[kSecMatchLimit] = kSecMatchLimitOne + + var result: AnyObject? + let status = unsafe SecItemCopyMatching(query as CFDictionary, &result) + if status == errSecItemNotFound { return nil } + guard status == errSecSuccess else { + throw KeychainError.unexpectedStatus(status) + } + return result as? Data + } + + /// Removes all keychain items stored under this service. + func removeAll() throws { + let status = SecItemDelete(baseQuery as CFDictionary) + guard status == errSecSuccess || status == errSecItemNotFound else { + throw KeychainError.unexpectedStatus(status) + } + } +} diff --git a/Mactrix/Models/HomeserverLogin.swift b/Mactrix/Models/HomeserverLogin.swift index d79fc7d..6fe8b96 100644 --- a/Mactrix/Models/HomeserverLogin.swift +++ b/Mactrix/Models/HomeserverLogin.swift @@ -49,7 +49,11 @@ struct HomeserverLogin { let matrixClient = await MatrixClient(storeID: storeID, client: unauthenticatedClient) let userSession = try matrixClient.userSession() - try userSession.saveUserToKeychain() + do { + try userSession.saveUserToKeychain() + } catch { + print(error.localizedDescription) + } return matrixClient } diff --git a/Mactrix/Models/MatrixClient.swift b/Mactrix/Models/MatrixClient.swift index 8988476..9c89a13 100644 --- a/Mactrix/Models/MatrixClient.swift +++ b/Mactrix/Models/MatrixClient.swift @@ -1,6 +1,5 @@ import AsyncAlgorithms import Foundation -import KeychainAccess import MatrixRustSDK import OSLog import SwiftUI @@ -41,20 +40,15 @@ struct UserSession: Codable { func saveUserToKeychain() throws { let keychainData = try JSONEncoder().encode(self) - let keychain = Keychain(service: applicationID) - try keychain.set(keychainData, key: Self.keychainKey) + try AppKeychain().save(keychainData, forKey: Self.keychainKey) } static func loadUserFromKeychain() throws -> Self? { Logger.matrixClient.debug("Load user from keychain") - /* #if DEBUG - if true { - return try JSONDecoder().decode(Self.self, from: DevSecrets.matrixSession.data(using: .utf8)!) - } - #endif */ - let keychain = Keychain(service: applicationID) - guard let keychainData = try keychain.getData(keychainKey) else { return nil } - return try JSONDecoder().decode(Self.self, from: keychainData) + if let keychainData = try AppKeychain().load(forKey: Self.keychainKey) { + return try JSONDecoder().decode(Self.self, from: keychainData) + } + return nil } } @@ -148,8 +142,7 @@ class MatrixClient { try? await client.logout() try? FileManager.default.removeItem(at: .sessionData(for: storeID)) try? FileManager.default.removeItem(at: .sessionCaches(for: storeID)) - let keychain = Keychain(service: applicationID) - try keychain.removeAll() + try AppKeychain().removeAll() Logger.matrixClient.debug("matrix client sign out complete") } diff --git a/Mactrix/Models/MatrixClientSessionDelegate.swift b/Mactrix/Models/MatrixClientSessionDelegate.swift index e0db0e3..455d357 100644 --- a/Mactrix/Models/MatrixClientSessionDelegate.swift +++ b/Mactrix/Models/MatrixClientSessionDelegate.swift @@ -1,5 +1,4 @@ import Foundation -import KeychainAccess import MatrixRustSDK import OSLog import SwiftUI