@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +266 -267
- package/package.json +2 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +68 -68
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,495 +1,354 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cryptokit
|
|
3
|
-
description: "
|
|
3
|
+
description: "CryptoKit primitives in Swift: SHA-2 and SHA-3, HMAC, AES-GCM and ChaChaPoly, P256, P384, P521, Curve25519 and ML-DSA signatures, ECDH with HKDF, HPKE, ML-KEM and X-Wing, Secure Enclave keys. Use when hashing, encrypting, signing or exchanging keys, or migrating from CommonCrypto. Not for Keychain policy (swift-security)."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# CryptoKit
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
availability for HPKE (iOS 17+) and SHA-3 / post-quantum APIs (iOS 26+).
|
|
15
|
-
Prefer CryptoKit over CommonCrypto or raw Security framework APIs for new
|
|
16
|
-
cryptographic primitive code targeting Swift 6.3+.
|
|
17
|
-
|
|
18
|
-
## Contents
|
|
19
|
-
|
|
20
|
-
- [Hashing](#hashing)
|
|
21
|
-
- [HMAC](#hmac)
|
|
22
|
-
- [Symmetric Encryption](#symmetric-encryption)
|
|
23
|
-
- [Public-Key Signing](#public-key-signing)
|
|
24
|
-
- [Key Agreement](#key-agreement)
|
|
25
|
-
- [HPKE](#hpke)
|
|
26
|
-
- [Post-Quantum CryptoKit](#post-quantum-cryptokit)
|
|
27
|
-
- [Secure Enclave](#secure-enclave)
|
|
28
|
-
- [Common Mistakes](#common-mistakes)
|
|
29
|
-
- [Review Checklist](#review-checklist)
|
|
30
|
-
- [References](#references)
|
|
10
|
+
CryptoKit is Apple's Swift API for hashing, message authentication, symmetric
|
|
11
|
+
encryption, signatures, key agreement, HPKE, quantum-secure key encapsulation
|
|
12
|
+
and signatures, and keys kept in the Secure Enclave. For new code (Swift 6.3+)
|
|
13
|
+
reach for it before CommonCrypto or raw Security framework calls.
|
|
31
14
|
|
|
32
|
-
|
|
15
|
+
| Feature set | Minimum iOS |
|
|
16
|
+
|---|---|
|
|
17
|
+
| Hashing, HMAC, AES-GCM, ChaChaPoly, P-curves, Curve25519, Secure Enclave P256 | 13 |
|
|
18
|
+
| HPKE | 17 |
|
|
19
|
+
| SHA-3, ML-KEM, ML-DSA, X-Wing | 26 |
|
|
33
20
|
|
|
34
|
-
|
|
35
|
-
SHA3_256, SHA3_384, and SHA3_512 are available on iOS 26+. All conform
|
|
36
|
-
to the `HashFunction` protocol.
|
|
21
|
+
## Hashing
|
|
37
22
|
|
|
38
|
-
|
|
23
|
+
`SHA256`, `SHA384` and `SHA512` exist from iOS 13; `SHA3_256`, `SHA3_384` and
|
|
24
|
+
`SHA3_512` from iOS 26. All of them conform to `HashFunction`, so the API is the
|
|
25
|
+
same for each.
|
|
39
26
|
|
|
40
27
|
```swift
|
|
41
28
|
import CryptoKit
|
|
29
|
+
import Foundation
|
|
42
30
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
let hex = digest.compactMap { String(format: "%02x", $0) }.joined()
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
SHA384 and SHA512 work identically -- substitute the type name.
|
|
49
|
-
|
|
50
|
-
### SHA-3 availability
|
|
51
|
-
|
|
52
|
-
Use SHA-3 only behind an availability check unless the deployment target is
|
|
53
|
-
iOS 26+:
|
|
54
|
-
|
|
55
|
-
```swift
|
|
56
|
-
if #available(iOS 26.0, *) {
|
|
57
|
-
let digest = SHA3_256.hash(data: data)
|
|
31
|
+
func fingerprint(_ blob: Data) -> String {
|
|
32
|
+
SHA256.hash(data: blob).map { String(format: "%02x", $0) }.joined()
|
|
58
33
|
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Incremental hashing
|
|
62
|
-
|
|
63
|
-
For large data or streaming input, hash incrementally:
|
|
64
|
-
|
|
65
|
-
```swift
|
|
66
|
-
var hasher = SHA256()
|
|
67
|
-
hasher.update(data: chunk1)
|
|
68
|
-
hasher.update(data: chunk2)
|
|
69
|
-
let digest = hasher.finalize()
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Digest comparison
|
|
73
34
|
|
|
74
|
-
|
|
75
|
-
|
|
35
|
+
func strongerFingerprint(_ blob: Data) -> Data {
|
|
36
|
+
if #available(iOS 26.0, *) {
|
|
37
|
+
return Data(SHA3_384.hash(data: blob))
|
|
38
|
+
}
|
|
39
|
+
return Data(SHA384.hash(data: blob))
|
|
40
|
+
}
|
|
76
41
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
42
|
+
func streamingDigest(_ chunks: [Data]) -> SHA512.Digest {
|
|
43
|
+
var running = SHA512()
|
|
44
|
+
for chunk in chunks {
|
|
45
|
+
running.update(data: chunk)
|
|
46
|
+
}
|
|
47
|
+
return running.finalize()
|
|
82
48
|
}
|
|
83
49
|
```
|
|
84
50
|
|
|
85
|
-
|
|
51
|
+
- Put SHA-3 behind `if #available(iOS 26.0, *)` unless the deployment target is
|
|
52
|
+
already 26.
|
|
53
|
+
- Use the incremental `update(data:)` / `finalize()` form for large or
|
|
54
|
+
streamed input.
|
|
55
|
+
- Compare digests with `==` on the digest values. Do not turn them into strings
|
|
56
|
+
or arrays for a security-relevant equality check.
|
|
86
57
|
|
|
87
|
-
HMAC
|
|
58
|
+
## HMAC
|
|
88
59
|
|
|
89
|
-
|
|
60
|
+
An HMAC proves that data came from someone holding a shared symmetric key.
|
|
90
61
|
|
|
91
62
|
```swift
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
let mac = HMAC<SHA256>.authenticationCode(for: data, using: key)
|
|
96
|
-
```
|
|
63
|
+
import CryptoKit
|
|
64
|
+
import Foundation
|
|
97
65
|
|
|
98
|
-
|
|
66
|
+
let webhookKey = SymmetricKey(size: .bits256)
|
|
99
67
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
)
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
This uses constant-time comparison internally.
|
|
68
|
+
func tag(_ body: Data) -> HMAC<SHA256>.MAC {
|
|
69
|
+
HMAC<SHA256>.authenticationCode(for: body, using: webhookKey)
|
|
70
|
+
}
|
|
107
71
|
|
|
108
|
-
|
|
72
|
+
func isGenuine(_ body: Data, mac: HMAC<SHA256>.MAC) -> Bool {
|
|
73
|
+
HMAC<SHA256>.isValidAuthenticationCode(mac, authenticating: body, using: webhookKey)
|
|
74
|
+
}
|
|
109
75
|
|
|
110
|
-
|
|
111
|
-
var
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
76
|
+
func tagInPieces(_ parts: [Data]) -> HMAC<SHA256>.MAC {
|
|
77
|
+
var signer = HMAC<SHA256>(key: webhookKey)
|
|
78
|
+
parts.forEach { signer.update(data: $0) }
|
|
79
|
+
return signer.finalize()
|
|
80
|
+
}
|
|
115
81
|
```
|
|
116
82
|
|
|
117
|
-
|
|
83
|
+
`isValidAuthenticationCode(_:authenticating:using:)` compares in constant time.
|
|
118
84
|
|
|
119
|
-
|
|
120
|
-
ChaChaPoly. Both produce a sealed box containing the nonce, ciphertext,
|
|
121
|
-
and authentication tag.
|
|
85
|
+
## Symmetric encryption
|
|
122
86
|
|
|
123
|
-
|
|
87
|
+
Both ciphers are AEAD: the sealed box bundles a nonce, the ciphertext and an
|
|
88
|
+
authentication tag.
|
|
124
89
|
|
|
125
|
-
|
|
126
|
-
|
|
90
|
+
- **AES-GCM** is the default. Apple silicon accelerates it in hardware.
|
|
91
|
+
- **ChaChaPoly** fits devices without AES acceleration or protocols that
|
|
92
|
+
mandate ChaCha20-Poly1305, such as TLS cipher suites or WireGuard.
|
|
127
93
|
|
|
128
94
|
```swift
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// Encrypt
|
|
133
|
-
let sealedBox = try AES.GCM.seal(plaintext, using: key)
|
|
134
|
-
let ciphertext = sealedBox.combined! // nonce + ciphertext + tag
|
|
135
|
-
|
|
136
|
-
// Decrypt
|
|
137
|
-
let box = try AES.GCM.SealedBox(combined: ciphertext)
|
|
138
|
-
let decrypted = try AES.GCM.open(box, using: key)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### ChaChaPoly
|
|
142
|
-
|
|
143
|
-
Use ChaChaPoly when AES hardware acceleration is unavailable or when
|
|
144
|
-
interoperating with protocols that require ChaCha20-Poly1305 (e.g., TLS,
|
|
145
|
-
WireGuard).
|
|
146
|
-
|
|
147
|
-
```swift
|
|
148
|
-
let sealedBox = try ChaChaPoly.seal(plaintext, using: key)
|
|
149
|
-
let combined = sealedBox.combined // Always non-optional for ChaChaPoly
|
|
150
|
-
|
|
151
|
-
let box = try ChaChaPoly.SealedBox(combined: combined)
|
|
152
|
-
let decrypted = try ChaChaPoly.open(box, using: key)
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Authenticated data
|
|
95
|
+
import CryptoKit
|
|
96
|
+
import Foundation
|
|
156
97
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
98
|
+
func lockNote(_ note: Data, key: SymmetricKey) throws -> Data {
|
|
99
|
+
let box = try AES.GCM.seal(note, using: key)
|
|
100
|
+
guard let packed = box.combined else { throw CryptoKitError.incorrectParameterSize }
|
|
101
|
+
return packed
|
|
102
|
+
}
|
|
160
103
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
plaintext, using: key, authenticating: header
|
|
165
|
-
)
|
|
166
|
-
let decrypted = try AES.GCM.open(
|
|
167
|
-
sealedBox, using: key, authenticating: header
|
|
168
|
-
)
|
|
169
|
-
```
|
|
104
|
+
func unlockNote(_ packed: Data, key: SymmetricKey) throws -> Data {
|
|
105
|
+
try AES.GCM.open(AES.GCM.SealedBox(combined: packed), using: key)
|
|
106
|
+
}
|
|
170
107
|
|
|
171
|
-
|
|
172
|
-
ChaChaPoly.
|
|
108
|
+
func lockWithChaCha(_ note: Data, key: SymmetricKey) throws -> Data {
|
|
109
|
+
try ChaChaPoly.seal(note, using: key).combined
|
|
110
|
+
}
|
|
173
111
|
|
|
174
|
-
|
|
175
|
-
|
|
112
|
+
func unlockWithChaCha(_ packed: Data, key: SymmetricKey) throws -> Data {
|
|
113
|
+
try ChaChaPoly.open(ChaChaPoly.SealedBox(combined: packed), using: key)
|
|
114
|
+
}
|
|
176
115
|
```
|
|
177
116
|
|
|
178
|
-
|
|
117
|
+
`combined` is optional on `AES.GCM.SealedBox` (it is `nil` when a non-standard
|
|
118
|
+
nonce size was used) and non-optional on `ChaChaPoly.SealedBox`.
|
|
179
119
|
|
|
180
|
-
|
|
181
|
-
Curve25519.
|
|
120
|
+
### Additional authenticated data
|
|
182
121
|
|
|
183
|
-
|
|
122
|
+
Metadata that must travel in the clear but must not be altered goes in the AAD.
|
|
123
|
+
It is authenticated, not encrypted.
|
|
184
124
|
|
|
185
125
|
```swift
|
|
186
|
-
|
|
187
|
-
|
|
126
|
+
import CryptoKit
|
|
127
|
+
import Foundation
|
|
188
128
|
|
|
189
|
-
|
|
190
|
-
|
|
129
|
+
func sealRecord(_ payload: Data, recordID: String, key: SymmetricKey) throws -> AES.GCM.SealedBox {
|
|
130
|
+
try AES.GCM.seal(payload, using: key, authenticating: Data(recordID.utf8))
|
|
131
|
+
}
|
|
191
132
|
|
|
192
|
-
|
|
193
|
-
|
|
133
|
+
func openRecord(_ box: AES.GCM.SealedBox, recordID: String, key: SymmetricKey) throws -> Data {
|
|
134
|
+
try AES.GCM.open(box, using: key, authenticating: Data(recordID.utf8))
|
|
135
|
+
}
|
|
194
136
|
```
|
|
195
137
|
|
|
196
|
-
|
|
138
|
+
### Keys
|
|
139
|
+
|
|
140
|
+
`SymmetricKey(size: .bits256)` gives AES-256-GCM or ChaChaPoly strength and is
|
|
141
|
+
the right default. Wrap existing key bytes with `SymmetricKey(data:)`.
|
|
197
142
|
|
|
198
|
-
|
|
199
|
-
[references/cryptokit-patterns.md](references/cryptokit-patterns.md) for
|
|
200
|
-
serialization examples.
|
|
143
|
+
## Signatures
|
|
201
144
|
|
|
202
|
-
|
|
145
|
+
ECDSA uses the NIST curves; Ed25519 comes from `Curve25519`.
|
|
203
146
|
|
|
204
147
|
```swift
|
|
205
|
-
|
|
206
|
-
|
|
148
|
+
import CryptoKit
|
|
149
|
+
import Foundation
|
|
207
150
|
|
|
208
|
-
|
|
209
|
-
let
|
|
151
|
+
func signRelease(_ manifest: Data) throws -> (P256.Signing.PublicKey, P256.Signing.ECDSASignature) {
|
|
152
|
+
let signer = P256.Signing.PrivateKey()
|
|
153
|
+
return (signer.publicKey, try signer.signature(for: manifest))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
func verifyRelease(_ manifest: Data, key: P256.Signing.PublicKey, sig: P256.Signing.ECDSASignature) -> Bool {
|
|
157
|
+
key.isValidSignature(sig, for: manifest)
|
|
158
|
+
}
|
|
210
159
|
|
|
211
|
-
|
|
212
|
-
let
|
|
160
|
+
func edSign(_ message: Data) throws -> (Curve25519.Signing.PublicKey, Data) {
|
|
161
|
+
let signer = Curve25519.Signing.PrivateKey()
|
|
162
|
+
return (signer.publicKey, try signer.signature(for: message))
|
|
163
|
+
}
|
|
213
164
|
```
|
|
214
165
|
|
|
215
|
-
|
|
166
|
+
P384 and P521 have the same shape as P256. NIST keys serialize as DER, PEM,
|
|
167
|
+
X9.63 or raw. Curve25519 keys offer only `rawRepresentation`.
|
|
216
168
|
|
|
217
169
|
### Choosing a curve
|
|
218
170
|
|
|
219
|
-
| Curve |
|
|
171
|
+
| Curve | Algorithm | Size | Notes |
|
|
220
172
|
|---|---|---|---|
|
|
221
|
-
| P256 | ECDSA | 256-bit |
|
|
222
|
-
| P384 | ECDSA | 384-bit |
|
|
223
|
-
| P521 | ECDSA | 521-bit |
|
|
224
|
-
| Curve25519 | Ed25519 | 256-bit |
|
|
173
|
+
| P256 | ECDSA | 256-bit | general default; Secure Enclave capable |
|
|
174
|
+
| P384 | ECDSA | 384-bit | higher margin |
|
|
175
|
+
| P521 | ECDSA | 521-bit | top NIST level |
|
|
176
|
+
| Curve25519 | Ed25519 | 256-bit | fast and simple; no Secure Enclave |
|
|
225
177
|
|
|
226
|
-
|
|
227
|
-
protocols.
|
|
178
|
+
Start with P256. Use Curve25519 when a protocol specifies Ed25519.
|
|
228
179
|
|
|
229
|
-
## Key
|
|
180
|
+
## Key agreement
|
|
230
181
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
### ECDH with P256
|
|
182
|
+
ECDH turns two key pairs into one shared secret. Each side combines its own
|
|
183
|
+
private key with the other side's public key, and both arrive at the same value.
|
|
235
184
|
|
|
236
185
|
```swift
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
let
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
|
|
250
|
-
using: SHA256.self,
|
|
251
|
-
salt: Data("salt".utf8),
|
|
252
|
-
sharedInfo: Data("my-app-v1".utf8),
|
|
253
|
-
outputByteCount: 32
|
|
254
|
-
)
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
Bob computes the same `sharedSecret` using his private key and Alice's
|
|
258
|
-
public key. Both derive the same `symmetricKey`.
|
|
259
|
-
|
|
260
|
-
### ECDH with Curve25519
|
|
186
|
+
import CryptoKit
|
|
187
|
+
import Foundation
|
|
188
|
+
|
|
189
|
+
func sessionKey(mine: P256.KeyAgreement.PrivateKey, theirs: P256.KeyAgreement.PublicKey) throws -> SymmetricKey {
|
|
190
|
+
let secret = try mine.sharedSecretFromKeyAgreement(with: theirs)
|
|
191
|
+
return secret.hkdfDerivedSymmetricKey(
|
|
192
|
+
using: SHA256.self,
|
|
193
|
+
salt: Data("chat-v2-salt".utf8),
|
|
194
|
+
sharedInfo: Data("chat-v2 message key".utf8),
|
|
195
|
+
outputByteCount: 32
|
|
196
|
+
)
|
|
197
|
+
}
|
|
261
198
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
|
|
271
|
-
using: SHA256.self,
|
|
272
|
-
salt: Data(),
|
|
273
|
-
sharedInfo: Data("context".utf8),
|
|
274
|
-
outputByteCount: 32
|
|
275
|
-
)
|
|
199
|
+
func x25519Key(mine: Curve25519.KeyAgreement.PrivateKey, theirs: Curve25519.KeyAgreement.PublicKey) throws -> SymmetricKey {
|
|
200
|
+
try mine.sharedSecretFromKeyAgreement(with: theirs).hkdfDerivedSymmetricKey(
|
|
201
|
+
using: SHA256.self,
|
|
202
|
+
salt: Data(),
|
|
203
|
+
sharedInfo: Data("sync-pairing v1".utf8),
|
|
204
|
+
outputByteCount: 32
|
|
205
|
+
)
|
|
206
|
+
}
|
|
276
207
|
```
|
|
277
208
|
|
|
278
|
-
###
|
|
209
|
+
### Derivation
|
|
279
210
|
|
|
280
|
-
`SharedSecret` is not
|
|
281
|
-
a key using one of:
|
|
211
|
+
A `SharedSecret` is not a `SymmetricKey` and must always go through a KDF.
|
|
282
212
|
|
|
283
|
-
| Method | Standard |
|
|
213
|
+
| Method | Standard | When |
|
|
284
214
|
|---|---|---|
|
|
285
|
-
| `hkdfDerivedSymmetricKey` | HKDF
|
|
286
|
-
| `x963DerivedSymmetricKey` | ANSI X9.63 |
|
|
215
|
+
| `hkdfDerivedSymmetricKey` | HKDF, RFC 5869 | default choice |
|
|
216
|
+
| `x963DerivedSymmetricKey` | ANSI X9.63 | interoperating with X9.63 systems |
|
|
287
217
|
|
|
288
|
-
Always
|
|
289
|
-
|
|
218
|
+
Always pass a non-empty `sharedInfo` that names the protocol and purpose, so
|
|
219
|
+
the derived key cannot be reused somewhere else.
|
|
290
220
|
|
|
291
221
|
## HPKE
|
|
292
222
|
|
|
293
|
-
HPKE
|
|
294
|
-
|
|
223
|
+
HPKE (iOS 17+) is the ready-made way to encrypt to someone's public key.
|
|
224
|
+
Prefer it over wiring ECDH, HKDF and an AEAD together by hand.
|
|
295
225
|
|
|
296
226
|
```swift
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
var sender = try HPKE.Sender(
|
|
300
|
-
recipientKey: recipientKey.publicKey,
|
|
301
|
-
ciphersuite: .Curve25519_SHA256_ChachaPoly,
|
|
302
|
-
info: info
|
|
303
|
-
)
|
|
304
|
-
let encapsulatedKey = sender.encapsulatedKey
|
|
305
|
-
let ciphertext = try sender.seal(
|
|
306
|
-
plaintext,
|
|
307
|
-
authenticating: Data("metadata".utf8)
|
|
308
|
-
)
|
|
309
|
-
|
|
310
|
-
var recipient = try HPKE.Recipient(
|
|
311
|
-
privateKey: recipientKey,
|
|
312
|
-
ciphersuite: .Curve25519_SHA256_ChachaPoly,
|
|
313
|
-
info: info,
|
|
314
|
-
encapsulatedKey: encapsulatedKey
|
|
315
|
-
)
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
`HPKE.Sender` and `HPKE.Recipient` are stateful; keep them as `var`, send
|
|
319
|
-
`encapsulatedKey` alongside the ciphertext, and open messages in the same
|
|
320
|
-
order they were sealed. See [references/cryptokit-patterns.md](references/cryptokit-patterns.md)
|
|
321
|
-
for ciphersuite selection and post-quantum HPKE.
|
|
322
|
-
|
|
323
|
-
## Post-Quantum CryptoKit
|
|
324
|
-
|
|
325
|
-
iOS 26+ adds quantum-secure APIs:
|
|
326
|
-
|
|
327
|
-
- Key encapsulation: `MLKEM768`, `MLKEM1024`
|
|
328
|
-
- Hybrid HPKE: `XWingMLKEM768X25519` with `.XWingMLKEM768X25519_SHA256_AES_GCM_256`
|
|
329
|
-
- Digital signatures: `MLDSA65`, `MLDSA87`
|
|
330
|
-
- Secure Enclave variants: `SecureEnclave.MLKEM768`, `SecureEnclave.MLKEM1024`,
|
|
331
|
-
`SecureEnclave.MLDSA65`, `SecureEnclave.MLDSA87`
|
|
332
|
-
|
|
333
|
-
Use hybrid mechanisms for migration when both classical and quantum-secure
|
|
334
|
-
resistance matter. Account for much larger public keys, ciphertexts, and
|
|
335
|
-
signatures than P256 or Curve25519.
|
|
336
|
-
|
|
337
|
-
## Secure Enclave
|
|
338
|
-
|
|
339
|
-
The Secure Enclave provides hardware-backed key storage. Private keys
|
|
340
|
-
never leave the hardware. For classical elliptic-curve CryptoKit, Secure
|
|
341
|
-
Enclave supports P256 signing and key agreement. On iOS 26+ supported
|
|
342
|
-
hardware, CryptoKit also exposes Secure Enclave ML-KEM key encapsulation
|
|
343
|
-
and ML-DSA signing types.
|
|
344
|
-
|
|
345
|
-
### Availability check
|
|
227
|
+
import CryptoKit
|
|
228
|
+
import Foundation
|
|
346
229
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
230
|
+
func encryptForDevice(_ messages: [Data], to device: Curve25519.KeyAgreement.PublicKey) throws -> (Data, [Data]) {
|
|
231
|
+
let context = Data("device-backup v3".utf8)
|
|
232
|
+
var sender = try HPKE.Sender(recipientKey: device, ciphersuite: .Curve25519_SHA256_ChachaPoly, info: context)
|
|
233
|
+
let sealed = try messages.map { try sender.seal($0, authenticating: Data("header".utf8)) }
|
|
234
|
+
return (sender.encapsulatedKey, sealed)
|
|
351
235
|
}
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
### Creating a Secure Enclave signing key
|
|
355
|
-
|
|
356
|
-
```swift
|
|
357
|
-
let privateKey = try SecureEnclave.P256.Signing.PrivateKey()
|
|
358
|
-
let publicKey = privateKey.publicKey // Standard P256.Signing.PublicKey
|
|
359
|
-
|
|
360
|
-
let signature = try privateKey.signature(for: data)
|
|
361
|
-
let isValid = publicKey.isValidSignature(signature, for: data)
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### Access control
|
|
365
|
-
|
|
366
|
-
Use `SecAccessControl` with `.privateKeyUsage` when the key requires biometric
|
|
367
|
-
or passcode-gated use. Keep detailed Keychain policy decisions in the
|
|
368
|
-
`swift-security` domain.
|
|
369
|
-
|
|
370
|
-
### Persisting Secure Enclave keys
|
|
371
|
-
|
|
372
|
-
The `dataRepresentation` is an encrypted blob that only the same device's
|
|
373
|
-
Secure Enclave can restore. Store it in the Keychain.
|
|
374
|
-
|
|
375
|
-
```swift
|
|
376
|
-
// Export
|
|
377
|
-
let blob = privateKey.dataRepresentation
|
|
378
236
|
|
|
379
|
-
|
|
380
|
-
let
|
|
381
|
-
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
### Secure Enclave key agreement
|
|
386
|
-
|
|
387
|
-
```swift
|
|
388
|
-
let seKey = try SecureEnclave.P256.KeyAgreement.PrivateKey()
|
|
389
|
-
let peerPublicKey: P256.KeyAgreement.PublicKey = // from peer
|
|
390
|
-
|
|
391
|
-
let sharedSecret = try seKey.sharedSecretFromKeyAgreement(
|
|
392
|
-
with: peerPublicKey
|
|
393
|
-
)
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
## Common Mistakes
|
|
397
|
-
|
|
398
|
-
### 1. Using the shared secret directly as a key
|
|
399
|
-
|
|
400
|
-
```swift
|
|
401
|
-
// DON'T
|
|
402
|
-
let badKey = sharedSecret.withUnsafeBytes { bytes in
|
|
403
|
-
SymmetricKey(data: Data(bytes))
|
|
237
|
+
func decryptOnDevice(_ sealed: [Data], encapsulated: Data, key: Curve25519.KeyAgreement.PrivateKey) throws -> [Data] {
|
|
238
|
+
let context = Data("device-backup v3".utf8)
|
|
239
|
+
var receiver = try HPKE.Recipient(privateKey: key, ciphersuite: .Curve25519_SHA256_ChachaPoly,
|
|
240
|
+
info: context, encapsulatedKey: encapsulated)
|
|
241
|
+
return try sealed.map { try receiver.open($0, authenticating: Data("header".utf8)) }
|
|
404
242
|
}
|
|
405
|
-
|
|
406
|
-
// DO -- derive with HKDF
|
|
407
|
-
let goodKey = sharedSecret.hkdfDerivedSymmetricKey(
|
|
408
|
-
using: SHA256.self,
|
|
409
|
-
salt: salt,
|
|
410
|
-
sharedInfo: info,
|
|
411
|
-
outputByteCount: 32
|
|
412
|
-
)
|
|
413
243
|
```
|
|
414
244
|
|
|
415
|
-
|
|
245
|
+
- `HPKE.Sender` and `HPKE.Recipient` keep a sequence counter, so they must be
|
|
246
|
+
`var`.
|
|
247
|
+
- Ship `encapsulatedKey` together with the ciphertext.
|
|
248
|
+
- The recipient must open messages in the same order the sender sealed them.
|
|
416
249
|
|
|
417
|
-
|
|
418
|
-
// DON'T -- hardcoded nonce
|
|
419
|
-
let nonce = try AES.GCM.Nonce(data: Data(repeating: 0, count: 12))
|
|
420
|
-
let box = try AES.GCM.seal(data, using: key, nonce: nonce)
|
|
250
|
+
## Post-quantum CryptoKit (iOS 26+)
|
|
421
251
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
252
|
+
| Purpose | Types |
|
|
253
|
+
|---|---|
|
|
254
|
+
| Key encapsulation | `MLKEM768`, `MLKEM1024` |
|
|
255
|
+
| Hybrid HPKE | `XWingMLKEM768X25519` with `.XWingMLKEM768X25519_SHA256_AES_GCM_256` |
|
|
256
|
+
| Signatures | `MLDSA65`, `MLDSA87` |
|
|
257
|
+
| Secure Enclave | `SecureEnclave.MLKEM768`, `SecureEnclave.MLKEM1024`, `SecureEnclave.MLDSA65`, `SecureEnclave.MLDSA87` |
|
|
425
258
|
|
|
426
|
-
|
|
259
|
+
- During a migration where both classical and quantum resistance matter, use
|
|
260
|
+
the hybrid X-Wing suite.
|
|
261
|
+
- Keys, ciphertexts and signatures are far larger than their P256 or
|
|
262
|
+
Curve25519 counterparts. Budget for that in storage and on the wire.
|
|
427
263
|
|
|
428
|
-
|
|
429
|
-
// DON'T -- manually strip tag and decrypt
|
|
430
|
-
// DO -- always use AES.GCM.open() or ChaChaPoly.open()
|
|
431
|
-
// which verifies the tag automatically
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### 4. Using Insecure hashes for security
|
|
435
|
-
|
|
436
|
-
```swift
|
|
437
|
-
// DON'T -- MD5/SHA1 for integrity or security
|
|
438
|
-
import CryptoKit
|
|
439
|
-
let bad = Insecure.MD5.hash(data: data)
|
|
440
|
-
|
|
441
|
-
// DO -- use SHA256 or stronger
|
|
442
|
-
let good = SHA256.hash(data: data)
|
|
443
|
-
```
|
|
264
|
+
## Secure Enclave
|
|
444
265
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
security-sensitive operations.
|
|
266
|
+
The Secure Enclave generates and holds private keys in hardware; they never
|
|
267
|
+
leave it.
|
|
448
268
|
|
|
449
|
-
|
|
269
|
+
- Classical support covers P256 signing and P256 key agreement only.
|
|
270
|
+
- On iOS 26 with supported hardware, ML-KEM and ML-DSA keys can live there too.
|
|
271
|
+
- Always check `SecureEnclave.isAvailable` and fall back to software keys.
|
|
450
272
|
|
|
451
273
|
```swift
|
|
452
|
-
|
|
453
|
-
|
|
274
|
+
import CryptoKit
|
|
275
|
+
import Foundation
|
|
276
|
+
import Security
|
|
454
277
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
278
|
+
enum DeviceKeyError: Error {
|
|
279
|
+
case noSecureEnclave
|
|
280
|
+
case accessControlRejected
|
|
281
|
+
}
|
|
458
282
|
|
|
459
|
-
|
|
283
|
+
func deviceSigningKey() throws -> Data {
|
|
284
|
+
guard SecureEnclave.isAvailable else { throw DeviceKeyError.noSecureEnclave }
|
|
285
|
+
guard let rule = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
|
286
|
+
[.privateKeyUsage, .biometryCurrentSet], nil) else {
|
|
287
|
+
throw DeviceKeyError.accessControlRejected
|
|
288
|
+
}
|
|
289
|
+
let key = try SecureEnclave.P256.Signing.PrivateKey(accessControl: rule)
|
|
290
|
+
_ = key.publicKey
|
|
291
|
+
return key.dataRepresentation
|
|
292
|
+
}
|
|
460
293
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
294
|
+
func restoreDeviceKey(_ blob: Data) throws -> SecureEnclave.P256.Signing.PrivateKey {
|
|
295
|
+
try SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: blob)
|
|
296
|
+
}
|
|
464
297
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
298
|
+
func enclaveAgreement(with peer: P256.KeyAgreement.PublicKey) throws -> SharedSecret {
|
|
299
|
+
let mine = try SecureEnclave.P256.KeyAgreement.PrivateKey()
|
|
300
|
+
return try mine.sharedSecretFromKeyAgreement(with: peer)
|
|
301
|
+
}
|
|
468
302
|
```
|
|
469
303
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
-
|
|
482
|
-
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
-
|
|
304
|
+
- The public key of a Secure Enclave signing key is an ordinary
|
|
305
|
+
`P256.Signing.PublicKey`; signing and verifying look the same as in software.
|
|
306
|
+
- Gate use behind biometrics or the passcode with a `SecAccessControl` that
|
|
307
|
+
includes `.privateKeyUsage`. Deeper Keychain policy choices belong to
|
|
308
|
+
`swift-security`.
|
|
309
|
+
- `dataRepresentation` is an encrypted blob that only the same device's Secure
|
|
310
|
+
Enclave can turn back into a key. Store it in the Keychain and restore it
|
|
311
|
+
with `init(dataRepresentation:)`.
|
|
312
|
+
|
|
313
|
+
## Common mistakes
|
|
314
|
+
|
|
315
|
+
- **Using the raw shared secret as a key.** Always derive with HKDF.
|
|
316
|
+
- **Fixed or repeated nonces.** Passing something like
|
|
317
|
+
`AES.GCM.Nonce(data: Data(count: 12))` to `seal(_:using:nonce:)` for every
|
|
318
|
+
message breaks GCM. Let CryptoKit pick a random nonce by leaving the argument
|
|
319
|
+
out.
|
|
320
|
+
- **Decrypting without the tag check.** Never peel the tag off and decrypt by
|
|
321
|
+
hand. `AES.GCM.open` and `ChaChaPoly.open` verify it for you.
|
|
322
|
+
- **Insecure hashes for security.** `Insecure.MD5` and `Insecure.SHA1` exist
|
|
323
|
+
for legacy checksums and protocol interop only.
|
|
324
|
+
- **Key bytes in `UserDefaults`.** Keys go in the Keychain; see
|
|
325
|
+
[the Keychain patterns](references/cryptokit-patterns.md#keychain-storage).
|
|
326
|
+
- **Secure Enclave without an availability check.** Key creation fails in
|
|
327
|
+
Simulator and on unsupported hardware.
|
|
328
|
+
|
|
329
|
+
## Review checklist
|
|
330
|
+
|
|
331
|
+
- [ ] CryptoKit is used instead of CommonCrypto or raw Security calls
|
|
332
|
+
- [ ] Hashing uses SHA-256 or stronger; no MD5 or SHA-1 for security
|
|
333
|
+
- [ ] HMACs are verified with `isValidAuthenticationCode`
|
|
334
|
+
- [ ] Encryption is AES-GCM or ChaChaPoly with 256-bit keys
|
|
335
|
+
- [ ] Nonces are random, never hardcoded or reused
|
|
336
|
+
- [ ] AAD protects metadata that needs integrity
|
|
337
|
+
- [ ] Every `SharedSecret` goes through HKDF
|
|
338
|
+
- [ ] `sharedInfo` is non-empty and specific to the context
|
|
339
|
+
- [ ] HPKE replaces hand-built ECDH + HKDF + AEAD for recipient encryption on iOS 17+
|
|
340
|
+
- [ ] SHA-3 and post-quantum code is behind an iOS 26 availability check
|
|
341
|
+
- [ ] `SecureEnclave.isAvailable` is checked
|
|
342
|
+
- [ ] Secure Enclave `dataRepresentation` is stored in the Keychain
|
|
343
|
+
- [ ] Private keys are never logged, printed or serialized without need
|
|
344
|
+
- [ ] Symmetric keys live in the Keychain, not `UserDefaults` or loose files
|
|
345
|
+
- [ ] Export compliance (`ITSAppUsesNonExemptEncryption`) has been considered
|
|
487
346
|
|
|
488
347
|
## References
|
|
489
348
|
|
|
490
|
-
-
|
|
491
|
-
-
|
|
492
|
-
-
|
|
493
|
-
-
|
|
494
|
-
-
|
|
495
|
-
-
|
|
349
|
+
- [CryptoKit patterns](references/cryptokit-patterns.md) - key serialization, Keychain storage, AES key wrap, HKDF, HPKE ciphersuites, post-quantum usage, the Insecure namespace, sealed box layout, digest signing, export compliance, performance, CommonCrypto migration
|
|
350
|
+
- [CryptoKit](https://developer.apple.com/documentation/cryptokit)
|
|
351
|
+
- [HPKE](https://developer.apple.com/documentation/cryptokit/hpke)
|
|
352
|
+
- [Enhancing your app's privacy and security with quantum-secure workflows](https://developer.apple.com/documentation/cryptokit/enhancing-your-app-s-privacy-and-security-with-quantum-secure-workflows)
|
|
353
|
+
- [Performing common cryptographic operations](https://developer.apple.com/documentation/cryptokit/performing-common-cryptographic-operations)
|
|
354
|
+
- [Storing CryptoKit keys in the keychain](https://developer.apple.com/documentation/cryptokit/storing-cryptokit-keys-in-the-keychain)
|