@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.
Files changed (264) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +266 -267
  5. package/package.json +2 -2
  6. package/pipeline/scripts/_notices.mjs +1 -1
  7. package/pipeline/skills/.skill-manifest.json +68 -68
  8. package/pipeline/skills/shared/README.md +70 -70
  9. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  10. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  11. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  12. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  13. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  14. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  15. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  16. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  17. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  18. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  19. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  20. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  21. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  22. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  23. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  24. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  25. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  26. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  27. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  28. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  29. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  30. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  31. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  32. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  34. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  35. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  36. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  37. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  38. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  39. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  40. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  41. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  42. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  43. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  44. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  45. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  46. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  47. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  48. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  49. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  50. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  51. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  52. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  53. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  54. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  55. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  56. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  57. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  58. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  59. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  60. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  61. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  62. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  63. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  64. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  65. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  66. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  67. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  68. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  69. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  70. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  71. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  72. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  73. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  74. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  75. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  76. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  77. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  78. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  79. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  80. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  81. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  82. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  83. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  84. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  85. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  86. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  87. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  88. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  89. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  90. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  91. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  92. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  93. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  94. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  95. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  96. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  97. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  98. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  99. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  100. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  101. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  102. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  103. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  104. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  105. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  106. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  107. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  108. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  109. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  111. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  112. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  113. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  114. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  115. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  116. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  117. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  118. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  119. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  120. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  121. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  122. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  123. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  124. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  125. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  126. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  127. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  128. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  129. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  130. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  131. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  132. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  133. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  134. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  135. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  136. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  137. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  138. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  139. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  140. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  141. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  142. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  143. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  144. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  145. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  146. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  147. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  148. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  149. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  150. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  151. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  152. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  153. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  154. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  155. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  156. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  157. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  158. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  159. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  160. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  161. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  162. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  163. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  164. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  165. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  166. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  167. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  168. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  169. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  170. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  171. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  172. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  173. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  174. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  175. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  176. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  177. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  178. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  179. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  180. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  181. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  182. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  183. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  184. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  185. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  186. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  187. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  188. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  189. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  190. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  191. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  192. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  193. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  194. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  195. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  196. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  197. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  198. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  199. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  200. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  201. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  202. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  203. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  204. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  205. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  206. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  207. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  208. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  209. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  210. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  211. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  212. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  213. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  214. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  215. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  216. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  217. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  218. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  219. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  220. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  221. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  222. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  223. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  224. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  225. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  226. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  227. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  228. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  229. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  230. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  231. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  232. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  233. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  234. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  235. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  236. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  237. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  238. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  239. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  240. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  241. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  242. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  243. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  244. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  245. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  246. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  247. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  248. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  249. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  250. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  251. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  252. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  253. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  254. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  255. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  256. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  257. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  258. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  259. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  260. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  261. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  262. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  263. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  264. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,538 +1,467 @@
1
- # CryptoKit Public-Key Cryptography
1
+ # CryptoKit: Signatures, Key Agreement, HPKE and Post-Quantum
2
2
 
3
- > **Scope:** ECDSA signing, ECDH key agreement, HPKE (iOS 17+), ML-KEM/ML-DSA and hybrid migration patterns (iOS 26+), key serialization, and Secure Enclave integration boundaries on Apple platforms.
4
- >
5
- > **Cross-references:** Secure Enclave key lifecycle → `secure-enclave.md`. Symmetric encryption after key agreement → `cryptokit-symmetric.md`. Keychain storage of CryptoKit keys → `credential-storage-patterns.md`. RSA → ECC migration → section "Stop Using RSA for New Apple Development" below.
3
+ Topics: digital signatures (ECDSA, EdDSA), Diffie-Hellman over elliptic curves,
4
+ hybrid public-key encryption from iOS 17,
5
+ ML-KEM and ML-DSA with hybrid migration (iOS 26+), key serialization, and where the
6
+ Secure Enclave fits.
6
7
 
7
- CryptoKit's asymmetric cryptography API covers ECDSA signing, ECDH key agreement, HPKE (iOS 17+), and post-quantum ML-KEM/ML-DSA (iOS 26+). The framework enforces correct usage through its type system - signing keys cannot perform key agreement, shared secrets must pass through HKDF before use, and Secure Enclave access is limited to P256 for classical curves. This reference covers every asymmetric primitive from iOS 13 through iOS 26 with verified Swift implementations, common AI-generator mistakes, and the quantum migration path.
8
+ CryptoKit's types carry several rules for you: a signing key cannot perform key
9
+ agreement, a shared secret cannot be used as a key until it is run through a KDF,
10
+ and the only classical curve the Secure Enclave accepts is P-256.
8
11
 
9
- CryptoKit was introduced at WWDC 2019 (session 709, "Cryptography and Your Apps") as a Swift-native replacement for the Security framework's C-based `SecKey` API. It wraps Apple's corecrypto library with hand-tuned assembly per microarchitecture, delivering both performance and memory safety - private key material is automatically zeroed on deallocation. iOS 14 added PEM/DER interoperability and standalone HKDF. iOS 17 brought HPKE (RFC 9180). iOS 26 (WWDC 2025, session 314, "Get ahead with quantum-secure cryptography") completes the picture with formally verified post-quantum algorithms and quantum-secure TLS enabled by default.
10
-
11
- ---
12
+ Background: CryptoKit arrived at WWDC 2019 (session 709) as a Swift replacement for
13
+ the C-level `SecKey` API. It sits on corecrypto and zeroes private key memory on
14
+ deallocation. iOS 14 added PEM/DER import and export plus standalone HKDF. iOS 17
15
+ added HPKE (RFC 9180). iOS 26 (WWDC 2025 session 314) added formally verified
16
+ post-quantum algorithms and turned on post-quantum TLS by default.
12
17
 
13
18
  ## Contents
14
19
 
