|
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// SecItem.swift
|
|
|
|
|
|
|
|
// Keychain
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Created by Bram Kolkman on 23/02/2026.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Security
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public enum SecItem {
|
|
|
|
|
|
|
|
/// Adds an item to the keychain.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Use this function to store credentials, keys, certificates, and other secure data in the keychain.
|
|
|
|
|
|
|
|
/// The keychain automatically encrypts the data and manages access control.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Parameter attributes: A dictionary containing the attributes of the item to add. At minimum,
|
|
|
|
|
|
|
|
/// include `kSecClass` to specify the item class and `kSecValueData` or `kSecValueRef` for the item's value.
|
|
|
|
|
|
|
|
/// Additional attributes control access, persistence, and synchronization behavior.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Returns: If you include the `kSecReturnAttributes`, `kSecReturnData`, `kSecReturnRef`, or
|
|
|
|
|
|
|
|
/// `kSecReturnPersistentRef` key with a value of `true` in the attributes dictionary, this function
|
|
|
|
|
|
|
|
/// returns the requested data. Otherwise, it returns `nil`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Throws: A `SecError` if the item could not be added. Common errors include duplicate items
|
|
|
|
|
|
|
|
/// (`errSecDuplicateItem`) or invalid parameters (`errSecParam`).
|
|
|
|
|
|
|
|
@discardableResult
|
|
|
|
|
|
|
|
public static func add(_ attributes: [CFString: Any]) throws -> CFTypeRef? {
|
|
|
|
|
|
|
|
var result: CFTypeRef?
|
|
|
|
|
|
|
|
let status = SecItemAdd(attributes as CFDictionary, &result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch status {
|
|
|
|
|
|
|
|
case errSecSuccess:
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
throw SecError(status)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Searches for and retrieves items from the keychain.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Use this function to find keychain items that match specified search criteria. You can retrieve
|
|
|
|
|
|
|
|
/// the item's data, attributes, or a persistent reference that you can use for later access.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Parameter query: A dictionary containing the search criteria. At minimum, include `kSecClass`
|
|
|
|
|
|
|
|
/// to specify the item class. Add search attributes like `kSecAttrAccount` or `kSecAttrService`
|
|
|
|
|
|
|
|
/// to narrow results. Include return type keys (`kSecReturnData`, `kSecReturnAttributes`,
|
|
|
|
|
|
|
|
/// `kSecReturnRef`, or `kSecReturnPersistentRef`) to specify what data to return.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Returns: The requested keychain item data, attributes, or reference as specified by the return
|
|
|
|
|
|
|
|
/// type keys in the query. Returns `nil` if no matching item is found (`errSecItemNotFound`).
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Throws: A `SecError` if the search fails for reasons other than the item not being found.
|
|
|
|
|
|
|
|
/// Common errors include invalid parameters (`errSecParam`) or user interaction cancelled
|
|
|
|
|
|
|
|
/// (`errSecUserCanceled`).
|
|
|
|
|
|
|
|
public static func copy(matching query: [CFString: Any]) throws (SecError) -> CFTypeRef? {
|
|
|
|
|
|
|
|
var result: CFTypeRef?
|
|
|
|
|
|
|
|
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch status {
|
|
|
|
|
|
|
|
case errSecSuccess:
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
case errSecItemNotFound:
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
throw SecError(status)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Modifies items in the keychain that match a search query.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Use this function to change the attributes or data of existing keychain items. The function
|
|
|
|
|
|
|
|
/// finds items matching the search criteria and applies the specified updates to them.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Parameter query: A dictionary containing the search criteria for items to update. At minimum,
|
|
|
|
|
|
|
|
/// include `kSecClass` to specify the item class. Add search attributes to identify specific items.
|
|
|
|
|
|
|
|
/// Do not include return type keys in the query dictionary.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Parameter updates: A dictionary containing the attributes to modify. Include only the attributes
|
|
|
|
|
|
|
|
/// you want to change. You cannot modify the `kSecClass` attribute.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Throws: A `SecError` if the update fails. Common errors include item not found (`errSecItemNotFound`),
|
|
|
|
|
|
|
|
/// invalid parameters (`errSecParam`), or attempting to create a duplicate item.
|
|
|
|
|
|
|
|
public static func update(matching query: [CFString: Any], with updates: [CFString: Any]) throws {
|
|
|
|
|
|
|
|
let status = SecItemUpdate(query as CFDictionary, updates as CFDictionary)
|
|
|
|
|
|
|
|
switch status {
|
|
|
|
|
|
|
|
case errSecSuccess:
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
throw SecError(status)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Removes items from the keychain that match a search query.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Use this function to delete keychain items that are no longer needed. The function finds all items
|
|
|
|
|
|
|
|
/// matching the search criteria and removes them from the keychain. If no matching items exist,
|
|
|
|
|
|
|
|
/// the function succeeds without error.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Parameter query: A dictionary containing the search criteria for items to delete. At minimum,
|
|
|
|
|
|
|
|
/// include `kSecClass` to specify the item class. Add search attributes to identify specific items.
|
|
|
|
|
|
|
|
/// To delete all items of a class, specify only `kSecClass`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// - Throws: A `SecError` if the deletion fails for reasons other than the item not being found.
|
|
|
|
|
|
|
|
/// Common errors include invalid parameters (`errSecParam`) or user interaction required
|
|
|
|
|
|
|
|
/// (`errSecInteractionNotAllowed`).
|
|
|
|
|
|
|
|
public static func delete(matching query: [CFString: Any]) throws {
|
|
|
|
|
|
|
|
let status = SecItemDelete(query as CFDictionary)
|
|
|
|
|
|
|
|
switch status {
|
|
|
|
|
|
|
|
case errSecSuccess, errSecItemNotFound:
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
throw SecError(status)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|