# Swift Security

> Use when working with iOS/macOS Keychain Services (SecItem queries, kSecClass, OSStatus errors), biometric authentication (LAContext, Face ID, Touch ID), CryptoKit (AES-GCM, ChaChaPoly, ECDSA, ECDH, HPKE, ML-KEM), Secure Enclave, secure credential storage (OAuth tokens, API keys), certificate pinning (SecTrust, SPKI), keychain sharing across apps/extensions, migrating secrets from UserDefaults or plists, or OWASP MASVS/MASTG mobile compliance on Apple platforms.

- Skill: `aibot88/swift-security` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add aibot88/swift-security`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aibot88/swift-security/raw
- Safety review: pending (external: skill-scanner PASS, skillspector WARNING)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- License: MIT
- Author: aibot88 (https://skillmd.com/u/aibot88)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/aibot88/swift-security

---


# Keychain & Security Expert Skill

> **Philosophy:** Non-opinionated, correctness-focused. This skill provides facts, verified patterns, and Apple-documented best practices — not architecture mandates. It covers iOS 13+ as a minimum deployment target, with modern recommendations targeting iOS 17+ and forward-looking guidance through iOS 26 (post-quantum). Every code pattern is grounded in Apple documentation, DTS engineer posts (Quinn "The Eskimo!"), WWDC sessions, and OWASP MASTG — never from memory alone.
>
> **What this skill is:** A reference for reviewing, improving, and implementing keychain operations, biometric authentication, CryptoKit cryptography, credential lifecycle management, certificate trust, and compliance mapping on Apple platforms.
>
> **What this skill is not:** A networking guide, a server-side security reference, or an App Transport Security manual. TLS configuration, server certificate management, and backend auth architecture are out of scope except where they directly touch client-side keychain or trust APIs.

---

## Contents

- [Decision Tree](#decision-tree)
- [Domain Selection Guide](#domain-selection-guide)
- [Agent Behavioral Rules](#agent-behavioral-rules)
- [Common Mistakes](#common-mistakes)
- [Top-Level Review Checklist](#top-level-review-checklist)
- [Version Reference Table](#version-reference-table)
- [Self-Review Gate](#self-review-gate)

## Decision Tree

Determine the user's intent, then follow the matching branch. If ambiguous, ask.

```
                        ┌─────────────────────┐
                        │  What is the task?   │
                        └─────────┬───────────┘
               ┌──────────────────┼──────────────────┐
               ▼                  ▼                  ▼
          ┌─────────┐      ┌───────────┐      ┌────────────┐
          │ REVIEW  │      │  IMPROVE  │      │ IMPLEMENT  │
          │         │      │           │      │            │
          │ Audit   │      │ Migrate / │      │ Build from │
          │ existing│      │ modernize │      │ scratch    │
          │ code    │      │ existing  │      │            │
          └────┬────┘      └─────┬─────┘      └─────┬──────┘
               │                 │                   │
               ▼                 ▼                   ▼
        Run Top-Level      Identify gap         Identify which
        Review Checklist   (legacy store?        domain(s) apply,
        (§ below) against  wrong API?            load reference
        the code.          missing auth?)        file(s), follow
        Flag each item     Load migration +      ✅ patterns.
        as ✅ / ❌ /       domain-specific        Implement with
        ⚠️ N/A.           reference files.       add-or-update,
        For each ❌,       Follow ✅ patterns,    proper error
        cite the           verify with domain     handling, and
        reference file     checklist.             correct access
        and specific                              control from
        section.                                  the start.
