CryptoKit API Reference

SkillCommerce & finance

Use when needing CryptoKit API details — hash functions (SHA2/SHA3), HMAC, AES-GCM/ChaChaPoly encryption, ECDSA/EdDSA signatures, ECDH key agreement, ML-KEM/ML-DSA post-quantum algorithms, HPKE encryption, Secure Enclave key types, key representations (raw/DER/PEM/x963), or Swift Crypto cross-platform parity. Covers complete CryptoKit API surface.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the CryptoKit API Reference skill

What this skill tells your AI

The instructions your AI receives, as published by comeonoliver/skillshub in skills/CharlesWiltgen/Axiom/axiom-cryptokit-ref/SKILL.md and read by ahel’s review.

Complete API reference for Apple CryptoKit: hashing, HMAC, symmetric encryption, key agreement, digital signatures, post-quantum cryptography, HPKE, Secure Enclave, key derivation, and Swift Crypto cross-platform parity.

Quick Reference

import CryptoKit

// Generate a symmetric key
let key = SymmetricKey(size: .bits256)

// AES-GCM encrypt
let sealed = try AES.GCM.seal(plaintext, using: key)
let combined = sealed.combined!  // nonce + ciphertext + tag

// AES-GCM decrypt
let sealedBox = try AES.GCM.SealedBox(combined: combined)
let decrypted = try AES.GCM.open(sealedBox, using: key)

// ECDSA sign (P256)
let signingKey = P256.Signing.PrivateKey()
let signature = try signingKey.signature(for: data)
let valid = signingKey.publicKey.isValidSignature(signature, for: data)

// Secure Enclave key
let seKey = try SecureEnclave.P256.Signing.PrivateKey()
let seSignature = try seKey.signature(for: data)

Hashing

Hash Functions

AlgorithmTypeOutput SizeUse
SHA256SHA25632 bytesGeneral purpose, most common
SHA384SHA38448 bytesTLS, certificate chains
SHA512SHA51264 bytesHigh-security contexts
SHA3_256SHA3_25632 bytesNIST post-quantum companion
SHA3_384SHA3_38448 bytesPost-quantum companion
SHA3_512SHA3_51264 bytesPost-quantum companion
Insecure.MD5Insecure.MD516 bytesLegacy interop only
Insecure.SHA1Insecure.SHA120 bytesLegacy interop only

Single-Call Hashing

let digest = SHA256.hash(data: data)
// digest conforms to Sequence of UInt8
let hex = digest.map { String(format: "%02x", $0) }.joined()

Streaming (Incremental) Hashing

var hasher = SHA256()
hasher.update(data: chunk1)
hasher.update(data: chunk2)
hasher.update(bufferPointer: unsafePointer)
let digest = hasher.finalize()  // SHA256Digest

HashFunction Protocol

All hash types conform to HashFunction with: byteCount, blockByteCount, init(), update(data:), update(bufferPointer:), finalize(), and hash(data:).

Digest conforms to Sequence (of UInt8), supports constant-time ==, and converts to Data(digest) or Array(digest). description returns hex string.


Message Authentication (HMAC)

SymmetricKey

let key = SymmetricKey(size: .bits128)                          // .bits128, .bits192, .bits256
let key = SymmetricKey(size: SymmetricKeySize(bitCount: 512))   // Custom size
let key = SymmetricKey(data: existingKeyData)                   // From existing material

key.bitCount                                  // Key size in bits
key.withUnsafeBytes { bytes in /* ... */ }    // Only way to access raw bytes

HMAC Generation and Verification

// HMAC is generic over HashFunction
let authCode = HMAC<SHA256>.authenticationCode(for: data, using: key)
// authCode: HMAC<SHA256>.MAC

let valid = HMAC<SHA256>.isValidAuthenticationCode(authCode, authenticating: data, using: key)

// Data representation
let macData = Data(authCode)

Iterative HMAC

var hmac = HMAC<SHA256>(key: key)
hmac.update(data: chunk1)
hmac.update(data: chunk2)
let authCode = hmac.finalize()

Symmetric Encryption

AES-GCM

// Seal (encrypt + authenticate)
let sealed = try AES.GCM.seal(plaintext, using: key)
let sealed = try AES.GCM.seal(plaintext, using: key, nonce: customNonce)
let sealed = try AES.GCM.seal(
    plaintext,
    using: key,
    nonce: customNonce,
    authenticating: associatedData  // AAD — authenticated but not encrypted
)

// SealedBox properties
sealed.nonce        // AES.GCM.Nonce (12 bytes)
sealed.ciphertext   // Data
sealed.tag          // Data (16 bytes)
sealed.combined     // Data? (nonce + ciphertext + tag)

