$npx -y skills add dpearson2699/swift-ios-skills --skill cryptokitUse Apple CryptoKit for Swift cryptographic primitives. Use when hashing with SHA-2 or SHA-3, generating HMACs, encrypting with AES-GCM or ChaChaPoly, signing with P256/P384/P521/Curve25519 or ML-DSA keys, performing ECDH, HPKE, ML-KEM, or X-Wing key exchange, using Secure Enclav
| 1 | # CryptoKit |
| 2 | |
| 3 | Apple CryptoKit provides a Swift-native API for cryptographic operations: |
| 4 | hashing, message authentication, symmetric encryption, public-key signing, |
| 5 | key agreement, HPKE, quantum-secure key encapsulation/signing, and Secure |
| 6 | Enclave-backed keys. Most core primitives are available on iOS 13+; check |
| 7 | availability for HPKE (iOS 17+) and SHA-3 / post-quantum APIs (iOS 26+). |
| 8 | Prefer CryptoKit over CommonCrypto or raw Security framework APIs for new |
| 9 | cryptographic primitive code targeting Swift 6.3+. |
| 10 | |
| 11 | ## Contents |
| 12 | |
| 13 | - [Hashing](#hashing) |
| 14 | - [HMAC](#hmac) |
| 15 | - [Symmetric Encryption](#symmetric-encryption) |
| 16 | - [Public-Key Signing](#public-key-signing) |
| 17 | - [Key Agreement](#key-agreement) |
| 18 | - [HPKE](#hpke) |
| 19 | - [Post-Quantum CryptoKit](#post-quantum-cryptokit) |
| 20 | - [Secure Enclave](#secure-enclave) |
| 21 | - [Common Mistakes](#common-mistakes) |
| 22 | - [Review Checklist](#review-checklist) |
| 23 | - [References](#references) |
| 24 | |
| 25 | ## Hashing |
| 26 | |
| 27 | Use SHA256/SHA384/SHA512 on iOS 13+; SHA3_256/SHA3_384/SHA3_512 require iOS 26+. All conform to `HashFunction`. |
| 28 | |
| 29 | ### One-shot hashing |
| 30 | |
| 31 | ```swift |
| 32 | import CryptoKit |
| 33 | |
| 34 | let data = Data("Hello, world!".utf8) |
| 35 | let digest = SHA256.hash(data: data) |
| 36 | let hex = digest.compactMap { String(format: "%02x", $0) }.joined() |
| 37 | ``` |
| 38 | |
| 39 | ### SHA-3 availability |
| 40 | |
| 41 | Use SHA-3 only behind an availability check unless the deployment target is |
| 42 | iOS 26+: |
| 43 | |
| 44 | ```swift |
| 45 | if #available(iOS 26.0, *) { |
| 46 | let digest = SHA3_256.hash(data: data) |
| 47 | } |
| 48 | ``` |
| 49 | |
| 50 | ### Incremental hashing |
| 51 | |
| 52 | For large data or streaming input, hash incrementally: |
| 53 | |
| 54 | ```swift |
| 55 | var hasher = SHA256() |
| 56 | hasher.update(data: chunk1) |
| 57 | hasher.update(data: chunk2) |
| 58 | let digest = hasher.finalize() |
| 59 | ``` |
| 60 | |
| 61 | ### Digest comparison |
| 62 | |
| 63 | Compare CryptoKit digest values directly. Do not convert digests to |
| 64 | strings or arrays for security-sensitive equality checks. |
| 65 | |
| 66 | ```swift |
| 67 | let expected = SHA256.hash(data: reference) |
| 68 | let actual = SHA256.hash(data: received) |
| 69 | if expected == actual { |
| 70 | // Data integrity verified |
| 71 | } |
| 72 | ``` |
| 73 | |
| 74 | ## HMAC |
| 75 | |
| 76 | Use HMAC when a protocol requires keyed message authentication; verify with `isValidAuthenticationCode` rather than comparing serialized values yourself. |
| 77 | |
| 78 | ### Computing an authentication code |
| 79 | |
| 80 | ```swift |
| 81 | let key = SymmetricKey(size: .bits256) |
| 82 | let data = Data("message".utf8) |
| 83 | |
| 84 | let mac = HMAC<SHA256>.authenticationCode(for: data, using: key) |
| 85 | ``` |
| 86 | |
| 87 | ### Verifying an authentication code |
| 88 | |
| 89 | ```swift |
| 90 | let isValid = HMAC<SHA256>.isValidAuthenticationCode( |
| 91 | mac, authenticating: data, using: key |
| 92 | ) |
| 93 | ``` |
| 94 | |
| 95 | ### Incremental HMAC |
| 96 | |
| 97 | ```swift |
| 98 | var hmac = HMAC<SHA256>(key: key) |
| 99 | hmac.update(data: chunk1) |
| 100 | hmac.update(data: chunk2) |
| 101 | let mac = hmac.finalize() |
| 102 | ``` |
| 103 | |
| 104 | ## Symmetric Encryption |
| 105 | |
| 106 | CryptoKit provides two authenticated encryption ciphers: AES-GCM and |
| 107 | ChaChaPoly. Both produce a sealed box containing the nonce, ciphertext, |
| 108 | and authentication tag. |
| 109 | |
| 110 | ### AES-GCM |
| 111 | |
| 112 | The default choice for symmetric encryption. Hardware-accelerated on Apple |
| 113 | silicon. |
| 114 | |
| 115 | ```swift |
| 116 | let key = SymmetricKey(size: .bits256) |
| 117 | let plaintext = Data("Secret message".utf8) |
| 118 | |
| 119 | // Encrypt |
| 120 | let sealedBox = try AES.GCM.seal(plaintext, using: key) |
| 121 | let ciphertext = sealedBox.combined! // nonce + ciphertext + tag |
| 122 | |
| 123 | // Decrypt |
| 124 | let box = try AES.GCM.SealedBox(combined: ciphertext) |
| 125 | let decrypted = try AES.GCM.open(box, using: key) |
| 126 | ``` |
| 127 | |
| 128 | ### ChaChaPoly |
| 129 | |
| 130 | Use ChaChaPoly when AES hardware acceleration is unavailable or when |
| 131 | interoperating with protocols that require ChaCha20-Poly1305 (e.g., TLS, |
| 132 | WireGuard). |
| 133 | |
| 134 | ```swift |
| 135 | let sealedBox = try ChaChaPoly.seal(plaintext, using: key) |
| 136 | let combined = sealedBox.combined // Always non-optional for ChaChaPoly |
| 137 | |
| 138 | let box = try ChaChaPoly.SealedBox(combined: combined) |
| 139 | let decrypted = try ChaChaPoly.open(box, using: key) |
| 140 | ``` |
| 141 | |
| 142 | ### Authenticated data |
| 143 | |
| 144 | Both ciphers support additional authenticated data (AAD). The AAD is |
| 145 | authenticated but not encrypted -- useful for metadata that must remain |
| 146 | in the clear but be tamper-proof. |
| 147 | |
| 148 | ```swift |
| 149 | let header = Data("v1".utf8) |
| 150 | let sealedBox = try AES.GCM.seal( |
| 151 | plaintext, using: key, authenticating: header |
| 152 | ) |
| 153 | let decrypted = try AES.GCM.open( |
| 154 | sealedBox, using: key, authenticating: header |
| 155 | ) |
| 156 | ``` |
| 157 | |
| 158 | Use `.bits256` as the default `SymmetricKey` size for AES-256-GCM or |
| 159 | ChaChaPoly. To create a key from existing data: |
| 160 | |
| 161 | ```swift |
| 162 | let key = SymmetricKey(data: existingKeyData) |
| 163 | ``` |
| 164 | |
| 165 | ## Public-Key Signing |
| 166 | |
| 167 | CryptoKit supports ECDSA signing with NIST curves and Ed25519 via |
| 168 | Curve25519. |
| 169 | |
| 170 | ### NIST curves: P256, P384, P521 |
| 171 | |
| 172 | ```swift |
| 173 | let signingKey = P256.Signing.PrivateKey() |
| 174 | let publicKey = signingKey.publicKey |
| 175 | |
| 176 | // Sign |
| 177 | let signature = try signingKey.signature(for: data) |
| 178 | |
| 179 | // Verify |
| 180 | let isValid = publicKey.isValidSignature(signature, for: data) |
| 181 | ``` |
| 182 | |
| 183 | P384 and P521 use |