```

---

### Branch 1 — REVIEW (Audit Existing Code)

**Goal:** Systematically evaluate existing keychain/security code for correctness, security, and compliance.

**Procedure:**

1. **Run the Top-Level Review Checklist** (below) against the code under review. Score each item ✅ / ❌ / ⚠️ N/A.
2. **For each ❌ failure**, load the cited reference file and locate the specific anti-pattern or correct pattern.
3. **Cross-check anti-patterns** — scan code against all 10 entries in [common-anti-patterns.md](references/common-anti-patterns.md). Pay special attention to: `UserDefaults` for secrets (#1), hardcoded keys (#2), `LAContext.evaluatePolicy()` as sole auth gate (#3), ignored `OSStatus` (#4).
4. **Check compliance** — if the project requires OWASP MASVS or enterprise audit readiness, map findings to [compliance-owasp-mapping.md](references/compliance-owasp-mapping.md) categories M1, M3, M9, M10.
5. **Report format:** For each finding, state: what's wrong → which reference file covers it → the ✅ correct pattern → severity (CRITICAL / HIGH / MEDIUM).

**Key reference files for review:**

- Start with: [common-anti-patterns.md](references/common-anti-patterns.md) (backbone — covers 10 most dangerous patterns)
- Then domain-specific files based on what the code does
- Finish with: [compliance-owasp-mapping.md](references/compliance-owasp-mapping.md) (if compliance is relevant)

---

### Branch 2 — IMPROVE (Migrate / Modernize)

**Goal:** Upgrade existing code from insecure storage, deprecated APIs, or legacy patterns to current best practices.

**Procedure:**

1. **Identify the migration type:**
   - Insecure storage → Keychain: Load [migration-legacy-stores.md](references/migration-legacy-stores.md) + [credential-storage-patterns.md](references/credential-storage-patterns.md)
   - Legacy Security framework → CryptoKit: Load [cryptokit-symmetric.md](references/cryptokit-symmetric.md) or [cryptokit-public-key.md](references/cryptokit-public-key.md) + [migration-legacy-stores.md](references/migration-legacy-stores.md)
   - RSA → Elliptic Curve: Load [cryptokit-public-key.md](references/cryptokit-public-key.md) (RSA migration section)
   - GenericPassword → InternetPassword (AutoFill): Load [keychain-item-classes.md](references/keychain-item-classes.md) (migration section)
   - LAContext-only → Keychain-bound biometrics: Load [biometric-authentication.md](references/biometric-authentication.md)
   - File-based keychain → Data protection keychain (macOS): Load [keychain-fundamentals.md](references/keychain-fundamentals.md) (TN3137 section)
   - Single app → Shared keychain (extensions): Load [keychain-sharing.md](references/keychain-sharing.md)
   - Leaf pinning → SPKI/CA pinning: Load [certificate-trust.md](references/certificate-trust.md)

2. **Follow the migration pattern** in the relevant reference file. Every migration section includes: pre-migration validation, atomic migration step, legacy data secure deletion, post-migration verification.

3. **Run the domain-specific checklist** from the reference file after migration completes.

4. **Verify no regressions** using guidance from [testing-security-code.md](references/testing-security-code.md).

---

### Branch 3 — IMPLEMENT (Build from Scratch)

**Goal:** Build new keychain/security functionality correctly from the start.

**Procedure:**

1. **Identify which domain(s) the task touches.** Use the Domain Selection Guide below.
2. **Load the relevant reference file(s).** Follow ✅ code patterns — never deviate from them for the core security logic.
3. **Apply Core Guidelines** (below) to every implementation.
4. **Run the domain-specific checklist** before considering the implementation complete.
5. **Add tests** following [testing-security-code.md](references/testing-security-code.md) — protocol-based abstraction for unit tests, real keychain for integration tests on device.

**Domain Selection Guide:**

| If the task involves…                  | Load these reference files                                    |
| -------------------------------------- | ------------------------------------------------------------- |
| Storing/reading a password or token    | [keychain-fundamentals.md](references/keychain-fundamentals.md) + [credential-storage-patterns.md](references/credential-storage-patterns.md) |
| Choosing which `kSecClass` to use      | [keychain-item-classes.md](references/keychain-item-classes.md)                                    |
| Setting when items are accessible      | [keychain-access-control.md](references/keychain-access-control.md)                                  |
| Face ID / Touch ID gating              | [biometric-authentication.md](references/biometric-authentication.md) + [keychain-access-control.md](references/keychain-access-control.md)  |
| Hardware-backed keys                   | [secure-enclave.md](references/secure-enclave.md)                                           |
| Encrypting / hashing data              | [cryptokit-symmetric.md](references/cryptokit-symmetric.md)                                      |
| Signing / key exchange / HPKE          | [cryptokit-public-key.md](references/cryptokit-public-key.md)                                     |
| OAuth tokens / API keys / logout       | [credential-storage-patterns.md](references/credential-storage-patterns.md)                              |
| Sharing between app and extension      | [keychain-sharing.md](references/keychain-sharing.md)                                         |
| TLS pinning / client certificates      | [certificate-trust.md](references/certificate-trust.md)                                        |
| Replacing UserDefaults / plist secrets | [migration-legacy-stores.md](references/migration-legacy-stores.md)                                  |
| Writing tests for security code        | [testing-security-code.md](references/testing-security-code.md)                                    |
| Enterprise audit / OWASP compliance    | [compliance-owasp-mapping.md](references/compliance-owasp-mapping.md)                                 |

---

## Core Guidelines

These seven rules are non-negotiable. Every keychain/security implementation must satisfy all of them.

**1. Never ignore `OSStatus`.** Every `SecItem*` call returns an `OSStatus`. Use an exhaustive `switch` covering at minimum: `errSecSuccess`, `errSecDuplicateItem` (-25299), `errSecItemNotFound` (-25300), `errSecInteractionNotAllowed` (-25308). Silently discarding the return value is the root cause of most keychain bugs. → [keychain-fundamentals.md](references/keychain-fundamentals.md)

**2. Never use `LAContext.evaluatePolicy()` as a standalone auth gate.** This returns a `Bool` that is trivially patchable at runtime via Frida. Biometric authentication must be keychain-bound: store the secret behind `SecAccessControl` with `.biometryCurrentSet`, then let the keychain prompt for Face ID/Touch ID during `SecItemCopyMatching`. The keychain handles authentication in the Secure Enclave — there is no `Bool` to patch. → [biometric-authentication.md](references/biometric-authentication.md)

**3. Never store secrets in `UserDefaults`, `Info.plist`, `.xcconfig`, or `NSCoding` archives.** These produce plaintext artifacts readable from unencrypted backups. The Keychain is the only Apple-sanctioned store for credentials. → [credential-storage-patterns.md](references/credential-storage-patterns.md), [common-anti-patterns.md](references/common-anti-patterns.md)

**4. Never call `SecItem*` on `@MainActor`.** Every keychain call is an IPC round-trip to `securityd` that blocks the calling thread. Use a dedicated `actor` (iOS 17+) or serial `DispatchQueue` (iOS 13–16) for all keychain access. → [keychain-fundamentals.md](references/keychain-fundamentals.md)

**5. Always set `kSecAttrAccessible` explicitly.** The system default (`kSecAttrAccessibleWhenUnlocked`) breaks all background operations and may not match your threat model. Choose the most restrictive class that satisfies your access pattern. For background tasks: `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`. For highest sensitivity: `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly`. → [keychain-access-control.md](references/keychain-access-control.md)

**6. Always use the add-or-update pattern.** `SecItemAdd` followed by `SecItemUpdate` on `errSecDuplicateItem`. Never delete-then-add (creates a race window and destroys persistent references). Never call `SecItemAdd` without handling the duplicate case. → [keychain-fundamentals.md](references/keychain-fundamentals.md)

**7. Always target the data protection keychain on macOS.** Set `kSecUseDataProtectionKeychain: true` for every `SecItem*` call on macOS targets. Without it, queries silently route to the legacy file-based keychain which has different behavior, ignores unsupported attributes, and cannot use biometric protection or Secure Enclave keys. Mac Catalyst and iOS-on-Mac do this automatically. → [keychain-fundamentals.md](references/keychain-fundamentals.md)

---

## Quick Reference Tables

### Accessibility Constants — Selection Guide

| Constant                         | When Decryptable             | Survives Backup | Survives Device Migration | Background Safe | Use When                                               |
| -------------------------------- | ---------------------------- | --------------- | ------------------------- | --------------- | ------------------------------------------------------ |
| `WhenPasscodeSetThisDeviceOnly`  | Unlocked + passcode set      | ❌              | ❌                        | ❌              | Highest-security secrets; removed if passcode removed  |
| `WhenUnlockedThisDeviceOnly`     | Unlocked                     | ❌              | ❌                        | ❌              | Device-bound secrets not needed in background          |
| `WhenUnlocked`                   | Unlocked                     | ✅              | ✅                        | ❌              | Syncable secrets (system default — avoid implicit use) |
| `AfterFirstUnlockThisDeviceOnly` | After first unlock → restart | ❌              | ❌                        | ✅              | **Background tasks, push handlers, device-bound**      |
| `AfterFirstUnlock`               | After first unlock → restart | ✅              | ✅                        | ✅              | Background tasks that must survive restore             |

**Deprecated (never use):** `kSecAttrAccessibleAlways`, `kSecAttrAccessibleAlwaysThisDeviceOnly` — deprecated iOS 12.

**Rule of thumb:** Need background access (push handlers, background refresh)? Start with `AfterFirstUnlockThisDeviceOnly`. Foreground-only? Start with `WhenUnlockedThisDeviceOnly`. Tighten to `WhenPasscodeSetThisDeviceOnly` for high-value secrets. Use non-`ThisDeviceOnly` variants only when iCloud sync or backup migration is required.

### CryptoKit Algorithm Selection

| Need                            | Algorithm                                       | Min iOS | Notes                                                                       |
| ------------------------------- | ----------------------------------------------- | ------- | --------------------------------------------------------------------------- |
| Hash data                       | `SHA256` / `SHA384` / `SHA512`                  | 13      | `SHA3_256`/`SHA3_512` available iOS 26+                                     |
| Authenticate data (MAC)         | `HMAC<SHA256>`                                  | 13      | Always verify with constant-time comparison (built-in)                      |
| Encrypt data (authenticated)    | `AES.GCM`                                       | 13      | 256-bit key, 96-bit nonce, 128-bit tag. **Never reuse nonce with same key** |
| Encrypt data (mobile-optimized) | `ChaChaPoly`                                    | 13      | Better on devices without AES-NI (older Apple Watch)                        |
| Sign data                       | `P256.Signing` / `Curve25519.Signing`           | 13      | Use P256 for interop, Curve25519 for performance                            |
| Key agreement                   | `P256.KeyAgreement` / `Curve25519.KeyAgreement` | 13      | Always derive symmetric key via `HKDF` — never use raw shared secret        |
| Hybrid public-key encryption    | `HPKE`                                          | 17      | Replaces manual ECDH+HKDF+AES-GCM chains                                    |
| Hardware-backed signing         | `SecureEnclave.P256.Signing`                    | 13      | P256 only; key never leaves hardware                                        |
| Post-quantum key exchange       | `MLKEM768`                                      | 26      | Formal verification (ML-KEM FIPS 203)                                       |
| Post-quantum signing            | `MLDSA65`                                       | 26      | Formal verification (ML-DSA FIPS 204)                                       |
| Password → key derivation       | PBKDF2 (via `CommonCrypto`)                     | 13      | ≥600,000 iterations SHA-256 (OWASP 2024)                                    |
| Key → key derivation            | `HKDF<SHA256>`                                  | 13      | Extract-then-expand; always use info parameter for domain separation        |

### Anti-Pattern Detection — Quick Scan

When reviewing code, search for these patterns. Any match is a finding.
`❌` = insecure pattern signature to detect in user code. `✅` = apply the corrective pattern in the referenced file.

| Search For                                                              | Anti-Pattern                    | Severity | Reference                    |
| ----------------------------------------------------------------------- | ------------------------------- | -------- | ---------------------------- |
| `UserDefaults.standard.set` + token/key/secret/password                 | Plaintext credential storage    | CRITICAL | [common-anti-patterns.md](references/common-anti-patterns.md) #1 |
| Hardcoded base64/hex strings (≥16 chars) in source                      | Hardcoded cryptographic key     | CRITICAL | [common-anti-patterns.md](references/common-anti-patterns.md) #2 |
| `evaluatePolicy` without `SecItemCopyMatching` nearby                   | LAContext-only biometric gate   | CRITICAL | [common-anti-patterns.md](references/common-anti-patterns.md) #3 |
| `SecItemAdd` without checking return / `OSStatus`                       | Ignored error code              | HIGH     | [common-anti-patterns.md](references/common-anti-patterns.md) #4 |
| No `kSecAttrAccessible` in add dictionary                               | Implicit accessibility class    | HIGH     | [common-anti-patterns.md](references/common-anti-patterns.md) #5 |
| `AES.GCM.Nonce()` inside a loop with same key                           | Potential nonce reuse           | CRITICAL | [common-anti-patterns.md](references/common-anti-patterns.md) #6 |
| `sharedSecret.withUnsafeBytes` without HKDF                             | Raw shared secret as key        | HIGH     | [common-anti-patterns.md](references/common-anti-patterns.md) #7 |
| `kSecAttrAccessibleAlways`                                              | Deprecated accessibility        | HIGH     | [keychain-access-control.md](references/keychain-access-control.md) |
| `SecureEnclave.isAvailable` without `#if !targetEnvironment(simulator)` | Simulator false-negative trap   | MEDIUM   | [secure-enclave.md](references/secure-enclave.md)          |
| `kSecAttrSynchronizable: true` + `ThisDeviceOnly`                       | Contradictory constraints       | MEDIUM   | [keychain-item-classes.md](references/keychain-item-classes.md)   |
| `SecTrustEvaluate` (sync, deprecated)                                   | Legacy trust evaluation         | MEDIUM   | [certificate-trust.md](references/certificate-trust.md)       |
| `kSecClassGenericPassword` + `kSecAttrServer`                           | Wrong class for web credentials | MEDIUM   | [keychain-item-classes.md](references/keychain-item-classes.md)   |

