Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added item class feature #95

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions KeychainSwift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -124,6 +125,7 @@
508566981FA34EB1004208ED /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
508566991FA34EB1004208ED /* macOS_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = macOS_Tests.swift; sourceTree = "<group>"; };
5085669A1FA34EB1004208ED /* SynchronizableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizableTests.swift; sourceTree = "<group>"; };
58F9C89A21FC82E300B5430B /* KeychainSwiftItemClassOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainSwiftItemClassOptions.swift; sourceTree = "<group>"; };
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 = "<group>"; };
Expand Down Expand Up @@ -288,6 +290,7 @@
children = (
7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */,
7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */,
58F9C89A21FC82E300B5430B /* KeychainSwiftItemClassOptions.swift */,
7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */,
7ED6C9711B1C118F00FE8090 /* KeychainSwift.h */,
7ED6C96F1B1C118F00FE8090 /* Supporting Files */,
Expand Down Expand Up @@ -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 */,
Expand Down
48 changes: 34 additions & 14 deletions Sources/KeychainSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,19 @@ 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.

*/
@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
Expand All @@ -75,23 +77,27 @@ 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.

*/
@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

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
Expand All @@ -112,31 +118,34 @@ 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.

*/
@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)
}

/**

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
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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
]

Expand All @@ -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
Expand Down
43 changes: 43 additions & 0 deletions Sources/KeychainSwiftItemClassOptions.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
}