// Open (decrypt + verify)
let plaintext = try AES.GCM.open(sealedBox, using: key)
let plaintext = try AES.GCM.open(sealedBox, using: key, authenticating: associatedData)

AES-GCM SealedBox Construction

// From combined representation (nonce + ciphertext + tag)
let box = try AES.GCM.SealedBox(combined: combinedData)

// From components
let box = try AES.GCM.SealedBox(
    nonce: AES.GCM.Nonce(data: nonceData),
    ciphertext: ciphertextData,
    tag: tagData
)

AES-GCM Nonce

let nonce = AES.GCM.Nonce()                    // Random 12 bytes (recommended)
let nonce = try AES.GCM.Nonce(data: nonceData) // Custom (MUST be unique per key)

ChaChaPoly

Identical interface to AES-GCM. Preferred for software-only environments without AES-NI.

let sealed = try ChaChaPoly.seal(plaintext, using: key)
let sealed = try ChaChaPoly.seal(plaintext, using: key, authenticating: aad)

let plaintext = try ChaChaPoly.open(sealed, using: key)
let plaintext = try ChaChaPoly.open(sealed, using: key, authenticating: aad)

// SealedBox, Nonce — same pattern as AES.GCM
let box = try ChaChaPoly.SealedBox(combined: combined)
let nonce = ChaChaPoly.Nonce()

AES Key Wrapping

// Wrap a key with another key (RFC 3394)
let wrapped = try AES.KeyWrap.wrap(keyToWrap, using: wrappingKey)
// wrapped: Data

// Unwrap
let unwrapped = try AES.KeyWrap.unwrap(wrapped, using: wrappingKey)
// unwrapped: SymmetricKey

Key Agreement (ECDH)

Supported Curves

CurveType PrefixKey SizeUse
Curve25519Curve25519.KeyAgreement32 bytesModern, fast, safe defaults
P-256P256.KeyAgreement32 bytesNIST standard, Secure Enclave
P-384P384.KeyAgreement48 bytesHigher security NIST
P-521P521.KeyAgreement66 bytesMaximum NIST security

Private Key Creation

let privateKey = Curve25519.KeyAgreement.PrivateKey()   // Random
let privateKey = P256.KeyAgreement.PrivateKey()          // Random
let privateKey = P256.KeyAgreement.PrivateKey(compactRepresentable: true)

// From serialized representations
let privateKey = try P256.KeyAgreement.PrivateKey(rawRepresentation: rawData)
let privateKey = try P256.KeyAgreement.PrivateKey(derRepresentation: derData)
let privateKey = try P256.KeyAgreement.PrivateKey(pemRepresentation: pemString)
let privateKey = try P256.KeyAgreement.PrivateKey(x963Representation: x963Data)  // NIST only

Public Key Representations

let publicKey = privateKey.publicKey
publicKey.rawRepresentation              // Data (all curves)
publicKey.derRepresentation              // Data — SubjectPublicKeyInfo (all curves)
publicKey.pemRepresentation              // String (all curves)
publicKey.x963Representation             // Data — uncompressed point (NIST only)
publicKey.compactRepresentation          // Data? (NIST only)
publicKey.compressedRepresentation       // Data (NIST only)

Shared Secret Derivation

let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: peerPublicKey)
// sharedSecret: SharedSecret — NOT directly usable as a key

// Derive symmetric key with HKDF
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
    using: SHA256.self,
    salt: saltData,           // Can be empty Data()
    sharedInfo: infoData,     // Context/label data
    outputByteCount: 32       // Key size
)

// Derive with X9.63 KDF
let symmetricKey = sharedSecret.x963DerivedSymmetricKey(
    using: SHA256.self,
    sharedInfo: infoData,
    outputByteCount: 32
)

Signatures (ECDSA/EdDSA)

Supported Algorithms

CurveAlgorithmType Prefix
Curve25519Ed25519 (EdDSA)Curve25519.Signing
P-256ECDSAP256.Signing
P-384ECDSAP384.Signing
P-521ECDSAP521.Signing

Key Creation

let privateKey = P256.Signing.PrivateKey()
let privateKey = Curve25519.Signing.PrivateKey()

// Same representation constructors as KeyAgreement keys:
// init(rawRepresentation:), init(derRepresentation:),
// init(pemRepresentation:), init(x963Representation:) for NIST curves

Sign and Verify

// Sign raw data
let signature = try privateKey.signature(for: data)

// Sign a digest (skip re-hashing already-hashed data)
let digest = SHA256.hash(data: data)
let signature = try privateKey.signature(for: digest)  // NIST curves only