---

## Top-Level Review Checklist

Use this checklist for a rapid sweep across all 14 domains. Each item maps to one or more reference files for deep-dive investigation. For domain-specific deep checks, use the Summary Checklist at the bottom of each reference file.

- [ ] **1. Secrets are in Keychain, not UserDefaults/plist/source** — No credentials, tokens, or cryptographic keys in `UserDefaults`, `Info.plist`, `.xcconfig`, hardcoded strings, or `NSCoding` archives. OWASP M9 (Insecure Data Storage) directly violated. → [common-anti-patterns.md](references/common-anti-patterns.md) #1–2, [credential-storage-patterns.md](references/credential-storage-patterns.md), [migration-legacy-stores.md](references/migration-legacy-stores.md), [compliance-owasp-mapping.md](references/compliance-owasp-mapping.md)

- [ ] **2. Every `OSStatus` is checked** — All `SecItem*` calls handle return codes with exhaustive `switch` or equivalent. No ignored returns. `errSecInteractionNotAllowed` is handled non-destructively (retry later, never delete). → [keychain-fundamentals.md](references/keychain-fundamentals.md), [common-anti-patterns.md](references/common-anti-patterns.md) #4

- [ ] **3. Biometric auth is keychain-bound** — If biometrics are used, authentication is enforced via `SecAccessControl` + keychain access, not `LAContext.evaluatePolicy()` alone. → [biometric-authentication.md](references/biometric-authentication.md), [common-anti-patterns.md](references/common-anti-patterns.md) #3