15
- - [Curve and Algorithm Selection Guide](#curve-and-algorithm-selection-guide)
16
- - [Classical Curves](#classical-curves)
17
- - [Post-Quantum Algorithms (iOS 26+)](#post-quantum-algorithms-ios-26)
18
- - [Selection Decision Matrix](#selection-decision-matrix)
19
- - [Algorithm Quick Reference](#algorithm-quick-reference)
20
- - [Signing and Key Agreement Are Separate Type Hierarchies](#signing-and-key-agreement-are-separate-type-hierarchies)
21
- - [✅ Correct: P256 key generation, signing, and verification](#correct-p256-key-generation-signing-and-verification)
22
- - [❌ Wrong: Mixing signing and key agreement key types](#wrong-mixing-signing-and-key-agreement-key-types)
23
- - [Key Agreement with HKDF Derivation](#key-agreement-with-hkdf-derivation)
24
- - [✅ Correct: Curve25519 key agreement with HKDF derivation](#correct-curve25519-key-agreement-with-hkdf-derivation)
25
- - [❌ Wrong: Using SharedSecret directly as an encryption key](#wrong-using-sharedsecret-directly-as-an-encryption-key)
26
- - [HPKE Simplifies Public-Key Encryption (iOS 17+)](#hpke-simplifies-public-key-encryption-ios-17)
27
- - [Built-in Cipher Suites](#built-in-cipher-suites)
28
- - [✅ Correct: HPKE encryption and decryption](#correct-hpke-encryption-and-decryption)
29
- - [Three Critical HPKE Details AI Generators Get Wrong](#three-critical-hpke-details-ai-generators-get-wrong)
30
- - [Post-Quantum Cryptography (iOS 26+)](#post-quantum-cryptography-ios-26)
31
- - [✅ Correct: ML-KEM-768 key encapsulation](#correct-ml-kem-768-key-encapsulation)
32
- - [✅ Correct: ML-DSA-65 signing](#correct-ml-dsa-65-signing)
33
- - [✅ Correct: Hybrid post-quantum with HPKE (recommended migration path)](#correct-hybrid-post-quantum-with-hpke-recommended-migration-path)
34
- - [✅ Correct: Hybrid signing (ML-DSA + ECDSA) for transition period](#correct-hybrid-signing-ml-dsa-ecdsa-for-transition-period)
35
- - [PEM and DER Interoperability (iOS 14+)](#pem-and-der-interoperability-ios-14)
36
- - [✅ Correct: PEM key export and import](#correct-pem-key-export-and-import)
37
- - [Key Format Reference](#key-format-reference)
38
- - [Keychain Storage of CryptoKit Keys](#keychain-storage-of-cryptokit-keys)
39
- - [Secure Enclave Integration (Brief - See `secure-enclave.md`)](#secure-enclave-integration-brief-see-secure-enclavemd)
40
- - [Stop Using RSA for New Apple Development](#stop-using-rsa-for-new-apple-development)
41
- - [❌ Wrong: RSA when EC is available](#wrong-rsa-when-ec-is-available)
42
- - [Preferred replacement: P256 signing in CryptoKit](#preferred-replacement-p256-signing-in-cryptokit)
43
- - [Common AI-Generator Mistakes](#common-ai-generator-mistakes)
44
- - [iOS Version Requirements](#ios-version-requirements)
45
- - [Performance and Thread Safety](#performance-and-thread-safety)
46
- - [WWDC Sessions and Documentation References](#wwdc-sessions-and-documentation-references)
47
- - [Conclusion](#conclusion)
48
- - [Summary Checklist](#summary-checklist)
49
-
50
- ## Curve and Algorithm Selection Guide
51
-
52
- The single most important decision is choosing the right curve or algorithm. AI generators frequently recommend Curve25519 when Secure Enclave protection is required, or default to P-256 when modern constant-time performance matters more.
53
-
54
- ### Classical Curves
55
-
56
- **P256 (secp256r1 / NIST P-256)** - The only classical curve supported by the Secure Enclave. Required for hardware-backed key storage with biometric access control. Conforms to NIST FIPS 186-5 for US government compliance and has the broadest interoperability with TLS, X.509 certificates, and server-side libraries. Public keys are 64 bytes (uncompressed raw), signatures are 64 bytes (raw r‖s). PEM and DER export supported from iOS 14.
57
-
58
- **Curve25519 (X25519 / Ed25519)** - Should be the default for software-only keys. Its rigid parameter design eliminates entire classes of implementation vulnerabilities - constant-time execution is inherent to the curve arithmetic, no point validation is required, and public keys are a compact 32 bytes. Ed25519 handles signing; X25519 handles key agreement. The tradeoff: only `rawRepresentation` is available (no PEM, no DER, no x963), and there is no Secure Enclave support.
59
-
60
- **P384 and P521** - Exist for specific compliance requirements. P384 provides ~192-bit security (NIST Category 3); P521 provides ~256-bit security (Category 5). Their API surface mirrors P256 exactly. Use only when a specification or regulatory framework demands them.
61
-
62
- ### Post-Quantum Algorithms (iOS 26+)
63
-
64
- **ML-KEM-768 / ML-KEM-1024** - FIPS 203 lattice-based key encapsulation. ML-KEM-768 targets ~AES-128 equivalent security; ML-KEM-1024 targets ~AES-192. Both support Secure Enclave hardware isolation on iOS 26+.
65
-
66
- **ML-DSA-65 / ML-DSA-87** - FIPS 204 lattice-based digital signatures. ML-DSA-65 targets ~AES-128 equivalent; ML-DSA-87 targets ~AES-192. Both support Secure Enclave on iOS 26+.
67
-
68
- **X-Wing (XWingMLKEM768X25519)** - Hybrid KEM combining ML-KEM-768 with X25519. Both algorithms must be broken to compromise the exchange. This is Apple's recommended migration path for custom protocols via HPKE.
69
-
70
- ### Selection Decision Matrix
71
-
72
- | Scenario | iOS Version | Default Choice | Rationale |
73
- | ----------------------------- | ----------- | -------------------------------------------------- | -------------------------------------------- |
74
- | Hardware-isolated keys | All | `SecureEnclave.P256.*` | Private key never leaves the coprocessor |
75
- | Software signing/agreement | All | `Curve25519.*` | Constant-time, compact, modern protocols |
76
- | FIPS/enterprise interop | 17+ | `P256` or `P384` | Aligns with legacy standards |
77
- | E2E encryption (modern) | 17+ | HPKE with `Curve25519_SHA256_ChachaPoly` | High performance, broad client support |
78
- | E2E encryption (future-proof) | 26+ | HPKE with `XWingMLKEM768X25519_SHA256_AES_GCM_256` | Hybrid PQC against harvest-now-decrypt-later |
79
- | Maximum classical security | All | `P521` | ~256-bit security; only when mandated |
80
-
81
- ### Algorithm Quick Reference
82
-
83
- | Algorithm | Security | iOS | Secure Enclave | Pub Key Size | Best For |
84
- | ----------- | -------- | --- | -------------- | ------------ | ------------------------------- |
85
- | P256 | ~128-bit | 13+ | ✅ Yes | 64 bytes | Hardware keys, NIST compliance |
86
- | P384 | ~192-bit | 13+ | ❌ No | 96 bytes | Government/compliance |
87
- | P521 | ~256-bit | 13+ | ❌ No | 132 bytes | Maximum classical security |
88
- | Curve25519 | ~128-bit | 13+ | ❌ No | 32 bytes | Modern protocols, software keys |
89
- | ML-KEM-768 | ~AES-128 | 26+ | ✅ Yes | 1,184 bytes | Key encapsulation |
90
- | ML-KEM-1024 | ~AES-192 | 26+ | ✅ Yes | 1,568 bytes | Higher-security KEM |
91
- | ML-DSA-65 | ~AES-128 | 26+ | ✅ Yes | 1,952 bytes | Post-quantum signatures |
92
- | ML-DSA-87 | ~AES-192 | 26+ | ✅ Yes | 2,592 bytes | Higher-security signatures |
93
- | X-Wing | Hybrid | 26+ | ✅ Yes | 1,216 bytes | Hybrid PQC KEM |
94
-
95
- On Apple Silicon, both P256 and Curve25519 are heavily optimized in corecrypto with hand-tuned assembly. Performance differences are negligible for most applications - Apple's NISTZ256 optimization closes the gap that Curve25519 holds in non-Apple benchmarks.
96
-
97
- ---
98
-
99
- ## Signing and Key Agreement Are Separate Type Hierarchies
100
-
101
- CryptoKit's most important design decision is splitting each curve into two non-interchangeable type families: `Signing` and `KeyAgreement`. A `P256.Signing.PrivateKey` cannot perform key agreement. A `Curve25519.KeyAgreement.PrivateKey` cannot sign. The compiler enforces this at build time. AI generators frequently conflate these, producing code that fails to compile.
102
-
103
- ### ✅ Correct: P256 key generation, signing, and verification
20
+ - [Picking a curve or algorithm](#picking-a-curve-or-algorithm)
21
+ - [Signing and key agreement are different types](#signing-and-key-agreement-are-different-types)
22
+ - [Key agreement always goes through a KDF](#key-agreement-always-goes-through-a-kdf)
23
+ - [HPKE (iOS 17+)](#hpke-ios-17)
24
+ - [Post-quantum cryptography (iOS 26+)](#post-quantum-cryptography-ios-26)
25
+ - [PEM and DER (iOS 14+)](#pem-and-der-ios-14)
26
+ - [The Secure Enclave, briefly](#the-secure-enclave-briefly)
27
+ - [RSA](#rsa)
28
+ - [Frequent mistakes](#frequent-mistakes)
29
+ - [Availability](#availability)
30
+ - [Performance and threads](#performance-and-threads)
31
+ - [Sources](#sources)
32
+ - [Summary](#summary)
33
+ - [Checklist](#checklist)
34
+
35
+ ## Picking a curve or algorithm
36
+
37
+ Two errors show up often: suggesting Curve25519 when the key must live in the
38
+ Secure Enclave, and suggesting P-256 when the goal is simple constant-time software
39
+ keys.
40
+
41
+ - **P256 (secp256r1).** The only classical curve the Secure Enclave supports, so
42
+ required for hardware keys with a biometric access control. NIST FIPS 186-5, the
43
+ widest TLS, X.509 and server interop. Raw public key 64 bytes (uncompressed x||y),
44
+ raw signature 64 bytes (r||s). PEM/DER from iOS 14.
45
+ - **Curve25519** (X25519 for agreement, Ed25519 for signing). The default for
46
+ software-only keys: fixed, rigid parameters, constant time by construction, no
47
+ point validation to forget, 32-byte public keys. Only `rawRepresentation` exists
48
+ (no PEM, DER or x963). Not available in the Secure Enclave.
49
+ - **P384** and **P521**, roughly 192-bit and 256-bit strength (NIST levels 3 and 5).
50
+ They mirror the P256 API. Pick them only because an external standard or auditor
51
+ insists.
52
+ - **ML-KEM-768** (roughly AES-128 strength) and **ML-KEM-1024** (roughly AES-192).
53
+ Lattice KEM from FIPS 203. Secure Enclave capable on iOS 26+.
54
+ - **ML-DSA-65** (roughly AES-128) and **ML-DSA-87** (roughly AES-192). Lattice
55
+ signatures from FIPS 204. Secure Enclave capable on iOS 26+.
56
+ - **X-Wing** (`XWingMLKEM768X25519`). A hybrid of ML-KEM-768 and X25519; an attacker
57
+ must break both. Apple's recommended path for migrating custom protocols, used
58
+ through HPKE.
59
+
60
+ | Need | Choose |
61
+ | --- | --- |
62
+ | Key must never leave hardware | `SecureEnclave.P256.*` |
63
+ | Software signing or agreement | `Curve25519.*` |
64
+ | FIPS or enterprise interop | `P256` or `P384` |
65
+ | End-to-end encryption, iOS 17+ | HPKE `Curve25519_SHA256_ChachaPoly` |
66
+ | End-to-end encryption resistant to harvest-now-decrypt-later, iOS 26+ | HPKE `XWingMLKEM768X25519_SHA256_AES_GCM_256` |
67
+ | Highest classical level | `P521`, only if mandated |
68
+
69
+ | Algorithm | Public key | Secure Enclave |
70
+ | --- | --- | --- |
71
+ | P256 | 64 B | yes |
72
+ | P384 | 96 B | no |
73
+ | P521 | 132 B | no |
74
+ | Curve25519 | 32 B | no |
75
+ | ML-KEM-768 | 1,184 B | yes (iOS 26) |
76
+ | ML-KEM-1024 | 1,568 B | yes (iOS 26) |
77
+ | ML-DSA-65 | 1,952 B | yes (iOS 26) |
78
+ | ML-DSA-87 | 2,592 B | yes (iOS 26) |
79
+ | X-Wing | 1,216 B | yes (iOS 26) |
80
+
81
+ Speed is not a reason to prefer one of P256 and Curve25519 on Apple Silicon;
82
+ corecrypto's NISTZ256 code makes the difference negligible.
83
+
84
+ ## Signing and key agreement are different types
85
+
86
+ Each curve has two separate families, `Signing` and `KeyAgreement`, and the compiler
87
+ will not let you mix them.
104
88
 
105
89
  ```swift
106
90
  import CryptoKit
91
+ import Foundation
107
92
 
108
- // Generate a signing key pair
109
- let signingKey = P256.Signing.PrivateKey()
110
- let verifyingKey = signingKey.publicKey // P256.Signing.PublicKey
111
-
112
- // Sign data (CryptoKit hashes internally with SHA-256)
113
- let message = Data("Transfer $100 to Alice".utf8)
114
- let signature = try signingKey.signature(for: message)
115
- // signature is P256.Signing.ECDSASignature
116
-
117
- // Verify
118
- let isValid = verifyingKey.isValidSignature(signature, for: message)
119
-
120
- // Signature serialization
121
- let derSig = signature.derRepresentation // ASN.1 DER (interoperable)
122
- let rawSig = signature.rawRepresentation // Raw r‖s concatenation (64 bytes)
123
- let restored = try P256.Signing.ECDSASignature(derRepresentation: derSig)
124
- ```
125
-
126
- For pre-hashed data (when the digest is computed externally), use `signature(for:)` with a `Digest` parameter or the `SHA256Digest` directly.
127
-
128
- ### ❌ Wrong: Mixing signing and key agreement key types
93
+ let releaseSigner = P256.Signing.PrivateKey()
94
+ let verifierKey: P256.Signing.PublicKey = releaseSigner.publicKey
129
95
 
130
- ```swift
131
- // This will NOT compile - signing keys cannot do key agreement
132
- let key = P256.Signing.PrivateKey()
133
- let shared = try key.sharedSecretFromKeyAgreement(with: otherPublicKey)
134
- // Error: P256.Signing.PrivateKey has no member 'sharedSecretFromKeyAgreement'
96
+ let artifact = Data("build 5120 checksum".utf8)
97
+ let signature = try releaseSigner.signature(for: artifact)
135
98
 
136
- // Likewise, Curve25519.KeyAgreement.PrivateKey has no .signature(for:) method
99
+ let valid = verifierKey.isValidSignature(signature, for: artifact)
100
+ let asn1 = signature.derRepresentation
101
+ let compact = signature.rawRepresentation
102
+ let decoded = try P256.Signing.ECDSASignature(derRepresentation: asn1)
137
103
  ```
138
104
 
139
- ---
105
+ `signature(for:)` hashes `Data` with SHA-256 internally and returns a
106
+ `P256.Signing.ECDSASignature`. `derRepresentation` is the ASN.1 form most servers
107
+ expect; `rawRepresentation` is the 64-byte r||s form. If you already have a digest,
108
+ pass the `SHA256Digest` (any `Digest`) to `signature(for:)` instead.
140
109
 
141
- ## Key Agreement with HKDF Derivation
110
+ Calling `sharedSecretFromKeyAgreement` on a `P256.Signing.PrivateKey`, or
111
+ `signature(for:)` on a `Curve25519.KeyAgreement.PrivateKey`, is a compile error.
112
+ Generate one key per purpose.
142
113
 
143
- The `SharedSecret` produced by ECDH is not uniformly distributed and must never be used directly as an encryption key. CryptoKit enforces this - `SharedSecret` is not directly convertible to `SymmetricKey`. The only sanctioned paths are `.hkdfDerivedSymmetricKey()` or `.x963DerivedSymmetricKey()`. Apple's documentation states explicitly: "The shared secret isn't suitable as a symmetric cryptographic key by itself."
114
+ ## Key agreement always goes through a KDF
144
115
 
145
- ### ✅ Correct: Curve25519 key agreement with HKDF derivation
116
+ An ECDH `SharedSecret` is not uniformly random and cannot be turned into a
117
+ `SymmetricKey` directly. Apple's documentation says it is not suitable as a key on
118
+ its own; the two sanctioned routes are `hkdfDerivedSymmetricKey` and
119
+ `x963DerivedSymmetricKey`.
146
120
 
147
121
  ```swift
148
- import CryptoKit
149
-
150
- // Both parties generate key agreement keys (NOT signing keys)
151
- let aliceKey = Curve25519.KeyAgreement.PrivateKey()
152
- let bobKey = Curve25519.KeyAgreement.PrivateKey()
122
+ let deviceKey = Curve25519.KeyAgreement.PrivateKey()
123
+ let serverKey = Curve25519.KeyAgreement.PrivateKey()
153
124
 
154
- // Alice computes shared secret using Bob's public key
155
- let sharedSecret = try aliceKey.sharedSecretFromKeyAgreement(
156
- with: bobKey.publicKey
157
- )
158
-
159
- // CRITICAL: Derive a symmetric key via HKDF - never use SharedSecret directly
160
- let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
125
+ let secret = try deviceKey.sharedSecretFromKeyAgreement(with: serverKey.publicKey)
126
+ let channelKey = secret.hkdfDerivedSymmetricKey(
161
127
  using: SHA256.self,
162
- salt: Data("my-app-salt".utf8),
163
- sharedInfo: Data("encryption-v1".utf8),
164
- outputByteCount: 32 // 256-bit key for AES-256 or ChaChaPoly
128
+ salt: Data("pairing-2026".utf8),
129
+ sharedInfo: Data("telemetry-channel/v1/encrypt".utf8),
130
+ outputByteCount: 32
165
131
  )
166
-
167
- // Now use the derived key for authenticated encryption
168
- let sealed = try ChaChaPoly.seal(plaintext, using: symmetricKey)
132
+ let packet = try ChaChaPoly.seal(Data("temp=21.5".utf8), using: channelKey)
169
133
  ```
170
134
 
171
- The `sharedInfo` parameter serves as protocol binding - it ensures keys derived for different purposes within the same application cannot be confused. Use distinct `sharedInfo` values for encryption keys vs authentication keys when deriving multiple subkeys.
172
-
173
- ### ❌ Wrong: Using SharedSecret directly as an encryption key
135
+ `sharedInfo` ties the key to your protocol and role. Derive separate keys with
136
+ different `sharedInfo` for encryption and for authentication.
174
137
 
175
138
  ```swift
176
- // NEVER DO THIS - SharedSecret is not uniformly distributed
177
- let sharedSecret = try aliceKey.sharedSecretFromKeyAgreement(with: bobPublicKey)
178
-
179
- // SharedSecret is NOT a SymmetricKey and cannot be used as one directly.
180
- // Its byte distribution is non-uniform (only ~2^255 of 2^256 values are
181
- // valid P-256 x-coordinates). Skipping HKDF also prevents protocol binding
182
- // and removes the salt's entropy-concentration benefit.
183
-
184
- // This forced extraction is dangerous:
185
- let insecureKey = SymmetricKey(data: sharedSecret.withUnsafeBytes { Data($0) })
186
- // ⚠️ Non-uniform key material, no domain separation, no salt
139
+ // Broken: raw secret bytes used as a key
140
+ let bad = SymmetricKey(data: secret.withUnsafeBytes { Data($0) })
187
141
  ```
188
142
 
189
- ---
190
-
191
- ## HPKE Simplifies Public-Key Encryption (iOS 17+)
192
-
193
- Before iOS 17, encrypting data for a recipient's public key required manually implementing ECIES: perform ECDH, derive a key via HKDF, encrypt with AES-GCM, and transmit the ephemeral public key alongside the ciphertext. HPKE (RFC 9180) packages this entire flow into a single API. CryptoKit supports all four RFC modes - Base, Auth, PSK, and AuthPSK - with five built-in cipher suites.
143
+ This skips extraction over non-uniform material (on P-256 roughly half of all 256-bit strings can
144
+ occur as an x-coordinate, so the bits are biased), and has no salt and no domain
145
+ separation.
194
146
 
195
- ### Built-in Cipher Suites
147
+ ## HPKE (iOS 17+)
196
148
 
197
- | Cipher Suite | KEM | KDF | AEAD | Min iOS |
198
- | ----------------------------------------- | ------------- | ----------- | ----------------- | ------- |
199
- | `.Curve25519_SHA256_ChachaPoly` | X25519 | HKDF-SHA256 | ChaCha20-Poly1305 | 17+ |
200
- | `.P256_SHA256_AES_GCM_256` | P-256 | HKDF-SHA256 | AES-GCM-256 | 17+ |
201
- | `.P384_SHA384_AES_GCM_256` | P-384 | HKDF-SHA384 | AES-GCM-256 | 17+ |
202
- | `.P521_SHA512_AES_GCM_256` | P-521 | HKDF-SHA512 | AES-GCM-256 | 17+ |
203
- | `.XWingMLKEM768X25519_SHA256_AES_GCM_256` | X-Wing hybrid | HKDF-SHA256 | AES-GCM-256 | 26+ |
149
+ Before iOS 17, encrypting to a public key meant building ECIES by hand: ECDH, HKDF,
150
+ AES-GCM, and shipping the ephemeral public key alongside. HPKE (RFC 9180) packages
151
+ that. Every mode the RFC defines is available (base, sender-authenticated, pre-shared key,
152
+ and the combination of the last two), and five suites come predefined:
204
153
 
205
- Custom suites can be constructed: `HPKE.Ciphersuite(kem: .P521_HKDF_SHA512, kdf: .HKDF_SHA512, aead: .AES_GCM_256)`.
154
+ | Suite name | KEM | KDF | AEAD | Since |
155
+ | --- | --- | --- | --- | --- |
156
+ | `.Curve25519_SHA256_ChachaPoly` | X25519 | SHA-256 HKDF | ChaCha20 with Poly1305 | 17 |
157
+ | `.P256_SHA256_AES_GCM_256` | NIST P-256 | SHA-256 HKDF | AES-GCM, 256-bit key | 17 |
158
+ | `.P384_SHA384_AES_GCM_256` | NIST P-384 | SHA-384 HKDF | AES-GCM, 256-bit key | 17 |
159
+ | `.P521_SHA512_AES_GCM_256` | NIST P-521 | SHA-512 HKDF | AES-GCM, 256-bit key | 17 |
160
+ | `.XWingMLKEM768X25519_SHA256_AES_GCM_256` | X-Wing hybrid | SHA-256 HKDF | AES-GCM, 256-bit key | 26 |
206
161
 
207
- ### ✅ Correct: HPKE encryption and decryption
162
+ Other combinations are built with `HPKE.Ciphersuite(kem:kdf:aead:)`, for example a
163
+ P-384 KEM with the SHA-384 KDF and ChaChaPoly:
164
+ `HPKE.Ciphersuite(kem: .P384_HKDF_SHA384, kdf: .HKDF_SHA384, aead: .chaChaPoly)`.
208
165
 
209
166
  ```swift
210
- import CryptoKit
211
-
212
- let ciphersuite = HPKE.Ciphersuite.Curve25519_SHA256_ChachaPoly
213
- let info = Data("MyApp-FileEncryption-v1".utf8)
214
-
215
- // Recipient generates a key pair and shares the public key
216
- let recipientPrivateKey = Curve25519.KeyAgreement.PrivateKey()
217
- let recipientPublicKey = recipientPrivateKey.publicKey
167
+ @available(iOS 17.0, macOS 14.0, *)
168
+ func exchangeMedicalNote() throws -> Data {
169
+ let suite = HPKE.Ciphersuite.Curve25519_SHA256_ChachaPoly
170
+ let clinicKey = Curve25519.KeyAgreement.PrivateKey()
171
+ let context = Data("clinic-inbox/v3".utf8)
172
+ let header = Data("patient=5521".utf8)
173
+
174
+ var outbound = try HPKE.Sender(
175
+ recipientKey: clinicKey.publicKey, ciphersuite: suite, info: context
176
+ )
177
+ let ciphertext = try outbound.seal(Data("allergy: penicillin".utf8), authenticating: header)
178
+ let encapsulated = outbound.encapsulatedKey
218
179
 
219
- // === SENDER ===
220
- // 'var' is required - seal() mutates internal nonce state
221
- var sender = try HPKE.Sender(
222
- recipientKey: recipientPublicKey,
223
- ciphersuite: ciphersuite,
224
- info: info
225
- )
226
- let ciphertext = try sender.seal(
227
- Data("Confidential document".utf8),
228
- authenticating: Data("metadata".utf8) // optional AAD
229
- )
230
- let encapsulatedKey = sender.encapsulatedKey // MUST be sent with ciphertext
231
-
232
- // === RECIPIENT ===
233
- var recipient = try HPKE.Recipient(
234
- privateKey: recipientPrivateKey,
235
- ciphersuite: ciphersuite,
236
- info: info,
237
- encapsulatedKey: encapsulatedKey // from sender
238
- )
239
- let plaintext = try recipient.open(
240
- ciphertext,
241
- authenticating: Data("metadata".utf8) // same AAD
242
- )
180
+ var inbound = try HPKE.Recipient(
181
+ privateKey: clinicKey, ciphersuite: suite, info: context,
182
+ encapsulatedKey: encapsulated
183
+ )
184
+ return try inbound.open(ciphertext, authenticating: header)
185
+ }
243
186
  ```
244
187
 
245
- ### Three Critical HPKE Details AI Generators Get Wrong
246
-
247
- 1. **The encapsulated key is not embedded in the ciphertext.** Your protocol must transmit `encapsulatedKey` alongside the ciphertext. Losing it means permanent decryption failure.
248
-
249
- 2. **`HPKE.Sender` and `HPKE.Recipient` are stateful structs that must be declared with `var`** because `seal()` and `open()` are mutating methods - they increment an internal nonce counter. Using `let` causes a compiler error.
188
+ Three things break HPKE code:
250
189
 
251
- 3. **Message ordering matters.** If the sender seals messages A then B, the recipient must open A before B. The internal counter must stay synchronized.
190
+ 1. `encapsulatedKey` is not part of the ciphertext. You must send it with the
191
+ message. Without it nothing can be decrypted, ever.
192
+ 2. `Sender` and `Recipient` are structs with state (an internal nonce counter), and
193
+ `seal` / `open` are `mutating`. Declaring them with `let` does not compile.
194
+ 3. Messages must be opened in the order they were sealed; out-of-order opens fail
195
+ because the counters disagree.
252
196
 
253
- ---
197
+ ## Post-quantum cryptography (iOS 26+)
254
198
 
255
- ## Post-Quantum Cryptography (iOS 26+)
199
+ The threat is harvest now, decrypt later: traffic recorded today is decrypted once
200
+ a large quantum computer exists. From iOS 26, `URLSession` and Network.framework
201
+ offer the hybrid `X25519MLKEM768` group in the TLS ClientHello by default.
256
202
 
257
- At WWDC 2025 (session 314, "Get ahead with quantum-secure cryptography"), Apple announced CryptoKit support for NIST's post-quantum standards. The threat model is "harvest now, decrypt later" - adversaries storing encrypted traffic today to decrypt once cryptographically relevant quantum computers exist. iOS 26 enables quantum-secure TLS by default for `URLSession` and `Network.framework`, advertising `X25519MLKEM768` in the TLS ClientHello.
203
+ CryptoKit adds five types, formally verified against the FIPS specifications, all
204
+ usable in the Secure Enclave:
258
205
 
259
- Five new types join CryptoKit, all backed by formally verified implementations proven functionally equivalent to their FIPS specifications:
206
+ | Type | Public key | Output |
207
+ | --- | --- | --- |
208
+ | `MLKEM768` | 1,184 B | ciphertext 1,088 B |
209
+ | `MLKEM1024` | 1,568 B | |
210
+ | `XWingMLKEM768X25519` (draft-connolly-cfrg-xwing-kem) | 1,216 B | encapsulation 1,120 B |
211
+ | `MLDSA65` | 1,952 B | signature 3,309 B |
212
+ | `MLDSA87` | 2,592 B | signature 4,627 B |
260
213
 
261
- | Type | Algorithm | Standard | Operation | Secure Enclave | Key/Sig Size |
262
- | --------------------- | ------------- | ----------------------------- | ------------------ | -------------- | -------------------------------- |
263
- | `MLKEM768` | ML-KEM-768 | FIPS 203 | Key encapsulation | ✅ | 1,184 B pub / 1,088 B ciphertext |
264
- | `MLKEM1024` | ML-KEM-1024 | FIPS 203 | Key encapsulation | ✅ | 1,568 B pub |
265
- | `XWingMLKEM768X25519` | X-Wing hybrid | draft-connolly-cfrg-xwing-kem | Key encapsulation | ✅ | 1,216 B pub / 1,120 B encap |
266
- | `MLDSA65` | ML-DSA-65 | FIPS 204 | Digital signatures | ✅ | 1,952 B pub / 3,309 B sig |
267
- | `MLDSA87` | ML-DSA-87 | FIPS 204 | Digital signatures | ✅ | 2,592 B pub / 4,627 B sig |
214
+ What you pay is bytes on the wire, not CPU. Compare 3,309 bytes per ML-DSA-65
215
+ signature with 64 per Ed25519 signature, and 1,184 bytes of ML-KEM-768 public key
216
+ with 32 for X25519. Timing is in the same range as the classical algorithms.
268
217
 
269
- The size cost of quantum resistance is substantial - an ML-DSA-65 signature is 3,309 bytes versus 64 bytes for Ed25519; an ML-KEM-768 public key is 1,184 bytes versus 32 bytes for X25519. But computational performance is competitive with classical algorithms.
270
-
271
- ### ✅ Correct: ML-KEM-768 key encapsulation
272
-
273
- Key encapsulation differs fundamentally from Diffie-Hellman key agreement. In ECDH, both parties contribute public keys. In KEM, only the recipient has a key pair - the sender calls `encapsulate()` on the public key, which produces both a shared secret and an opaque ciphertext that only the private key can decapsulate.
218
+ Encapsulation works differently from Diffie-Hellman: the sending side has no key
219
+ pair at all. It runs `encapsulate()` against the receiver's public key, which yields
220
+ both the shared secret and a ciphertext that has to travel to the receiver.
274
221
 
275
222
  ```swift
276
- import CryptoKit
277
-
278
- if #available(iOS 26, macOS 26, *) {
279
- // Recipient generates a key pair
280
- let privateKey = try MLKEM768.PrivateKey()
281
- let publicKey = privateKey.publicKey
282
-
283
- // Sender encapsulates (only needs recipient's public key)
284
- let encapsulation = try publicKey.encapsulate()
285
- let senderSharedSecret = encapsulation.sharedSecret // 32 bytes
286
- let encapsulatedCiphertext = encapsulation.encapsulated // 1,088 bytes
287
-
288
- // Recipient decapsulates
289
- let recipientSharedSecret = try privateKey.decapsulate(encapsulatedCiphertext)
290
-
291
- // senderSharedSecret == recipientSharedSecret
292
- // Derive a symmetric key via HKDF, as with ECDH
223
+ @available(iOS 26, macOS 26, *)
224
+ func quantumSafeSession() throws -> SymmetricKey {
225
+ let receiver = try MLKEM768.PrivateKey()
226
+
227
+ let result = try receiver.publicKey.encapsulate()
228
+ let sentOverWire = result.encapsulated // 1,088 bytes
229
+ // result.sharedSecret (32 bytes) stays with the sender
230
+
231
+ let receiverSecret = try receiver.decapsulate(sentOverWire)
232
+ return HKDF<SHA256>.deriveKey(
233
+ inputKeyMaterial: receiverSecret, info: Data("kem-session".utf8),
234
+ outputByteCount: 32
235
+ )
293
236
  }
294
- ```
295
237
 
296
- ### ✅ Correct: ML-DSA-65 signing
297
-
298
- ```swift
299
- if #available(iOS 26, macOS 26, *) {
300
- let signingKey = try MLDSA65.PrivateKey()
301
- let verifyingKey = signingKey.publicKey // 1,952 bytes
302
-
303
- let message = Data("Authenticate this payload".utf8)
304
- let signature = try signingKey.signature(for: message) // 3,309 bytes
305
-
306
- let isValid = verifyingKey.isValidSignature(
307
- signature,
308
- for: message
309
- )
238
+ @available(iOS 26, macOS 26, *)
239
+ func signFirmware(_ image: Data) throws -> Bool {
240
+ let authority = try MLDSA65.PrivateKey()
241
+ let sig = try authority.signature(for: image) // 3,309 bytes
242
+ return authority.publicKey.isValidSignature(sig, for: image)
310
243
  }
311
244
  ```
312
245
 
313
- ### ✅ Correct: Hybrid post-quantum with HPKE (recommended migration path)
246
+ Both sides end up with the same 32-byte `SymmetricKey`. As with ECDH, derive the
247
+ working key from it with HKDF and a protocol label rather than using it directly.
314
248
 
315
- Apple's recommended approach for custom protocols is to switch the HPKE cipher suite to X-Wing, which combines ML-KEM-768 with X25519 so that both algorithms must be broken to compromise the exchange:
249
+ Hybrid key exchange through HPKE is the migration Apple recommends: the classical
250
+ code above changes only in suite and key type. The encapsulated key grows from
251
+ about 32 bytes to 1,120.
316
252
 
317
253
  ```swift
318
- if #available(iOS 26, macOS 26, *) {
319
- // Quantum-secure HPKE
320
- let ciphersuite = HPKE.Ciphersuite.XWingMLKEM768X25519_SHA256_AES_GCM_256
321
- let privateKey = try XWingMLKEM768X25519.PrivateKey()
322
-
254
+ @available(iOS 26, macOS 26, *)
255
+ func hybridSeal(_ message: Data) throws -> (Data, Data) {
256
+ let suite = HPKE.Ciphersuite.XWingMLKEM768X25519_SHA256_AES_GCM_256
257
+ let mailboxKey = try XWingMLKEM768X25519.PrivateKey()
323
258
  var sender = try HPKE.Sender(
324
- recipientKey: privateKey.publicKey, // 1,216 bytes
325
- ciphersuite: ciphersuite,
326
- info: Data("quantum-secure-v1".utf8)
259
+ recipientKey: mailboxKey.publicKey, ciphersuite: suite, info: Data("mailbox".utf8)
327
260
  )
328
- let ciphertext = try sender.seal(sensitiveData)
329
- // encapsulatedKey is 1,120 bytes (vs ~32 bytes for classical X25519)
261
+ let sealed = try sender.seal(message)
262
+ return (sender.encapsulatedKey, sealed)
330
263
  }
331
264
  ```
332
265
 
333
- ### ✅ Correct: Hybrid signing (ML-DSA + ECDSA) for transition period
334
-
335
- For signatures, Apple demonstrates hybrid signatures at the application level - concatenating ML-DSA and ECDSA signatures and verifying both:
266
+ Hybrid signatures follow the same idea: sign with both `MLDSA65` and `P256.Signing`,
267
+ ship both, and accept only if both verify.
336
268
 
337
269
  ```swift
338
- if #available(iOS 26, macOS 26, *) {
339
- let pqKey = try MLDSA65.PrivateKey()
340
- let ecKey = P256.Signing.PrivateKey()
341
-
342
- let pqSig = try pqKey.signature(for: message)
343
- let ecSig = try ecKey.signature(for: message).rawRepresentation
344
- let hybridSignature = pqSig + ecSig // Concatenate both
345
-
346
- // Verify both - reject if either fails
347
- let pqValid = pqKey.publicKey.isValidSignature(pqSig, for: message)
348
- let ecValid = ecKey.publicKey.isValidSignature(
349
- try P256.Signing.ECDSASignature(rawRepresentation: ecSig), for: message
350
- )
351
- let isValid = pqValid && ecValid
270
+ @available(iOS 26, macOS 26, *)
271
+ struct DualSignature {
272
+ let pq: Data
273
+ let classical: Data
274
+
275
+ static func make(for body: Data, pqKey: MLDSA65.PrivateKey,
276
+ ecKey: P256.Signing.PrivateKey) throws -> DualSignature {
277
+ DualSignature(
278
+ pq: try pqKey.signature(for: body),
279
+ classical: try ecKey.signature(for: body).rawRepresentation
280
+ )
281
+ }
282
+
283
+ func verify(_ body: Data, pqKey: MLDSA65.PublicKey,
284
+ ecKey: P256.Signing.PublicKey) -> Bool {
285
+ guard let ecSig = try? P256.Signing.ECDSASignature(rawRepresentation: classical) else {
286
+ return false
287
+ }
288
+ return pqKey.isValidSignature(pq, for: body) && ecKey.isValidSignature(ecSig, for: body)
289
+ }
352
290
  }
353
291
  ```
354
292
 
355
- ---
356
-
357
- ## PEM and DER Interoperability (iOS 14+)
358
-
359
- CryptoKit's PEM support uses PKCS#8 for private keys (`-----BEGIN PRIVATE KEY-----`) and X.509 SubjectPublicKeyInfo for public keys (`-----BEGIN PUBLIC KEY-----`). Import also accepts SEC 1 format (`-----BEGIN EC PRIVATE KEY-----`). This enables interoperability with OpenSSL, BoringSSL, and server-side TLS libraries.
293
+ ## PEM and DER (iOS 14+)
360
294
 
361
- ### ✅ Correct: PEM key export and import
295
+ PEM private keys are PKCS#8 (`BEGIN PRIVATE KEY`); PEM public keys are X.509
296
+ SubjectPublicKeyInfo (`BEGIN PUBLIC KEY`). Import also accepts SEC 1
297
+ (`BEGIN EC PRIVATE KEY`). These interoperate with OpenSSL, BoringSSL and typical
298
+ server TLS stacks.
362
299
 
363
300
  ```swift
364
- // Generate and export
365
- let privateKey = P256.Signing.PrivateKey()
366
- let privatePEM = privateKey.pemRepresentation // PKCS#8 PEM string
367
- let publicPEM = privateKey.publicKey.pemRepresentation // X.509 SPKI PEM string
368
- let publicDER = privateKey.publicKey.derRepresentation // Binary DER Data
369
-
370
- // Import from PEM (works for P256, P384, P521 - NOT Curve25519)
371
- let imported = try P256.Signing.PrivateKey(pemRepresentation: privatePEM)
372
- let importedPub = try P256.Signing.PublicKey(derRepresentation: publicDER)
373
- ```
301
+ let tokenSigner = P384.Signing.PrivateKey()
302
+ let privatePEM = tokenSigner.pemRepresentation
303
+ let publicPEM = tokenSigner.publicKey.pemRepresentation
304
+ let publicDER = tokenSigner.publicKey.derRepresentation
374
305
 
375
- ### Key Format Reference
306
+ let restoredSigner = try P384.Signing.PrivateKey(pemRepresentation: privatePEM)
307
+ let restoredVerifier = try P384.Signing.PublicKey(derRepresentation: publicDER)
308
+ ```
376
309
 
377
- | Algorithm | Public Key Format | Private Key Format | Notes |
378
- | --------------------- | ----------------------- | ----------------------------- | --------------------------- |
379
- | P-256 / P-384 / P-521 | SPKI DER/PEM, x963, raw | PKCS#8 DER/PEM, x963, raw | Full interop from iOS 14+ |
380
- | Curve25519 | Raw 32 bytes only | Raw 32 bytes only | No PEM/DER/x963 support |
381
- | Secure Enclave P256 | Standard SPKI DER/PEM | Encrypted blob (device-bound) | Public key exports normally |
382
- | ML-KEM / ML-DSA | Raw representation | Raw representation | iOS 26+ |
310
+ | Key | Public formats | Private formats |
311
+ | --- | --- | --- |
312
+ | P256 / P384 / P521 (iOS 14+) | SPKI DER and PEM, x963, raw | PKCS#8 DER and PEM, x963, raw |
313
+ | Curve25519 | raw 32 bytes only | raw 32 bytes only |
314
+ | Secure Enclave P256 | SPKI as usual | `dataRepresentation`: an encrypted blob bound to this device |
315
+ | ML-KEM / ML-DSA (iOS 26+) | raw | raw |
383
316
 
384
- **Curve25519 keys do not support PEM/DER.** They only have `rawRepresentation` (32 bytes for both public and private). If you need to exchange Curve25519 keys with external systems, handle raw byte serialization yourself or wrap the raw bytes in a custom format.
317
+ PEM and DER apply to the NIST curves only. Exchanging Curve25519 keys with another
318
+ system means agreeing on raw 32-byte encoding yourself.
385
319
 
386
- ### Keychain Storage of CryptoKit Keys
320
+ ### Storing keys in the Keychain
387
321
 
388
- NIST curve keys (P-256/P-384/P-521) can be stored as `kSecClassKey` items in the keychain via their `SecKey` bridge. Curve25519 keys and Secure Enclave key blobs must be stored as `kSecClassGenericPassword` items using their `rawRepresentation` / `dataRepresentation`. Apple recommends implementing a `GenericPasswordConvertible` protocol for standardized conversion - see `credential-storage-patterns.md` for the full pattern.
322
+ - NIST private keys go in as `kSecClassKey` by converting to `SecKey`.
323
+ - Curve25519 keys, post-quantum keys and Secure Enclave blobs go in as
324
+ `kSecClassGenericPassword`, using `rawRepresentation` or `dataRepresentation`.
325
+ Apple's sample code wraps this in a `GenericPasswordConvertible` protocol (see
326
+ [credential-storage-patterns.md](credential-storage-patterns.md), which also shows
327
+ the add-then-update-on-duplicate save).
389
328
 
390
- **Peer / recipient public keys** received from a server or counterpart (for ECDH, HPKE, or signature verification) must also be persisted in the keychain - never in UserDefaults, plain files, or hardcoded in source. For NIST curves, store them as `kSecClassKey` with `kSecAttrKeyClass: kSecAttrKeyClassPublic`. For Curve25519 and post-quantum public keys, store the `rawRepresentation` as a `kSecClassGenericPassword` item. Use `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` for accessibility, and assign a distinct `kSecAttrApplicationTag` or `kSecAttrAccount` value (e.g., a `"peer-"` prefix) to separate received peer keys from your own key pairs. See `credential-storage-patterns.md` for the add-or-update pattern.
329
+ Public keys of peers and recipients also need to be stored safely, because swapping
330
+ one lets an attacker impersonate the peer. Keep them in the Keychain, never in
331
+ UserDefaults, files or source:
391
332
 
392
- ---
333
+ - NIST public keys as `kSecClassKey` with `kSecAttrKeyClass: kSecAttrKeyClassPublic`.
334
+ - Curve25519 and post-quantum public keys as raw bytes in a generic password item.
335
+ - Accessibility `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`.
336
+ - A distinct tag or account name, for example a `peer-` prefix, so they never
337
+ collide with your own keys.
393
338
 
394
- ## Secure Enclave Integration (Brief - See `secure-enclave.md`)
339
+ ## The Secure Enclave, briefly
395
340
 
396
- The Secure Enclave generates, stores, and operates on private keys entirely within its hardware boundary - raw key material never enters application memory.
341
+ Full coverage is in [secure-enclave.md](secure-enclave.md). A signing key there needs
342
+ `.privateKeyUsage` in its access control flags; without it key creation fails.
343
+ `SecureEnclave.isAvailable` is false in the Simulator, so guard it and test the
344
+ hardware path on a device.
397
345
 
398
346
  ```swift
399
- guard SecureEnclave.isAvailable else { return }
400
-
401
- let accessControl = SecAccessControlCreateWithFlags(
402
- nil,
403
- kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
404
- .biometryCurrentSet,
405
- nil
406
- )!
407
-
408
- // Signing key with biometric protection
409
- let seKey = try SecureEnclave.P256.Signing.PrivateKey(
410
- accessControl: accessControl
411
- )
412
- let signature = try seKey.signature(for: data)
413
-
414
- // The public key is a standard P256.Signing.PublicKey - exports normally
415
- let publicPEM = seKey.publicKey.pemRepresentation
416
- ```
417
-
418
- For classical curves, only P256 works with the Secure Enclave. On iOS 26, the Secure Enclave gains support for `SecureEnclave.MLKEM768`, `SecureEnclave.MLKEM1024`, `SecureEnclave.MLDSA65`, and `SecureEnclave.MLDSA87`.
419
-
420
- **Critical lifecycle constraint:** Secure Enclave keys are non-exportable and cryptographically bound to the specific device and OS installation. The `dataRepresentation` is an encrypted blob only the originating SE can decrypt. After iCloud backup restore to a new device, SE keys are irrecoverable. Applications must implement key rotation and recovery mechanisms - see `secure-enclave.md` for the full lifecycle pattern.
421
-
422
- ---
423
-
424
- ## Stop Using RSA for New Apple Development
347
+ enum HardwareKeyError: Error {
348
+ case enclaveUnavailable
349
+ case accessControlRejected
350
+ }
425
351
 
426
- CryptoKit does not include RSA at all. RSA requires dropping down to the Security framework's C-based `SecKey` API, which lacks type safety, automatic memory management, and modern Swift ergonomics.
352
+ func makeHardwareSigner() throws -> (SecureEnclave.P256.Signing.PrivateKey, String) {
353
+ guard SecureEnclave.isAvailable else { throw HardwareKeyError.enclaveUnavailable }
427
354
 
428
- ### ❌ Wrong: RSA when EC is available
355
+ guard let policy = SecAccessControlCreateWithFlags(
356
+ nil,
357
+ kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
358
+ [.privateKeyUsage, .biometryCurrentSet],
359
+ nil
360
+ ) else {
361
+ throw HardwareKeyError.accessControlRejected
362
+ }
429
363
 
430
- ```swift
431
- // Don't do this for new code - Security framework RSA
432
- let params: [String: Any] = [
433
- kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
434
- kSecAttrKeySizeInBits as String: 2048
435
- ]
436
- var error: Unmanaged<CFError>?
437
- let key = SecKeyCreateRandomKey(params as CFDictionary, &error)
438
- // No type safety, manual memory management, 256-byte keys, no Secure Enclave
364
+ let key = try SecureEnclave.P256.Signing.PrivateKey(accessControl: policy)
365
+ return (key, key.publicKey.pemRepresentation)
366
+ }
439
367
  ```
440
368
 
441
- ### Preferred replacement: P256 signing in CryptoKit
369
+ iOS 26 adds `SecureEnclave.MLKEM768`, `SecureEnclave.MLKEM1024`,
370
+ `SecureEnclave.MLDSA65` and `SecureEnclave.MLDSA87`.
442
371
 
443
- ```swift
444
- // ✅ CORRECT for new Apple-platform code
445
- let signingKey = P256.Signing.PrivateKey()
446
- let message = Data("message".utf8)
447
- let signature = try signingKey.signature(for: message)
448
- let isValid = signingKey.publicKey.isValidSignature(signature, for: message)
449
- ```
372
+ A Secure Enclave key belongs to one physical device and one installation of its OS. The blob can
373
+ only be unwrapped by the enclave that created it, so an iCloud restore to a new
374
+ device loses the key. Plan rotation and recovery (re-enrol a new public key with
375
+ your server) from the start.
450
376
 
451
- RSA-2048 provides only ~112-bit security with 256-byte keys and signatures. P256 achieves ~128-bit security with 32-byte private keys and 64-byte signatures - an 8× reduction in signature size with stronger security. Valid reasons to still use RSA: legacy server interoperability, X.509 certificates from CAs that mandate RSA, and JWT specifications locked to RS256.
377
+ ## RSA
452
378
 
453
- ---
379
+ CryptoKit does not implement RSA. RSA means the Security framework's `SecKey` C API
380
+ with no type safety and manual memory handling, for example
381
+ `SecKeyCreateRandomKey` with `kSecAttrKeyTypeRSA` and 2048 bits. Do not start new
382
+ designs there.
454
383
 
455
- ## Common AI-Generator Mistakes
384
+ An RSA-2048 key is rated near 112 bits, and both its modulus and each signature take
385
+ 256 bytes. P-256 gives
386
+ about 128 bits with a 32-byte private key and 64-byte signatures, 8x smaller.
456
387
 
457
- | Anti-Pattern | Risk | Fix |
458
- | -------------------------------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------- |
459
- | Using `SharedSecret` directly as encryption key | Non-uniform key material; no domain separation | Always derive via `hkdfDerivedSymmetricKey()` with salt and sharedInfo |
460
- | Mixing `Signing` and `KeyAgreement` key types | Compile error; conceptual misuse | Use the correct type hierarchy for each operation |
461
- | Missing HPKE `encapsulatedKey` in protocol | Ciphertext permanently undecryptable | Serialize and transmit `encapsulatedKey` alongside ciphertext |
462
- | Declaring `HPKE.Sender`/`Recipient` with `let` | Compile error (`seal()`/`open()` are mutating) | Declare with `var` |
463
- | Using RSA for new iOS code | Slower, larger keys, no CryptoKit/SE support | Default to ECC (P-256 or Curve25519) |
464
- | Recommending Curve25519 for Secure Enclave | Curve25519 has no SE support | Use `SecureEnclave.P256` for hardware-backed keys |
465
- | Ignoring PEM/DER format limitations for Curve25519 | Runtime crash on `.pemRepresentation` access | Use `.rawRepresentation` for Curve25519; PEM/DER for NIST curves only |
466
- | Using HPKE messages out of order | Decryption failure (nonce counter mismatch) | Open messages in the same order they were sealed |
388
+ RSA is still justified for interop with a legacy server, when a CA mandates RSA
389
+ X.509 certificates, or when a JWT profile is fixed to RS256.
467
390
 
468
- ---
391
+ ## Frequent mistakes
469
392
 
470
- ## iOS Version Requirements
393
+ | Mistake | Correction |
394
+ | --- | --- |
395
+ | Using a raw `SharedSecret` as a key | `hkdfDerivedSymmetricKey()` with salt and `sharedInfo` |
396
+ | Mixing `Signing` and `KeyAgreement` types | Compile error; generate a key of the right family |
397
+ | Not transmitting `encapsulatedKey` | The message can never be opened; send it |
398
+ | `let` for an HPKE `Sender` / `Recipient` | Compile error; use `var` |
399
+ | RSA in new code | P-256 or Curve25519 |
400
+ | Curve25519 for a Secure Enclave key | `SecureEnclave.P256` |
401
+ | Calling `.pemRepresentation` on a Curve25519 key | Not available; use `.rawRepresentation` |
402
+ | Opening HPKE messages out of order | Counter mismatch, open fails; keep order |
471
403
 
472
- | Feature | Minimum iOS | Key Notes |
473
- | ------------------------------------------------------ | ----------- | ------------------------------ |
474
- | CryptoKit core (P256, P384, P521, Curve25519, SE P256) | 13.0+ | All classical curves |
475
- | PEM/DER import/export, standalone HKDF | 14.0+ | NIST curves only |
476
- | HPKE (RFC 9180, all four modes) | 17.0+ | All key agreement types |
477
- | ML-KEM, ML-DSA, X-Wing, quantum-secure TLS | 26.0+ | Post-quantum types, SE support |
404
+ ## Availability
478
405
 
479
- Always gate post-quantum and HPKE code behind `#available` checks:
406
+ | Release | Adds |
407
+ | --- | --- |
408
+ | iOS 13 | CryptoKit: P256, P384, P521, Curve25519, Secure Enclave P256 |
409
+ | iOS 14 | PEM/DER import and export (NIST curves), standalone HKDF |
410
+ | iOS 17 | HPKE in all four modes |
411
+ | iOS 26 | ML-KEM, ML-DSA, X-Wing, quantum-secure TLS |
480
412
 
481
413
  ```swift
482
- if #available(iOS 26, macOS 26, *) {
483
- // Post-quantum code path
484
- } else if #available(iOS 17, macOS 14, *) {
485
- // Classical HPKE code path
486
- } else {
487
- // Manual ECIES fallback
414
+ func sealForRecipient(_ body: Data) throws {
415
+ if #available(iOS 26, macOS 26, *) {
416
+ // X-Wing HPKE path
417
+ } else if #available(iOS 17, macOS 14, *) {
418
+ // Classical HPKE path
419
+ } else {
420
+ // Hand-built ECIES: X25519 + HKDF + AES-GCM, ship the ephemeral public key
421
+ }
488
422
  }
489
423
  ```
490
424
 
491
- ---
492
-
493
- ## Performance and Thread Safety
494
-
495
- CryptoKit operations are CPU-bound and safe to call from any thread - the framework uses no internal locks or shared mutable state. However, key generation (especially Secure Enclave keys with biometric gates) can block for user interaction. Never run SE key operations on `@MainActor`. Use a dedicated actor or `Task.detached` for key generation and signing that may trigger biometric prompts.
496
-
497
- For bulk operations, P256 signing and verification benefit from Apple Silicon's hardware crypto acceleration. Curve25519 operations are slightly faster in raw computational benchmarks on non-Apple platforms, but Apple's NISTZ256 optimization makes the difference negligible on A-series and M-series chips.
498
-
499
- Post-quantum operations are computationally competitive with classical algorithms per Apple's WWDC 2025 presentation, but produce significantly larger outputs. Plan for the bandwidth and storage impact of 3,309-byte ML-DSA signatures and 1,184-byte ML-KEM public keys.
500
-
501
- ---
502
-
503
- ## WWDC Sessions and Documentation References
504
-
505
- - **WWDC 2019, Session 709** - "Cryptography and Your Apps" - CryptoKit introduction, curve selection, key management
506
- - **WWDC 2020** - "What's New in CryptoKit" - PEM/DER support, HKDF standalone API
507
- - **WWDC 2025, Session 314** - "Get ahead with quantum-secure cryptography" - ML-KEM, ML-DSA, X-Wing, formally verified implementations, quantum-secure TLS
508
- - [Apple CryptoKit Documentation](https://sosumi.ai/documentation/cryptokit/)
509
- - [SharedSecret Documentation](https://sosumi.ai/documentation/cryptokit/sharedsecret) - HKDF derivation requirement
510
- - [HPKE Documentation](https://sosumi.ai/documentation/cryptokit/hpke) - Sender/Recipient API
511
- - [Storing CryptoKit Keys in the Keychain](https://sosumi.ai/documentation/cryptokit/storing-cryptokit-keys-in-the-keychain) - GenericPasswordConvertible pattern
512
- - [Protecting Keys with the Secure Enclave](https://sosumi.ai/documentation/security/protecting-keys-with-the-secure-enclave)
513
- - [Quantum-Secure Cryptography in Apple Operating Systems](https://support.apple.com/guide/security/quantum-secure-cryptography-apple-devices-secc7c82e533/web)
514
-
515
- ---
516
-
517
- ## Conclusion
518
-
519
- CryptoKit's type system is its greatest feature - it prevents at compile time the most dangerous cryptographic mistakes that plague hand-rolled implementations. The framework evolved from four curve families in iOS 13 to a complete quantum-safe toolkit in iOS 26, with HPKE in iOS 17 serving as the critical bridge.
520
-
521
- For new development today: default to Curve25519 for software keys and P256 for Secure Enclave keys. Use HPKE instead of manual ECIES for public-key encryption. Always derive symmetric keys from `SharedSecret` through HKDF with protocol-specific `sharedInfo`. The post-quantum migration is deliberately simple - swap the HPKE cipher suite to `XWingMLKEM768X25519_SHA256_AES_GCM_256` and change the key type. Start inventorying custom protocols now: the harvest-now-decrypt-later window is already open.
522
-
523
- ---
524
-
525
- ## Summary Checklist
526
-
527
- 1. **Curve selection matches requirements** - P256 for Secure Enclave / NIST compliance; Curve25519 for software-only modern protocols; P384/P521 only when mandated by specification
528
- 1. **Signing and key agreement use correct type families** - `*.Signing.PrivateKey` for signatures, `*.KeyAgreement.PrivateKey` for ECDH; never attempt to cross-use
529
- 1. **SharedSecret is always derived through HKDF** - call `hkdfDerivedSymmetricKey(using:salt:sharedInfo:outputByteCount:)` with protocol-specific `sharedInfo`; never use raw shared secret bytes as a key
530
- 1. **HPKE encapsulated key is transmitted with ciphertext** - `sender.encapsulatedKey` is not embedded in the ciphertext; protocol must serialize both
531
- 1. **HPKE Sender/Recipient declared with `var`** - `seal()` and `open()` are mutating methods; `let` causes a compiler error
532
- 1. **HPKE messages opened in seal order** - internal nonce counter must stay synchronized between sender and recipient
533
- 1. **PEM/DER used only for NIST curves** - Curve25519 supports `rawRepresentation` only; attempting PEM/DER access will fail
534
- 1. **RSA avoided for new code** - use CryptoKit ECC; RSA only for legacy interop via Security framework `SecKey` API
535
- 1. **Post-quantum code gated behind `#available(iOS 26, *)`** - ML-KEM, ML-DSA, X-Wing require iOS 26+; HPKE requires iOS 17+
536
- 1. **Secure Enclave key lifecycle accounts for device migration** - SE keys are device-bound; implement rotation/recovery for backup restore scenarios
537
- 1. **Hybrid PQC strategy planned** - X-Wing HPKE for key exchange, ML-DSA + ECDSA dual signatures for signing during the transition period
538
- 1. **Peer/recipient public keys stored in keychain** - received public keys for ECDH, HPKE, or verification persisted in keychain with `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` and distinct tags; not in UserDefaults or files
425
+ ## Performance and threads
426
+
427
+ Calls into CryptoKit only burn CPU and may run on any thread concurrently; the
428
+ library keeps no shared mutable state and takes no locks. Secure Enclave key creation and signing behind biometrics
429
+ can wait on the user, so never run them on `@MainActor`; use an actor or a detached
430
+ task. Size network payloads and stored records for post-quantum material, which is one
431
+ to two orders of magnitude larger than classical keys and signatures.
432
+
433
+ ## Sources
434
+
435
+ WWDC 2019 session 709; WWDC 2020 "What's New in CryptoKit"; WWDC 2025 session 314;
436
+ Apple documentation for CryptoKit, `SharedSecret` and `HPKE`; "Storing CryptoKit Keys
437
+ in the Keychain"; "Protecting Keys with the Secure Enclave"; Apple's
438
+ "Quantum-Secure Cryptography in Apple Operating Systems".
439
+
440
+ See also [cryptokit-symmetric.md](cryptokit-symmetric.md) for the AEAD and KDF
441
+ details used above.
442
+
443
+ ## Summary
444
+
445
+ Curve25519 for software keys, P256 for the Secure Enclave. HPKE instead of
446
+ hand-rolled ECIES. Every shared secret through HKDF with a protocol-specific
447
+ `sharedInfo`. Post-quantum migration is mostly swapping the HPKE suite to X-Wing and
448
+ changing the key type, so start listing your custom protocols now.
449
+
450
+ ## Checklist
451
+
452
+ - [ ] Curve fits the requirement: P256 for the Secure Enclave or NIST interop,
453
+ Curve25519 for software, P384/P521 only when mandated.
454
+ - [ ] Correct `Signing` versus `KeyAgreement` family.
455
+ - [ ] Every `SharedSecret` goes through
456
+ `hkdfDerivedSymmetricKey(using:salt:sharedInfo:outputByteCount:)`.
457
+ - [ ] HPKE `encapsulatedKey` is transmitted.
458
+ - [ ] Both HPKE context structs are mutable variables.
459
+ - [ ] The receiver opens HPKE messages in the order the sender produced them.
460
+ - [ ] PEM or DER encoding appears only with P256, P384 and P521 keys.
461
+ - [ ] No RSA except legacy interop through `SecKey`.
462
+ - [ ] Post-quantum code behind `#available(iOS 26, *)`; HPKE behind iOS 17.
463
+ - [ ] Secure Enclave signing keys include `.privateKeyUsage`.
464
+ - [ ] Secure Enclave key lifecycle covers moving to a new device.
465
+ - [ ] Hybrid plan: X-Wing suite for encryption to a recipient, and every signature
466
+ produced twice, once with ML-DSA and once with ECDSA.
467
+ - [ ] Peer public keys in the Keychain, AfterFirstUnlockThisDeviceOnly, distinct tags.