// /* * Copyright (c) 2025 BWI GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import Security import Foundation enum SharedKeychainConfig { static var sharedGroup: String { Bundle.main.object(forInfoDictionaryKey: "KeychainMigrationSharedGroup") as? String ?? "" } static var service = "de.bwi.sharedkeychain" } // bwi #7565 do some keychain magic, needs to have entitlement for SharedKeychainConfig.sharedGroup and needs to store it in info.plist to hide team id. enum SharedKeychain { @discardableResult static func save( account: String, value: String ) -> OSStatus { let data = value.data(using: .utf8)! // Delete existing item first SharedKeychain.delete(account: account) // Add new value let addQuery: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, kSecAttrService as String: SharedKeychainConfig.service, kSecAttrAccessGroup as String: SharedKeychainConfig.sharedGroup, kSecValueData as String: data ] return SecItemAdd(addQuery as CFDictionary, nil) } static func load( account: String ) -> String? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, kSecAttrService as String: SharedKeychainConfig.service, kSecAttrAccessGroup as String: SharedKeychainConfig.sharedGroup, kSecMatchLimit as String: kSecMatchLimitOne, kSecReturnData as String: true ] var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) guard status == errSecSuccess, let data = result as? Data, let value = String(data: data, encoding: .utf8) else { return nil } return value } @discardableResult static func delete( account: String ) -> OSStatus { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, kSecAttrService as String: SharedKeychainConfig.service, kSecAttrAccessGroup as String: SharedKeychainConfig.sharedGroup ] return SecItemDelete(query as CFDictionary) } }