- [ ] **4. Accessibility classes are explicit and correct** — Every keychain item has an explicit `kSecAttrAccessible` value matching its access pattern (background vs foreground, device-bound vs syncable). No deprecated `Always` constants. → [keychain-access-control.md](references/keychain-access-control.md)

- [ ] **5. No `SecItem*` calls on `@MainActor`** — All keychain operations run on a dedicated `actor` or background queue. No synchronous keychain access in UI code, `viewDidLoad`, or `application(_:didFinishLaunchingWithOptions:)`. → [keychain-fundamentals.md](references/keychain-fundamentals.md)

- [ ] **6. Correct `kSecClass` for each item type** — Web credentials use `InternetPassword` (not GenericPassword) for AutoFill. Cryptographic keys use `kSecClassKey` with proper `kSecAttrKeyType`. App secrets use `GenericPassword` with `kSecAttrService` + `kSecAttrAccount`. → [keychain-item-classes.md](references/keychain-item-classes.md)

- [ ] **7. CryptoKit used correctly** — Nonces never reused with the same key. ECDH shared secrets always derived through `HKDF` before use as symmetric keys. `SymmetricKey` material stored in Keychain, not in memory or files. Crypto operations covered by protocol-based unit tests. → [cryptokit-symmetric.md](references/cryptokit-symmetric.md), [cryptokit-public-key.md](references/cryptokit-public-key.md), [testing-security-code.md](references/testing-security-code.md)

