source/UberCore/Authentication/Tokens/KeychainWrapper.swift (58 lines of code) (raw):

// // KeychainWrapper.swift // UberRides // // Copyright © 2016 Uber Technologies, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. /// Wraps saving and retrieving objects from keychain. class KeychainWrapper: NSObject { private static let serviceName = "com.uber.rides-ios-sdk" private var accessGroup = "" private let Class = kSecClass as String private let AttrAccount = kSecAttrAccount as String private let AttrService = kSecAttrService as String private let AttrAccessGroup = kSecAttrAccessGroup as String private let AttrGeneric = kSecAttrGeneric as String private let AttrAccessible = kSecAttrAccessible as String private let ReturnData = kSecReturnData as String private let ValueData = kSecValueData as String private let MatchLimit = kSecMatchLimit as String /** Set the access group for keychain to use. - parameter group: String representing name of keychain access group. */ func setAccessGroup(_ accessGroup: String) { self.accessGroup = accessGroup } /** Save an object to keychain. - parameter object: object conforming to NSCoding to save to keychain. - parameter key: key for the object. - returns: true if object was successfully added to keychain. */ func setObject(_ object: NSCoding, key: String) -> Bool { var keychainItemData = getKeychainItemData(key) let value = NSKeyedArchiver.archivedData(withRootObject: object) keychainItemData[AttrAccessible] = kSecAttrAccessibleWhenUnlocked keychainItemData[ValueData] = value as AnyObject? var result: OSStatus = SecItemAdd(keychainItemData as CFDictionary, nil) if result == errSecDuplicateItem { result = SecItemUpdate(keychainItemData as CFDictionary, [ValueData: value] as CFDictionary) } return result == errSecSuccess } /** Get an object from the keychain. - parameter key: the key associated to the object to retrieve. - returns: the object in keychain or nil if none exists for the given key. */ func getObjectForKey(_ key: String) -> NSCoding? { var keychainItemData = getKeychainItemData(key) keychainItemData[MatchLimit] = kSecMatchLimitOne keychainItemData[ReturnData] = kCFBooleanTrue var data: AnyObject? let result = withUnsafeMutablePointer(to: &data) { SecItemCopyMatching(keychainItemData as CFDictionary, UnsafeMutablePointer($0)) } var object: AnyObject? if let data = data as? Data { object = NSKeyedUnarchiver.unarchiveObject(with: data) as AnyObject? } return result == noErr ? object as? NSCoding : nil } /** Remove an object from keychain - parameter key: key for object to remove. - returns: true if object was successfully deleted. */ func deleteObjectForKey(_ key: String) -> Bool { let keychainItemData = getKeychainItemData(key) let result = SecItemDelete(keychainItemData as CFDictionary) return result == noErr } /** Helper method to build keychain query dictionary. - returns: dictionary of base attributes for keychain query. */ private func getKeychainItemData(_ key: String) -> [String: AnyObject] { var keychainItemData = [String: AnyObject]() let identifier = key.data(using: String.Encoding.utf8) keychainItemData[AttrGeneric] = identifier as AnyObject? keychainItemData[AttrAccount] = identifier as AnyObject? keychainItemData[AttrService] = type(of: self).serviceName as AnyObject? keychainItemData[Class] = kSecClassGenericPassword if !accessGroup.isEmpty { keychainItemData[AttrAccessGroup] = accessGroup as AnyObject? } return keychainItemData } }