CryptoKit
Apple CryptoKit provides a Swift-native API for cryptographic operations:
hashing, message authentication, symmetric encryption, public-key signing,
key agreement, HPKE, quantum-secure key encapsulation/signing, and Secure
Enclave-backed keys. Most core primitives are available on iOS 13+; check
availability for HPKE (iOS 17+) and SHA-3 / post-quantum APIs (iOS 26+).
Prefer CryptoKit over CommonCrypto or raw Security framework APIs for new
cryptographic primitive code targeting Swift 6.3+.
Contents
Workflow
- Define the security property: hashing, authentication, authenticated encryption, signing, key agreement, or envelope encryption.
- Choose a current CryptoKit primitive and verify platform/peer interoperability before designing storage formats.
- Define key generation, persistence, rotation, access control, serialization, and deletion as one lifecycle.
- Bind context with authenticated data or a KDF and make nonce/sequence ownership impossible to reuse accidentally.
- Verify round trips, tampering, wrong keys, malformed inputs, rotation, Secure Enclave availability, and cross-platform vectors.
Route by Task
- Read core implementation details for hashes, HMAC, AEAD, signatures, key agreement, HPKE, post-quantum APIs, and Secure Enclave usage.
- Read extended CryptoKit patterns for serialization, Keychain integration, AES key wrapping, insecure legacy migration, and interoperability recipes.
Core Decisions
- Use authenticated encryption; never reuse a nonce with the same key.
- Derive keys from shared secrets instead of using raw agreement output.
- Treat authentication/tag failures as hard failures without partial plaintext use.
- Store long-lived secrets in Keychain or Secure Enclave-backed keys, not preferences.
Common Mistakes
1. Using the shared secret directly as a key
// DON'T
let badKey = sharedSecret.withUnsafeBytes { bytes in
SymmetricKey(data: Data(bytes))
}
// DO -- derive with HKDF
let goodKey = sharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: salt,
sharedInfo: info,
outputByteCount: 32
)
2. Reusing nonces
// DON'T -- hardcoded nonce
let nonce = try AES.GCM.Nonce(data: Data(repeating: 0, count: 12))
let box = try AES.GCM.seal(data, using: key, nonce: nonce)
// DO -- let CryptoKit generate a random nonce (default behavior)
let box = try AES.GCM.seal(data, using: key)
3. Ignoring authentication tag verification
// DON'T -- manually strip tag and decrypt
// DO -- always use AES.GCM.open() or ChaChaPoly.open()
// which verifies the tag automatically
4. Using Insecure hashes for security
// DON'T -- MD5/SHA1 for integrity or security
import CryptoKit
let bad = Insecure.MD5.hash(data: data)
// DO -- use SHA256 or stronger
let good = SHA256.hash(data: data)
Insecure.MD5 and Insecure.SHA1 exist only for legacy compatibility
(checksum verification, protocol interop). Never use them for new
security-sensitive operations.
5. Storing symmetric keys in UserDefaults
// DON'T
UserDefaults.standard.set(rawKeyData, forKey: "encryptionKey")
// DO -- store in Keychain
// See references/cryptokit-patterns.md for Keychain storage patterns
6. Not checking Secure Enclave availability
// DON'T -- crash on simulator or unsupported hardware
let key = try SecureEnclave.P256.Signing.PrivateKey()
// DO
guard SecureEnclave.isAvailable else { /* fallback */ }
let key = try SecureEnclave.P256.Signing.PrivateKey()
Review Checklist
References
1---2name: cryptokit3description: Use 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 Enclave CryptoKit keys, or migrating CommonCrypto code to CryptoKit.4---56# CryptoKit78Apple CryptoKit provides a Swift-native API for cryptographic operations:9hashing, message authentication, symmetric encryption, public-key signing,10key agreement, HPKE, quantum-secure key encapsulation/signing, and Secure11Enclave-backed keys. Most core primitives are available on iOS 13+; check12availability for HPKE (iOS 17+) and SHA-3 / post-quantum APIs (iOS 26+).13Prefer CryptoKit over CommonCrypto or raw Security framework APIs for new14cryptographic primitive code targeting Swift 6.3+.1516## Contents1718- [Workflow](#workflow)19- [Route by Task](#route-by-task)20- [Core Decisions](#core-decisions)21- [Common Mistakes](#common-mistakes)22- [Review Checklist](#review-checklist)23- [References](#references)2425## Workflow26271. Define the security property: hashing, authentication, authenticated encryption, signing, key agreement, or envelope encryption.282. Choose a current CryptoKit primitive and verify platform/peer interoperability before designing storage formats.293. Define key generation, persistence, rotation, access control, serialization, and deletion as one lifecycle.304. Bind context with authenticated data or a KDF and make nonce/sequence ownership impossible to reuse accidentally.315. Verify round trips, tampering, wrong keys, malformed inputs, rotation, Secure Enclave availability, and cross-platform vectors.3233## Route by Task3435- Read [core implementation details](references/core-implementation.md) for hashes, HMAC, AEAD, signatures, key agreement, HPKE, post-quantum APIs, and Secure Enclave usage.36- Read [extended CryptoKit patterns](references/cryptokit-patterns.md) for serialization, Keychain integration, AES key wrapping, insecure legacy migration, and interoperability recipes.3738## Core Decisions3940- Use authenticated encryption; never reuse a nonce with the same key.41- Derive keys from shared secrets instead of using raw agreement output.42- Treat authentication/tag failures as hard failures without partial plaintext use.43- Store long-lived secrets in Keychain or Secure Enclave-backed keys, not preferences.4445## Common Mistakes4647### 1. Using the shared secret directly as a key4849```swift50// DON'T51let badKey = sharedSecret.withUnsafeBytes { bytes in52 SymmetricKey(data: Data(bytes))53}5455// DO -- derive with HKDF56let goodKey = sharedSecret.hkdfDerivedSymmetricKey(57 using: SHA256.self,58 salt: salt,59 sharedInfo: info,60 outputByteCount: 3261)62```6364### 2. Reusing nonces6566```swift67// DON'T -- hardcoded nonce68let nonce = try AES.GCM.Nonce(data: Data(repeating: 0, count: 12))69let box = try AES.GCM.seal(data, using: key, nonce: nonce)7071// DO -- let CryptoKit generate a random nonce (default behavior)72let box = try AES.GCM.seal(data, using: key)73```7475### 3. Ignoring authentication tag verification7677```swift78// DON'T -- manually strip tag and decrypt79// DO -- always use AES.GCM.open() or ChaChaPoly.open()80// which verifies the tag automatically81```8283### 4. Using Insecure hashes for security8485```swift86// DON'T -- MD5/SHA1 for integrity or security87import CryptoKit88let bad = Insecure.MD5.hash(data: data)8990// DO -- use SHA256 or stronger91let good = SHA256.hash(data: data)92```9394`Insecure.MD5` and `Insecure.SHA1` exist only for legacy compatibility95(checksum verification, protocol interop). Never use them for new96security-sensitive operations.9798### 5. Storing symmetric keys in UserDefaults99100```swift101// DON'T102UserDefaults.standard.set(rawKeyData, forKey: "encryptionKey")103104// DO -- store in Keychain105// See references/cryptokit-patterns.md for Keychain storage patterns106```107108### 6. Not checking Secure Enclave availability109110```swift111// DON'T -- crash on simulator or unsupported hardware112let key = try SecureEnclave.P256.Signing.PrivateKey()113114// DO115guard SecureEnclave.isAvailable else { /* fallback */ }116let key = try SecureEnclave.P256.Signing.PrivateKey()117```118119## Review Checklist120121- [ ] Using CryptoKit, not CommonCrypto or raw Security framework122- [ ] SHA256+ for hashing; no MD5/SHA1 for security purposes123- [ ] HMAC verification uses `isValidAuthenticationCode` (constant-time)124- [ ] AES-GCM or ChaChaPoly for symmetric encryption; 256-bit keys125- [ ] Nonces are random (default) -- not hardcoded or reused126- [ ] Authenticated data (AAD) used where metadata needs integrity127- [ ] SharedSecret derived via HKDF, not used directly128- [ ] sharedInfo parameter is non-empty and context-specific129- [ ] HPKE used instead of custom ECDH+HKDF+AEAD for recipient public-key encryption on iOS 17+130- [ ] SHA-3 and post-quantum APIs guarded with iOS 26+ availability131- [ ] Secure Enclave availability checked before use132- [ ] Secure Enclave key `dataRepresentation` stored in Keychain133- [ ] Private keys not logged, printed, or serialized unnecessarily134- [ ] Symmetric keys stored in Keychain, not UserDefaults or files135- [ ] Encryption export compliance considered (`ITSAppUsesNonExemptEncryption`)136137## References138139- Extended patterns (key serialization, Insecure module, Keychain integration, AES key wrapping, HPKE): [references/cryptokit-patterns.md](references/cryptokit-patterns.md)140- Apple documentation: [CryptoKit](https://sosumi.ai/documentation/cryptokit)141- Apple documentation: [HPKE](https://sosumi.ai/documentation/cryptokit/hpke)142- Apple documentation: [Quantum-secure workflows](https://sosumi.ai/documentation/cryptokit/enhancing-your-app-s-privacy-and-security-with-quantum-secure-workflows)143- Apple sample: [Performing Common Cryptographic Operations](https://sosumi.ai/documentation/cryptokit/performing-common-cryptographic-operations)144- Apple sample: [Storing CryptoKit Keys in the Keychain](https://sosumi.ai/documentation/cryptokit/storing-cryptokit-keys-in-the-keychain)145- [Core implementation details](references/core-implementation.md) -- setup, API wiring, and focused implementation recipes moved out of the entrypoint.