// Verify
let valid = privateKey.publicKey.isValidSignature(signature, for: data)
let valid = privateKey.publicKey.isValidSignature(signature, for: digest)

Signature Representations

// NIST curves (P256/P384/P521)
signature.derRepresentation  // Data — use for cross-platform interop
signature.rawRepresentation  // Data — r || s concatenated

// Reconstruct from DER
let sig = try P256.Signing.ECDSASignature(derRepresentation: derData)
let sig = try P256.Signing.ECDSASignature(rawRepresentation: rawData)

// Curve25519 — raw bytes only (64 bytes, no DER)
signature.rawRepresentation

Cross-Platform Encoding

Use derRepresentation when exchanging signatures with non-CryptoKit systems (OpenSSL, Java, Go). Use rawRepresentation for CryptoKit-to-CryptoKit or when wire size matters (DER adds 6-8 bytes overhead).


Post-Quantum Cryptography: ML-KEM

Key Encapsulation Mechanism based on Module-Lattice (FIPS 203). iOS 18.4+.

Parameter Sets

TypeSecurity LevelPublic KeyCiphertextShared Secret
MLKEM768128-bit (AES-128 equivalent)1,184 bytes1,088 bytes32 bytes
MLKEM1024256-bit (AES-256 equivalent)1,568 bytes1,568 bytes32 bytes

Key Generation

let privateKey = MLKEM768.PrivateKey()
let publicKey = privateKey.publicKey

let privateKey = MLKEM1024.PrivateKey()

Encapsulation and Decapsulation

// Sender: encapsulate with recipient's public key
let result = try recipientPublicKey.encapsulate()
// result.sharedSecret: SymmetricKey (32 bytes)
// result.encapsulatedKey: Data (ciphertext to send)

// Recipient: decapsulate with private key
let sharedSecret = try privateKey.decapsulate(result.encapsulatedKey)
// sharedSecret: SymmetricKey — matches sender's sharedSecret

Key Representations

// Public key
publicKey.rawRepresentation                // Data

// Private key
privateKey.rawRepresentation               // Data (expanded form)
privateKey.seedRepresentation              // Data (compact seed, 64 bytes)

// Reconstruct
let pk = try MLKEM768.PrivateKey(rawRepresentation: rawData)
let pk = try MLKEM768.PrivateKey(seedRepresentation: seedData)

// Integrity-checked (validates key consistency)
let pk = try MLKEM768.PrivateKey(integrityCheckedRepresentation: data)

Post-Quantum Cryptography: ML-DSA

Digital Signature Algorithm based on Module-Lattice (FIPS 204). iOS 18.4+.

Parameter Sets

TypeSecurity LevelPublic KeySignature
MLDSA65128-bit1,952 bytes3,309 bytes
MLDSA87256-bit2,592 bytes4,627 bytes

Key Generation

let privateKey = MLDSA65.PrivateKey()
let publicKey = privateKey.publicKey

let privateKey = MLDSA87.PrivateKey()

Sign and Verify

// Sign
let signature = try privateKey.signature(for: data)

// Sign with context (domain separation)
let signature = try privateKey.signature(for: data, context: contextData)

// Verify
let valid = publicKey.isValidSignature(signature, for: data)
let valid = publicKey.isValidSignature(signature, for: data, context: contextData)

Key and Signature Representations

// Public key
publicKey.rawRepresentation

// Private key
privateKey.rawRepresentation
privateKey.seedRepresentation

// Reconstruct
let pk = try MLDSA65.PrivateKey(rawRepresentation: rawData)
let pk = try MLDSA65.PrivateKey(seedRepresentation: seedData)

// Signature
signature.rawRepresentation
let sig = try MLDSA65.Signature(rawRepresentation: rawData)

Hybrid Post-Quantum: X-Wing KEM

Combines ML-KEM768 + Curve25519 ECDH for hybrid post-quantum key exchange. If either algorithm holds, the combined scheme holds. iOS 18.4+.

let privateKey = XWingMLKEM768X25519.PrivateKey()
let publicKey = privateKey.publicKey

// Encapsulate
let result = try publicKey.encapsulate()
// result.sharedSecret, result.encapsulatedKey

// Decapsulate
let sharedSecret = try privateKey.decapsulate(result.encapsulatedKey)

// Representations
publicKey.rawRepresentation
privateKey.rawRepresentation
privateKey.seedRepresentation

HPKE (Hybrid Public Key Encryption)

Hybrid Public Key Encryption (RFC 9180). Combines KEM + KDF + AEAD into a single encryption scheme. iOS 17+ (classical ciphersuites). Post-quantum ciphersuites (XWing) require iOS 26+.

