From 430fac27adadcdf988f6989a03df2279c1270a67 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Mon, 21 Oct 2024 16:28:17 -0700 Subject: [PATCH] Deprecate more types --- .../Credentials/CredentialStorage.swift | 4 +- .../Credentials/CredentialTypes.swift | 44 +++++++++++++++++++ .../SpeziSecureStorage/Keys/KeyStorage.swift | 19 ++++++++ .../SpeziSecureStorage/SecureStorage.swift | 22 +++++++++- .../SecureStorageItemTypes.swift | 15 +++---- 5 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 Sources/SpeziSecureStorage/Credentials/CredentialTypes.swift diff --git a/Sources/SpeziSecureStorage/Credentials/CredentialStorage.swift b/Sources/SpeziSecureStorage/Credentials/CredentialStorage.swift index edd3f20..d91bb18 100644 --- a/Sources/SpeziSecureStorage/Credentials/CredentialStorage.swift +++ b/Sources/SpeziSecureStorage/Credentials/CredentialStorage.swift @@ -96,8 +96,8 @@ public final class CredentialStorage: Module, DefaultInitializable, EnvironmentA /// - Parameters: /// - itemTypes: The types of items. /// - accessGroup: The access group associated with the credentials. - public func deleteAll(types itemTypes: SecureStorageItemTypes = .all, accessGroup: String? = nil) throws { - for kSecClassType in itemTypes.kSecClass { + public func deleteAll(types: CredentialTypes, accessGroup: String? = nil) throws { + for kSecClassType in types.kSecClasses { do { var query: [String: Any] = [kSecClass as String: kSecClassType] // Only append the accessGroup attribute if the `CredentialsStore` is configured to use KeyChain access groups diff --git a/Sources/SpeziSecureStorage/Credentials/CredentialTypes.swift b/Sources/SpeziSecureStorage/Credentials/CredentialTypes.swift new file mode 100644 index 0000000..fee8236 --- /dev/null +++ b/Sources/SpeziSecureStorage/Credentials/CredentialTypes.swift @@ -0,0 +1,44 @@ +// +// CredentialType.swift +// SpeziStorage +// +// Created by Paul Kraft on 21.10.2024. +// + +import CryptoKit +import Foundation + +public struct CredentialTypes: OptionSet { + /// Credentials that are created using a server name. + /// + /// Refers to any credentials that are stored with a server name using ``CredentialStorage``. + public static let server = CredentialTypes(rawValue: 1 << 1) + /// Credentials that are created without supplying a server name. + /// + /// Refers to any credentials that are stored without a server name using ``CredentialStorage``. + public static let nonServer = CredentialTypes(rawValue: 1 << 2) + + /// Any credentials created with the `CredentialStorage` module. + /// + /// Refers to any credentials that are created using ``CredentialStorage``. + public static let all: CredentialTypes = [.server, .nonServer] + + var kSecClasses: [CFString] { + var kSecClasses: [CFString] = [] + if self.contains(.server) { + kSecClasses.append(kSecClassGenericPassword) + } + if self.contains(.nonServer) { + kSecClasses.append(kSecClassInternetPassword) + } + return kSecClasses + } + + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } +} + +extension CredentialTypes: Sendable {} diff --git a/Sources/SpeziSecureStorage/Keys/KeyStorage.swift b/Sources/SpeziSecureStorage/Keys/KeyStorage.swift index 9b8bb69..7b1eda8 100644 --- a/Sources/SpeziSecureStorage/Keys/KeyStorage.swift +++ b/Sources/SpeziSecureStorage/Keys/KeyStorage.swift @@ -113,6 +113,25 @@ public final class KeyStorage: Module, DefaultInitializable, EnvironmentAccessib } } + /// Deletes all keys currently stored in the keychain or the secure enclave. + public func deleteAll() throws { + do { + var query: [String: Any] = [kSecClass as String: kSecClassKey] + + // Use Data protection keychain on macOS + #if os(macOS) + query[kSecUseDataProtectionKeychain as String] = true + #endif + + try SecureStorageError.execute(SecItemDelete(query as CFDictionary)) + } catch SecureStorageError.notFound { + // We are fine it no keychain items have been found and therefore non had been deleted. + return + } catch { + print(error) + } + } + private func keyQuery(forTag tag: String) -> [String: Any] { var query: [String: Any] = [ kSecClass as String: kSecClassKey, diff --git a/Sources/SpeziSecureStorage/SecureStorage.swift b/Sources/SpeziSecureStorage/SecureStorage.swift index c276025..2bb62af 100644 --- a/Sources/SpeziSecureStorage/SecureStorage.swift +++ b/Sources/SpeziSecureStorage/SecureStorage.swift @@ -165,7 +165,27 @@ public final class SecureStorage: Module, DefaultInitializable, EnvironmentAcces /// - itemTypes: The types of items. /// - accessGroup: The access group associated with the credentials. public func deleteAllCredentials(itemTypes: SecureStorageItemTypes = .all, accessGroup: String? = nil) throws { - try credentialStorage.deleteAll(types: itemTypes, accessGroup: accessGroup) + for kSecClassType in itemTypes.kSecClasses { + do { + var query: [String: Any] = [kSecClass as String: kSecClassType] + // Only append the accessGroup attribute if the `CredentialsStore` is configured to use KeyChain access groups + if let accessGroup { + query[kSecAttrAccessGroup as String] = accessGroup + } + + // Use Data protection keychain on macOS + #if os(macOS) + query[kSecUseDataProtectionKeychain as String] = true + #endif + + try SecureStorageError.execute(SecItemDelete(query as CFDictionary)) + } catch SecureStorageError.notFound { + // We are fine it no keychain items have been found and therefore non had been deleted. + continue + } catch { + print(error) + } + } } /// Update existing credentials found in the Keychain. diff --git a/Sources/SpeziSecureStorage/SecureStorageItemTypes.swift b/Sources/SpeziSecureStorage/SecureStorageItemTypes.swift index 55e4786..9958d4a 100644 --- a/Sources/SpeziSecureStorage/SecureStorageItemTypes.swift +++ b/Sources/SpeziSecureStorage/SecureStorageItemTypes.swift @@ -10,6 +10,7 @@ import Security /// Types of items that can be stored in the secure storage. +@available(*, deprecated, message: "Migrate your code to use `CredentialStorage` and `KeyStorage`.") public struct SecureStorageItemTypes: OptionSet { /// Any keys created with the `SecureStorage` module. /// @@ -34,19 +35,18 @@ public struct SecureStorageItemTypes: OptionSet { public let rawValue: Int - - var kSecClass: [CFString] { - var kSecClass: [CFString] = [] + var kSecClasses: [CFString] { + var kSecClasses: [CFString] = [] if self.contains(.keys) { - kSecClass.append(kSecClassKey) + kSecClasses.append(kSecClassKey) } if self.contains(.serverCredentials) { - kSecClass.append(kSecClassGenericPassword) + kSecClasses.append(kSecClassGenericPassword) } if self.contains(.nonServerCredentials) { - kSecClass.append(kSecClassInternetPassword) + kSecClasses.append(kSecClassInternetPassword) } - return kSecClass + return kSecClasses } @@ -55,5 +55,4 @@ public struct SecureStorageItemTypes: OptionSet { } } - extension SecureStorageItemTypes: Sendable {}