- [ ] **8. Secure Enclave constraints respected** — SE keys are P256 only (classical), never imported (always generated on-device), device-bound (no backup/sync). Availability checks guard against simulator and keychain-access-groups entitlement issues. → [secure-enclave.md](references/secure-enclave.md)

- [ ] **9. Sharing and access groups configured correctly** — `kSecAttrAccessGroup` uses full `TEAMID.group.identifier` format. Entitlements match between app and extensions. No accidental cross-app data exposure. → [keychain-sharing.md](references/keychain-sharing.md)

- [ ] **10. Certificate trust evaluation is current** — Uses `SecTrustEvaluateAsyncWithError` (not deprecated synchronous `SecTrustEvaluate`). Pinning strategy uses SPKI hash or `NSPinnedDomains` (not leaf certificate pinning which breaks on annual rotation). → [certificate-trust.md](references/certificate-trust.md)

- [ ] **11. macOS targets data protection keychain** — All macOS `SecItem*` calls include `kSecUseDataProtectionKeychain: true` (except Mac Catalyst / iOS-on-Mac where it's automatic). → [keychain-fundamentals.md](references/keychain-fundamentals.md)

---

## References Index

| #   | File                             | One-Line Description                                                                                                  | Risk     |
| --- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------- |
| 1   | [keychain-fundamentals.md](references/keychain-fundamentals.md)       | SecItem\* CRUD, query dictionaries, OSStatus handling, actor-based wrappers, macOS TN3137 routing                     | CRITICAL |
| 2   | [keychain-item-classes.md](references/keychain-item-classes.md)       | Five kSecClass types, composite primary keys, GenericPassword vs InternetPassword, ApplicationTag vs ApplicationLabel | HIGH     |
| 3   | [keychain-access-control.md](references/keychain-access-control.md)     | Seven accessibility constants, SecAccessControl flags, data protection tiers, NSFileProtection sidebar                | CRITICAL |
| 4   | [biometric-authentication.md](references/biometric-authentication.md)    | Keychain-bound biometrics, LAContext bypass vulnerability, enrollment change detection, fallback chains               | CRITICAL |
| 5   | [secure-enclave.md](references/secure-enclave.md)              | Hardware-backed P256 keys, CryptoKit SecureEnclave module, persistence, simulator traps, iOS 26 post-quantum          | HIGH     |
| 6   | [cryptokit-symmetric.md](references/cryptokit-symmetric.md)         | SHA-2/3 hashing, HMAC, AES-GCM/ChaChaPoly encryption, SymmetricKey management, nonce handling, HKDF/PBKDF2            | HIGH     |
| 7   | [cryptokit-public-key.md](references/cryptokit-public-key.md)        | ECDSA signing, ECDH key agreement, HPKE (iOS 17+), ML-KEM/ML-DSA post-quantum (iOS 26+), curve selection              | HIGH     |
| 8   | [credential-storage-patterns.md](references/credential-storage-patterns.md) | OAuth2/OIDC token lifecycle, API key storage, refresh token rotation, runtime secrets, logout cleanup                 | CRITICAL |
| 9   | [keychain-sharing.md](references/keychain-sharing.md)            | Access groups, Team ID prefixes, app extensions, Keychain Sharing vs App Groups entitlements, iCloud sync             | MEDIUM   |
| 10  | [certificate-trust.md](references/certificate-trust.md)           | SecTrust evaluation, SPKI/CA/leaf pinning, NSPinnedDomains, client certificates (mTLS), trust policies                | HIGH     |
| 11  | [migration-legacy-stores.md](references/migration-legacy-stores.md)     | UserDefaults/plist/NSCoding → Keychain migration, secure deletion, first-launch cleanup, versioned migration          | MEDIUM   |
| 12  | [common-anti-patterns.md](references/common-anti-patterns.md)        | Top 10 AI-generated security mistakes with ❌/✅ code pairs, detection heuristics, OWASP mapping                      | CRITICAL |
| 13  | [testing-security-code.md](references/testing-security-code.md)       | Protocol-based mocking, simulator vs device differences, CI/CD keychain, Swift Testing, mutation testing              | MEDIUM   |
| 14  | [compliance-owasp-mapping.md](references/compliance-owasp-mapping.md)    | OWASP Mobile Top 10 (2024), MASVS v2.1.0, MASTG test IDs, M1/M3/M9/M10 mapping, audit readiness                       | MEDIUM   |

---

## Authoritative Sources

These are the primary sources underpinning all reference files. When in doubt, defer to these over any secondary source.

- **Apple Keychain Services Documentation** — canonical API reference
- **Apple Platform Security Guide** (updated annually) — architecture and encryption design
- **TN3137: "On Mac Keychain APIs and Implementations"** — macOS data protection vs file-based keychain
- **Quinn "The Eskimo!" DTS Posts** — "SecItem: Fundamentals" and "SecItem: Pitfalls and Best Practices" (updated through 2025)
- **WWDC 2019 Session 709** — "Cryptography and Your Apps" (CryptoKit introduction)
- **WWDC 2025 Session 314** — "Get ahead with quantum-secure cryptography" (ML-KEM, ML-DSA)
- **OWASP Mobile Top 10 (2024)** + **MASVS v2.1.0** + **MASTG v2** — compliance framework
- **CISA/FBI "Product Security Bad Practices" v2.0** (January 2025) — hardcoded credentials classified as national security risk

---

## Agent Behavioral Rules

> The sections below govern how an AI agent should behave when using this skill: what's in scope, what's out, tone calibration, common mistakes to avoid, how to select reference files, and output formatting requirements.

### Scope Boundaries — Inclusions

This skill is authoritative for **client-side Apple platform security** across iOS, macOS, tvOS, watchOS, and visionOS:

- **Keychain Services** — `SecItemAdd`, `SecItemCopyMatching`, `SecItemUpdate`, `SecItemDelete`, query dictionary construction, `OSStatus` handling, actor/thread isolation, the data protection keychain on macOS (TN3137)
- **Keychain item classes** — `kSecClassGenericPassword`, `kSecClassInternetPassword`, `kSecClassKey`, `kSecClassCertificate`, `kSecClassIdentity`, composite primary keys, AutoFill integration
- **Access control** — The seven `kSecAttrAccessible` constants, `SecAccessControlCreateWithFlags`, data protection tiers, `NSFileProtection` correspondence
- **Biometric authentication** — `LAContext` + keychain binding, the boolean gate vulnerability, enrollment change detection, fallback chains, `evaluatedPolicyDomainState`
- **Secure Enclave** — CryptoKit `SecureEnclave.P256` module, hardware constraints (P256-only, no import, no export, no symmetric), persistence via keychain, simulator traps, iOS 26 post-quantum (ML-KEM, ML-DSA)
- **CryptoKit symmetric** — SHA-2/SHA-3 hashing, HMAC, AES-GCM, ChaChaPoly, `SymmetricKey` lifecycle, nonce handling, HKDF, PBKDF2
- **CryptoKit public-key** — ECDSA signing (P256/Curve25519), ECDH key agreement, HPKE (iOS 17+), ML-KEM/ML-DSA (iOS 26+), curve selection
- **Credential storage patterns** — OAuth2/OIDC token lifecycle, API key storage, refresh token rotation, runtime secret fetching, logout cleanup
- **Keychain sharing** — Access groups, Team ID prefixes, `keychain-access-groups` vs `com.apple.security.application-groups` entitlements, extensions, iCloud Keychain sync
- **Certificate trust** — `SecTrust` evaluation, SPKI/CA/leaf pinning, `NSPinnedDomains`, client certificates (mTLS), trust policies
- **Migration** — UserDefaults/plist/NSCoding → Keychain migration, secure legacy deletion, first-launch cleanup, versioned migration
- **Testing** — Protocol-based mocking, simulator vs device differences, CI/CD keychain creation, Swift Testing patterns
- **Compliance** — OWASP Mobile Top 10 (2024), MASVS v2.1.0, MASTG v2 test IDs, CISA/FBI Bad Practices

**Edge cases that ARE in scope:** Client-side certificate loading for mTLS pinning ([certificate-trust.md](references/certificate-trust.md)). Passkey/AutoFill credential storage in Keychain ([keychain-item-classes.md](references/keychain-item-classes.md), [credential-storage-patterns.md](references/credential-storage-patterns.md)). `@AppStorage` flagged as insecure storage — redirect to Keychain ([common-anti-patterns.md](references/common-anti-patterns.md)).

### Scope Boundaries — Exclusions

Do **not** answer the following topics using this skill. Briefly explain they are out of scope and suggest where to look.

| Topic                                          | Why excluded                                                                         | Redirect to                                                                                                                                       |
| ---------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **App Transport Security (ATS)**               | Server-side TLS policy, not client keychain                                          | Apple's ATS documentation, `Info.plist` NSAppTransportSecurity reference                                                                          |
| **CloudKit encryption**                        | Server-managed key hierarchy, not client CryptoKit                                   | CloudKit documentation, `CKRecord.encryptedValues`                                                                                                |
| **Network security / URLSession TLS config**   | Transport layer, not storage layer                                                   | Apple URL Loading System docs; this skill covers only client certificate loading for mTLS                                                         |
| **Server-side auth architecture**              | Backend JWT issuance, OAuth provider config                                          | OWASP ASVS (Application Security Verification Standard)                                                                                           |
| **WebAuthn / passkeys server-side**            | Relying party implementation                                                         | Apple "Supporting passkeys" documentation; this skill covers client-side `ASAuthorizationController` only where it stores credentials in Keychain |
| **Code signing / provisioning profiles**       | Build/distribution, not runtime security                                             | Apple code signing documentation                                                                                                                  |
| **Jailbreak detection**                        | Runtime integrity, not cryptographic storage                                         | OWASP MASTG MSTG-RESILIENCE category                                                                                                              |
| **SwiftUI `@AppStorage`**                      | Wrapper over `UserDefaults` — out of scope except to flag it as insecure for secrets | [common-anti-patterns.md](references/common-anti-patterns.md) #1 flags it; no deeper coverage                                                                                         |
| **Cross-platform crypto (OpenSSL, LibSodium)** | Third-party libraries, not Apple frameworks                                          | Respective library documentation                                                                                                                  |

---

### Tone Rules

This skill is **non-opinionated and correctness-focused**. Tone calibrates based on severity.

**Default tone — advisory.** Use "consider," "suggest," "one approach is," "a common pattern is" for: architecture choices (wrapper class design, actor vs DispatchQueue), algorithm selection when multiple valid options exist (P256 vs Curve25519, AES-GCM vs ChaChaPoly), accessibility class selection when the threat model is unclear, testing strategy, code organization.

**Elevated tone — directive.** Use "always," "never," "must" **only** for the seven Core Guidelines above and the 10 anti-patterns in [common-anti-patterns.md](references/common-anti-patterns.md). These are security invariants, not style preferences. The exhaustive list of directives:

1. Never ignore `OSStatus` — always check return codes from `SecItem*` calls. → [keychain-fundamentals.md](references/keychain-fundamentals.md)
2. Never use `LAContext.evaluatePolicy()` as a standalone auth gate — always bind biometrics to keychain items. → [biometric-authentication.md](references/biometric-authentication.md)
3. Never store secrets in `UserDefaults`, `Info.plist`, `.xcconfig`, or `NSCoding` archives. → [credential-storage-patterns.md](references/credential-storage-patterns.md), [common-anti-patterns.md](references/common-anti-patterns.md)
4. Never call `SecItem*` on `@MainActor` — always use a background actor or queue. → [keychain-fundamentals.md](references/keychain-fundamentals.md)
5. Always set `kSecAttrAccessible` explicitly on every `SecItemAdd`. → [keychain-access-control.md](references/keychain-access-control.md)
6. Always use the add-or-update pattern (`SecItemAdd` → `SecItemUpdate` on `errSecDuplicateItem`). → [keychain-fundamentals.md](references/keychain-fundamentals.md)
7. Always set `kSecUseDataProtectionKeychain: true` on macOS targets. → [keychain-fundamentals.md](references/keychain-fundamentals.md)
8. Never reuse a nonce with the same AES-GCM key. → [cryptokit-symmetric.md](references/cryptokit-symmetric.md), [common-anti-patterns.md](references/common-anti-patterns.md)
9. Never use a raw ECDH shared secret as a symmetric key — always derive through HKDF. → [cryptokit-public-key.md](references/cryptokit-public-key.md), [common-anti-patterns.md](references/common-anti-patterns.md)
10. Never use `Insecure.MD5` or `Insecure.SHA1` for security purposes. → [cryptokit-symmetric.md](references/cryptokit-symmetric.md), [common-anti-patterns.md](references/common-anti-patterns.md)

If a pattern is not on this list, use advisory tone. Do not escalate warnings beyond what the reference files support.

**Tone when declining.** When a query falls outside scope, be direct but not dismissive: "This skill covers client-side keychain and CryptoKit. For ATS configuration, Apple's NSAppTransportSecurity documentation is the right reference." State the boundary, suggest an alternative, move on.

---

## Common Mistakes

Before finalizing any output, scan for all 10. Each links to the reference file containing the correct pattern.
Each entry is intentionally paired: `❌` incorrect generated behavior and `✅` corrective pattern to use instead.

**Mistake #1 — Generating `LAContext.evaluatePolicy()` as the sole biometric gate.** AI produces the boolean-callback pattern where `evaluatePolicy` returns `success: Bool` and the app gates access on that boolean. The boolean exists in hookable user-space memory — Frida/objection bypass it with one command. **✅ Correct pattern:** Store a secret behind `SecAccessControl` with `.biometryCurrentSet`, retrieve via `SecItemCopyMatching`. → [biometric-authentication.md](references/biometric-authentication.md)

**Mistake #2 — Suggesting `SecureEnclave.isAvailable` without simulator guard.** AI generates `if SecureEnclave.isAvailable { ... }` without `#if !targetEnvironment(simulator)`. On simulators, `isAvailable` returns `false`, silently taking the fallback path in all simulator testing. **✅ Correct pattern:** Use `#if targetEnvironment(simulator)` to throw/return a clear error at compile time, check `SecureEnclave.isAvailable` only in device builds. → [secure-enclave.md](references/secure-enclave.md)

**Mistake #3 — Importing external keys into the Secure Enclave.** AI generates `SecureEnclave.P256.Signing.PrivateKey(rawRepresentation: someData)`. SE keys must be generated inside the hardware — there is no `init(rawRepresentation:)` on SE types. `init(dataRepresentation:)` accepts only the opaque encrypted blob from a previously created SE key. **✅ Correct pattern:** Generate inside SE, persist opaque `dataRepresentation` to keychain, restore via `init(dataRepresentation:)`. → [secure-enclave.md](references/secure-enclave.md)

**Mistake #4 — Using `SecureEnclave.AES` or SE for symmetric encryption.** AI generates references to non-existent SE symmetric APIs. The SE's internal AES engine is not exposed as a developer API. Pre-iOS 26, the SE supports only P256 signing and key agreement. iOS 26 adds ML-KEM and ML-DSA, not symmetric primitives. **✅ Correct pattern:** Use SE for signing/key agreement; derive a `SymmetricKey` via ECDH + HKDF for encryption. → [secure-enclave.md](references/secure-enclave.md), [cryptokit-symmetric.md](references/cryptokit-symmetric.md)

**Mistake #5 — Omitting `kSecAttrAccessible` in `SecItemAdd`.** AI builds add dictionaries without an accessibility attribute. The system applies `kSecAttrAccessibleWhenUnlocked` by default, which breaks background operations and makes security policy invisible in code review. **✅ Correct pattern:** Always set `kSecAttrAccessible` explicitly. → [keychain-access-control.md](references/keychain-access-control.md)

**Mistake #6 — Using `SecItemAdd` without handling `errSecDuplicateItem`.** AI checks only for `errSecSuccess`, or uses delete-then-add. Without duplicate handling, the second save silently fails. Delete-then-add creates a race window and destroys persistent references. **✅ Correct pattern:** Add-or-update pattern. → [keychain-fundamentals.md](references/keychain-fundamentals.md)

**Mistake #7 — Specifying explicit nonces for AES-GCM encryption.** AI creates a nonce manually and passes it to `AES.GCM.seal`. Manual nonce mana

…(truncated)
