diff --git a/KeychainSwift.xcodeproj/project.pbxproj b/KeychainSwift.xcodeproj/project.pbxproj index 25815f5..1aa5ecb 100644 --- a/KeychainSwift.xcodeproj/project.pbxproj +++ b/KeychainSwift.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 508566A81FA34EB1004208ED /* macOS_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566991FA34EB1004208ED /* macOS_Tests.swift */; }; 508566A91FA34EB1004208ED /* SynchronizableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5085669A1FA34EB1004208ED /* SynchronizableTests.swift */; }; 508566AA1FA34EB1004208ED /* SynchronizableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5085669A1FA34EB1004208ED /* SynchronizableTests.swift */; }; + 58F9C89B21FC82E300B5430B /* KeychainSwiftItemClassOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F9C89A21FC82E300B5430B /* KeychainSwiftItemClassOptions.swift */; }; 7E3A6B681D3F62CC007C5B1F /* KeychainSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */; }; 7E3A6B691D3F62CC007C5B1F /* KeychainSwiftAccessOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */; }; 7E3A6B6A1D3F62CC007C5B1F /* TegKeychainConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */; }; @@ -124,6 +125,7 @@ 508566981FA34EB1004208ED /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 508566991FA34EB1004208ED /* macOS_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = macOS_Tests.swift; sourceTree = ""; }; 5085669A1FA34EB1004208ED /* SynchronizableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizableTests.swift; sourceTree = ""; }; + 58F9C89A21FC82E300B5430B /* KeychainSwiftItemClassOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainSwiftItemClassOptions.swift; sourceTree = ""; }; 7E3A6B601D3F62C2007C5B1F /* macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 7E3A6B7B1D3F6779007C5B1F /* macOS Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "macOS Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 7E3A6B7D1D3F6779007C5B1F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -288,6 +290,7 @@ children = ( 7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */, 7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */, + 58F9C89A21FC82E300B5430B /* KeychainSwiftItemClassOptions.swift */, 7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */, 7ED6C9711B1C118F00FE8090 /* KeychainSwift.h */, 7ED6C96F1B1C118F00FE8090 /* Supporting Files */, @@ -761,6 +764,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 58F9C89B21FC82E300B5430B /* KeychainSwiftItemClassOptions.swift in Sources */, 7ED6C9911B1C128100FE8090 /* KeychainSwiftAccessOptions.swift in Sources */, 7ED6C9901B1C128100FE8090 /* KeychainSwift.swift in Sources */, 7ED6C9921B1C128100FE8090 /* TegKeychainConstants.swift in Sources */, diff --git a/Sources/KeychainSwift.swift b/Sources/KeychainSwift.swift index 21dfbb9..86cbc3f 100644 --- a/Sources/KeychainSwift.swift +++ b/Sources/KeychainSwift.swift @@ -53,6 +53,7 @@ open class KeychainSwift { - parameter key: Key under which the text value is stored in the keychain. - parameter value: Text string to be written to the keychain. + - parameter withClass: Value that specifies the class of a keychain item. - parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. - returns: True if the text was successfully written to the keychain. @@ -60,10 +61,11 @@ open class KeychainSwift { */ @discardableResult open func set(_ value: String, forKey key: String, - withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { + withClass classOption: KeychainSwiftItemClassOptions = .defaultOption, + withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { if let value = value.data(using: String.Encoding.utf8) { - return set(value, forKey: key, withAccess: access) + return set(value, forKey: key, withClass: classOption, withAccess: access) } return false @@ -75,6 +77,7 @@ open class KeychainSwift { - parameter key: Key under which the data is stored in the keychain. - parameter value: Data to be written to the keychain. + - parameter withClass: Value that specifies the class of a keychain item. - parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. - returns: True if the text was successfully written to the keychain. @@ -82,6 +85,7 @@ open class KeychainSwift { */ @discardableResult open func set(_ value: Data, forKey key: String, + withClass classOption: KeychainSwiftItemClassOptions = .defaultOption, withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { delete(key) // Delete any existing key before saving it @@ -89,9 +93,11 @@ open class KeychainSwift { let accessible = access?.value ?? KeychainSwiftAccessOptions.defaultOption.value let prefixedKey = keyWithPrefix(key) + + let itemClass: CFString = classOption.value var query: [String : Any] = [ - KeychainSwiftConstants.klass : kSecClassGenericPassword, + KeychainSwiftConstants.klass : itemClass, KeychainSwiftConstants.attrAccount : prefixedKey, KeychainSwiftConstants.valueData : value, KeychainSwiftConstants.accessible : accessible @@ -112,6 +118,7 @@ open class KeychainSwift { - parameter key: Key under which the value is stored in the keychain. - parameter value: Boolean to be written to the keychain. + - parameter withClass: Value that specifies the class of a keychain item. - parameter withAccess: Value that indicates when your app needs access to the value in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. - returns: True if the value was successfully written to the keychain. @@ -119,12 +126,13 @@ open class KeychainSwift { */ @discardableResult open func set(_ value: Bool, forKey key: String, + withClass classOption: KeychainSwiftItemClassOptions = .defaultOption, withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { let bytes: [UInt8] = value ? [1] : [0] let data = Data(bytes: bytes) - return set(data, forKey: key, withAccess: access) + return set(data, forKey: key, withClass: classOption, withAccess: access) } /** @@ -132,11 +140,12 @@ open class KeychainSwift { Retrieves the text value from the keychain that corresponds to the given key. - parameter key: The key that is used to read the keychain item. + - parameter ofClass: Value that specifies the class of a keychain item. - returns: The text value from the keychain. Returns nil if unable to read the item. */ - open func get(_ key: String) -> String? { - if let data = getData(key) { + open func get(_ key: String, ofClass classOption: KeychainSwiftItemClassOptions = .defaultOption) -> String? { + if let data = getData(key, ofClass: classOption) { if let currentString = String(data: data, encoding: .utf8) { return currentString @@ -153,19 +162,22 @@ open class KeychainSwift { Retrieves the data from the keychain that corresponds to the given key. - parameter key: The key that is used to read the keychain item. + - parameter ofClass: Value that specifies the class of a keychain item. - returns: The text value from the keychain. Returns nil if unable to read the item. */ - open func getData(_ key: String) -> Data? { + open func getData(_ key: String, ofClass classOption: KeychainSwiftItemClassOptions = .defaultOption) -> Data? { // The lock prevents the code to be run simlultaneously // from multiple threads which may result in crashing readLock.lock() defer { readLock.unlock() } let prefixedKey = keyWithPrefix(key) + + let itemClass = classOption.value var query: [String: Any] = [ - KeychainSwiftConstants.klass : kSecClassGenericPassword, + KeychainSwiftConstants.klass : itemClass, KeychainSwiftConstants.attrAccount : prefixedKey, KeychainSwiftConstants.returnData : kCFBooleanTrue, KeychainSwiftConstants.matchLimit : kSecMatchLimitOne @@ -191,11 +203,12 @@ open class KeychainSwift { Retrieves the boolean value from the keychain that corresponds to the given key. - parameter key: The key that is used to read the keychain item. + - parameter ofClass: Value that specifies the class of a keychain item. - returns: The boolean value from the keychain. Returns nil if unable to read the item. */ - open func getBool(_ key: String) -> Bool? { - guard let data = getData(key) else { return nil } + open func getBool(_ key: String, ofClass classOption: KeychainSwiftItemClassOptions = .defaultOption) -> Bool? { + guard let data = getData(key, ofClass: classOption) else { return nil } guard let firstBit = data.first else { return nil } return firstBit == 1 } @@ -205,15 +218,18 @@ open class KeychainSwift { Deletes the single keychain item specified by the key. - parameter key: The key that is used to delete the keychain item. + - parameter ofClass: Value that specifies the class of a keychain item. - returns: True if the item was successfully deleted. */ @discardableResult - open func delete(_ key: String) -> Bool { + open func delete(_ key: String, ofClass classOption: KeychainSwiftItemClassOptions = .defaultOption) -> Bool { let prefixedKey = keyWithPrefix(key) + + let itemClass = classOption.value var query: [String: Any] = [ - KeychainSwiftConstants.klass : kSecClassGenericPassword, + KeychainSwiftConstants.klass : itemClass, KeychainSwiftConstants.attrAccount : prefixedKey ] @@ -230,12 +246,16 @@ open class KeychainSwift { Deletes all Keychain items used by the app. Note that this method deletes all items regardless of the prefix settings used for initializing the class. + - parameter itemsOfClass: Value that specifies the class of a keychain item. - returns: True if the keychain items were successfully deleted. */ @discardableResult - open func clear() -> Bool { - var query: [String: Any] = [ kSecClass as String : kSecClassGenericPassword ] + open func clear(itemsOfClass classOption: KeychainSwiftItemClassOptions = .defaultOption) -> Bool { + + let itemClass = classOption.value + + var query: [String: Any] = [ kSecClass as String : itemClass ] query = addAccessGroupWhenPresent(query) query = addSynchronizableIfRequired(query, addingItems: false) lastQueryParameters = query diff --git a/Sources/KeychainSwiftItemClassOptions.swift b/Sources/KeychainSwiftItemClassOptions.swift new file mode 100644 index 0000000..74be050 --- /dev/null +++ b/Sources/KeychainSwiftItemClassOptions.swift @@ -0,0 +1,43 @@ +// +// KeychainSwiftItemClassOptions.swift +// KeychainSwift +// +// Created by Oleynik Gennady on 26/01/2019. +// Copyright © 2019 Marketplacer. All rights reserved. +// + + +/** + Keychain items come in a variety of classes according to the kind of data they hold, such as passwords, cryptographic keys, and certificates. The item's class dictates which attributes apply and enables the system to decide whether or not the data should be encrypted on disk. For example, passwords require encryption, but certificates don't because they are not secret. + */ + +/** + KeychainSwiftItemClassOptions is an wrapper on system kSecClass values + */ + +public enum KeychainSwiftItemClassOptions { + case genericPassword + case internetPassword + case certificate + case key + case identity + + public static var defaultOption: KeychainSwiftItemClassOptions { + return .genericPassword + } + + var value: CFString { + switch self { + case .genericPassword: + return kSecClassGenericPassword + case .internetPassword: + return kSecClassInternetPassword + case .certificate: + return kSecClassCertificate + case .key: + return kSecClassKey + case .identity: + return kSecClassIdentity + } + } +}