Predefined Ciphersuites

CiphersuiteKEMKDFAEAD
.XWingMLKEM768X25519_SHA256_AES_GCM_256X-WingHKDF-SHA256AES-256-GCM
.Curve25519_SHA256_ChachaPolyCurve25519HKDF-SHA256ChaCha20Poly1305
.Curve25519_SHA256_AES_GCM_128Curve25519HKDF-SHA256AES-128-GCM
.Curve25519_SHA256_AES_GCM_256Curve25519HKDF-SHA256AES-256-GCM
.P256_SHA256_AES_GCM_128P-256HKDF-SHA256AES-128-GCM
.P256_SHA256_AES_GCM_256P-256HKDF-SHA256AES-256-GCM
.P384_SHA384_AES_GCM_256P-384HKDF-SHA384AES-256-GCM
.P521_SHA512_AES_GCM_256P-521HKDF-SHA512AES-256-GCM

Custom Ciphersuite Composition

let ciphersuite = HPKE.Ciphersuite(
    kem: .Curve25519_HKDF_SHA256,
    kdf: .HKDF_SHA256,
    aead: .AES_GCM_128
)
KEM Options

.Curve25519_HKDF_SHA256, .P256_HKDF_SHA256, .P384_HKDF_SHA384, .P521_HKDF_SHA512, .XWingMLKEM768X25519_SHA256

KDF Options

.HKDF_SHA256, .HKDF_SHA384, .HKDF_SHA512

AEAD Options

.AES_GCM_128, .AES_GCM_256, .chaChaPoly, .exportOnly

Sender (Encrypt)

var sender = try HPKE.Sender(
    recipientKey: recipientPublicKey,
    ciphersuite: .Curve25519_SHA256_ChachaPoly,
    info: infoData                    // Binding context (can be empty)
)

let ciphertext = try sender.seal(plaintext)
let ciphertext = try sender.seal(plaintext, authenticating: aad)

let encapsulatedKey = sender.encapsulatedKey  // Send alongside ciphertext

// Export secret (for key derivation without encryption)
let exported = try sender.exportSecret(context: ctx, outputByteCount: 32)

Recipient (Decrypt)

var recipient = try HPKE.Recipient(
    privateKey: recipientPrivateKey,
    ciphersuite: .Curve25519_SHA256_ChachaPoly,
    info: infoData,
    encapsulatedKey: encapsulatedKey   // From sender
)

let plaintext = try recipient.open(ciphertext)
let plaintext = try recipient.open(ciphertext, authenticating: aad)

let exported = try recipient.exportSecret(context: ctx, outputByteCount: 32)

Additional Modes

Both Sender and Recipient accept optional authentication and PSK parameters:

// Authenticated mode — proves sender identity
var sender = try HPKE.Sender(
    recipientKey: recipientPublicKey, ciphersuite: ciphersuite, info: infoData,
    authenticatedBy: senderPrivateKey
)
var recipient = try HPKE.Recipient(
    privateKey: recipientPrivateKey, ciphersuite: ciphersuite, info: infoData,
    encapsulatedKey: encapsulatedKey, authenticatedBy: senderPublicKey
)

// PSK mode — adds pre-shared key binding
// Add to either Sender or Recipient init:
//   presharedKey: psk,                 // SymmetricKey
//   presharedKeyIdentifier: pskID      // Data

HPKE Error Types

HPKE.Errors.sealFailure           // Encryption failed
HPKE.Errors.openFailure           // Decryption/authentication failed
HPKE.Errors.encapsulationFailure  // KEM encapsulation failed
HPKE.Errors.decapsulationFailure  // KEM decapsulation failed
HPKE.Errors.exportFailure         // Secret export failed

Secure Enclave

Hardware-backed key storage. Keys never leave the Secure Enclave chip. Device-bound and non-exportable.

Availability Check

SecureEnclave.isAvailable  // false on Simulator, true on devices with SE

Supported Key Types

TypeUse
SecureEnclave.P256.Signing.PrivateKeyECDSA signatures
SecureEnclave.P256.KeyAgreement.PrivateKeyECDH key agreement
SecureEnclave.MLKEM768.PrivateKeyPost-quantum KEM (iOS 18.4+)
SecureEnclave.MLKEM1024.PrivateKeyPost-quantum KEM (iOS 18.4+)
SecureEnclave.MLDSA65.PrivateKeyPost-quantum signatures (iOS 18.4+)
SecureEnclave.MLDSA87.PrivateKeyPost-quantum signatures (iOS 18.4+)

Key Creation

let key = try SecureEnclave.P256.Signing.PrivateKey()  // Default access control

