Created
May 6, 2016 21:41
-
-
Save cabotmoose/529600f3c34b94acba151c3774d39aa1 to your computer and use it in GitHub Desktop.
Swift 2 Gude to Keychain Encryption: Part One
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // KeychainManager.swift | |
| // KeychainAccess | |
| // | |
| // Created by Cameron Smith on 5/6/16. | |
| // Copyright © 2016 Flying Moose Development. All rights reserved. | |
| // | |
| import Foundation | |
| public enum KeychainError:ErrorType { | |
| case InvalidInput // If the value cannot be encoded as NSData | |
| case OperationUnimplemented // -4 | errSecUnimplemented | |
| case InvalidParam // -50 | errSecParam | |
| case MemoryAllocationFailure // -108 | errSecAllocate | |
| case TrustResultsUnavailable // -25291 | errSecNotAvailable | |
| case AuthFailed // -25293 | errSecAuthFailed | |
| case DuplicateItem // -25299 | errSecDuplicateItem | |
| case ItemNotFound // -25300 | errSecItemNotFound | |
| case ServerInteractionNotAllowed // -25308 | errSecInteractionNotAllowed | |
| case DecodeError // - 26275 | errSecDecode | |
| case Unknown(Int) // Another error code not defined | |
| } | |
| public struct KeychainManager { | |
| public static func queryData (itemKey:String) throws -> AnyObject? { | |
| let queryLoad: [String: AnyObject] = [ | |
| kSecClass as String: kSecClassGenericPassword, | |
| kSecAttrAccount as String: itemKey, | |
| kSecReturnData as String: kCFBooleanTrue, | |
| kSecMatchLimit as String: kSecMatchLimitOne | |
| ] | |
| var result: AnyObject? | |
| let resultCodeLoad = withUnsafeMutablePointer(&result) { | |
| SecItemCopyMatching(queryLoad, UnsafeMutablePointer($0)) | |
| } | |
| if let err = mapResultCode(resultCodeLoad) { | |
| throw err | |
| } | |
| guard let resultVal = result as? NSData, keyValue = NSString(data: resultVal, encoding: NSUTF8StringEncoding) as? String else { | |
| print("KeychainManager: Error parsing keychain result: \(resultCodeLoad)") | |
| return nil | |
| } | |
| return keyValue | |
| } | |
| public static func addData(itemKey:String, itemValue:String) throws { | |
| guard let valueData = itemValue.dataUsingEncoding(NSUTF8StringEncoding) else { | |
| throw KeychainError.InvalidInput | |
| } | |
| let queryAdd: [String: AnyObject] = [ | |
| kSecClass as String: kSecClassGenericPassword, | |
| kSecAttrAccount as String: itemKey, | |
| kSecValueData as String: valueData, | |
| kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked | |
| ] | |
| let resultCode = SecItemAdd(queryAdd as CFDictionaryRef, nil) | |
| if let err = mapResultCode(resultCode) { | |
| throw err | |
| } else { | |
| print("KeychainManager: Item added successfully") | |
| } | |
| } | |
| public static func updateData(itemKey:String, itemValue:String) throws { | |
| guard let valueData = itemValue.dataUsingEncoding(NSUTF8StringEncoding) else { | |
| throw KeychainError.InvalidInput | |
| } | |
| let updateQuery: [String: AnyObject] = [ | |
| kSecClass as String: kSecClassGenericPassword, | |
| kSecAttrAccount as String: itemKey | |
| ] | |
| let updateAttributes : [String:AnyObject] = [ | |
| kSecValueData as String: valueData | |
| ] | |
| if SecItemCopyMatching(updateQuery, nil) == noErr { | |
| let updateResultCode = SecItemUpdate(updateQuery, updateAttributes) | |
| if let err = mapResultCode(updateResultCode) { | |
| throw err | |
| } else { | |
| print("KeychainManager: Successfully updated data") | |
| } | |
| } | |
| } | |
| public static func deleteData(itemKey:String) throws { | |
| let queryDelete: [String: AnyObject] = [ | |
| kSecClass as String: kSecClassGenericPassword, | |
| kSecAttrAccount as String: itemKey | |
| ] | |
| let resultCodeDelete = SecItemDelete(queryDelete as CFDictionaryRef) | |
| if let err = mapResultCode(resultCodeDelete) { | |
| throw err | |
| } else { | |
| print("KeychainManager: Successfully deleted data") | |
| } | |
| } | |
| static func mapResultCode(result:OSStatus) -> KeychainError? { | |
| switch result { | |
| case 0: | |
| return nil | |
| case -4: | |
| return .OperationUnimplemented | |
| case -50: | |
| return .InvalidParam | |
| case -108: | |
| return .MemoryAllocationFailure | |
| case -25291: | |
| return .TrustResultsUnavailable | |
| case -25293: | |
| return .AuthFailed | |
| case -25299: | |
| return .DuplicateItem | |
| case -25300: | |
| return .ItemNotFound | |
| case -25308: | |
| return .ServerInteractionNotAllowed | |
| case -26275: | |
| return .DecodeError | |
| default: | |
| return .Unknown(result.hashValue) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment