### Manual Install Steps for Swift Security Expert Skill Source: https://github.com/ivan-magda/swift-security-skill/blob/main/README.md Instructions for manually installing the swift-security-expert skill by cloning the repository and following tool-specific installation documentation. This method is for users who prefer not to use automated tools. ```bash 1. Clone this repository 2. Install or symlink the `swift-security-expert/` folder following your tool's skill installation docs 3. Ask your AI tool to use the "swift security expert" skill for security tasks ``` -------------------------------- ### AuthViewModel Example Usage Source: https://context7.com/ivan-magda/swift-security-skill/llms.txt An example of how to use the `KeychainManager` from a SwiftUI ViewModel to load an authentication token. ```APIDOC ## Class AuthViewModel ### Description An `ObservableObject` that demonstrates how to use `KeychainManager` to manage authentication state. It loads an authentication token asynchronously. ### Properties - **`isAuthenticated`** (@Published var Bool) - A published property indicating whether the user is authenticated. ### Methods #### `loadToken()` An asynchronous method that attempts to load an authentication token from the keychain using `KeychainManager.shared.load(for:)`. Updates the `isAuthenticated` property based on the result. - **`async`** - **`throws`** ``` -------------------------------- ### Swift Keychain Integration Tests with Setup/Teardown Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/testing-security-code.md Use setUp and tearDown methods to ensure a clean slate before each test and to remove any test artifacts afterward. This isolates tests and prevents interference between them. ```swift final class KeychainIntegrationTests: XCTestCase { private let testService = "com.tests.keychain-integration" private var keychain: KeychainService! override func setUp() { super.setUp() keychain = KeychainService(service: testService) try? keychain.deleteAll() // Clean slate } override func tearDown() { try? keychain.deleteAll() // Leave no trace super.tearDown() } func testSaveAndRetrieveToken() throws { let token = "test-jwt-token-12345" try keychain.save(token.data(using: .utf8)!, forKey: "access_token") let retrieved = try keychain.read(forKey: "access_token") XCTAssertEqual(String(data: retrieved!, encoding: .utf8), token) } } ``` -------------------------------- ### Detect Fresh App Installs with Keychain Cleanup Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/testing-security-code.md This function checks if the app has launched before. If it's a fresh install, it clears all keychain items to ensure no leftover sensitive data from previous installations. It uses UserDefaults to track the launch status. ```swift static func handleFreshInstall(keychain: KeychainServiceProtocol) { let hasLaunched = UserDefaults.standard.bool(forKey: "has_launched") if !hasLaunched { try? keychain.deleteAll() UserDefaults.standard.set(true, forKey: "has_launched") } } ``` -------------------------------- ### Test Secure Enclave Signing Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/testing-security-code.md Demonstrates how to correctly test Secure Enclave signing operations. The broken example shows a common pitfall, while the correct examples use `XCTSkipUnless` or a protocol-based fallback for simulator compatibility. ```swift // ❌ INCORRECT: Crashes on simulator and CI func testSecureEnclaveSigning_BROKEN() throws { let key = try SecureEnclave.P256.Signing.PrivateKey() // throws on simulator let sig = try key.signature(for: "data".data(using: .utf8)!) XCTAssertTrue(key.publicKey.isValidSignature(sig, for: "data".data(using: .utf8)!)) } ``` ```swift // ✅ CORRECT: Skip gracefully when SE unavailable func testSecureEnclaveSigning_withGuard() throws { try XCTSkipUnless(SecureEnclave.isAvailable, "Secure Enclave not available — skipping on simulator") let key = try SecureEnclave.P256.Signing.PrivateKey() let data = "authenticated payload".data(using: .utf8)! let sig = try key.signature(for: data) XCTAssertTrue(key.publicKey.isValidSignature(sig, for: data)) } ``` ```swift // ✅ CORRECT: Protocol-based test runs everywhere func testSigningWithFallback() throws { let signer = SigningKeyFactory.make() let data = "payload".data(using: .utf8)! let sigBytes = try signer.sign(data) XCTAssertFalse(sigBytes.isEmpty) let publicKey = try P256.Signing.PublicKey(derRepresentation: signer.publicKeyData()) let signature = try P256.Signing.ECDSASignature(derRepresentation: sigBytes) XCTAssertTrue(publicKey.isValidSignature(signature, for: data)) } ``` -------------------------------- ### Generate P256 Signing Key Pair, Sign, and Verify Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/cryptokit-public-key.md Demonstrates generating a P256 signing key pair, signing a message, and verifying the signature. Includes examples of serializing signatures to DER and raw formats, and restoring from DER. ```swift import CryptoKit // Generate a signing key pair let signingKey = P256.Signing.PrivateKey() let verifyingKey = signingKey.publicKey // P256.Signing.PublicKey // Sign data (CryptoKit hashes internally with SHA-256) let message = Data("Transfer $100 to Alice".utf8) let signature = try signingKey.signature(for: message) // signature is P256.Signing.ECDSASignature // Verify let isValid = verifyingKey.isValidSignature(signature, for: message) // Signature serialization let derSig = signature.derRepresentation // ASN.1 DER (interoperable) let rawSig = signature.rawRepresentation // Raw r‖s concatenation (64 bytes) let restored = try P256.Signing.ECDSASignature(derRepresentation: derSig) ``` -------------------------------- ### Incorrect App Initialization Without Cleanup Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/migration-legacy-stores.md This incorrect example demonstrates an app that initializes without performing first-launch keychain cleanup. This can lead to security vulnerabilities where a new user might inherit session tokens or credentials from a previous user who uninstalled the app. ```swift // ❌ INCORRECT: No first-launch cleanup — stale keychain from previous install @main struct BrokenApp: App { init() { // Reads keychain without checking for stale data if let token = try? keychainRead(service: "com.myapp", account: "authToken") { // This token might be from a PREVIOUS user who deleted the app. // The new user inherits someone else's session. AuthManager.shared.restoreSession(token: token) } } var body: some Scene { WindowGroup { ContentView() } } } ``` -------------------------------- ### Clear Keychain on Fresh Install Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/keychain-sharing.md This Swift function demonstrates a workaround to clear specific keychain items when an app is launched for the first time after a fresh install. It scopes deletion to a particular service to avoid removing unrelated shared items. Ensure the 'hasLaunchedBefore' UserDefaults key is managed appropriately. ```swift func clearKeychainOnFreshInstall() { let hasLaunchedBefore = UserDefaults.standard.bool(forKey: "hasLaunchedBefore") if !hasLaunchedBefore { // Scope deletion to specific service/group to avoid nuking shared items let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "com.example.authService" ] SecItemDelete(query as CFDictionary) UserDefaults.standard.set(true, forKey: "hasLaunchedBefore") } } ``` -------------------------------- ### Swift Keychain Sharing Setup and Operations Source: https://github.com/ivan-magda/swift-security-skill/blob/main/tests/test-plan-M-multi-domain-review.md This Swift code demonstrates setting up shared keychain constants and implementing functions to save and load session data between an app and its widget. It highlights potential issues with access group configuration, kSecClass usage, and synchronization flags. ```swift // Shared constants file (used by both targets) enum SharedKeychain { static let accessGroup = "com.myapp.shared" // In both entitlements files static let service = "shared-credentials" } // Main App — saves user session func saveSessionForWidget(session: SessionData) throws { let encoded = try JSONEncoder().encode(session) let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrAccessGroup as String: SharedKeychain.accessGroup, kSecAttrService as String: SharedKeychain.service, kSecAttrAccount as String: "userSession", kSecValueData as String: encoded, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kSecAttrSynchronizable as String: true ] let status = SecItemAdd(query as CFDictionary, nil) guard status == errSecSuccess || status == errSecDuplicateItem else { throw KeychainError(status: status) } } // Widget Extension — reads user session func loadSessionForWidget() -> SessionData? { let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrAccessGroup as String: SharedKeychain.accessGroup, kSecAttrService as String: SharedKeychain.service, kSecReturnData as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) guard status == errSecSuccess, let data = result as? Data else { return nil } return try? JSONDecoder().decode(SessionData.self, from: data) } ``` -------------------------------- ### AuthenticationManager with KeychainServiceProtocol Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/testing-security-code.md An example business logic class that depends on the KeychainServiceProtocol for storing and retrieving authentication tokens. This allows for easy testing with the mock implementation. ```swift final class AuthenticationManager { private let keychain: KeychainServiceProtocol init(keychain: KeychainServiceProtocol) { self.keychain = keychain } func storeToken(_ token: String) throws { guard let data = token.data(using: .utf8) else { throw KeychainError.unexpectedData } try keychain.save(data, forKey: "auth_token") } func retrieveToken() throws -> String? { guard let data = try keychain.read(forKey: "auth_token") else { return nil } return String(data: data, encoding: .utf8) } } ``` -------------------------------- ### SecAccessControl: Correct Flags with Logical Operator Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/keychain-access-control.md This example demonstrates the correct way to combine multiple `SecAccessControl` flags using the `.or` operator, ensuring `SecAccessControlCreateWithFlags` returns a valid access control object. ```swift // ✅ CORRECT — explicit .or between constraints let access = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlocked, [.biometryCurrentSet, .or, .devicePasscode], &error ) ``` -------------------------------- ### Incorrect Keychain Migration Implementation Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/migration-legacy-stores.md This example shows a flawed migration approach that runs on every launch without proper checks. It lacks version verification, data availability checks, and fails to delete legacy data, leading to potential crashes and security vulnerabilities. ```swift // ❌ INCORRECT: Runs every launch, no version check, no verification, no legacy delete func brokenMigration() { // No version check — runs every single launch // No isProtectedDataAvailable check — fails during pre-warm if let token = UserDefaults.standard.string(forKey: "authToken") { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "com.myapp", kSecAttrAccount as String: "authToken", kSecValueData as String: token.data(using: .utf8)! ] // No errSecDuplicateItem handling — crashes on second launch SecItemAdd(query as CFDictionary, nil) // Never deletes from UserDefaults — plaintext secret persists // No verification that write succeeded } } ``` -------------------------------- ### AuthViewModel for Token Loading Source: https://context7.com/ivan-magda/swift-security-skill/llms.txt An example ViewModel that uses the KeychainManager to asynchronously load an authentication token. It updates a published property to reflect the authentication status. ```swift // Usage from SwiftUI ViewModel — suspends, does NOT block MainActor @MainActor class AuthViewModel: ObservableObject { @Published var isAuthenticated = false func loadToken() async { do { let data = try await KeychainManager.shared.load(for: "authToken") isAuthenticated = data != nil } catch { isAuthenticated = false } } } ``` -------------------------------- ### Generate SPKI Hashes using OpenSSL Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/certificate-trust.md Command-line examples for generating SPKI hashes from PEM certificate files or live servers using OpenSSL. These commands extract the public key, convert it to DER format, and then compute the Base64 encoded SHA256 hash. ```bash # From a PEM certificate file: openssl x509 -in cert.pem -noout -pubkey | \ openssl pkey -pubin -outform der | \ openssl dgst -sha256 -binary | openssl enc -base64 ``` ```bash # From a live server: openssl s_client -connect api.example.com:443 /dev/null | \ openssl x509 -pubkey -noout | \ openssl pkey -pubin -outform der | \ openssl dgst -sha256 -binary | openssl enc -base64 ``` -------------------------------- ### Incorrect Access Control for Biometric Key Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/secure-enclave.md This example demonstrates an incorrect way to create an access control for a biometric-gated key. Omitting `.privateKeyUsage` will allow key creation but cause signing operations to fail. Always include `.privateKeyUsage` when biometric protection is intended for key usage. ```swift // ❌ Omitting .privateKeyUsage causes signing to fail let badControl = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, .biometryCurrentSet, // Missing .privateKeyUsage! nil )! // Key creation succeeds, but signing operations will fail ``` -------------------------------- ### First-Launch Keychain Cleanup Actor Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/migration-legacy-stores.md Use this actor at the very start of your app's lifecycle, before any SDK initialization, to clear stale keychain items from previous installations. It requires iOS 15+ for `isProtectedDataAvailable` and pre-warming behavior. Ensure `kSecAttrSynchronizableAny` is included in cleanup queries to handle iCloud-synced items. ```swift // ✅ CORRECT: First-launch cleanup with protected data guard // iOS 15+ required for isProtectedDataAvailable / pre-warming behavior actor FirstLaunchGuard { static let shared = FirstLaunchGuard() private let hasRunKey = "com.myapp.hasCompletedFirstLaunch" /// Call at the very start of app lifecycle, before SDK initialization. func performCleanupIfNeeded() async { let isSubsequentRun = UserDefaults.standard.bool(forKey: hasRunKey) guard !isSubsequentRun else { return } // iOS 15+ pre-warming guard: device may still be locked guard await isProtectedDataAvailable() else { await waitForProtectedData() return } // Wipe stale keychain items from a previous installation deleteAllKeychainItems() // Set flag so this only runs once per install UserDefaults.standard.set(true, forKey: hasRunKey) } private func deleteAllKeychainItems() { let classes: [CFString] = [ kSecClassGenericPassword, kSecClassInternetPassword, kSecClassCertificate, kSecClassKey, kSecClassIdentity ] for itemClass in classes { let query: NSDictionary = [ kSecClass: itemClass, kSecAttrSynchronizable: kSecAttrSynchronizableAny ] SecItemDelete(query) } } private func isProtectedDataAvailable() async -> Bool { await MainActor.run { UIApplication.shared.isProtectedDataAvailable } } private func waitForProtectedData() async { await withCheckedContinuation { continuation in NotificationCenter.default.addObserver( forName: UIApplication.protectedDataDidBecomeAvailableNotification, object: nil, queue: .main ) { _ in Task { self.deleteAllKeychainItems() UserDefaults.standard.set(true, forKey: self.hasRunKey) continuation.resume() } } } } } ``` -------------------------------- ### Complete App Launch Sequence with Migration Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/migration-legacy-stores.md This code demonstrates the correct ordering of operations during application launch. It includes first-launch cleanup, versioned migration, deferred cleanup of legacy files, and finally, initialization of SDKs like Firebase and analytics. This sequence ensures data integrity and a smooth user experience. ```swift // ✅ CORRECT: Complete launch sequence with migration @main struct MyApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, UIApplicationDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { Task { // 1. First-launch cleanup (stale keychain from previous install) await FirstLaunchGuard.shared.performCleanupIfNeeded() // 2. Versioned migration let state = await MigrationCoordinator.shared.migrateIfNeeded() switch state { case .upToDate: break case .migrated(let from, let to): os_log(.info, "Migrated schema v%d → v%d", from, to) case .deferred(let reason): os_log(.info, "Migration deferred: %{public}@", reason) case .failed(let error): os_log(.error, "Migration failed: %{public}@", error.localizedDescription) } // 3. Deferred cleanup of legacy files past rollback window await DeferredCleanup().cleanupIfExpired() // 4. NOW initialize Firebase, analytics, auth SDKs // Stale data cleared, migration complete or safely deferred } return true } } ``` -------------------------------- ### Install Swift Security Expert Skill via Claude Code Plugin Source: https://github.com/ivan-magda/swift-security-skill/blob/main/README.md Commands to add and install the swift-security-expert skill as a Claude Code Plugin. This allows for personal usage and project-wide configuration. ```bash /plugin marketplace add ivan-magda/swift-security-skill /plugin install swift-security-expert@swift-security-skill ``` -------------------------------- ### Create and Configure Temporary Keychain in GitHub Actions Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/testing-security-code.md This script creates a temporary keychain, sets its settings, unlocks it, and imports a certificate. It's crucial for CI environments that require secure credential management. Ensure the KEYCHAIN_PASSWORD and BUILD_CERTIFICATE_BASE64 environment variables are set. ```bash KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security set-keychain-settings -lut 21600 $KEYCHAIN_PATH security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH # Import cert + CRITICAL partition list step echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $RUNNER_TEMP/cert.p12 security import $RUNNER_TEMP/cert.p12 -P "$P12_PASSWORD" \ -A -t cert -f pkcs12 -k $KEYCHAIN_PATH security set-key-partition-list -S apple-tool:,apple: \ -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH ``` -------------------------------- ### Inspect Provisioning Profile Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/keychain-sharing.md Examine your application's provisioning profile to verify keychain-access-groups, com.apple.security.application-groups, and com.apple.developer.team-identifier are correctly configured. ```bash security cms -D -i YourApp.app/embedded.mobileprovision ``` -------------------------------- ### Secure Biometric Check with LAContext Source: https://github.com/ivan-magda/swift-security-skill/blob/main/tests/test-plan-I-improve-workflow.md This snippet demonstrates a basic biometric authentication check using LAContext. It's a starting point that needs modernization to be secure against bypasses. ```swift import LocalAuthentication class BiometricGate { func checkBiometric(completion: @escaping (Bool) -> Void) { let context = LAContext() context.evaluatePolicy( .deviceOwnerAuthenticationWithBiometrics, localizedReason: "Verify your identity" ) { success, _ in DispatchQueue.main.async { completion(success) } } } } // Usage in ViewController: biometricGate.checkBiometric { [weak self] success in if success { self?.showAccountDetails() } } ``` -------------------------------- ### Versioned Keychain Migration for Rotation Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/credential-storage-patterns.md Use versioned keychain items with `kSecAttrAccount` for backward-compatible migration during credential rotation. This example demonstrates migrating an older token format to a newer version. ```swift actor TokenMigrationManager { private let keychain: KeychainManager private static let currentVersion = 2 init(keychain: KeychainManager) { self.keychain = keychain } /// Call on app launch to migrate old token formats. func migrateIfNeeded() async throws { if let _ = try? await keychain.load(account: "oauth_tokens_v2") { return // Already current } if let oldData = try? await keychain.load(account: "oauth_tokens") { let migrated = try migrateV1ToV2(oldData) try await keychain.save(account: "oauth_tokens_v2", data: migrated) try await keychain.delete(account: "oauth_tokens") // Clean up old } } private func migrateV1ToV2(_ data: Data) throws -> Data { // Implement format conversion between versions return data } } ``` -------------------------------- ### Run Xcode Tests with Different Test Plans Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/testing-security-code.md Demonstrates how to run Xcode tests using different test plans for simulator-safe tests in CI and comprehensive tests on a physical device. The first command targets the simulator with 'CITests', while the second targets a physical device with 'DeviceTests'. ```bash # CI: simulator-safe tests on every push xcodebuild test -scheme MyApp \ -destination 'platform=iOS Simulator,name=iPhone 16' \ -testPlan CITests # Nightly: device farm runs everything xcodebuild test -scheme MyApp \ -destination 'platform=iOS,id=DEVICE_UDID' \ -testPlan DeviceTests ``` -------------------------------- ### Environment Detection for Simulator and Test Runner Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/testing-security-code.md Utilize ProcessInfo to detect if the code is running in a simulator environment or within an XCTest execution context. This is useful for conditional logic or test setup. ```swift struct EnvironmentDetector { static var isSimulator: Bool { ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil } static var isRunningTests: Bool { ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil } } ``` -------------------------------- ### Configure Claude Project for Swift Security Expert Skill Source: https://github.com/ivan-magda/swift-security-skill/blob/main/README.md JSON configuration for `.claude/settings.json` to automatically enable the swift-security-expert skill for all collaborators in a repository. It specifies the plugin to enable and its source. ```json { "enabledPlugins": { "swift-security-expert@swift-security-skill": true }, "extraKnownMarketplaces": { "swift-security-skill": { "source": { "source": "github", "repo": "ivan-magda/swift-security-skill" } } } } ``` -------------------------------- ### Monitor Biometric Enrollment Changes in Swift Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/biometric-authentication.md Detect changes in biometric enrollment status using `evaluatedPolicyDomainState`. This class helps proactively guide users through re-enrollment rather than encountering cryptic keychain errors. ```swift // ✅ Detect biometric enrollment changes via domainState class BiometricEnrollmentMonitor { private let domainStateKey = "com.app.biometric.domainState" /// Call after successful biometric setup to snapshot current enrollment func saveCurrentEnrollment() { let context = LAContext() guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) else { return } // domainState changes whenever biometric enrollment changes if let domainState = context.evaluatedPolicyDomainState { UserDefaults.standard.set(domainState, forKey: domainStateKey) } } /// Call on app launch or before biometric retrieval func hasEnrollmentChanged() -> Bool { let context = LAContext() guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) else { return true // Can't evaluate — treat as changed } guard let currentState = context.evaluatedPolicyDomainState, let savedState = UserDefaults.standard.data(forKey: domainStateKey) else { return true // No saved state — first run or data cleared } return currentState != savedState } } ``` -------------------------------- ### Software Signing Key Implementation Source: https://context7.com/ivan-magda/swift-security-skill/llms.txt Provides a software-based implementation of the `SigningKeyProvider` protocol for testing or scenarios where Secure Enclave is not used. It uses a standard P256 signing key. ```swift // Protocol abstraction for testable SE code protocol SigningKeyProvider { var publicKeyData: Data { get throws } func sign(_ data: Data) throws -> Data } final class SoftwareSigningKey: SigningKeyProvider { private let key = P256.Signing.PrivateKey() var publicKeyData: Data { get throws { key.publicKey.derRepresentation } } func sign(_ data: Data) throws -> Data { try key.signature(for: data).derRepresentation } } ``` -------------------------------- ### SwiftUI ViewModel Integration for Biometric Auth Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/biometric-authentication.md Integrate biometric authentication into your SwiftUI application using an ObservableObject ViewModel. This example shows how to call the `BiometricKeychain` and update UI state based on authentication success or failure. ```swift @MainActor class AuthViewModel: ObservableObject { @Published var isAuthenticated = false @Published var errorMessage: String? private let keychain = BiometricKeychain() func authenticate() { Task { do { let secret = try await keychain.retrieveSecret( account: "user_token", service: "com.myapp.auth" ) self.isAuthenticated = true self.processToken(secret) } catch { self.errorMessage = error.localizedDescription } } } } ``` -------------------------------- ### Fastlane CI Test Lane Configuration Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/testing-security-code.md Defines a Fastlane lane for CI testing. It includes setup for CI environment, syncing code signing, and running tests with specified scheme, test plan, and device. ```ruby lane :ci_test do setup_ci(timeout: 3600) sync_code_signing(type: "development", readonly: is_ci) run_tests(scheme: "MyApp", testplan: "CITests", device: "iPhone 16") end ``` -------------------------------- ### Keychain Query Styles Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/keychain-fundamentals.md Demonstrates two correct styles for defining Keychain queries using either CFString keys or String keys. Choose one style for consistency. ```swift let query: [CFString: Any] = [kSecClass: kSecClassGenericPassword] SecItemAdd(query as CFDictionary, nil) ``` ```swift let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword] SecItemAdd(query as CFDictionary, nil) ``` -------------------------------- ### Implement Storage Migration Logic Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/testing-security-code.md This class handles migrating data from UserDefaults to Keychain. It checks a migration version and moves sensitive data like authentication tokens, ensuring they are stored more securely. Test this logic with isolated UserDefaults and mock Keychain services. ```swift final class StorageMigrationManager { private let defaults: UserDefaults private let keychain: KeychainServiceProtocol init(defaults: UserDefaults = .standard, keychain: KeychainServiceProtocol) { self.defaults = defaults self.keychain = keychain } func migrateIfNeeded() throws { let version = defaults.integer(forKey: "migration_version") if version < 1 { if let token = defaults.string(forKey: "auth_token"), let data = token.data(using: .utf8) { try keychain.save(data, forKey: "auth_token") defaults.removeObject(forKey: "auth_token") } } defaults.set(1, forKey: "migration_version") } } ``` -------------------------------- ### Handle Keychain Errors with OSStatus Switch Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/common-anti-patterns.md When adding or updating items in the Keychain, always check the status code. This example shows how to handle success, duplicate items, and other potential errors, preventing silent data loss. ```swift func saveToken(_ token: Data) { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "com.app.auth", kSecAttrAccount as String: "accessToken", kSecValueData as String: token ] SecItemAdd(query as CFDictionary, nil) // Return value ignored! } ``` ```swift func saveToKeychain(value: Data, service: String, account: String) throws { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account, kSecValueData as String: value, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] let status = SecItemAdd(query as CFDictionary, nil) switch status { case errSecSuccess: return case errSecDuplicateItem: let search: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account ] let updateStatus = SecItemUpdate( search as CFDictionary, [kSecValueData as String: value] as CFDictionary) guard updateStatus == errSecSuccess else { throw KeychainError.updateFailed(updateStatus) } case errSecInteractionNotAllowed: throw KeychainError.deviceLocked case errSecAuthFailed: throw KeychainError.authenticationFailed default: throw KeychainError.unexpectedStatus(status) } } ``` -------------------------------- ### Perform First-Launch Keychain Cleanup Source: https://context7.com/ivan-magda/swift-security-skill/llms.txt This code deletes any orphaned Keychain items from previous app installations on the first launch. It iterates through common Keychain item classes and removes them if they exist. Ensure this runs before any new Keychain items are added. ```swift import Security // ✅ First-launch Keychain cleanup (items survive uninstall — delete stale items) class FirstLaunchManager { private static let isFirstLaunchKey = "com.app.first_launch_completed" static func performFirstLaunchCleanupIfNeeded() { guard !UserDefaults.standard.bool(forKey: isFirstLaunchKey) else { return } // Delete any orphaned Keychain items from previous installs let classes: [CFString] = [kSecClassGenericPassword, kSecInternetPassword, kSecClassKey, kSecClassCertificate, kSecClassIdentity] for secClass in classes { SecItemDelete([kSecClass as String: secClass] as CFDictionary) } UserDefaults.standard.set(true, forKey: isFirstLaunchKey) } } ``` -------------------------------- ### XCTest Patterns for Secure Enclave Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/secure-enclave.md Demonstrates how to mock Secure Enclave operations for unit testing and how to conditionally skip tests when the Secure Enclave is not available. ```swift import XCTest @testable import MyApp final class AuthServiceTests: XCTestCase { func testSignChallenge() throws { let mock = MockSigningKey() mock.signatureToReturn = Data([0xDE, 0xAD]) let service = AuthService(signingKey: mock) let result = try service.signChallenge(Data("test".utf8)) XCTAssertEqual(mock.signCallCount, 1) XCTAssertEqual(result, Data([0xDE, 0xAD])) } func testRealSEKey() throws { #if targetEnvironment(simulator) throw XCTSkip("Secure Enclave not available on Simulator") #else guard SecureEnclave.isAvailable else { throw XCTSkip("Secure Enclave not available on this hardware") } let key = try SESigningKey() let signature = try key.sign(Data("test".utf8)) XCTAssertFalse(signature.isEmpty) #endif } } ``` -------------------------------- ### Conditional Availability Checks for CryptoKit Features Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/cryptokit-public-key.md Always gate post-quantum and HPKE code behind `#available` checks to ensure compatibility with older iOS versions. This example shows how to handle different feature sets based on the OS version. ```swift if #available(iOS 26, macOS 26, *) { // Post-quantum code path } else if #available(iOS 17, macOS 14, *) { // Classical HPKE code path } else { // Manual ECIES fallback } ``` -------------------------------- ### Runtime API Key Fetch with Keychain Cache Source: https://context7.com/ivan-magda/swift-security-skill/llms.txt This actor demonstrates how to fetch API keys at runtime, avoiding embedding them in Info.plist or .xcconfig files which compile to plaintext. It first attempts to load the key from a Keychain cache and falls back to fetching from a backend if not found. App Attest is recommended for app integrity proof in production. ```swift // ✅ Runtime API key fetch — never embed in Info.plist or .xcconfig // (xcconfig values compile to plaintext in the app bundle) actor RuntimeAPIKeyManager { private let keychain: KeychainManager func apiKey(for service: String) async throws -> String { // Try Keychain cache first if let data = try? await keychain.load(for: "apikey_\(service)"), let key = String(data: data, encoding: .utf8) { return key } // Fetch from backend (use App Attest for app integrity proof in production) let key = try await fetchFromBackend(service: service) try await keychain.save(Data(key.utf8), for: "apikey_\(service)", accessibility: kSecAttrAccessibleWhenUnlockedThisDeviceOnly) return key } private func fetchFromBackend(service: String) async throws -> String { let (data, _) = try await URLSession.shared.data( from: URL(string: "https://api.example.com/keys/\(service)")!) let json = try JSONDecoder().decode([String: String].self, from: data) guard let value = json["key"] else { throw MigrationError.missingKey } return value } enum MigrationError: Error { case missingKey } init(keychain: KeychainManager) { self.keychain = keychain } } ``` -------------------------------- ### HMAC Hashing: CommonCrypto vs. CryptoKit Source: https://github.com/ivan-magda/swift-security-skill/blob/main/swift-security-expert/references/cryptokit-symmetric.md Demonstrates HMAC hashing using legacy CommonCrypto with C-style pointers versus CryptoKit's type-safe and constant-time verification. CryptoKit is recommended for its enhanced security features. ```swift // ❌ Legacy CommonCrypto — C-style pointers import CommonCrypto var hmac = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), keyBytes, keyData.count, dataBytes, data.count, &hmac) // ✅ CryptoKit — generic, type-safe, constant-time verification built in import CryptoKit let mac = HMAC.authenticationCode(for: data, using: key) let valid = HMAC.isValidAuthenticationCode(mac, authenticating: data, using: key) ```