// With biometric access control
let accessControl = SecAccessControlCreateWithFlags(
    nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
    [.privateKeyUsage, .biometryCurrentSet], nil
)!
let key = try SecureEnclave.P256.Signing.PrivateKey(accessControl: accessControl)

// With pre-prompted biometric context
let context = LAContext()
context.localizedReason = "Sign transaction"
let key = try SecureEnclave.P256.Signing.PrivateKey(
    accessControl: accessControl, authenticationContext: context
)

Persistence and Usage

// dataRepresentation is an opaque device-bound blob — store in Keychain
let wrapped = key.dataRepresentation
let restored = try SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: wrapped)
let restored = try SecureEnclave.P256.Signing.PrivateKey(
    dataRepresentation: wrapped, authenticationContext: context
)

// SE keys use the same sign/verify/agree API as software keys
let signature = try seKey.signature(for: data)
let valid = seKey.publicKey.isValidSignature(signature, for: data)
let publicKeyData = seKey.publicKey.derRepresentation  // Public key IS exportable

Key Derivation (HKDF)

HMAC-based Key Derivation Function (RFC 5869).

One-Step Derivation

let derivedKey = HKDF<SHA256>.deriveKey(
    inputKeyMaterial: SymmetricKey(data: ikm),
    salt: saltData,                  // Optional, can be empty
    info: infoData,                  // Context/label
    outputByteCount: 32
)
// derivedKey: SymmetricKey

Two-Step (Extract + Expand)

Use two-step when deriving multiple keys from the same input: extract once, expand with different info values.

let prk = HKDF<SHA256>.extract(inputKeyMaterial: SymmetricKey(data: ikm), salt: saltData)
let encKey = HKDF<SHA256>.expand(pseudoRandomKey: prk, info: Data("enc".utf8), outputByteCount: 32)
let macKey = HKDF<SHA256>.expand(pseudoRandomKey: prk, info: Data("mac".utf8), outputByteCount: 32)

Error Types

CryptoKitError

CryptoKitError.incorrectKeySize          // Key size doesn't match algorithm
CryptoKitError.incorrectParameterSize    // Parameter size invalid
CryptoKitError.authenticationFailure     // GCM/ChaCha tag verification failed, HMAC mismatch
CryptoKitError.underlyingCoreCryptoError(error:)  // Low-level failure
CryptoKitError.wrapFailure              // AES key wrap failed
CryptoKitError.unwrapFailure            // AES key unwrap failed

CryptoKitASN1Error

CryptoKitASN1Error.invalidASN1Object           // Malformed ASN.1 structure
CryptoKitASN1Error.invalidASN1IntegerEncoding   // Bad integer encoding
CryptoKitASN1Error.truncatedASN1Field           // Data ends prematurely
CryptoKitASN1Error.invalidFieldIdentifier       // Unknown ASN.1 tag
CryptoKitASN1Error.unexpectedFieldType          // Wrong ASN.1 type
CryptoKitASN1Error.invalidObjectIdentifier      // Bad OID
CryptoKitASN1Error.invalidPEMDocument           // PEM header/footer or Base64 invalid

HPKE and KEM Errors

HPKE.Errors.sealFailure
HPKE.Errors.openFailure
HPKE.Errors.encapsulationFailure
HPKE.Errors.decapsulationFailure
HPKE.Errors.exportFailure

KEM.Errors.decapsulationFailed

Swift Crypto Cross-Platform Parity

Apple's open-source swift-crypto provides CryptoKit APIs on Linux, Windows, and other platforms.

Import Difference

#if canImport(CryptoKit)
import CryptoKit
#else
import Crypto  // swift-crypto package
#endif

API Parity

Everything maps 1:1 except SecureEnclave.* (requires Apple hardware). Hashing, HMAC, AES-GCM, ChaChaPoly, ECDH, ECDSA/EdDSA, ML-KEM, ML-DSA, X-Wing, HPKE, HKDF, and AES Key Wrap are all available cross-platform.

// Package.swift
.package(url: "https://github.com/apple/swift-crypto.git", from: "3.0.0")
// Target: .product(name: "Crypto", package: "swift-crypto")

Resources

WWDC: 2019-709, 2024-10120

Docs: /cryptokit, /cryptokit/performing-common-cryptographic-operations, /security/certificate-key-and-trust-services/keys/storing-keys-in-the-secure-enclave

Skills: axiom-cryptokit

Signals

GitHub stars
63
Forks
22
Last commit
Jun 2026
Advanced
Catalog kind
skill
Gateway key
axiom-cryptokit-ref
Source
github.com/comeonoliver/skillshub