.agents/skills/cryptokit/SKILL.md
Perform cryptographic operations using Apple CryptoKit. Use when hashing data with SHA256/SHA384/SHA512, generating HMAC authentication codes, encrypting with AES-GCM or ChaChaPoly, signing with P256/P384/P521/Curve25519 keys, performing ECDH key agreement, storing keys in the Secure Enclave, or migrating from CommonCrypto to CryptoKit.
npx skillsauth add DFly7/iOS-FastAPI-Supabase-AI cryptokitInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Apple CryptoKit provides a Swift-native API for cryptographic operations: hashing, message authentication, symmetric encryption, public-key signing, key agreement, and Secure Enclave key storage. Available on iOS 13+. Prefer CryptoKit over CommonCrypto or raw Security framework APIs in all new code targeting Swift 6.3+.
CryptoKit provides SHA256, SHA384, and SHA512 hash functions. All conform
to the HashFunction protocol.
import CryptoKit
let data = Data("Hello, world!".utf8)
let digest = SHA256.hash(data: data)
let hex = digest.compactMap { String(format: "%02x", $0) }.joined()
SHA384 and SHA512 work identically -- substitute the type name.
For large data or streaming input, hash incrementally:
var hasher = SHA256()
hasher.update(data: chunk1)
hasher.update(data: chunk2)
let digest = hasher.finalize()
CryptoKit digests use constant-time comparison by default. Direct ==
checks between digests are safe against timing attacks.
let expected = SHA256.hash(data: reference)
let actual = SHA256.hash(data: received)
if expected == actual {
// Data integrity verified
}
HMAC provides message authentication using a symmetric key and a hash function.
let key = SymmetricKey(size: .bits256)
let data = Data("message".utf8)
let mac = HMAC<SHA256>.authenticationCode(for: data, using: key)
let isValid = HMAC<SHA256>.isValidAuthenticationCode(
mac, authenticating: data, using: key
)
This uses constant-time comparison internally.
var hmac = HMAC<SHA256>(key: key)
hmac.update(data: chunk1)
hmac.update(data: chunk2)
let mac = hmac.finalize()
CryptoKit provides two authenticated encryption ciphers: AES-GCM and ChaChaPoly. Both produce a sealed box containing the nonce, ciphertext, and authentication tag.
The default choice for symmetric encryption. Hardware-accelerated on Apple silicon.
let key = SymmetricKey(size: .bits256)
let plaintext = Data("Secret message".utf8)
// Encrypt
let sealedBox = try AES.GCM.seal(plaintext, using: key)
let ciphertext = sealedBox.combined! // nonce + ciphertext + tag
// Decrypt
let box = try AES.GCM.SealedBox(combined: ciphertext)
let decrypted = try AES.GCM.open(box, using: key)
Use ChaChaPoly when AES hardware acceleration is unavailable or when interoperating with protocols that require ChaCha20-Poly1305 (e.g., TLS, WireGuard).
let sealedBox = try ChaChaPoly.seal(plaintext, using: key)
let combined = sealedBox.combined // Always non-optional for ChaChaPoly
let box = try ChaChaPoly.SealedBox(combined: combined)
let decrypted = try ChaChaPoly.open(box, using: key)
Both ciphers support additional authenticated data (AAD). The AAD is authenticated but not encrypted -- useful for metadata that must remain in the clear but be tamper-proof.
let header = Data("v1".utf8)
let sealedBox = try AES.GCM.seal(
plaintext, using: key, authenticating: header
)
let decrypted = try AES.GCM.open(
sealedBox, using: key, authenticating: header
)
| Size | Use |
|---|---|
| .bits128 | AES-128-GCM; adequate for most uses |
| .bits192 | AES-192-GCM; uncommon |
| .bits256 | AES-256-GCM or ChaChaPoly; recommended default |
let key = SymmetricKey(size: .bits256)
To create a key from existing data:
let key = SymmetricKey(data: existingKeyData)
CryptoKit supports ECDSA signing with NIST curves and Ed25519 via Curve25519.
let signingKey = P256.Signing.PrivateKey()
let publicKey = signingKey.publicKey
// Sign
let signature = try signingKey.signature(for: data)
// Verify
let isValid = publicKey.isValidSignature(signature, for: data)
P384 and P521 use the same API -- substitute the curve name.
NIST key representations:
// Export
let der = signingKey.derRepresentation
let pem = signingKey.pemRepresentation
let x963 = signingKey.x963Representation
let raw = signingKey.rawRepresentation
// Import
let restored = try P256.Signing.PrivateKey(derRepresentation: der)
let signingKey = Curve25519.Signing.PrivateKey()
let publicKey = signingKey.publicKey
// Sign
let signature = try signingKey.signature(for: data)
// Verify
let isValid = publicKey.isValidSignature(signature, for: data)
Curve25519 keys use rawRepresentation only (no DER/PEM/X9.63).
| Curve | Signature Scheme | Key Size | Typical Use | |---|---|---|---| | P256 | ECDSA | 256-bit | General purpose; Secure Enclave support | | P384 | ECDSA | 384-bit | Higher security requirements | | P521 | ECDSA | 521-bit | Maximum NIST security level | | Curve25519 | Ed25519 | 256-bit | Fast; simple API; no Secure Enclave |
Use P256 by default. Use Curve25519 when interoperating with Ed25519-based protocols.
Key agreement lets two parties derive a shared symmetric key from their public/private key pairs using ECDH.
// Alice
let aliceKey = P256.KeyAgreement.PrivateKey()
// Bob
let bobKey = P256.KeyAgreement.PrivateKey()
// Alice computes shared secret
let sharedSecret = try aliceKey.sharedSecretFromKeyAgreement(
with: bobKey.publicKey
)
// Derive a symmetric key using HKDF
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: Data("salt".utf8),
sharedInfo: Data("my-app-v1".utf8),
outputByteCount: 32
)
Bob computes the same sharedSecret using his private key and Alice's
public key. Both derive the same symmetricKey.
let aliceKey = Curve25519.KeyAgreement.PrivateKey()
let bobKey = Curve25519.KeyAgreement.PrivateKey()
let sharedSecret = try aliceKey.sharedSecretFromKeyAgreement(
with: bobKey.publicKey
)
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: Data(),
sharedInfo: Data("context".utf8),
outputByteCount: 32
)
SharedSecret is not directly usable as a SymmetricKey. Always derive
a key using one of:
| Method | Standard | Use |
|---|---|---|
| hkdfDerivedSymmetricKey | HKDF (RFC 5869) | Recommended default |
| x963DerivedSymmetricKey | ANSI X9.63 | Interop with X9.63 systems |
Always provide a non-empty sharedInfo string to bind the derived key
to a specific protocol context.
The Secure Enclave provides hardware-backed key storage. Private keys never leave the hardware. Only P256 signing and key agreement are supported for ECDH operations. Post-quantum key types (MLKEM, MLDSA) are also available in the Secure Enclave on supported hardware.
guard SecureEnclave.isAvailable else {
// Fall back to software keys
return
}
let privateKey = try SecureEnclave.P256.Signing.PrivateKey()
let publicKey = privateKey.publicKey // Standard P256.Signing.PublicKey
let signature = try privateKey.signature(for: data)
let isValid = publicKey.isValidSignature(signature, for: data)
Require biometric authentication to use the key:
let accessControl = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
[.privateKeyUsage, .biometryCurrentSet],
nil
)!
let privateKey = try SecureEnclave.P256.Signing.PrivateKey(
accessControl: accessControl
)
The dataRepresentation is an encrypted blob that only the same device's
Secure Enclave can restore. Store it in the Keychain.
// Export
let blob = privateKey.dataRepresentation
// Restore
let restored = try SecureEnclave.P256.Signing.PrivateKey(
dataRepresentation: blob
)
let seKey = try SecureEnclave.P256.KeyAgreement.PrivateKey()
let peerPublicKey: P256.KeyAgreement.PublicKey = // from peer
let sharedSecret = try seKey.sharedSecretFromKeyAgreement(
with: peerPublicKey
)
// DON'T
let badKey = SymmetricKey(data: sharedSecret)
// DO -- derive with HKDF
let goodKey = sharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: salt,
sharedInfo: info,
outputByteCount: 32
)
// 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)
// DON'T -- manually strip tag and decrypt
// DO -- always use AES.GCM.open() or ChaChaPoly.open()
// which verifies the tag automatically
// 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.
// DON'T
UserDefaults.standard.set(key.rawBytes, forKey: "encryptionKey")
// DO -- store in Keychain
// See references/cryptokit-patterns.md for Keychain storage patterns
// 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()
isValidAuthenticationCode (constant-time)dataRepresentation stored in KeychainITSAppUsesNonExemptEncryption)development
Resolve Swift concurrency compiler errors, adopt approachable concurrency (SE-0466), and write data-race-safe async code. Use when fixing Sendable conformance errors, actor isolation warnings, or strict concurrency diagnostics; when adopting default MainActor isolation, @concurrent, nonisolated(nonsending), or Task.immediate; when designing actor-based architectures, structured concurrency with TaskGroup, or background work offloading; or when migrating from @preconcurrency to full Swift 6 strict concurrency.
development
Implement Swift Codable models for JSON and property-list encoding and decoding with JSONDecoder, JSONEncoder, CodingKeys, and custom init(from:) or encode(to:). Use when parsing API responses, remapping keys, flattening nested JSON, handling date or data decoding strategies, decoding heterogeneous arrays, or integrating Codable with URLSession, SwiftData, or UserDefaults.
development
Implement, review, or improve data visualizations using Swift Charts. Use when building bar, line, area, point, pie, or donut charts; when adding chart selection, scrolling, or annotations; when plotting functions with vectorized BarPlot, LinePlot, AreaPlot, or PointPlot; when customizing axes, scales, legends, or foregroundStyle grouping; or when creating specialized visualizations like heat maps, Gantt charts, stacked/grouped bars, sparklines, or threshold lines.
data-ai
Postgres performance optimization and best practices from Supabase. Use this skill when writing, reviewing, or optimizing Postgres queries, schema designs, or database configurations.